KonfigurationFormate

Formate

Die zwölf unterstützten Formate, wie jedes seine Dateien liest und schreibt, und die Garantie der Dokumentreihenfolge der Keys.

Maschinell übersetzte Seite

Diese Seite wurde automatisch übersetzt und kann daher Fehler oder seltsame Formulierungen enthalten. Die englische Version ist die maßgebliche Quelle. Das englische Original lesen.

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. Zwölf werden unterstützt: vier JSON-Varianten plus XLIFF, YAML, ARB, Java/Spring-Properties, Apple-.strings, Xcode String Catalogs (.xcstrings), Android strings.xml und gettext .po/.pot.

Die vier JSON-Formate

formatFürPlatzhalterPluraleICU
i18next-jsoni18next{{name}} und $t(...)-VerschachtelungsreferenzenCLDR-Plural-Suffix am Keynein
vue-i18n-jsonvue-i18n{name}, {0}Pipe im Wertnein
next-intl-jsonnext-intlICU-Argument- und Tag-NamenICU plural oder selectordinalja
ngx-translate-jsonngx-translate{{name}}keinenein

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.
  • Einzelklammer-Text in den Doppelklammer-Formaten. In i18next-json, ngx-translate-json und yaml ist ein Token der Form {name} Literaltext und keine Interpolation. Es wird deshalb nicht als Platzhalter extrahiert, und eine Übersetzung darf es fallen lassen oder umformulieren. Siehe die Erfindungs-Schranke für die eine Richtung, in der trotzdem geprüft wird.
  • 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 mit MIXED_STRUCTURE fehlschlagen, weil so eine Datei mehrdeutig statt erratbar ist.

Die Erfindungs-Schranke für Einzelklammern

Verfügbar ab 0.9.0

Dafür wird verbatra 0.9.0 oder neuer benötigt. Frühere Versionen haben es nicht, überprüfe daher deine installierte Version mit verbatra --version und aktualisiere, falls sie älter ist.

Unter i18next-json, ngx-translate-json und yaml wird ein {name}-förmiges Token, das in einer Übersetzung auftaucht und in der Quelle nie vorkam, als Erfindung abgelehnt. Das deckt sowohl einen frei erfundenen Platzhalter ab als auch einen, der in einen Namen verändert wurde, den die Quelle nie hatte. Die Schranke wirkt nur in diese Richtung: verbatra hat keine Einstellung für i18nexts eigene interpolation.prefix und interpolation.suffix. Wenn du also auf Einzelklammer-Delimiter umgestellt hast, wird eine Übersetzung, die einen deiner Platzhalter stillschweigend fallen lässt, nicht erkannt.

Namespace-Layouts

