Octibiz
Demo

Durchsucht Website und Dokumentation gemeinsam. Enter zeigt alle Treffer, Esc schließt.

Übersetzungen

Ein Plugin bringt die Übersetzungen seiner Oberfläche selbst mit. Die Übersetzungen des Backends nicht — die liegen zentral.

Als Nachlader, nicht als Verweis

localeLoaders: {
  de: () => import('./locales/de'),
  en: () => import('./locales/en'),
}

Die Dateien haben dieselbe Struktur wie eine Sprachdatei des Kerns: ein Namensraum, darunter die Schlüssel.

// locales/de.ts
export default {
  acmeShipping: {
    title: 'Sendungen',
    empty: 'Noch keine Sendung erfasst.',
    count: 'Eine Sendung | {n} Sendungen',
  },
}

Der Nachlader ist keine Stilfrage. Ein Manifest wird beim Start vollständig eingelesen. Ein direkter Verweis auf die Sprachdatei zieht damit jede Sprache jedes Plugins in das Einstiegspaket der Anwendung.

Gemessen vor der Umstellung: 651 KB komprimiert beim ersten Bildaufbau, davon der größte Teil Sprachkataloge. Jeder Anwender lud beide Sprachen, obwohl er eine benutzt. Mit Nachladern kommt beim Start nur die aktive Sprache, weitere folgen beim Umschalten.

Der ältere Schlüssel locales mit fertigen Objekten funktioniert weiter, damit bestehende Plugins nicht brechen. Für ein neues Plugin ist er die falsche Wahl.

Zusammenführen

Additiv und tief. Bei einem Schlüssel-Konflikt gewinnt der Kern, und es gibt eine Warnung. Zwei Plugins können denselben Namensraum nicht teilen, ohne sich zu stören: Wähl deinen Namensraum so eindeutig wie deinen Plugin-Schlüssel.

{ und @ sind Syntax, nicht Text

Ein Nachrichtentext ist keine Zeichenkette, sondern eine kleine Sprache:

  • {name} setzt einen Wert ein
  • | trennt Einzahl und Mehrzahl
  • @:key verweist auf einen anderen Schlüssel

Ein Hinweistext, der ein Beispiel zeigen will, wird damit zu ungültiger Syntax. Wer {{ feld }}, {SEQ:n} oder rohes JSON in eine Übersetzung schreibt, baut einen Fehler ein.

Der tückische Teil: Im Entwicklungsbetrieb ist es nur eine Konsolenmeldung. In der ausgelieferten Fassung wirft die Übersetzung, und die Ansicht rendert nicht mehr. Der Fehler fällt also erst nach dem Ausliefern auf.

Richtig ist die wörtliche Schreibweise:

export default {
  acmeShipping: {
    hinweis: "Platzhalter wie {'{{ feld }}'} werden beim Druck ersetzt.",
    muster: "Nummernkreis, zum Beispiel {'{SEQ:n}'}",
  },
}

Die Ausgabe bleibt identisch. Ein einzelnes @ maskiert das System beim Zusammenbau von selbst.

Schlüssel-Konventionen

Damit die Flächen des Kerns deine Beschriftungen finden, sind die Schlüssel vorgegeben:

FlächeSchlüssel
Navigationsgruppenav.groups.<key>
Navigationslinknav.links.<key>
Bereichareas.<key>
Einstellungs-Kachelsettings.<modul>.…
Suchtreffer-Artsearch.types.<plural>
Dashboard-BausteindashboardWidgets.w.<key>
Automatisierungs-Auslöserautomation.trigger.<schluesselInKleinCamel>
Automatisierungs-Aktionautomation.action.<schluesselInKleinCamel>

Manifeste sind Daten

Ruf im Manifest niemals die Übersetzungsfunktion auf. Es wird beim Start gelesen, lange bevor eine Sprache feststeht.

Überall, wo der Kern eine Beschriftung braucht, übergibst du den Schlüssel, nicht den Text: labelKey, nicht label.

Dasselbe gilt für Options- und Statuslisten. Wer das Label einmal auflöst und einfriert:

// Falsch: bleibt nach einem Sprachwechsel in der alten Sprache haengen.
const status = [{ label: t('acmeShipping.status.open'), value: 'open' }]

Die Anwendung schaltet die Sprache ohne Neuladen um. Ein eingefrorenes Label merkt davon nichts. Nutz stattdessen die Helfer aus @/sdk, deren Beschriftung bei jedem Zugriff frisch auflöst.

Eine neue Sprache

Eine zusätzliche Sprache braucht beide Hälften: den Beitrag der Oberfläche über das Manifest und den Sprach-Vertrag im Backend. Nur eine der beiden zu liefern ergibt eine Anwendung, die auf halbem Weg in die Ausweichsprache fällt.

Weiter

  • Oberfläche — wo die Schlüssel verwendet werden
  • Anatomie — warum Backend-Übersetzungen zentral liegen

plugin.i18n · Gilt ab Version 0.5.0