Conversation
The editor language was only settable via a static `lang:` in `bolt-redactor.yaml`, so on a multi-locale Bolt install every admin saw the editor in the same hard-coded language regardless of the locale they'd chosen for the backend. Bolt already resolves a per-user backend locale (LocaleSubscriber sets the request locale from the user's `_backend_locale`); the editor just never consulted it. - RedactorConfig now sets `lang` to the current request locale (RequestStack) after the config merge, so the toolbar always follows each user's Bolt locale. It is intentionally not overridable — the language is driven solely by the user's Bolt admin locale. - redactor_includes() auto-loads the matching `assets/redactor/langs/<code>.js` for the resolved locale, so users no longer add it to `includes` manually. English is built in; locales without a shipped translation are skipped and Redactor falls back to English on its own. - Document the behaviour in the README.
The formattingAdd 'small' entry used a hard-coded 'Small' title, so it showed English in every locale. Switch it to the '## small ##' i18n placeholder and add a 'small' entry to every langs/<locale>.js. Also always load langs/en.js (previously skipped for English), so its normalized table overrides redactor.min.js' built-in 'en' and provides the new 'small' label.
resolveLocale() set lang to the raw request locale even when no matching langs/<code>.js is shipped, while redactor_includes() skipped the missing file. This leaves the config pointing at a language table that was never loaded, which can leave the toolbar unlocalized or broken. Resolve the locale against the actually-shipped language file so lang and the loaded file always agree, falling back to English otherwise.
Audit of assets/redactor/langs against the key set actually used by redactor.min.js and the bundled plugins. The English file and the file structures were already correct (identical key order, comments and indentation everywhere); the values needed work. Mistranslations, e.g.: hu unorderedlist "Rendezett lista" (ordered) -> "Rendezetlen lista" hu undo "Kibont" (unfold) -> "Visszavonás" da deleted "Slettet" (deleted) -> "Gennemstreget" fi deleted "Poistettu" (deleted) -> "Yliviivaus" sv indent/outdent "utdrag" (excerpt) -> "indrag" hr horizontalrule "Redak" (row of text) -> "Crta" hu/fa/pl bulletslist "Golyók"/"گلولهها"/"Kule" (ammunition) it file "Documento" -> "File" fr image "Visuel" -> "Image" no lists "Punkt" -> "Lister" es quote "Citar" (verb) -> "Cita" Rendering bug: fi filename was "Nimi (valinnainen)", which rendered as "Nimi (valinnainen) (valinnainen)" because Redactor appends the separate `optional` key in parentheses. Also fixed: "window" instead of "tab" in link-in-new-tab (sk, sv, tr, ru); ungrammatical strings (hu upload-label, pt_br insert-row-above/below); spelling (de Hintergrundsfarbe, de Schriftgrösse, tr editorü, fr Editeur, pt_br Subtitulo); broken accessibility-help-label screen-reader text in nine locales (nl "RTF editor", hr, no, cs, da, pt_br, tr, pl, it); title/heading and cancel/undo collisions; and per-file inconsistencies in link terminology, capitalization, verb mood and *-abbr formatting. ar, en, ja and ko needed no changes. File modes normalized to 644.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Locale normalization, English base loading, and asset synchronization remain unresolved.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Localizes the Redactor editor using Bolt’s backend locale and expands bundled translation coverage.
Changes:
- Resolves and loads locale-specific Redactor language files.
- Localizes format and plugin labels.
- Normalizes bundled translations and documents locale behavior.
| File | Description |
|---|---|
src/TwigExtension.php |
Generates locale script includes. |
src/RedactorConfig.php |
Resolves the active locale. |
README.md |
Documents localization behavior. |
config/services.yaml |
Configures required dependencies. |
config/config.yaml |
Localizes the small format label. |
assets/redactor/plugins/properties/properties.min.js |
Localizes property labels. |
assets/redactor/plugins/inlinestyle/inlinestyle.min.js |
Localizes inline-style labels. |
assets/redactor/langs/zh_tw.js |
Updates Traditional Chinese translations. |
assets/redactor/langs/zh_cn.js |
Updates Simplified Chinese translations. |
assets/redactor/langs/tr.js |
Updates Turkish translations. |
assets/redactor/langs/sv.js |
Updates Swedish translations. |
assets/redactor/langs/sl.js |
Updates Slovenian translations. |
assets/redactor/langs/sk.js |
Updates Slovak translations. |
assets/redactor/langs/ru.js |
Updates Russian translations. |
assets/redactor/langs/ro.js |
Updates Romanian translations. |
assets/redactor/langs/pt_br.js |
Updates Brazilian Portuguese translations. |
assets/redactor/langs/pl.js |
Updates Polish translations. |
assets/redactor/langs/no.js |
Updates Norwegian translations. |
assets/redactor/langs/nl.js |
Updates Dutch translations. |
assets/redactor/langs/ko.js |
Updates Korean translations. |
assets/redactor/langs/ja.js |
Updates Japanese translations. |
assets/redactor/langs/it.js |
Updates Italian translations. |
assets/redactor/langs/hu.js |
Updates Hungarian translations. |
assets/redactor/langs/hr.js |
Updates Croatian translations. |
assets/redactor/langs/he.js |
Updates Hebrew translations. |
assets/redactor/langs/fr.js |
Updates French translations. |
assets/redactor/langs/fi.js |
Updates Finnish translations. |
assets/redactor/langs/fa.js |
Updates Persian translations. |
assets/redactor/langs/es.js |
Updates Spanish translations. |
assets/redactor/langs/en.js |
Normalizes English translations. |
assets/redactor/langs/de.js |
Updates German translations. |
assets/redactor/langs/da.js |
Updates Danish translations. |
assets/redactor/langs/cs.js |
Updates Czech translations. |
assets/redactor/langs/ar.js |
Updates Arabic translations. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| $locale = $request?->getLocale() ?: 'en'; | ||
|
|
||
| return $this->hasLangFile($locale) ? $locale : 'en'; |
There was a problem hiding this comment.
This comment is valid, but needs to be solved in the TwigExtension instead.
bobvandevijver
left a comment
There was a problem hiding this comment.
Couple of things need to be adjusted, as well as the styling issues.
| private function hasLangFile(string $locale): bool | ||
| { | ||
| if ($locale === '') { | ||
| return false; | ||
| } | ||
|
|
||
| $path = sprintf('%s/%s/assets/redactor/langs/%s.js', $this->projectDir, $this->publicFolder, $locale); | ||
|
|
||
| return is_file($path); | ||
| } |
There was a problem hiding this comment.
There is no need to check for file existence here, as that is also done before the script is included.
| // RedactorConfig::resolveLocale), so the toolbar is localized without the | ||
| // user having to add it to `includes` manually. This includes English: | ||
| // redactor.min.js ships a built-in `en` table, but langs/en.js overrides it | ||
| // with our normalized set (e.g. the `small` format label), so we load it too. |
There was a problem hiding this comment.
This comment mentions that the en file is always loaded, but I am not seeing that here.
| $locale = $request?->getLocale() ?: 'en'; | ||
|
|
||
| return $this->hasLangFile($locale) ? $locale : 'en'; |
There was a problem hiding this comment.
This comment is valid, but needs to be solved in the TwigExtension instead.
| } | ||
|
|
||
| $relative = sprintf('/assets/redactor/langs/%s.js', $lang); | ||
| $absolute = $this->projectDir . '/' . $this->publicFolder . $relative; |
There was a problem hiding this comment.
Please use Path::join instead
There was a problem hiding this comment.
You can also use this to prevent directory traversal: the lang file needs to be contained in the
Path::join($this->projectDir, $this->publicFolder, 'assets/redactor/langs');
| return ''; | ||
| } | ||
|
|
||
| return sprintf('<script src="%s"></script>', $relative) . "\n"; |
There was a problem hiding this comment.
| return sprintf('<script src="%s"></script>', $relative) . "\n"; | |
| return sprintf('<script src="%s"></script>', $relative); |
No need for the newline

Summary
This MR makes the Redactor editor UI follow the current Bolt backend locale and normalizes bundled Redactor translation files so plugin and format labels can be localized consistently.
Changes
langoption from the current request locale resolved by Bolt.assets/redactor/langs/<code>.jsfile fromredactor_includes().langs/en.jstoo, so its normalized table overrides the built-in English shipped inredactor.min.js; unsupported locales still fall back to Redactor's built-in English when no file exists.smallformat label: switch theformattingAddentry from a hard-coded'Small'title to the## small ##i18n placeholder and add asmallkey to every bundledlangs/<code>.js(previously it read "Small" in every locale, and empty under English).inlinestyleandpropertiesplugins to read more labels through Redactor's language system.Testing
bolt-cms-redactor-localized.mp4