Strings über mehrere Namespace-Dateien pro Locale zu verteilen, ist in i18next-Projekten üblich: common.json, auth.json und so weiter liegen nebeneinander in einem Locale-Verzeichnis. Eine verbatra-Konfiguration adressiert genau eine Datei pro Locale: files.pattern ist ein wörtlicher Pfad mit eingesetztem {locale}, er wird nie als Glob expandiert, und es gibt kein Namespace-Token. Ein Muster wie public/locales/{locale}/*.json wird als Datei mit dem wörtlichen Namen *.json gesucht und schlägt mit SOURCE_UNREADABLE fehl.

Ein Layout mit einem einzigen Namespace funktioniert wie konfiguriert:

files: {
  pattern: "public/locales/{locale}/common.json",
},

Eine Konfiguration pro Namespace

Schreibe für ein Projekt mit mehreren Namespaces eine Konfiguration pro Namespace und starte verbatra einmal pro Konfiguration. Jeder Dateiname funktioniert, da --config einen expliziten Pfad anhand seiner Endung lädt:

verbatra translate --config verbatra.common.config.ts
verbatra translate --config verbatra.auth.config.ts

Jeder Lauf behandelt seinen eigenen Namespace: Keys, die in einer Zieldatei fehlen, werden wie gewohnt übersetzt.

Nur der zuletzt gelaufene Namespace behält die Änderungserkennung

Alle Konfigurationen im selben Arbeitsverzeichnis teilen sich eine verbatra.lock.json, und jeder translate-Lauf ersetzt die aufgezeichnete Baseline dieser Locale durch die Keys, die er gerade verarbeitet hat. Nach dem zweiten Lauf hat der erste Namespace keine Baseline mehr, eine Änderung an einem seiner Quellstrings gilt für check und diff also als synchron und wird nicht neu übersetzt. Eine andere Reihenfolge der Läufe hilft nicht; wer zuletzt läuft, überschreibt den anderen. Um einen geänderten String in diesem Aufbau neu zu übersetzen, lösche diesen Key aus der Zieldatei (oder lösche die ganze Zieldatei), damit er als fehlend statt als geändert zählt.

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 id seiner Trans-Unit identifiziert, mit resname als 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 stabile id. Zwei Units, die zum selben Key auflösen (typischerweise eine doppelte id), lassen das Lesen mit INVALID_STRUCTURE fehlschlagen, 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 mit INVALID_STRUCTURE fehl.
  • 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 der Context-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, einschließlich des oben beschriebenen Schutzes gegen erfundene Einzelklammer-Token, 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_STRUCTURE fehl, statt ihn still zu Text zu verflachen.
  • Fehlerhafte Syntax wird als INVALID_YAML gemeldet, 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.description wird 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 der Context-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 bei java.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 \uXXXX werden 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.
  • Zeilenenden folgen dem Ziel. Eine Datei, die irgendwo CRLF enthält, wird komplett mit CRLF zurückgeschrieben, eine Datei nur mit CR mit CR, und alles andere, auch eine noch nicht existierende Datei, mit LF. Eine gemischte Datei läuft damit auf einen Stil zusammen, statt zeilenweise erhalten zu bleiben, und ein \r oder \n innerhalb eines Werts wird weiterhin escapt statt als Zeilenumbruch ausgegeben. So bleibt eine Übersetzungsänderung an zwei Keys ein Diff über zwei Zeilen, auch in den CRLF-Repositories, in denen diese Dateien oft liegen.
  • Platzhalter sind MessageFormat. Werte werden als java.text.MessageFormat gelesen, 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.

Apple .strings

Verfügbar ab 0.10.0

Dafür wird verbatra 0.10.0 oder neuer benötigt. Frühere Versionen haben es nicht, überprüfe daher deine installierte Version mit verbatra --version und aktualisiere, falls sie älter ist.

Das apple-strings-Format deckt Apples .strings-Dateien für iOS und macOS ab: eine flache Folge von "key" = "value";-Anweisungen, erkannt an der Endung .strings. Pluralregeln liegen in der begleitenden .stringsdict-Datei (gleiches Verzeichnis, gleicher Basisname, Endung .stringsdict), die verbatra automatisch zusammen mit der .strings-Datei liest und schreibt, unter derselben apple-strings-Format-ID.

  • Kodierung ist ausschließlich UTF-8. Xcodes genstrings kann UTF-16 mit einem Byte-Order-Mark ausgeben. verbatra erkennt ein UTF-16-Byte-Order-Mark, little oder big endian, und weist es mit INVALID_STRUCTURE zurück, statt es in korrupte, NUL-durchsetzte Keys zu parsen. Speichere die Datei vor dem Lauf als UTF-8 (Xcode kann das nativ).
  • Kommentare und Escapes. Ein /* ... */-Blockkommentar unmittelbar vor einem Eintrag wird als dessen Beschreibung gelesen: Er erreicht den Provider als Kontext zur Begriffsklärung und erscheint in der Context-Spalte einer exportierten Arbeitsmappe. Er ist nur lesbar und wird nie zurückgeschrieben. Ein //-Zeilenkommentar bleibt beim Schreiben erhalten, trägt aber keine Beschreibung. Die Escapes \", \\, \n, \t und das vierstellige Hex-Unicode-Escape \U werden beim Lesen dekodiert; beim Schreiben werden Anführungszeichen, Backslash, Zeilenumbruch und Tabulator escapt, und jedes andere Zeichen, auch Nicht-ASCII-Text, wird als rohes UTF-8 geschrieben.
  • Fehlendes Ziel. Anders als bei XLIFF wird ein noch nicht existierendes .strings-Ziel aus den Einträgen synthetisiert, statt den Schreibvorgang scheitern zu lassen, wie beim Properties-Format.
  • Reihenfolge, Kommentare und Leerzeilen bleiben erhalten. Ein Schreibvorgang liest das Ziel erneut ein und baut es aus dieser Struktur wieder auf: Jeder vorhandene Key wird an Ort und Stelle mit seinem neuen Wert neu geschrieben, ein Key, den das Ziel noch nicht hat, wird in Quellreihenfolge angehängt, und ein aus den Einträgen entfernter Key wird zusammen mit seinem eigenen vorangehenden Kommentar aus dem Ziel entfernt.
  • Platzhalter sind im printf-Stil. %@, %d, %1$@ und das escapte Literal %% werden extrahiert und über die Übersetzung hinweg geschützt, wobei Flags, Breite, Präzision und Längenmodifikatoren (%05.2f, %ld) als Dekoration gelten und nicht zur Identität des Tokens gehören. Ein positionelles Vertauschen wie %1$@ %2$@ zu %2$@ %1$@ wird akzeptiert, denn genau dafür gibt es positionelle Spezifizierer. Ein bloßes %, gefolgt von gewöhnlichem Text, wie in "50% off", extrahiert nichts.
  • Plurale liegen in der begleitenden .stringsdict. Localizable.strings bildet ein Paar mit Localizable.stringsdict im selben Verzeichnis; verbatra findet sie automatisch und führt ihre Plural-Kategorien in derselben Locale-Ressource zusammen, sodass die Übersetzung weiterhin genau ein files.pattern pro Locale anspricht. Jede in einem Eintrag vorhandene CLDR-Kategorie (zero, one, two, few, many, other) wird zu einem eigenen übersetzbaren Eintrag, mit demselben <key>_<Kategorie>-Suffix, den auch i18next-json verwendet, zum Beispiel photo_count_one und photo_count_other. Eine Locale, die nur einen Teil der Kategorien liefert - der Regelfall, da die meisten Sprachen nur one und other brauchen - rundet exakt mit diesen: Für ein Ziel wird keine erfunden, und aus der Quelle wird keine verworfen. Printf-Platzhalter innerhalb einer Kategorie-Formatzeichenkette, etwa %d in "%d photos", werden genauso extrahiert und geschützt wie in .strings-Werten. Eine fehlerhafte .stringsdict (ungültiges XML, eine fehlende %#@variable@-Substitution oder eine nicht unterstützte Plural-Kategorie) wirft einen strukturierten Fehler, der Datei und Key benennt. Ein noch nicht existierendes .stringsdict-Ziel wird genauso angelegt wie ein .strings-Ziel, .lproj-Verzeichnis inklusive, und seine nicht übersetzbare Struktur (der Format-Key, der Substitutionsvariablenname und der Werttyp) bleibt bei einem erneuten Schreiben erhalten.
  • Eine Datei pro Locale, .lproj inklusive. files.pattern mit {locale}.lproj/Localizable.strings adressiert Apples Bundle-Layout pro Locale direkt: {locale} ist eine wörtliche Token-Ersetzung, braucht also keinen eigenen Locale-Style, und ein Schreibvorgang legt ein fehlendes {locale}.lproj-Verzeichnis genauso an wie jedes andere fehlende Zielverzeichnis.

Xcode String Catalogs (.xcstrings)

Verfügbar ab 0.10.0

Dafür wird verbatra 0.10.0 oder neuer benötigt. Frühere Versionen haben es nicht, überprüfe daher deine installierte Version mit verbatra --version und aktualisiere, falls sie älter ist.

Das apple-xcstrings-Format deckt Xcodes String-Catalog-Dateien ab, .xcstrings, eingeführt in Xcode 15 als Ersatz für das Paar .strings/.stringsdict in neuen Projekten: ein einziges JSON-Dokument, das die Strings, Plurale und den Übersetzungsstatus aller Locales gemeinsam enthält, erkannt an der Endung .xcstrings. Das unterscheidet sich strukturell von jedem anderen von verbatra unterstützten Format, die alle eine Datei pro Locale verwenden: Ein apple-xcstrings-Katalog ist eine Datei für alle Locales zugleich.

  • Ein gemeinsamer Katalog statt einer Datei pro Locale. files.pattern braucht weiterhin das {locale}-Token, aber für dieses Format wird es unabhängig von der eingesetzten Locale zum selben Pfad aufgelöst, zum Beispiel adressiert {locale}Localizable.xcstrings eine einzige Datei Localizable.xcstrings. Die Quell-Locale und jede konfigurierte Ziel-Locale teilen sich also eine physische Datei.
  • Schreibvorgänge in einen gemeinsamen Katalog laufen seriell. Da jeder Schreibvorgang pro Locale dieselbe Datei berührt, serialisiert verbatra jede Operation, die sie schreiben kann - translate, eine Studio-Bearbeitung, eine Einzelkey-Neuübersetzung und den Workbook-Import - gegeneinander. In der Praxis heißt das: Ein apple-xcstrings-Projekt führt diese Operationen nacheinander aus, selbst wenn --concurrency über 1 gesetzt ist; jedes andere Format führt seine Provider-Aufrufe weiterhin wie konfiguriert parallel aus.
  • Der Key ist der Quell-String. Ein Katalog-Key ohne expliziten localizations-Eintrag für die im Dokument selbst angegebene sourceLanguage fällt auf den Key-Text selbst zurück, passend zu Xcodes eigener Konvention. Ein Key ohne Eintrag für jede andere Locale gilt für diese Locale einfach als noch nicht übersetzt, genau wie ein fehlender Key in der Zieldatei jedes anderen Formats.
  • Plurale liegen in variations.plural. Jede unter variations.plural einer Locale vorhandene CLDR-Kategorie (zero, one, two, few, many, other) wird zu einem eigenen übersetzbaren Eintrag, mit demselben <key>_<Kategorie>-Suffix, den auch i18next-json und die .stringsdict-Unterstützung des Apple-.strings-Formats verwenden, zum Beispiel %lld photos_one und %lld photos_other. Eine Locale, die nur einen Teil der Kategorien liefert, rundet exakt mit diesen: Keine wird erfunden, keine wird verworfen.
  • Platzhalter sind im printf-Stil. %@, %d, %1$@, %lld und das escapte Literal %% werden extrahiert und über die Übersetzung hinweg geschützt, genau wie bei Apple-.strings-Werten; ein Längenmodifikator wie das ll in %lld ist Dekoration und nicht Teil der Identität des Tokens, also sind %lld und %d derselbe Platzhalter.
  • shouldTranslate: false wird respektiert. Ein so markierter Key wird nie an einen Provider gesendet und nie geändert zurückgeschrieben.
  • Schreibvorgänge patchen das Dokument, statt es neu aufzubauen. Ein Schreibvorgang liest den aktuellen Katalog erneut ein und aktualisiert nur die berührten Localizations, sodass extractionState, jede andere Locale, nicht übersetzbare Einträge, Plural-Kategorien, die oberste version des Katalogs und seine sourceLanguage unverändert erhalten bleiben. Ein gerade übersetzter Wert erhält stringUnit.state: "translated"; die bestehende Localization eines unveränderten Werts, Status inklusive, bleibt byteidentisch statt neu geschrieben zu werden.
  • Der Zielkatalog muss bereits existieren. Anders als bei .strings oder .properties legt verbatra keinen neuen .xcstrings-Katalog an: Erstelle ihn zuerst in Xcode und richte files.pattern dann darauf aus.
  • Fehlerhafte Eingaben sind spezifisch. Ein strukturierter AdapterError benennt die Datei und, wenn das Problem innerhalb eines Eintrags liegt, auch Key und Locale, zum Beispiel bei einem Eintrag ohne stringUnit- und ohne variations.plural-Feld oder bei einer Plural-Kategorie außerhalb der CLDR-Menge.

Android strings.xml

Verfügbar ab 0.10.0

Dafür wird verbatra 0.10.0 oder neuer benötigt. Frühere Versionen haben es nicht, überprüfe daher deine installierte Version mit verbatra --version und aktualisiere, falls sie älter ist.

Das android-xml-Format deckt Androids Ressourcendateien ab, res/values/strings.xml für die Quell-Locale und res/values-<Qualifier>/strings.xml für jedes Ziel, erkannt an der Endung .xml. Setze files.localeStyle auf android (siehe die Konfigurationsdatei), damit sich {locale} in files.pattern zum richtigen Ressourcen-Qualifier-Verzeichnis statt zu einem wörtlichen BCP-47-Tag auflöst.

  • <string> und <plurals>. Ein <string name="key">value</string> ist ein Eintrag. Ein <plurals name="key"> wird zu einem eigenen Eintrag pro <item quantity="..."> (zero, one, two, few, many, other), mit dem Key key[quantity], zum Beispiel count[one] und count[other]. Eine Locale, die nur einen Teil der Kategorien liefert, rundet exakt mit diesen: Keine wird erfunden, keine wird verworfen. Jeder Ressourcenname name wird gegen Androids Bezeichnergrammatik geprüft (ein Buchstabe oder Unterstrich, dann Buchstaben, Ziffern oder Unterstriche), bevor er zum Key wird, sodass ein Name keinen Zusammenstoß mit [quantity]-förmigem echtem Plural-Key-Raum fälschen kann.
  • translatable="false" wird respektiert. Ein <string> oder <plurals> mit diesem Attribut wird nie an einen Provider gesendet und bleibt auf der Festplatte unverändert.
  • formatted="false" wird normal übersetzt. Das Attribut sagt nur Androids eigenem Build-Werkzeug, keine printf-Argumente zu prüfen; es ändert nichts an dem, was verbatra tut. Platzhalter im Stil %s/%d werden weiterhin extrahiert und über die Übersetzung hinweg geschützt.
  • <string-array> und Inline-Markup sind in dieser Version durchgereicht. Ein <string-array> und ein <string>, dessen Inhalt kein reiner Text ist (ein Inline-Element wie <b>, <xliff:g> oder ein CDATA-Abschnitt), bleiben exakt so erhalten, wie sie sind, und gehen nie in verbatras übersetzbare Menge ein. Übersetzung auf Item-Ebene für diese ist eine mögliche künftige Erweiterung, heute aber nicht unterstützt.
  • Escaping wird beim Lesen dekodiert, beim Schreiben neu kodiert. \', \", \n, \t und ein führendes \@ oder \? werden beim Lesen zu ihren wörtlichen Zeichen dekodiert, und dieselben Zeichen (plus ein führendes, nicht escaptes @ oder ?) werden beim Schreiben wieder escapt; XMLs eigene Entities &, < und > werden unabhängig davon von der XML-Schicht darunter behandelt, sodass ein übersetzter Wert mit einem dieser Zeichen sicher zurückgeschrieben wird, ohne dass du selbst escapen musst.
  • Platzhalter sind im printf-Stil. %s, %d, %1$s und das escapte Literal %% werden extrahiert und über die Übersetzung hinweg geschützt. Ein bloßes % gefolgt von gewöhnlichem Text, wie in "50% off", extrahiert nichts: Der Extraktor behandelt ein Leerzeichen nicht als gültiges Konvertierungsflag, anders als Javas eigenes String.format, sodass ein Prozentzeichen in gewöhnlichem Fließtext nie für einen Platzhalter gehalten wird.
  • Schreibvorgänge aktualisieren Ziele an Ort und Stelle; eine fehlende Zieldatei wird neu erzeugt. Eine bestehende res/values-<Qualifier>/strings.xml wird gepatcht: Ein übersetzter Key wird aktualisiert, ein aus der Quelle entfernter Key wird gelöscht, und das übergeordnete Verzeichnis wird angelegt, falls es noch nicht existiert. Eine noch nicht existierende Zieldatei wird von Grund auf neu erzeugt, das Verzeichnis res/values-<Qualifier>/ eingeschlossen.
  • Einschränkung: Ein zu Durchreichen degradierter Key wird nach der Übersetzung entfernt, nicht erhalten. Wenn ein Key bereits in einer Zieldatei übersetzt war und dann in der Quelldatei translatable="false" oder Inline-Markup erhält, kann der nächste Lauf das nicht von einer echten Löschung des Keys unterscheiden und entfernt die veraltete Übersetzung aus der Zieldatei. Das ist eng begrenzt (es betrifft nur einen Key, dessen Quellklassifikation sich ändert, nachdem er bereits übersetzt wurde) und über Versionskontrolle oder einen erneuten Übersetzungslauf wiederherstellbar; es betrifft keinen Key, der seine ursprüngliche Klassifikation behält, einschließlich gewöhnlicher neuer, geänderter und entfernter Keys.
  • Einschränkung: --prune hat kein Bewusstsein für Plural-Quantitäten. verbatras Erkennung verwaister Keys und --prune vergleicht Keys gegen die eigene Key-Menge der Quelle, ohne Sonderfall für key[quantity]-Gruppen. Eine Ziel-Locale, die mehr Plural-Quantitäten braucht als die Quelle deklariert (zum Beispiel eine Sprache mit mehr grammatischen Numerus-Unterscheidungen), hat nur im Ziel vorhandene Quantitäten, die ein --prune-Lauf nicht von einem echt entfernten Key unterscheiden kann, und kann sie löschen. Standardmäßig deaktiviert; ein gewöhnlicher verbatra translate-Lauf ohne --prune ist davon nicht betroffen.
  • Fehlerhafte Eingaben sind spezifisch. Fehlerhaftes XML löst einen strukturierten AdapterError aus, der das Problem benennt; ein Dokument, dessen Wurzelelement nicht <resources> ist, ein Ressourcenname, der die Bezeichnergrammatik verletzt, ein <plurals>-Item mit einer Quantität außerhalb der CLDR-Menge oder zwei Elemente, die sich zum selben Key auflösen, werden alle namentlich gemeldet statt als generischer Parse-Fehler. Eine DTD- oder Entity-Deklaration wird rundweg abgelehnt.

gettext .po/.pot

Verfügbar ab 0.10.0

Dafür wird verbatra 0.10.0 oder neuer benötigt. Frühere Versionen haben es nicht, überprüfe daher deine installierte Version mit verbatra --version und aktualisiere, falls sie älter ist.

Das gettext-po-Format deckt GNU gettexts .po- und .pot-Kataloge ab, erkannt an beiden Endungen: eine flache Folge von msgid/msgstr-Einträgen, mit der msgid als Key, nie in einen Baum aufgespalten.

  • msgctxt-Disambiguierung. Ein msgctxt vor einem Eintrag setzt sich mit dessen msgid zu einem Key zusammen, sodass zwei Einträge mit derselben msgid unter verschiedenen Kontexten nie kollidieren. Das zusammengesetzte Format nutzt einen reservierten Private-Use-Codepoint (U+E000) als internen Trenner, ein Zeichen, das keine echte .po-Datei legitim enthält; ein Quelltext, der es doch enthält, wird mit einem strukturierten Fehler abgelehnt statt den Key still zu beschädigen.
  • Plurale sind msgid_plural/msgstr[n], nach Index geschlüsselt, nicht nach CLDR-Kategorie. Jedes msgstr[n] wird zu einem eigenen Eintrag, mit dem Key key[n] (n der rohe Dezimalindex), zum Beispiel one item[0] und one item[1]. Das ist bewusst nicht die <key>_<category>-Suffixkonvention, die die JSON-Formate und Apples .stringsdict verwenden: Einen gettext-Index in eine CLDR-Kategorie zu übersetzen würde bedeuten, den Plural-Forms-C-Ausdruck der Datei selbst auszuwerten, was verbatra nicht tut. Der Plural-Forms-Header wird wörtlich als unangetasteter Text erhalten; verbatra liest daraus nur die Ganzzahl nplurals=N, um jeden msgstr[n]-Index auf Gültigkeit zu prüfen. Eine Datei mit Plural-Einträgen und ohne Plural-Forms-Header wird abgelehnt, genau wie msgfmt --check es selbst durchsetzt.
  • fuzzy-Einträge werden gelesen, nicht übersprungen. Das bestehende msgstr eines #, fuzzy-Eintrags wird als aktueller Wert dieses Eintrags gelesen, genau wie bei jedem anderen übersetzten Eintrag; verbatra behandelt fuzzy nicht als fehlend. Das Flag bleibt bei jedem Schreibvorgang wörtlich erhalten und wird nie automatisch gesetzt oder entfernt.
  • Kommentare und der Header bleiben unangetastet erhalten. #.-Entwicklerkommentare werden zur Beschreibung des Eintrags: Sie erreichen den Provider als Kontext zur Disambiguierung und erscheinen in der Kontext-Spalte einer exportierten Arbeitsmappe. #:-Quellverweise und andere #,-Flags bleiben beim Schreiben erhalten, werden aber nicht weiter interpretiert. Ein #~-obsoleter Block bleibt als inerter Text erhalten und wird nie zu einem übersetzbaren Eintrag. Der Header-Eintrag (der msgid ""-Block mit Content-Type, Plural-Forms und ähnlichen Metadaten) wird von einem Schreibvorgang nie angefasst.
  • .pot-Vorlagen werden sauber gelesen. Eine .pot-Quellvorlage, deren Einträge ein leeres msgstr tragen, wird ohne Fehler gelesen; der Wert jedes Eintrags ist einfach die leere Zeichenkette, bis er übersetzt wird.
  • Fehlende Zieldatei. Wie bei den Formaten properties und Apple .strings wird eine noch nicht existierende .po-Zieldatei aus den Einträgen heraus neu erzeugt statt den Schreibvorgang scheitern zu lassen, einschließlich eines minimalen Headers. Eine neu erzeugte Datei mit Plural-Einträgen erhält einen Plural-Forms-Header, der auf den höchsten vorhandenen Index zugeschnitten ist; für eine oder zwei Formen ist das der universelle Standardausdruck, für drei oder mehr ein sicherer, im gültigen Bereich liegender Ersatzwert statt einer geratenen, möglicherweise falschen sprachlichen Regel; säe die Zieldatei zuerst aus einer echten zielsprachlichen Vorlage vor, wenn du die exakte Grammatik brauchst.
  • Platzhalter sind im printf-Stil, einschließlich Pythons benannter Form. %s, %d, %1$s, das Python-Style %(name)s und das escapte Literal %% werden extrahiert und über die Übersetzung hinweg geschützt.
  • Fehlerhafte Eingaben sind spezifisch. Ein strukturierter AdapterError benennt die betroffene msgid und die physische Zeile, zum Beispiel eine unterminierte gequotete Zeichenkette, eine unbekannte Escape-Sequenz, einen nicht zusammenhängenden msgstr[n]-Index oder einen Plural-Eintrag ohne Plural-Forms-Header, statt eines generischen Parse-Fehlers.
  • Einschränkung: --prune hat kein Bewusstsein für Plural-Indizes. verbatras Erkennung verwaister Keys und --prune vergleicht Keys gegen die eigene Key-Menge der Quelle, ohne Sonderfall für key[n]-Gruppen. Eine Ziel-Locale, die mehr Plural-Formen braucht als die Quelle deklariert (zum Beispiel eine Sprache mit mehr grammatischen Numerus-Unterscheidungen), hat nur im Ziel vorhandene Indizes, die ein --prune-Lauf nicht von einem echt entfernten Key unterscheiden kann, und kann sie löschen. Standardmäßig deaktiviert; ein gewöhnlicher verbatra translate-Lauf ohne --prune ist davon nicht betroffen.

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, properties für Java- oder Spring-.properties-Dateien, apple-strings für Apple-.strings-Dateien, apple-xcstrings für Xcode-String-Catalog-.xcstrings-Dateien, android-xml für Android strings.xml und gettext-po für gettext-.po/.pot-Kataloge.

Edit on GitHub