Skip to content

Internationalisierung

Die Editor-Oberfläche unterstützt den Wechsel der Locale. Jedes Label, jeder Tooltip, jeder Platzhalter und jede Nachricht wird durch einen Übersetzungsschlüssel gesteuert — keine fest codierten Zeichenketten.

Dabei sind zwei Sprachen im Spiel, und sie sind nicht dasselbe. init({ locale }) legt die Sprache der Editor-Oberfläche fest. Die settings.locale einer Vorlage — das Feld „Inhaltssprache“ in den Vorlagen-Einstellungen — legt die Sprache der verfassten E-Mail fest und wird zu <html lang> in der gerenderten Ausgabe. Wer auf Deutsch arbeitet, aber eine englische Kampagne schreibt, setzt die erste auf de und die zweite auf en.

Die Schreibrichtung ist eine dritte, unabhängige Einstellung. settings.direction ("ltr" oder "rtl") ist das dir der Leinwand und das gerenderte <mjml dir>. Ungesetzt folgt sie der Inhaltssprache (ar, he, fa, ur, … → RTL). Sie folgt weder init({ locale }) noch der Host-Seite. Der Schalter „Rechts-nach-links“ in den Vorlagen-Einstellungen schreibt einen expliziten Wert; danach treibt die Locale sie nicht mehr.

Locale festlegen

Übergeben Sie die Option locale an init():

ts
import { init } from '@templatical/editor';

const editor = await init({
  container: '#editor',
  locale: 'de',
});

Integrierte Locales

CodeSprache
enEnglisch (Standard)
deDeutsch
pt-BRPortugiesisch (Brasilien)
esSpanisch
caKatalanisch
frFranzösisch
nlNiederländisch

Locale-Auflösung

Der Editor normalisiert Locale-Codes, indem er Regionssuffixe entfernt:

EingabeAufgelöst
'en'en
'en-US'en
'en-GB'en
'de-AT'de
'fr-BE'fr
'it'en (nicht unterstützt, fällt auf Englisch zurück)

Wenn die aufgelöste Locale nicht unterstützt wird, fällt der Editor auf Englisch zurück und warnt einmalig auf der Konsole — mit einer Liste der Locales, die funktioniert hätten. Stillschweigend erfolgt der Rückfall nur beim Cloud-Teil, der absichtlich weniger Locales mitbringt (siehe unten).

Asynchrones Laden

Locale-Dateien werden asynchron mit dynamischem import() geladen. Nur die aktive Locale wird in den Client gebündelt — die anderen Locale-Dateien sind nicht in Ihrem Build enthalten. Das bedeutet, dass das Wechseln von Locales zur Laufzeit eine erneute Initialisierung des Editors erfordert:

ts
async function switchLocale(newLocale: string) {
  editor.unmount();
  editor = await init({
    container: '#editor',
    locale: newLocale,
  });
}

Wie Übersetzungen funktionieren

Übersetzungen sind verschachtelte Objekte, die nach UI-Abschnitt organisiert sind:

ts
{
  blocks: {
    paragraph: 'Paragraph',
    image: 'Image',
    button: 'Button',
    // ...
  },
  toolbar: {
    duplicate: 'Duplicate',
    delete: 'Delete',
    // ...
  },
  blockSettings: {
    spacing: 'Spacing',
    padding: 'Padding',
    // ...
  },
  templateSettings: {
    layout: 'Layout',
    // ...
  },
}

Einige Zeichenketten unterstützen Platzhalter-Interpolation mit {placeholder}-Syntax:

ts
{
  header: {
    templatesUsed: '{used}/{max} templates used',
  },
}

Standardtext für Blöcke

Neue Blöcke starten mit Platzhaltertext, und dieser folgt der Locale — ein Button, der unter locale: 'de' eingefügt wird, zeigt „Hier klicken“ und nicht „Click Here“.

Welcher Locale er folgt, hängt davon ab, für wen der Text bestimmt ist:

StandardwertFolgtBegründung
Text von Titel, Absatz und Buttoninit({ locale })Hinweise an die bearbeitende Person. Sie sind zum Überschreiben gedacht und passen daher zur umgebenden Oberfläche.
Video-alt, Countdown-Einheitenlabels und Ablaufmeldungsettings.locale der VorlageDieser Text wird mit der E-Mail ausgeliefert und folgt deshalb der Sprache der E-Mail — nicht der Sprache der bearbeitenden Person.

