Sulu CMS 3 for multilingual company websites
A company website in several languages is more than a translated page. Every language needs its own addresses, the same content building blocks and an editorial workflow that does not force people to hunt in three places for every change. We build such websites with Sulu, a content management system based on Symfony. This website is an example in its own right: it runs on Sulu 3 in German, English and Turkish.
Why Sulu?
Sulu is a full Symfony project. Anything beyond maintaining pages – a contact form with validation, a custom controller, a connection to a CRM – we write in a framework we know well, using the same tools for testing and static analysis. There is no plugin ecosystem to learn first, and no gap between the CMS and the application.
At the same time, Sulu ships with an editing interface that understands multilingual content from the ground up. A page exists once and has its own version for each language.
Languages and addresses
Languages are defined in the webspace. German is the default language and has no prefix; English and Turkish get /en and /tr. More importantly, each language gets its own readable addresses: /leistungen, /en/services and /tr/hizmetler are the same page. Search engines find the terms people actually search for in each language, and visitors see addresses they can read.
In Sulu 3 the matching field type is called route. Anyone coming from Sulu 2 will know it as resource_locator – one of the places where older templates need adjusting.
Templates as a contract
Sulu describes page templates in XML. Every property of a template is available in the Twig template under the same name. That is exactly where the risk lies: if someone renames a field on one side, the other side breaks silently.
We therefore record in a property contract which fields exist, what type they have and how the frontend renders them. Backend, frontend and content all work against this document, and renaming only happens together.
Building blocks instead of one-off pages
- Block library: text, cards, lists, key figures, process steps, FAQ and other blocks that can be combined on any page. Every block shares the same settings for colour, spacing and alignment.
- Snippets: header and footer are maintained once per language and included everywhere.
- Excerpt: title, short description and icon for teasers come from Sulu’s excerpt data, so lists and navigation need no extra fields.
Content as code
When a website launches, we do not create the page tree, copy, legal texts, SEO data and blog posts by hand in the admin. They live as data in the project and are imported with a console command. This has several benefits: all three languages demonstrably share the same structure, automated tests check completeness and the length of meta tags, and a correction reaches every environment the same way code does. From then on, the editors take over in the admin.
Blog and search
Sulu manages blog posts as articles with a template of their own. The overview is an ordinary page backed by a dedicated controller.
Search runs on SEAL with the Loupe adapter. The index is stored as files on the server. There is no additional search service to operate or pay for, and no search query ever leaves the server.
Frontend and operations
Output is rendered with Twig. For styling we use Tailwind CSS 4, built with Vite. Tailwind scans the templates for class names, so every template change needs a fresh build. We serve fonts ourselves, which means no requests go to third-party servers.
A few quirks are worth knowing:
- Three consoles. Sulu separates an admin, a website and a generic kernel. Migrations run through the admin console, and the admin and website caches have to be cleared separately.
- MariaDB is not MySQL. The database connection has to state the MariaDB version. Otherwise Doctrine generates SQL that MariaDB rejects.
- Configuration is cached. After changing the webspace or templates, both caches need to be cleared.
Deployment uses Deployer, and importing the content is a separate step that has to be triggered on purpose.
When it pays off
Sulu is a particularly good fit when a website has several languages, is maintained by editors and is expected to grow custom features later. For a simple business-card site that nobody updates, the effort is often too high. Where the line lies is something we work out together.