Sulu CMS Internationalization: Quick Guide to Page Translations
Sulu CMS uses the sulu_content table to store page translations, linking them to sulu_page via a translations association. URLs use locale prefixes and fallback to a default locale. This guide explains how to add translations, configure routing, and avoid common pitfalls.
30 Oct 2025, 17:19 UTC

What you need to know right away
In Sulu, every page can have multiple language versions. The system stores each locale in the sulu_content table and links them to the sulu_page entity via a translations association. URLs are built with a locale prefix (e.g. /en/about) and fall back to the default locale if a translation is missing.
How Sulu stores translations
When you create a page and add a translation, Sulu writes a row to sulu_content:
id | page_id | locale | title | content | …
The page_id references sulu_page.id. Doctrine loads the translations lazily, so a page entity only pulls its translations when you access the translations collection.
Configuring locale prefixes
By default Sulu prefixes every URL with the locale. The setting lives in config/packages/sulu.yaml:
# config/packages/sulu.yaml
sulu:
routing:
locale_prefix: true
default_locale: en
Changing default_locale after content exists can hide pages that lack a translation for the new default.
Adding a translation in the Admin UI
- Open the page you want to translate.
- Click Add language in the top‑right corner.
- Choose the target locale (e.g.
fr) and fill in the translated fields. - Save. Sulu creates a new row in
sulu_contentwithlocale = 'fr'.
After saving, the page appears under /fr/about (if the page slug is about) and the language selector shows English and French.
Practical example: Adding a French translation for page ID 1
Assume a page with ID 1 exists in English. You can verify the new translation via SQL and via the browser.
# Run as a database user with SELECT rights
SELECT * FROM sulu_content WHERE page_id = 1 AND locale = 'fr';
In the browser, request https://your-site.com/fr/about. The page title should be the French translation you entered.
Common pitfalls and limits
- Missing translation tables: A fresh install does not create
sulu_contentautomatically. Runbin/console sulu:installorbin/console sulu:database:updateto create them. - Locale prefix override: Custom routing rules that remove the prefix will break language detection. Keep
locale_prefixenabled unless you handle detection manually. - Lazy loading surprises: Accessing
$page->getTranslations()triggers a database hit. In performance‑critical code, consider eager loading viaJOIN FETCHif you need many pages. - Default locale change: If you switch the default locale after content creation, pages without a translation in the new default become inaccessible until you add one.
How to verify the setup works
- Check the
sulu_contenttable for rows with the expectedlocalevalues. - Open a page in the admin UI and confirm the language selector lists all added locales.
- Browse
/en/aboutand/fr/aboutand verify that the page title and body match the translations.
When all three checks succeed, your internationalization configuration is correct.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.