Formate
Die acht unterstützten Formate, wie jedes seine Dateien liest und schreibt, und die Garantie der Dokumentreihenfolge der Keys.
Maschinell übersetzte Seite
verbatra arbeitet über einen Format-Adapter auf deinen Locale-Dateien. Jeder Adapter liest eine Datei in eine formatneutrale Form, auf der Vergleich, Hashing und Integritätsprüfungen laufen, und schreibt sie dann in der ursprünglichen Form der Datei zurück. Du wählst das Format über das format-Feld in deiner Konfiguration. Acht werden unterstützt: vier JSON-Varianten plus XLIFF, YAML, ARB und Java/Spring-Properties.
Die vier JSON-Formate
format | Für | Platzhalter | Plurale | ICU |
|---|---|---|---|---|
i18next-json | i18next | {{name}} und $t(...)-Verschachtelungsreferenzen | CLDR-Plural-Suffix am Key | nein |
vue-i18n-json | vue-i18n | {name}, {0} | Pipe im Wert | nein |
next-intl-json | next-intl | ICU-Argument- und Tag-Namen | ICU plural oder selectordinal | ja |
ngx-translate-json | ngx-translate | {{name}} | keine | nein |
Alle vier lesen verschachtelte JSON-Objekte mit String-Blättern. Die Unterschiede liegen in der Nachrichtensyntax:
- i18next nutzt
{{double-brace}}-Interpolation und leitet den Plural aus dem CLDR-Suffix am Key ab (_zero,_one,_two,_few,_many,_other). Zusätzlich extrahiert und schützt es$t()-Verschachtelungsreferenzen (die den Inhalt eines anderen Keys in den Wert einfügen), zum Beispiel$t(common.foo)und$t(common.foo, { options }), als Platzhalter, sodass eine Übersetzung, die eine davon verwirft oder verändert, die Integritätsprüfung nicht besteht. Zwei Einschränkungen: Verschachtelte Klammern innerhalb der$t()-Optionen werden nicht unterstützt, und nur das Standard-Präfix$t(wird erkannt. - vue-i18n nutzt Einfach-Klammer-Tokens
{name}und{0}und leitet den Plural aus einer Pipe im Wert ab. - next-intl-Werte sind ICU MessageFormat: Die Platzhalter sind die ICU-Argument- und Rich-Text-Tag-Namen, der Plural folgt einem ICU-plural- oder selectordinal-Argument, und der ICU-Körper wird wortgetreu durch die Pipeline getragen. Ein Wert, der nicht als ICU parst, wird als ungültig gemeldet und übersprungen, nie geworfen.
- ngx-translate teilt i18nexts
{{double-brace}}-Interpolation, hat aber kein$t()-Nesting, keinen eingebauten Plural und kein ICU. Seine Dateien dürfen flach (gepunktete Keys) oder verschachtelt sein, und verbatra erhält beim Schreiben den Stil, den die Datei verwendet; eine neue Zieldatei wird verschachtelt geschrieben. Einzigartig: Eine Datei, die flache gepunktete Keys mit verschachtelten Objekten mischt, lässt das Lesen mitMIXED_STRUCTUREfehlschlagen, weil so eine Datei mehrdeutig statt erratbar ist.
XLIFF
Das xliff-Format deckt .xlf- und .xliff-Dateien ab, XLIFF 1.2 (file/body/trans-unit) und 2.0 (file/unit/segment). Anders als die Baum-Formate ist ein XLIFF-Dokument eine flache Liste von Trans-Units.
- Keys. Jeder Eintrag wird über die
idseiner Trans-Unit identifiziert, mitresnameals Rückfallebene. Eine Unit ohne beides fällt auf einen Positions-Key zurück, der sich verschiebt, wenn frühere Units hinzukommen oder wegfallen, gib also jeder Unit eine stabileid. Zwei Units, die zum selben Key auflösen (typischerweise eine doppelteid), lassen das Lesen mitINVALID_STRUCTUREfehlschlagen, statt still eine zu behalten. - Werte. Der Wert kommt aus
<target>, wenn vorhanden, sonst aus<source>. - Schreiben aktualisiert Targets an Ort und Stelle. verbatra schreibt jeden Wert in sein
<target>und lässt<source>, Attribute und<note>-Elemente unangetastet, damit das Dokument den Roundtrip übersteht. Weil eine flache Key/Wert-Zuordnung kein XLIFF-Dokument rekonstruieren kann, muss die Zieldatei bereits existieren: verbatra aktualisiert Targets in einer vorbereiteten Zieldatei (der Standard-XLIFF-Workflow) und legt keine fehlende an; ein fehlendes Ziel schlägt mitINVALID_STRUCTUREfehl. - Inline-Markup. Inline-Platzhalterelemente (
x,g,bx,ex,ph,it,mrk) und Einfach-Klammer-{name}-Interpolation werden als Platzhalter extrahiert und über die Übersetzung hinweg geschützt. Beim Schreiben überleben nur diese per Allowlist zugelassenen Elemente (mit ihren eigenen minimalen, nicht ausführbaren Attributen) als lebendiges Markup in<target>; alles andere in einem übersetzten Wert, einschließlich eines unerwarteten Elements oder eines Namespace-Konflikts, wird stattdessen als reiner Text geschrieben. - Notes als Kontext. Die
<note>einer Trans-Unit (in 2.0 die<notes><note>der Unit, geteilt von jedem Segment der Unit) wird als Entwicklerkontext gelesen: Der Inhalt erreicht den Provider als Kontext zur Begriffsklärung und erscheint in derContext-Spalte einer exportierten Arbeitsmappe. Er ist schreibgeschützt und wird nie zurückgeschrieben.
YAML
Das yaml-Format deckt .yml- und .yaml-Dateien ab: ein verschachtelter Baum in YAML-Syntax, dieselbe Form wie eine verschachtelte JSON-Datei, verarbeitet von derselben Baum-Pipeline. Es nimmt i18next-kompatible {{double-brace}}-Interpolation an und erkennt nur über die Dateiendung.
- YAML-Kommentare überstehen einen Schreibvorgang nicht, so wie JSON kein Kommentarkonzept hat.
- Skalare Nicht-String-Keys behalten ihre String-Form (
1:wird als"1"gelesen,true:als"true"). - Ein zusammengesetzter Key (eine Map oder Sequenz als Mapping-Key) hat keine treue String-Form, das Lesen schlägt also mit
INVALID_STRUCTUREfehl, statt ihn still zu Text zu verflachen. - Fehlerhafte Syntax wird als
INVALID_YAMLgemeldet, und die Anker-Alias-Expansion ist begrenzt, ein bösartiges Dokument kann das Parsen also nicht sprengen.
ARB
Das arb-Format deckt Flutters .arb-Dateien ab: JSON mit einem flachen Objekt aus Nachrichten-Keys neben @-präfixierten Metadaten (@key-Metadaten pro Nachricht und @@-präfixierte globale Einträge wie @@locale).
- Metadaten werden erhalten, nie übersetzt.
@-präfixierte Keys werden vor der Übersetzung entfernt und beim Schreiben an ihrer Dokumentposition wieder eingefügt. Eine Zieldatei, die existiert, aber beschädigt ist, lässt den Schreibvorgang fehlschlagen, statt ihre Metadaten still zu löschen. - Nachrichten sind ICU. Platzhalter, Plural-Behandlung und ICU-Validierung funktionieren genau wie bei
next-intl-json. - Beschreibungen werden Kontext. Jede
@key.descriptionwird als Entwicklerkontext für diese Nachricht gelesen: Die Beschreibung erreicht den Provider als Kontext zur Begriffsklärung (nie als zu übersetzender Text) und erscheint in derContext-Spalte einer exportierten Arbeitsmappe. Auch sie ist schreibgeschützt und wird nie zurückgeschrieben.
Properties
Das properties-Format deckt Javas und Springs .properties-Dateien ab, erkannt an der Endung .properties: eine flache Liste von Key/Wert-Zeilen. Keys bleiben als flache Keys wortgetreu erhalten und werden nie in einen Baum aufgeteilt.
- Trennzeichen und Kommentare. Ein Key wird von seinem Wert durch
=,:oder Whitespace getrennt, wobei Leerzeichen rund um das Trennzeichen ignoriert werden, wie beijava.util.Properties. Eine Zeile, deren erstes Nicht-Leerzeichen#oder!ist, ist ein Kommentar. - Fortsetzungen und Escapes. Eine Zeile, die auf einen Backslash endet, wird auf der nächsten fortgesetzt. Die Standard-Escapes (
\t,\n,\r,\f,\\, ein escaptes Trenn- oder Kommentarzeichen) und\uXXXXwerden beim Lesen dekodiert. Die Eingabe wird als UTF-8 gelesen; beim Schreiben wird jedes Nicht-ASCII-Zeichen als ASCII-sicheres\uXXXX-Escape ausgegeben, damit die Datei auch unter einem alten ISO-8859-1-Reader lädt. - Reihenfolge, Kommentare und Leerzeilen bleiben erhalten. Ein Schreibvorgang liest das Ziel erneut ein und behält dessen Key-Reihenfolge, Kommentare und Leerzeilen: Jede vorhandene Key-Zeile wird an Ort und Stelle mit ihrem neuen Wert neu geschrieben, und ein Key, den die Datei noch nicht hat, wird in Quellreihenfolge angehängt. Ein doppelter Key behält seine erste Position und nimmt den letzten Wert, wie
Properties.load. - Platzhalter sind MessageFormat. Werte werden als
java.text.MessageFormatgelesen, sodass{0}, die typisierte Form{0,number}, die stilisierte Form{0,number,integer}und benannte Argumente wie{count}extrahiert und über die Übersetzung hinweg geschützt werden. Unternachrichten-Argumente (plural,select,selectordinal,choice) werden erkannt, sodass das Übersetzen des Zweigtexts eine Übereinstimmung bleibt, das Entfernen oder Umbenennen eines Arguments jedoch nicht. - Einschränkung: Einfache Anführungszeichen werden nicht interpretiert. Die Single-Quote-Maskierung von MessageFormat wird nicht beachtet, sodass ein maskiertes Literal wie
'{0}'weiterhin als Platzhalter gelesen wird. Das ist Absicht, damit ein gewöhnlicher Apostroph im übersetzten Text nie einen folgenden Platzhalter verschluckt.
Dokumentreihenfolge der Keys
Schreibvorgänge erhalten die Key-Reihenfolge deines Dokuments. Die JSON-Familie, YAML und ARB tragen Keys exakt in Dokumentreihenfolge durch den Roundtrip:
- Ein ganzzahlartiger Key wie
"2","10"oder"404"behält seine Position, statt nach vorn gehoben und neu sortiert zu werden, eine Datei mit numerischen Ids, HTTP-Statuscodes oder Jahreszahlen als Keys bleibt also in ihrer eigenen Reihenfolge. - Ein Key, den ein
translate-Lauf einer Zieldatei hinzufügt, wird nach den bestehenden Keys des Ziels angehängt, in der Reihenfolge des Quelldokuments, statt alphabetisch einsortiert zu werden. - ARB-Metadatenblöcke überstehen den Roundtrip ebenfalls an ihrer Dokumentposition.
XLIFF ist nicht betroffen: Es aktualisiert bestehende <target>-Elemente an Ort und Stelle, die Reihenfolge des Dokuments wurde also nie neu aufgebaut.
Gepunktete Keys und Kollisionen
Für i18next-json, vue-i18n-json und next-intl-json übersteht ein wörtliches gepunktetes Blatt (ein Key wie "foo.bar" als einzelnes Blatt) den Roundtrip verlustfrei: verbatra liest ihn und schreibt ihn mit seiner Form auf der Platte zurück, nicht neu verschachtelt in foo und dann bar. Echte verschachtelte Pfade bleiben verschachtelt.
Der eine Fall, der bewusst fehlschlägt, ist eine echte Kollision, bei der eine Datei denselben effektiven Pfad sowohl als wörtliches gepunktetes Blatt als auch als echten verschachtelten Pfad ausdrückt (zum Beispiel "foo.bar" neben "foo": { "bar": ... }). Dieses Lesen schlägt mit INVALID_STRUCTURE fehl, statt zu raten oder Daten zu beschädigen.
ngx-translate-json behandelt einen gepunkteten Key als verschachtelten Pfad statt als wörtliches Blatt, es gibt also keine Mehrdeutigkeit zwischen gepunktet und wörtlich zu erhalten, aber es bekommt dieselbe Kollisionssicherheit: Ein gepunkteter Key, dessen Pfad mit einem echten verschachtelten Pfad kollidiert, auch wenn einer der Vorfahr des anderen ist, schlägt mit INVALID_STRUCTURE fehl, statt still einen Wert zu überschreiben.
Wie Dateien gelesen und geschrieben werden
verbatra begrenzt beim Lesen Eingabegröße und Verschachtelungstiefe und widersteht einer Datei, die sich unter ihm ändert. Es schreibt atomar: erst in eine temporäre Datei, dann per Umbenennen an den Zielort, ein unterbrochener Schreibvorgang hinterlässt also nie eine halbfertige Locale-Datei. Wenn verbatra eine Datei nicht verarbeiten kann, erzeugt es einen strukturierten, geheimnisfreien Fehler mit stabilem Code statt eines rohen: INVALID_JSON, INVALID_YAML oder INVALID_XML für fehlerhafte Syntax, INVALID_STRUCTURE für eine parsebare Datei der falschen Form, MAX_DEPTH_EXCEEDED und INPUT_TOO_LARGE für die Obergrenzen und MIXED_STRUCTURE für ngx-translates Mischfall.
Eine baumbasierte Locale-Datei (die JSON-Familie, YAML und ARB) kann ein Blatt tragen, das kein String ist, zum Beispiel ein count: 5 oder enabled: true neben den übersetzbaren Keys. verbatra akzeptiert das: Ein Blatt darf String, Zahl, Boolean oder null sein, und nur ein Blatt eines anderen Typs, etwa ein Array, schlägt mit INVALID_STRUCTURE fehl. Ein Nicht-String-Blatt ist vom übersetzbaren Bestand ausgeschlossen: Es wird nie übersetzt, gehasht, verglichen oder auf Platzhalter oder ICU geprüft. Ausschluss ist keine Erhaltung: Schreibt verbatra später dieselbe Locale-Datei, wird der Schreibvorgang aus den Strings aufgebaut, die es verwaltet, ein beim Lesen vorhandenes Nicht-String-Blatt wird also nicht in die Ausgabe übernommen. Muss so ein Wert einen Neuschrieb überleben, halte ihn aus den Dateien heraus, in die verbatra schreibt. XLIFF-Trans-Unit-Werte und .properties-Werte sind immer Strings, dort gilt das also nicht.
Warum das Format wichtig ist
Das Format sagt verbatra zwei Dinge, die es für einen sicheren Lauf braucht. Erstens die Platzhaltersyntax, damit die Platzhalter-Integritätsprüfung weiß, was sie vor und nach der Übersetzung vergleichen soll. Zweitens, für die ICU-Formate, welche Werte zu validieren sind, damit ein Quell-Key mit ungültigem ICU übersprungen statt in kaputtem Zustand gesendet wird. Das falsche Format vergleicht die falschen Tokens, wähle es also passend zu deiner i18n-Bibliothek: i18next-json für i18next, vue-i18n-json für vue-i18n, next-intl-json für next-intl, ngx-translate-json für ngx-translate, xliff für XLIFF-Dateien, yaml für YAML-basierte i18n, arb für Flutter und properties für Java- oder Spring-.properties-Dateien.