Redactor UI strings (buttons, dropdowns, placeholders, messages) are driven by language packs. English is built into the core bundle. Other languages are not shipped as ready-made files — you download the reference en.js, translate it, and load your language yourself.
Note
To translate, you can download a file with English as an example and replace words and phrases in your language.
How it works#
-
English (
en) — included inredactor.js/redactor.min.js. Works out of the box withlang: 'en'(default). -
Your language — copy the reference file
en.jsfrom the Redactor package (all default keys and English values). Translate the strings, save as e.g.de.jsorfr.js, and register it before creating editors. -
At init — set
langto your language code. Redactor merges built-in English, plugin strings,registerLangpacks, andi18noverrides into one dictionary for the instance. -
In code — use
getLang('buttons.bold')or## buttons.bold ##placeholders parsed withparseLang().
Merge order
For the active lang code, strings are merged (later wins):
- Built-in pack for that language (only
enin core) static defaultLang[lang]from enabled pluginsRedactor.registerLang(lang, …)— global registryi18n[lang]from editor init options
Missing keys fall back to English (en) automatically.
Reference file en.js#
Download en.js from your Redactor package. It lists every UI key grouped by area (buttons, image, link, table, hotkeys, …).
Workflow:
- Duplicate
en.js→de.js(or your locale). - Translate string values; keep keys unchanged.
- Load the file and call
Redactor.registerLang('de', translations). - Set
lang: 'de'on editors.
Example shape (simplified):
// de.js — after translating en.js
Redactor.registerLang('de', {
buttons: {
add: 'Hinzufügen',
bold: 'Fett',
italic: 'Kursiv',
save: 'Speichern'
},
image: {
'tab-upload': 'Hochladen',
'tab-url': 'URL'
}
});
In source development, the English tree lives in editor/config/defaultLang.js under the en key.
Change the UI language#
Script tags — load your translation file
<script src="/assets/redactor/redactor.min.js"></script>
<script src="/assets/redactor/lang/de.js"></script>
<script>
Redactor('#entry', {
lang: 'de'
});
</script>
de.js should call Redactor.registerLang('de', { … }) when it loads.
ESM — register before init
import Redactor from './redactor.esm.min.js';
import { de } from './lang/de.js';
Redactor.registerLang('de', de);
Redactor('#entry', {
lang: 'de'
});
Register a language at runtime#
Redactor.registerLang(code, data) adds or merges a global language pack. Call it before Redactor('#entry') on the same page (safe to call multiple times — packs are deep-merged).
Redactor.registerLang('sv', {
buttons: {
bold: 'Fet',
italic: 'Kursiv'
},
counter: {
words: 'Ord',
chars: 'Tecken'
}
});
Redactor('#entry', { lang: 'sv' });
All editors on the page share langRegistry. Per-editor overrides still go through i18n.
Overrides at init — i18n#
Patch individual strings without a full language file:
const app = Redactor('#entry', {
lang: 'en',
i18n: {
en: {
buttons: {
save: 'Publish'
}
}
}
});
For a custom language mixed with English fallbacks:
Redactor('#entry', {
lang: 'sv',
i18n: {
sv: {
buttons: {
bold: 'Fet',
italic: 'Kursiv'
}
}
}
});
Use i18n for small projects or to override a few labels on top of registerLang.
Language variables in plugins#
static defaultLang
Plugins ship translations per language code:
class GreetingPlugin {
static defaultLang = {
en: {
greeting: {
title: 'Greeting',
text: 'Hello from plugin!'
}
},
de: {
greeting: {
title: 'Begrüßung',
text: 'Hallo vom Plugin!'
}
}
};
start() {
this.app.toolbar.add('greeting', {
title: '## greeting.title ##',
command: 'greeting.insert'
});
}
}
When lang: 'de' is set, Redactor picks defaultLang.de from each enabled plugin and merges it into i18n.
Plugins can also register extra packs in start():
Redactor.registerLang('de', {
greeting: { title: 'Begrüßung' }
});
Getting strings in plugin code
getLang(path, fallback?) — resolve a dot-path from the active i18n pack:
const text = this.app.getLang('greeting.text');
const label = this.app.getLang('buttons.save', 'Save');
If the key is missing in the current language, Redactor falls back to English, then to fallback, then returns the path string.
parseLang(str) — replace ## key ## placeholders (used in button title, form label, placeholder):
// Config — resolved when the UI renders
title: '## greeting.title ##'
// In code
const message = this.app.parseLang('## greeting.text ##');
this.app.toast.show(this.app.parseLang('## autosave.saved ##'));
Redactor UI reads ## buttons.bold ## in buttonsObj and resolves it through parseLang automatically.
Plain strings vs placeholders
| Approach | When to use |
|---|---|
'## greeting.title ##' in button/form config |
Labels built by Redactor UI |
this.app.getLang('greeting.text') |
Strings you assign in JavaScript |
this.app.parseLang('## greeting.text ##') |
Mixed text with placeholders |
Key naming#
Keys use nested groups and dot paths:
buttons.bold
buttons.save
image.tab-upload
image.url-placeholder
link.edit-link
table.add-row-below
hotkeys.meta-b
greeting.title ← plugin namespace
Match the structure in en.js when translating. Plugin keys usually use the plugin name as the top-level group (greeting, counter, accordion, …).
Multiple editors, one language#
Register once, use everywhere:
Redactor.registerLang('de', germanPack);
Redactor('#article', { lang: 'de' });
Redactor('#summary', { lang: 'de' });
Per-editor i18n overrides apply only to that instance.
RTL and spellcheck#
Language code does not set text direction automatically. For RTL editors:
Redactor('#entry', {
lang: 'ar',
dir: 'rtl'
});
notranslate: true adds translate="no" on the editor to discourage browser translation extensions from rewriting content.