Alles davon lässt sich mit blockDefaults überschreiben; diese Angaben gewinnen gegen beide Locales:

ts
const editor = await init({
  container: '#editor',
  locale: 'de',
  blockDefaults: {
    // Eigener Wortlaut, in beliebiger Sprache.
    button: { text: 'Jetzt kaufen' },
    paragraph: { content: '<p>Text hier eingeben</p>' },
  },
});

Zusammengeführt wird tief verschachtelt: Wird ein Feld überschrieben, bleibt der Rest lokalisiert — button: { backgroundColor: '#ff6600' } behält das übersetzte Label.

TIP

Ohne gesetzte Locale verhält sich alles wie zuvor: Die englischen Standardwerte sind mit den Werkseinstellungen identisch.

Datumsangaben

Relative Angaben („vor 5 Min.“) stammen aus Übersetzungsschlüsseln. Absolute Datums- und Zeitangaben — Einträge der Versionshistorie, Tooltips gespeicherter Blöcke, die Änderungszeit der Vorlage — werden von Intl aus init({ locale }) erzeugt und erscheinen damit in derselben Sprache wie die Oberfläche daneben statt in der Sprache des Browsers.

Die Leinwand trägt außerdem die settings.locale der Vorlage als lang-Attribut, damit Rechtschreibprüfung und Silbentrennung des Browsers den Text nach den Regeln der Sprache bewerten, in der er tatsächlich verfasst ist. Sie setzt dir aus settings.direction (aufgelöst wie oben), damit eine RTL-Host-Seite nicht in eine LTR-E-Mail durchsickert und eine LTR-Host-Seite arabischen Text nicht linksläufig belässt.

Eine neue Locale beitragen

So fügen Sie eine neue Sprache hinzu:

  1. Kopieren Sie packages/editor/src/i18n/locales/en.ts in eine neue Datei, benannt nach Ihrem Locale-Code (z. B. <locale>.ts)
  2. Übersetzen Sie alle String-Werte, behalten Sie die Schlüsselstruktur bei und annotieren Sie das Objekt mit typeof en (siehe Beispiel unten)
  3. Führen Sie pnpm run typecheck aus — durch die typeof en-Annotation schlagen fehlende, überzählige oder falsch verschachtelte Schlüssel bereits zur Compile-Zeit fehl
  4. Führen Sie pnpm run test aus, um die Platzhalter-Parität zu überprüfen — Tests prüfen, dass jedes {placeholder}-Token aus den englischen Strings auch in Ihren Übersetzungen vorkommt

Ein Registrierungsschritt ist nicht nötig. Die Liste der unterstützten Locales wird zur Build-Zeit aus den Dateien in locales/ abgeleitet — <locale>.ts ablegen genügt, und init({ locale: '<locale>' }) findet sie automatisch.

Beispielstruktur für eine neue Locale:

ts
// packages/editor/src/i18n/locales/<locale>.ts
import type en from './en';

const translations: typeof en = {
  blocks: {
    paragraph: '…',
    image: '…',
    button: '…',
    section: '…',
    divider: '…',
    spacer: '…',
    // ... all keys from en.ts
  },
  toolbar: {
    duplicate: '…',
    delete: '…',
    // ...
  },
  // ... all sections from en.ts
};

export default translations;

Cloud-Strings sind optional

Die Übersetzungen sind in zwei Teile aufgeteilt. Der OSS-Teil (locales/*.ts) deckt alles im Open-Source-Editor ab. Ein separater Cloud-Teil (locales/cloud/*.ts) deckt Funktionen ab, die nur über initCloud() verfügbar sind — KI, Kollaboration, Template-Bewertung, das Lint-Speichergatter und Plan-Limits. Den Cloud-Teil müssen Sie nicht übersetzen: Existiert locales/cloud/<locale>.ts nicht, fallen Cloud-Funktionen auf Englisch zurück, während der Rest des Editors in Ihrer Sprache erscheint.

Reichen Sie einen Pull Request mit Ihrer Übersetzungsdatei ein. Beiträge für jede Sprache sind willkommen.