Stellen Sie Ihre Frage und erhalten Sie einen Resümee des Dokuments, indem Sie diese Seite und den AI-Anbieter Ihrer Wahl referenzieren
Der Inhalt dieser Seite wurde mit einer KI übersetzt.
Den englischen Originaltext ansehenWenn Sie eine Idee haben, um diese Dokumentation zu verbessern, zögern Sie bitte nicht, durch das Einreichen eines Pull-Requests auf GitHub beizutragen.
GitHub-Link zur DokumentationMarkdown des Dokuments in die Zwischenablage kopieren
ICU Message Format: Syntax und typische Stolpersteine
ICU MessageFormat ist eine String-Syntax, mit der eine Übersetzung ihre eigene Verzweigungslogik enthalten kann: Plurale, geschlechtsspezifische Formen sowie Zahlen- und Datumsformatierungen. Der Grundgedanke ist, dass Grammatik in die Hände des Übersetzers gehört und nicht in den Code eines Entwicklers, der if (count === 1) schreibt. Dieser Artikel behandelt die Syntax, die sprachabhängigen Besonderheiten, an denen naive Implementierungen scheitern, und wie das JS-Ökosystem damit umgeht.
Inhaltsverzeichnis
Das Problem, ganz konkret
Hier ist der Code, den fast jeder Entwickler zuerst schreibt:
Kopieren Sie den Code in die Zwischenablage
Das funktioniert im Englischen, scheitert jedoch in den meisten anderen Sprachen:
- Russisch und Polnisch benötigen drei oder vier Formen, nicht nur zwei.
- Japanisch benötigt nur eine, und das angehängte Leerzeichen ist fehl am Platz.
- Arabisch benötigt sechs Formen, und die Zahl selbst sollte im Zahlensystem des jeweiligen Locales dargestellt werden.
- Französisch verlangt vor bestimmten Satzzeichen ein geschütztes Leerzeichen, das durch
+ " "zerstört wird.
Das grundlegende Problem besteht darin, dass der Satz in Fragmente zerlegt wurde. Ein Übersetzer sieht item und items ohne Kontext und kann die Satzstellung nicht anpassen. ICU MessageFormat löst dies, indem der gesamte Satz als zusammenhängender String erhalten bleibt und dem Übersetzer Verzweigungsoperatoren bereitgestellt werden.
Einfache Argumente
Die kleinste Einheit ist ein Platzhalter in einfachen geschweiften Klammern:
Kopieren Sie den Code in die Zwischenablage
Wird beim Formatieren { name: "Alice" } übergeben, erhält man Hello, Alice!. Geschweifte Klammern sind die einzigen Sonderzeichen. Um eine wörtliche geschweifte Klammer auszugeben, wird sie in einfache Anführungszeichen gesetzt: '{'.
Das ist bereits das gesamte Konzept der "Interpolation". Alles andere in ICU baut darauf auf.
Plural
plural wählt einen Zweig basierend auf einem numerischen Wert aus:
Kopieren Sie den Code in die Zwischenablage
Drei wichtige Punkte:
#wird durch den formatierten Wert voncountersetzt, passend zum Locale formatiert. So wird1234inen-USzu1,234und inde-DEzu1.234.otherist zwingend erforderlich. Jede ICU-Implementierung wirft einen Fehler oder schlägt bei der Validierung fehl, wennotherfehlt. Es dient als Fallback, wenn keine Kategorie zutrifft.=0,=1, … treffen auf exakte Werte zu und werden vor den CLDR-Kategorien geprüft. Nutzen Sie diese für spezifische Texte ("Keine Nachrichten"), nicht als Ersatz fürone.
Kopieren Sie den Code in die Zwischenablage
offset
offset:n zieht n vom Wert ab, bevor sowohl die Kategorieauswahl als auch die Ersetzung von # erfolgt. Dies dient Mustern wie "Alice und 3 anderen Personen gefällt das":
Kopieren Sie den Code in die Zwischenablage
Bei count: 4 rendert # den Wert 3. offset ist nützlich, wird aber je nach Laufzeitumgebung unterschiedlich gut unterstützt. Prüfen Sie dies vorab in Ihrer Umgebung.
Pluralkategorien sind sprachabhängig
Hier passieren die meisten Fehler. Die Kategorienamen zero, one, two, few, many, other sind keine universellen Platzhalter für jede Sprache. Jedes Locale verwendet eine Teilmenge, die durch die CLDR-Pluralregeln definiert ist, und diese Regeln folgen grammatikalischen Prinzipien, nicht rein numerischer Intuition.
Tabelle in einem Modal öffnen, um alle Daten übersichtlich anzuzeigen
| Sprache | Tag | Verwendete Kategorien | Anzahl |
|---|---|---|---|
| Japanisch | ja | other | 1 |
| Chinesisch | zh | other | 1 |
| Englisch | en | one, other | 2 |
| Deutsch | de | one, other | 2 |
| Französisch | fr | one, many, other | 3 |
| Tschechisch | cs | one, few, many, other | 4 |
| Polnisch | pl | one, few, many, other | 4 |
| Russisch | ru | one, few, many, other | 4 |
| Arabisch | ar | zero, one, two, few, many, other | 6 |
| Walisisch | cy | zero, one, two, few, many, other | 6 |
Zwei oft überraschende Konsequenzen:
onebedeutet nicht zwangsläufig "1". Im Russischen decktoneZahlen wie 1, 21, 31, 101 ab: jede Zahl, die auf 1 endet, außer jene auf 11. Im Französischen fällt0unterone.- Das Hinzufügen einer Kategorie zur englischen Quelle bewirkt nichts. Die englische Vorlage benötigt lediglich
oneundother. Die polnische Übersetzung verlangt vier Zweige, und diese Struktur gehört in den polnischen String, nicht in den englischen. Jedes Format, das alle Locales in dieselbe Schlüsselstruktur zwingt, führt hier zu Konflikten.
Das tatsächliche Verhalten einer Laufzeitumgebung lässt sich direkt testen:
Kopieren Sie den Code in die Zwischenablage
Intl.PluralRules stellt CLDR-Daten in allen modernen Browsern und in Node bereit. Bibliotheken mit CLDR-Unterstützung rufen intern fast immer diese API auf.
select und selectordinal
select verzweigt basierend auf einem beliebigen String: einem Geschlecht, einer Rolle, einem Status oder einem Tarifmodell.
Kopieren Sie den Code in die Zwischenablage
Schlüssel werden exakt verglichen, und other ist auch hier obligatorisch. select ist das richtige Werkzeug, sobald der Satzbau von einem Enum-Wert abhängt, da Sprachen sich darin unterscheiden, welche Enums grammatikalische Auswirkungen haben.
selectordinal funktioniert analog zu plural, nutzt jedoch die ordinalen Pluralregeln, die sich von den kardinalen unterscheiden:
Kopieren Sie den Code in die Zwischenablage
Das Englische nutzt vier ordinale Kategorien (1st, 2nd, 3rd, 4th), obwohl es nur zwei kardinale kennt. Genau wegen dieser Asymmetrie sind beide Operatoren getrennt.
Zahlen-, Datums- und Zeitargumente
ICU kann interpolierte Werte direkt formatieren:
Kopieren Sie den Code in die Zwischenablage
Der moderne Standard ist das Skeleton, eingeführt mit ICU 60 und gekennzeichnet durch das Präfix ::. Skeletons sind wesentlich ausdrucksstärker als herkömmliche Bezeichner:
Kopieren Sie den Code in die Zwischenablage
Die Unterstützung von Skeletons variiert im Ökosystem. FormatJS unterstützt sie vollständig, während andere Runtimes nur die herkömmlichen Formate wie number, currency oder date, long akzeptieren. Prüfen Sie die Unterstützung von :: in Ihrer Zielumgebung.
Schachtelung und Lesbarkeit
ICU ist modular aufgebaut. Ein Plural-Zweig kann ein Select enthalten, das wiederum ein weiteres Plural enthalten kann:
Kopieren Sie den Code in die Zwischenablage
Dies ist das klassische ICU-Beispiel und zugleich das beste Argument gegen übermäßige Schachtelung. Ab zwei Ebenen schleichen sich bei Übersetzern leicht Klammerfehler ein, und TMS-Editoren stoßen an ihre Grenzen. Schachteln Sie maximal zwei Ebenen. Wird eine dritte nötig, teilen Sie den Satz besser in zwei Meldungen auf.
Wie JS-Bibliotheken mit ICU umgehen
Tabelle in einem Modal öffnen, um alle Daten übersichtlich anzuzeigen
| Bibliothek | ICU-Unterstützung | Was tatsächlich geschrieben wird |
|---|---|---|
| react-intl (FormatJS) | Nativ, vollständig | ICU-Strings, inklusive Skeletons und Rich-Text-Tags |
| next-intl | Nativ | ICU-Strings über intl-messageformat von FormatJS |
| i18next | Plugin erforderlich | Suffix-Schlüssel key_one / key_other und {{name}}; ICU via i18next-icu |
| vue-i18n | Partiell / proprietär | {name}-Interpolation und durch Pipes getrennte Pluralzweige |
Angular ($localize) | Teilmenge | ICU plural / select in Templates, exportiert nach XLIFF |
Einige Klarstellungen zur Tabelle:
- Die Standard-Syntax von i18next ist kein ICU, was kein Nachteil sein muss. Suffix-Schlüssel (
item_one,item_few) bildenIntl.PluralRules-Kategorien ab und lassen sich in flachem JSON oft leichter bearbeiten.selectund verschachtelte Logik fehlen jedoch, sodass man entwederi18next-icubenötigt oder die Logik im Code abbildet. - Die Pipe-Plurale von vue-i18n verwenden standardmäßig eine regellose Zuordnung pro Locale anstelle echter CLDR-Kategorien. Das funktioniert, legt die Pluralregel jedoch in die App-Konfiguration statt in die Daten.
- FormatJS ist die Referenz im JavaScript-Bereich. Spricht man von "ICU MessageFormat" in JS, ist meist die FormatJS-Spezifikation gemeint.
- Volle ICU-Unterstützung verursacht Bundle-Kosten. Der Parser und das Skeleton-Handling fügen rund 10 KB komprimiertes JavaScript hinzu. Siehe warum ICU nicht für JavaScript gemacht ist.
Wie Intlayer das Problem löst
Intlayer verzichtet auf eine String-DSL. Verzweigungsoperatoren sind typisierte Funktionen in Inhaltsdeklarationsdateien, sodass jedes Locale nur die Kategorien definiert, die seine Grammatik erfordert:
Kopieren Sie den Code in die Zwischenablage
Kopieren Sie den Code in die Zwischenablage
Die Zuordnung zu ICU-Konzepten ist direkt:
Tabelle in einem Modal öffnen, um alle Daten übersichtlich anzuzeigen
| ICU-Konstrukt | Intlayer |
|---|---|
{name} | insert("Hello {{name}}") oder automatische Erkennung |
{count, plural, …} | plural({ one, few, many, other }) |
{value, select, …} | select({ draft, published, fallback }) |
Geschlechterzweig in select | gender({ male, female, fallback }) |
Boolescher Zweig in select | cond({ true, false }) |
| Numerische Bereiche (Nicht-CLDR) | enu({ "0": …, ">5": …, fallback: … }) |
{n, number, ::currency/EUR} | useCurrency()(1234.5, { currency: "EUR" }) |
plural delegiert die Kategorieauswahl an Intl.PluralRules, wodurch die oben genannte CLDR-Tabelle unverändert greift. Formatierungen bleiben getrennt: Zahlen, Daten, Währungen und Listen werden über Formatierungs-Hooks verarbeitet, anstatt in der Nachricht fest verdrahtet zu sein.
Einschränkungen im Überblick:
- Intlayer benötigt einen Build-Schritt: Der Compiler extrahiert Deklarationen zur Build-Zeit. Wer reines JSON zur Laufzeit laden möchte, nutzt ein anderes Modell.
pluralkann derzeit kein verschachteltest()in seinen Zweigen enthalten: Man schachteltpluralinnerhalb vont(), nicht umgekehrt.- Das Ökosystem ist jünger als das von i18next, mit weniger fertigen TMS-Integrationen und StackOverflow-Antworten.
Wer aus einer Codebasis mit vorhandenen ICU-Strings migriert, kann den react-intl-Kompatibilitätsadapter nutzen. Dieser verarbeitet plural, select, selectordinal, # sowie die klassischen Argumente number, date und time. Skeletons und offset: werden von diesem Resolver nicht abgedeckt. Der i18next-Adapter löst die Suffix-Form (key_one, key_male) wiederum über Intl.PluralRules auf.
Typische Fehler
- Plurallogik in JS fest codieren.
count === 1 ? a : berzeugt bei 8 von 10 Sprachen in der obigen Tabelle fehlerhafte Ausgaben. Ist der ternäre Operator erst einmal im Code, kann kein Übersetzer mehr eingreifen. - Übersetzte Fragmente aneinanderhängen. Wortstellung, Beugung und Satzzeichenabstände sind vom Locale abhängig. Belassen Sie Sätze stets im Ganzen.
otherweglassen. Dies ist eine Vorgabe der Spezifikation, keine optionale Konvention. Die meisten Parser verwerfen die Nachricht; andere rendern schlicht gar nichts.- Annehmen, dass Kategorien allgemeingültig sind. Dass eine englische Quelle
oneundothernutzt, bedeutet nicht, dass die polnische Datei nur zwei Zweige hat. Jedes Locale muss seine eigenen Zweige deklarieren dürfen. Siehe Inhaltsdeklaration pro Locale. =1stattoneverwenden.=1trifft ausschließlich auf den exakten Wert 1 zu. Im Russischen benötigt 21 die Kategorieone, für die=1niemals greifen würde.#außerhalb eines Pluralzweigs platzieren. Es hat nur innerhalb vonpluraloderselectordinaleine Sonderbedeutung. Überall sonst bleibt es ein simples Rautezeichen.- Vergessen, dass
#bereits formatiert ist. Wenn die unformatierte Zahl benötigt wird, sollte das Argument namentlich interpoliert werden.
Weiterführende Links
Kommentare
Noch keine Kommentare. Seien Sie der Erste, der seine Gedanken teilt.
