Languages

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.

Download en.js

How it works#

  1. English (en) — included in redactor.js / redactor.min.js. Works out of the box with lang: 'en' (default).

  2. Your language — copy the reference file en.js from the Redactor package (all default keys and English values). Translate the strings, save as e.g. de.js or fr.js, and register it before creating editors.

  3. At init — set lang to your language code. Redactor merges built-in English, plugin strings, registerLang packs, and i18n overrides into one dictionary for the instance.

  4. In code — use getLang('buttons.bold') or ## buttons.bold ## placeholders parsed with parseLang().

Merge order

For the active lang code, strings are merged (later wins):

  1. Built-in pack for that language (only en in core)
  2. static defaultLang[lang] from enabled plugins
  3. Redactor.registerLang(lang, …) — global registry
  4. i18n[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:

  1. Duplicate en.js → de.js (or your locale).
  2. Translate string values; keep keys unchanged.
  3. Load the file and call Redactor.registerLang('de', translations).
  4. 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.