Referenz / CLI
Maschinell übersetzte Seite
Exit-Codes und JSON-Ausgabe
Der Exit-Code-Vertrag, der JSON-Envelope jedes Befehls mit --json, die Codes, die die CLI selbst auslöst, und wie verbatra auf stderr und ins Terminal schreibt.
Alles, worauf sich ein Skript oder CI-Schritt über alle Befehle hinweg verlassen kann. Jede Befehlsseite ergänzt nur, was für diesen Befehl gilt.
Exit-Codes
Der Exit-Code ist der Vertrag, auf den dein CI-Schritt verzweigt:
| Code | Bedeutung |
|---|---|
0 | Erfolg, je Befehl wie unter der Tabelle aufgeführt |
1 | der Lauf kam durch, aber das Ergebnis ist nicht sauber: translate oder import endete mit mindestens einer fehlgeschlagenen oder unvollständigen Locale, check fand eine Locale nicht synchron (oder mit --qa eine eingecheckte Übersetzung mit einem Integritätsfehler oder mit --strict einer Review-Warnung oder einem unvollständigen Plural, oder mit --require-reviewed eine maschinell geschriebene Übersetzung, die niemand freigegeben hat, oder mit --sensitive Inhalt in Quelldatei oder Glossar, der sensibel aussieht), check --file fand einen Syntax- oder Integritätsfehler in der Datei (oder mit --strict eine Review-Warnung oder einen unvollständigen Plural), diff fand einen fehlenden oder geänderten Key (oder mit --unused fand ein vollständiger Scan einen ungenutzten Quell-Key), report provenance fand verbatra.provenance.json beschädigt oder von einer neueren verbatra-Version geschrieben, doctor fand mindestens eine fehlgeschlagene Prüfung, types --check stellte fest, dass die committete Deklaration veraltet ist, oder studio beziehungsweise mcp scheiterte beim Herunterfahren seines Servers |
2 | konnte nicht laufen: ein Fehler des gesamten Laufs (kaputte Konfiguration, unlesbare Quelle, kaputte Lock-Datei, eine Locale, die der Provider nicht unterstützt), ein Verwendungsfehler (eine leere --locales-Liste, INVALID_, oder ein ungültiges --debounce oder --port), ein nicht konfigurierter --locales-Wert (UNKNOWN_), init, dem ein benötigtes Flag fehlt, das mehrere passende Formate oder Dateimuster findet, das sich weigert, eine bestehende Konfiguration zu überschreiben, oder das keine gültige Konfiguration anlegen kann, watch, das nicht starten oder nicht stoppen kann, studio, das die Konfiguration nicht laden, @verbatra/studio nicht importieren oder seinen Server nicht starten kann, oder mcp, dem --config einen Pfad nennt, den es nicht gibt, oder das @verbatra/mcp nicht importieren, seinen Server nicht starten oder --redact-values mit dem installierten @verbatra/mcp nicht umsetzen kann (eine fehlende oder ungültige Konfiguration aus der Suche startet ihn unkonfiguriert) |
3 | translate (auch als Dry Run und mit --estimate) in einem rein menschlichen Projekt (provider: { id: "none" }) lief sauber durch, hat aber Keys für eine menschliche Übersetzung übrig gelassen; eine fehlgeschlagene oder teilweise Locale endet weiterhin mit 1 |
130 | translate oder import wurde mit Ctrl-C (SIGINT) abgebrochen, nachdem die Schreibsperren freigegeben waren, oder watch, studio oder mcp wurde durch einen zweiten Interrupt zwangsbeendet |
143 | translate oder import wurde mit SIGTERM beendet, nachdem die Schreibsperren freigegeben waren |
Exit 0 bedeutet je Befehl:
translateoderimport: jede Locale wurde vollständig abgeschlossencheck: jede Locale ist synchrondiff: keine ausstehenden Änderungenreport: der Bericht wurde ausgegebendoctor: kein Problem in der Einrichtungextract: der Scan wurde abgeschlossenpseudo: die Pseudolocale wurde erzeugttypes: die Deklaration wurde erzeugtexport: die Arbeitsmappe wurde geschriebentmx: der Übersetzungsspeicher wurde importiert oder exportiertinit: das Projekt wurde angelegt oder war schon auf dem aktuellen Standwatch,studioodermcp: wurde sauber beendet--helpoder--version: die Hilfe oder die Version wurde ausgegeben
Bei 130 und 143 endet stderr mit verbatra: interrupted (SIGINT), released locks (oder SIGTERM), oder unter --json mit dem Datensatz {"type":"interrupted","signal":"SIGINT","locksReleased":true}. Scheitert das Freigeben der Sperren oder ist es nach 5 Sekunden nicht fertig, sagt die Zeile das stattdessen (could not release locks: <reason> oder lock release did not finish within 5s), und der JSON-Datensatz trägt "locksReleased":false mit "reason":"failed" (plus message) oder "reason":"timed-out" (plus deadlineMs).
Einige Randfälle, die du kennen solltest:
Verfügbar ab 0.9.0- Eine unvollständige Locale zählt als Fehlschlag. Das heißt, die Datei wurde geschrieben, aber einige Keys fehlen weiterhin, meist weil ein Provider-Sub-Batch fehlschlug oder das Integritäts-Gate eine Übersetzung abgelehnt hat. Sie endet mit
1genau wie eine fehlgeschlagene Locale. Um beide auseinanderzuhalten, liespartialin der Zusammenfassung. Ein fehlgeschlagener Sub-Batch hinterlässt an der Locale einen HinweisSUB_, der den Fehlercode des Providers nennt, oderBATCH_ FAILED PROVIDER_, wenn der Fehlschlag kein strukturierter Provider-Fehler war.CALL_ FAILED - Eine Locale, die der Provider nicht unterstützt, ist ebenfalls ein Fehler des gesamten Laufs, nie eine einzelne fehlgeschlagene Locale.
translate(auch ein Dry Run und--estimate),watchbeim Start und eine Neuübersetzung eines einzelnen Keys lehnen sie mitLOCALE_ab und enden mitUNSUPPORTED_ BY_ PROVIDER 2, bevor ein Provider konstruiert oder irgendetwas ausgegeben wird; keine Locale des Laufs hat also begonnen, und nichts wurde geschrieben. Beurteilt wird gegen die Sprachtabelle, die verbatra für DeepL und Google Cloud Translation mitliefert; ein LLM-Provider akzeptiert jede Locale. Lass die Locale mit--localesweg, um die anderen zu übersetzen, und führeverbatra doctor --localesaus, um zu sehen, warum. Siehe Sprachunterstützung. - Eine kaputte Lock-Datei ist bei
translatewie beiimportein Fehler des gesamten Laufs, nie eine einzelne fehlgeschlagene Locale. Sie ist eine einzige gemeinsame Datei, deshalb bricht ein Lauf, dessen Lock-Datei mittendrin kaputtgeht, mit2ab, statt die restlichen Locales weiter abzuarbeiten. Was vor dem Abbruch geschrieben wurde, bleibt auf der Platte: Repariere die Lock-Datei und starte den Befehl erneut. - Ein einzelner Interrupt ist ein sauberer Stopp, und alle drei langlaufenden Befehle (
watch,studioundmcp) enden dabei mit0. Gehe darüber hinaus nicht davon aus, dass sie sich gleich verhalten: Scheitert der Stopp selbst, endetwatchmit2, währendstudioundmcpmit1enden. - Ein fehlgeschlagener Lauf während
watcherscheint als Datensatz im Ausgabestrom, nie als Exit-Code ungleich null. exporthat keinen Fehlermodus pro Locale: Der Befehl endet mit0oder2, nie mit1.doctorliest eine kaputte Konfiguration anders als jeder andere Befehl: Eine Konfiguration, die die Suche nicht findet, oder eine, die die Validierung nicht besteht, ist eine fehlgeschlagene Prüfung und Exit1, denn genau das zu melden ist die Aufgabe des Befehls. Exit2gibt es nur, wenn er überhaupt nicht laufen kann, etwa bei einem explizit angegebenen--config-Pfad, der nicht existiert.- Ein Fehlermodus liegt außerhalb des Vertrags: ein Parse-Fehler, der kein Verwendungsfehler ist, wird weitergeworfen, und das Binary fängt ihn nicht ab. Damit greift Nodes Standardverhalten für eine unbehandelte Rejection statt eines der sechs Codes.
- Ein geschützter Key (ein veralteter Wert, den ein Mensch geschrieben, importiert oder außerhalb von verbatra geändert hat, oder ein gepinnter Key) ändert den Exit-Code von
translatein einem Projekt mit Provider nicht: Der Befehl endet mit0und sagt auf stderr, wie viele Keys er übrig gelassen hat (gepinnte Keys zählen nicht mit).checkendet dafür trotzdem mit1, weil der Key veraltet bleibt, bis ein Mensch ihn auflöst. In einem rein menschlichen Projekt zählt er zum Exit3.
JSON-Ausgabe
Jeder Befehl außer studio und mcp akzeptiert --json für maschinenlesbare Ausgabe auf stdout: translate, watch, check, diff, report, export, import, tmx, doctor, pseudo, types, extract und init. Jeder Datensatz ist eine Zeile, und jeder Datensatz steckt im selben Envelope. Du verzweigst also auf ein einziges Feld und musst nie raten, von welchem Befehl die Daten stammen, die du gerade in der Hand hast:
type Envelope<TResult> =
| { ok: true; version: 1; command: string; result: TResult }
| {
ok: false;
version: 1;
command: string | null;
code: string;
message: string;
causeCode?: string; // the code of the error this one wraps, when it has one
candidates?: string[]; // the values to choose from, on an error that has them
missing?: string[]; // the flags still to pass, on an init usage error
hint?: string; // the next step that resolves the error, when there is one
};version ist die Version dieser Envelope-Form, nicht die Version des Pakets. Es ist eine ganze Zahl, du vergleichst sie also mit ===, statt einen Bereich zu parsen, und sie ändert sich nur, wenn ein bestehendes Feld seine Bedeutung ändert oder verschwindet. Neue Felder können hinzukommen, ohne dass die Version steigt; ignoriere also die, die du nicht kennst.
Ein Lauf, der als Ganzes fehlschlägt, schreibt genau einen ok: false-Datensatz auf stdout und endet mit 2. Sein code ist derselbe stabile Fehlercode, den auch die stderr-Zeile trägt, und damit das, worauf du verzweigst:
{ "ok": false, "version": 1, "command": "translate", "code": "CONFIG_INVALID", "message": "..." }command ist nur dann null, wenn der Fehlschlag eintrat, bevor ein Unterbefehl aufgelöst war. candidates erscheint nur bei einem Fehler, der dich wählen lässt, etwa FORMAT_ von init, und listet die Werte auf, aus denen du wählst. missing erscheint bei einem Verwendungsfehler von init, der Flags unbeantwortet lässt, und nennt jedes Flag, das du noch übergeben musst. causeCode erscheint, wenn der Fehler einen anderen Fehler mit Code umhüllt, etwa ein PROVIDER_ mit der Ursache MISSING_; die stderr-Zeile endet dann mit (cause: MISSING_.
hint erscheint, wenn der Fehler einen nächsten Schritt hat: einen kurzen Satz im Imperativ, der ihn behebt, etwa "Set GEMINI_ für einen fehlenden Key oder "Run `verbatra check --help` to see the options and arguments it accepts." für einen USAGE_. Handle danach, statt message zu parsen. Er nennt Variablen, Flags und Pfade, nie den Wert eines Keys. Jeder Code aus dem SDK, von einem Provider und von einem Format-Adapter hat einen (das SDK exportiert denselben Text als errorHint), und ebenso jeder CLI-Fehlercode außer CLI_. Ohne --json folgt derselbe Text der Fehlerzeile auf stderr als next: ..., außer --quiet ist gesetzt. mcp --json ist die Ausnahme: Es wird mit einer einzigen USAGE_-Zeile auf stderr abgelehnt, ohne Envelope und ohne next:. Startfehler von studio und mcp bekommen ebenfalls keine next:-Zeile.
Das result eines erfolgreichen Envelopes ist bei jedem Befehl anders und steht auf seiner Seite unter JSON-Ausgabe: extract, translate, watch, check, diff, report, doctor, pseudo, types, export, import, tmx, init.
Schemas Verfügbar ab 0.12.0
Jeder Datensatz, den --json ausgibt, hat ein JSON Schema (Draft 2020-12). Die Schemas liegen in den npm-Paketen und sind unter festen URLs erreichbar, damit ein Skript, ein Agent oder ein Editor die Ausgabe prüfen kann, statt Prosa zu lesen. Jedes Ausgabe-Schema erlaubt Eigenschaften, die es nicht aufführt, so wie es der Envelope-Vertrag oben verspricht; behalte das bei, wenn du eines kopierst. Das Konfigurationsschema ist die bewusste Ausnahme: Es beschreibt, was du schreibst, und lehnt deshalb einen unbekannten Key ab, um einen Tippfehler zu erkennen.
| Dokument | URL | Datei im Paket |
|---|---|---|
| Jeder Envelope, Erfolg oder Fehler | envelope.json | @verbatra/cli/schemas/envelope.json |
Der Envelope eines Befehls, mit seinem result | https://verbatra.kreitz-webdev.de/schema/v1/<command>-envelope.json | @verbatra/cli/schemas/<command>-envelope.json |
| Der Fehler-Envelope | error-envelope.json | @verbatra/cli/schemas/error-envelope.json |
Das Ergebnis von init allein, ohne Envelope | init-result.json | @verbatra/cli/schemas/init-result.json |
| Eine JSON-Zeile auf stderr: Fortschritt, Warten auf eine Sperre, Abbruch | stderr-record.json | @verbatra/cli/schemas/stderr-record.json |
| Die Konfigurationsdatei | config.json | @verbatra/sdk/schemas/config.json oder @verbatra/sdk/config-schema.json: dasselbe Schema ohne $id und title |
Jede Befehlsseite verlinkt ihren Envelope unter JSON-Ausgabe, und https://verbatra.kreitz-webdev.de/schema/v1 listet jedes Dokument mit seinem Titel auf. Ein Befehls-Envelope verweist per URL in $ref auf das Schema seines result. Ein Validator lädt es also über das Netz, oder aus node_, wenn du diese Dateien vorher registrierst.
v1 folgt Envelope version: 1 und liefert immer das neueste Release aus. Ein neues Feld bleibt in v1; eine Änderung, die eine Nutzlast bricht, erhöht die version des Envelopes und fügt /schema/v2/ hinzu. Das Konfigurationsschema nutzt dasselbe URL-Schema, folgt aber den Änderungen der Konfiguration selbst. Die URL liefert immer das neueste Release aus, eine Kopie aus npm bleibt bei ihrer Version. Eine ältere Kopie kann deshalb einen Datensatz einer neueren CLI ablehnen, wo ein Schema geschlossene Werte aufzählt, etwa den type eines stderr-Datensatzes oder den command eines Envelopes.
CLI-Fehlercodes Verfügbar ab 0.12.0
Diese Codes löst die CLI selbst aus. Dieselbe Liste wird als CLI_ (mit dem Typ CliErrorCode) von @verbatra/cli ausgeliefert, sodass ein Skript einen Code dagegen prüfen kann. Jeder andere Code kommt vom SDK, einem Provider oder einem Format-Adapter. Was jeder Code bedeutet, welche Befehle ihn auslösen und was der nächste Schritt ist, steht unter Fehlercodes.
Fehler auf stderr
Jeder Fehler geht in beiden Modi als eine strukturierte Zeile (verbatra: error [CODE] message) auf stderr. Seit 0.12.0 gilt das auch für einen Fehler beim Parsen der Argumente (eine unbekannte Option oder ein unbekannter Befehl, ein fehlendes Argument): er gibt verbatra: error [USAGE_ aus und endet mit 2, wo vorher commanders eigenes error: unknown option '--nope' ohne Code stand. init folgt derselben Grammatik seit 0.12.0; davor gab ein fehlender Provider verbatra: --provider is required (...) ohne Code aus. Seit 0.12.0 folgt ohne --json der Zeile ein Hinweis als next: ..., und mit --json schreibt ein Parse-Fehler den USAGE_-Envelope samt hint auf stdout; mcp hält stdout für das Protokoll frei und lehnt --json daher nur auf stderr ab. Der Start von studio und mcp ist die Ausnahme von der strukturierten Zeile: Ist @verbatra/studio oder @verbatra/mcp nicht installiert, steht auf stderr allein die Installationsanweisung, ohne das Präfix verbatra:. Mit --json ist die message eines Parse-Fehlers commanders eigener Text samt Präfix error: . Ohne --json schreibt ein fehlgeschlagener Lauf weiterhin überhaupt nichts auf stdout. Datensätze zu Fortschritt und Lock-Wartezeit gehen immer auf stderr, auf stdout stehen also ausschließlich Envelopes.
Terminalausgabe Verfügbar ab 0.12.0
Ergebnisse gehen auf stdout. Fortschritt, Hinweise, Tipps, Warnungen und Fehler gehen auf stderr, und verbatra passt sie daran an, wohin stderr geht:
- In einem interaktiven Terminal sind feste Labels wie
error,[ok]undnext:eingefärbt, und ein Schritt, der länger als 300 ms dauert, zeigt einen ASCII-Spinner (| / - \). - Wenn stderr umgeleitet oder in eine Pipe geschrieben wird,
CIauf einen anderen Wert als leer,0oderfalsegesetzt ist oderTERMden Wertdumbhat, besteht die Ausgabe aus schlichten, nur angehängten Zeilen ohne Farbe und ohne Animation. - Die Statuslabels des
doctor-Berichts auf stdout ([ok ],[warn],[fail],[skip]) sind farbig, wenn stdout selbst ein interaktives Terminal ist; ein umgeleiteter Bericht bleibt schlicht, außerFORCE_schaltet die Farbe ein.COLOR --jsonfügt nie Farbe oder Animation hinzu, und stdout trägt ausschließlich Envelopes. Schlichteverbatra:-Warnzeilen können trotzdem auf stderr landen, ein Konsument parst also nur die stderr-Zeilen, die mit{beginnen. Siehe JSON-Ausgabe.
| Flag oder Variable | Wirkung |
|---|---|
-q, --quiet | gibt nur Ergebnisse, Warnungen und Fehler aus: lässt Fortschrittszeilen, next:-Tipps und informative stderr-Zeilen weg, während die Ergebniszusammenfassung samt ihren Hinweisen bleibt. Es wirkt vor oder nach dem Befehlsnamen |
--no-color | färbt die Ausgabe nie ein |
NO_, NODE_, VERBATRA_ | jeder nicht leere Wert schaltet die Farbe ab; VERBATRA_ übersteuert auch FORCE_ |
FORCE_ | 1, 2, 3, true oder ein leerer Wert schaltet die Farbe ein, auch wenn stderr oder stdout kein Terminal ist, und jeder andere Wert, auch 0, schaltet sie ab. Ist es gesetzt, gewinnt es gegen NO_, NODE_, CI und TERM=dumb; --no-color und VERBATRA_ haben trotzdem Vorrang |
VERBATRA_ | jeder nicht leere Wert ersetzt den Spinner durch statische Zeilen label... done (1.2s), für Screenreader und Terminalaufzeichnungen |
Die output-Beispiele auf den Befehlsseiten zeigen die schlichte Form. Ein interaktives Terminal zeigt einen laufenden Schritt als Spinner-Zeile, die mit [ok] und der verstrichenen Zeit endet.
Statuswörter sind reines ASCII ([ok], [warn], [fail], [skip]), damit die Ausgabe in jedem Terminal und Log gleich lesbar ist. Jede Zeile durchläuft dieselbe Schlüsselschwärzung wie die übrige Ausgabe, und eingefärbt werden nur die festen Labels, nie ein Schlüssel, ein Pfad oder eine Meldung.