Referenz / Konfiguration
Maschinell übersetzte Seite
Konfigurationsdatei
Jeder Konfigurations-Key mit Typ, Default und der Version, die ihn eingeführt hat, dazu wo die Datei liegt und wie eine fehlerhafte Konfiguration scheitert.
verbatra liest ein geheimnisfreies Konfigurationsobjekt und validiert es vor jeder Arbeit. Kein API-Schlüssel wird je daraus gelesen (Provider), die Konfiguration bleibt also gefahrlos committbar.
Eine minimale Konfiguration
import { defineConfig } from "@verbatra/cli";
export default defineConfig({
// Locales and files: required
sourceLocale: "en",
targetLocales: ["de", "fr"],
format: "i18next-json",
files: {
// {locale} is the only token
pattern: "locales/{locale}.json",
},
// Provider: required; the API key comes from the environment, never from here
provider: {
id: "gemini",
options: {
// example model id; use your provider's current one
model: "gemini-2.5-flash",
maxOutputTokens: 4096,
},
},
// Optional groups, each off or at its default until you add it:
// tone: "informal",
// glossary: "glossary.json",
// network: { policy: "local-only" },
// sensitiveData: { mode: "warn" },
// maxTokens: 200000,
// budgetBehavior: "stop",
});Siehe Locales und Dateien, provider, glossary, network, sensible Inhalte, Budget, rates und extract.
defineConfig typisiert sein Argument nur: options.model vervollständigt die Modell-IDs des Providers, zur Laufzeit gilt aber jeder nicht-leere String.
Jeder Key
Unbekannte Keys werden auf jeder Ebene außer $schema und Maps, deren Keys du selbst benennst, etwa maxLength, rates.table, localeMap und ein Inline-glossary, abgelehnt, also kann kein API-Schlüssel in der Konfiguration leben.
| Key | Typ | Default | Seit | Wirkung |
|---|---|---|---|---|
$schema | String | keiner | 0.9.0 | Zeigt einem Editor das JSON-Schema; zur Laufzeit ignoriert. |
sourceLocale | Locale-Code | erforderlich | 0.1.0 | Die Locale, in der deine Quellstrings geschrieben sind. |
targetLocales | Array von Locale-Codes | erforderlich | 0.1.0 | Mindestens eine; nie sourceLocale, nie zwei, die sich nur in der Groß- und Kleinschreibung unterscheiden. |
format | Format-Id | erforderlich | 0.1.0 | Das Format deiner Locale-Dateien, oder eine custom:-Id für einen Adapter von außerhalb. |
files.pattern | String | erforderlich | 0.1.0 | Der Pfad zu jeder Locale-Datei, mit einem {locale}-Token. |
files.localeStyle | "literal", "posix", "android" | "literal" | 0.7.0 | Wie {locale} im Pfad geschrieben wird. |
provider.id | Provider-Id | erforderlich | 0.1.0 | Der Übersetzungs-Provider, oder none für keine maschinelle Übersetzung. |
provider.options | Objekt | erforderlich, außer bei none | 0.1.0 | Die eigenen Optionen des Providers. |
glossary | Objekt oder Dateipfad | keines | 0.1.0 | Feste Übersetzungen, verbotene Wiedergaben und Begriffe, die unübersetzt bleiben. |
tone | "formal", "informal", "neutral" | keiner | 0.1.0 | Das Register, in dem übersetzt wird, angewendet pro Provider. |
prune | Boolean | false | 0.3.0 | Entfernt verwaiste Keys aus Zieldateien und der Lock-Datei; translate --prune überschreibt es. |
generatePlurals | Boolean | false | 0.3.0 | Synthetisiert die CLDR-Pluralformen, die eine Zielsprache braucht. |
fuzzyCache.enabled | Boolean | im Block erforderlich | 0.11.0 | Übersetzung einer leicht geänderten Quelle wiederverwenden. |
fuzzyCache.threshold | Zahl, 0.5 bis 1 | 0.9 | 0.11.0 | Wie ähnlich alte und neue Quelle sein müssen. |
humanEdits | "protect", "suggest", "overwrite" | "protect" | 0.12.0 | Was ein Lauf mit einem veralteten Key macht, den ein Mensch geschrieben hat. |
pinnedKeys | String-Array | keiner | 0.12.0 | Key-Muster (* als Platzhalter), die kein maschinelles Schreiben anfasst. |
network.policy | "any", "local-only", "allowlist" | "any" | 0.12.0 | Zu welchen Hosts sich ein Provider verbinden darf. |
network.allowedHosts | String-Array | keiner; bei "allowlist" erforderlich | 0.12.0 | Zusätzlich erlaubte Hosts: Namen, *.suffix-Platzhalter, IPs oder CIDR-Bereiche. |
sensitiveData.mode | "off", "warn", "block", "redact" | im Block Pflicht; "off" ohne den Block | 0.12.0 | Was ein Lauf mit sensiblen Inhalten vor dem Senden macht. |
sensitiveData.detectors | Array von Detektor-IDs | secret, email, iban, credit-card | 0.12.0 | Aktive Detektoren; phone, ip, private-host sind optional. |
sensitiveData.patterns | String-Array | keiner | 0.12.0 | Reguläre Ausdrücke für eigene Begriffe, etwa Codenamen. |
sensitiveData.allow | String-Array | keiner | 0.12.0 | Nie gemeldete Treffer; * ist ein Platzhalter, Groß- und Kleinschreibung zählt nicht. |
maxBatchSize | positive Ganzzahl | 50 | 0.3.0 | Die meisten Einträge pro Provider-Anfrage. |
maxLength | Zuordnung von Key zu positiver Ganzzahl | keines | 0.11.0 | Längenbudgets pro Key in Graphemclustern. |
maxTokens | positive Ganzzahl | keines | 0.5.0 | Ein Token-Budget für den ganzen Lauf. |
budgetBehavior | "warn", "stop" | "warn" | 0.5.0 | Ob das Erreichen von maxTokens warnt oder das Senden stoppt. |
rates.asOf | Datum YYYY-MM-DD | im Block erforderlich | 0.11.0 | Das Datum, an dem du die Raten gelesen hast. |
rates.currency | drei Großbuchstaben | im Block erforderlich | 0.11.0 | Die Währung jeder Rate, etwa USD. |
rates.table | Zuordnung von Raten-Key zu Rate | im Block erforderlich | 0.11.0 | Raten pro Raten-Key. |
extract.framework | "i18next" | im Block erforderlich | 0.11.0 | Nach welchen Aufrufstellen der Quell-Scan sucht. |
extract.roots | String-Array | im Block erforderlich | 0.11.0 | Die zu scannenden Verzeichnisse, mindestens eines. |
extract.exclude | String-Array | keiner | 0.11.0 | Weitere zu überspringende Verzeichnisnamen. |
extract.literals.ignore | String-Array | keiner | 0.11.0 | Exakte Texte, die doctor --literals zurückhält. |
extract.unused.ignore | String-Array | keiner | 0.11.0 | Keys oder *-Muster, die diff --unused nie meldet. |
Locales und Dateien
-
files.patternmuss{locale}enthalten, sein einziges Token, und wird gegen die Projektwurzel aufgelöst:locales/{locale}.jsonwird zulocales/de.json. Es ist ein wörtlicher Pfad, nie ein Glob, eine Konfiguration adressiert also eine Datei pro Locale (Namespace-Layouts);apple-xcstringslöst jede Locale zum selben Katalog auf (Xcode String Catalogs). -
files.localeStyleentscheidet, wozu das Token expandiert:literalschreibt das Tag wie konfiguriert, wie es die meisten Layouts brauchen.posixersetzt-durch_für gettext und Javasmessages_(aus{locale}.properties pt-BRwirdpt_); eine Variante wieBR de-1996hat keine posix-Schreibweise.gettext-poschreibt Schriften auf die gettext-Art (nächster Abschnitt).androidexpandiert zu einem Ressourcenverzeichnis, geschrieben alsres/{locale}/strings.xml:valuesfür die Quelle, dannvalues-de,values-pt-rBR,values-b+zh+Hans.
Ein Style lehnt eine Locale ab, die er nicht schreiben kann.
gettext-Locale-Namen
Mit format: "gettext-po" und localeStyle: "posix" tragen Dateien gettext-Namen (language_), während die Konfiguration BCP-47-Codes behält:
| Code in der Konfiguration | Dateiname |
|---|---|
pt-BR | pt_ |
es-419 | es_ |
sr-Latn | sr@latin |
sr-Latn-RS | sr_ |
uz-Cyrl | uz@cyrillic |
ks-Deva-IN | ks_ |
zh-Hant-TW | zh_ |
sr-Cyrl-RS | sr_ |
Eine Schrift, die Sprache und Region vorgeben, fällt weg, deshalb scheitern zh-TW und zh-Hant-TW zusammen mit LOCALE_; eine Schrift ohne gettext-Modifikator, etwa zh-Hant allein, scheitert mit LOCALE_. verbatra init liest diese Namen zurück. Andere Formate behalten die ICU-Schreibweise (sr_).
Locale-Codes
Jeder Locale-Code muss ein Tag sein, den Intl.getCanonicalLocales akzeptiert, mit einem Sprach-Subtag aus zwei oder drei Buchstaben, sonst scheitert das Laden mit CONFIG_: pt_ (schreib pt-BR mit localeStyle: "posix") und german werden abgelehnt. Ein gültiger, nicht kanonischer Code wie zh-hant-tw oder das veraltete iw funktioniert wie geschrieben.
provider
provider.id wählt einen der Provider oder none, dessen options fehlen darf und sonst {} sein muss (Ohne maschinelle Übersetzung arbeiten). Jeder andere Provider akzeptiert options.localeMap und options.requestTimeoutMs (Provider); ab 0.12.0 scheitert ein falsch platzierter Key unter provider, etwa localeMap, mit CONFIG_.
glossary
Begriffe, die auf eine feste Weise übersetzt werden, inline oder als Pfad zu einer JSON-Datei mit derselben Form (nie zusammengeführt). Version 1 ordnet jedem Quellbegriff eine Übersetzung für alle Locales zu:
export default defineConfig({
// ...
glossary: {
"Sign in": "Anmelden",
},
// or: glossary: "glossary.json",
});Ein relativer Pfad wird gegen das Verzeichnis der Konfigurationsdatei aufgelöst; die Datei muss UTF-8 und höchstens 1 MiB groß sein und wird einmal beim Laden gelesen. Eine fehlende Datei, ungültiges JSON oder eine ungültige Form scheitert mit CONFIG_ und nennt den Pfad sowie bei Version 2 das Feld. Leg einen Begriff _ in eine Datei; ein JavaScript-Literal macht daraus den Prototyp.
Glossar pro Locale (Version 2)
Version 2 ergänzt Übersetzungen pro Locale, verbotene Wiedergaben und Begriffe, die bleiben, wie sie geschrieben sind:
{
"version": 2,
"terms": [
{
"source": "Dashboard",
"targets": { "de": "Übersicht", "fr": "Tableau de bord" },
"forbidden": { "de": ["Instrumententafel"] },
"note": "The start page after sign-in",
"partOfSpeech": "noun"
},
{ "source": "Sign in", "target": "Anmelden", "caseSensitive": true }
],
"doNotTranslate": ["verbatra", { "term": "Acme", "caseSensitive": false }]
}| Feld | Bedeutung |
|---|---|
version | Immer 2; jede andere Zahl scheitert. |
terms[].source | Der Quellbegriff; jeder Begriff kommt einmal vor. |
terms[].target | Die Übersetzung für jede Locale ohne eigenen Eintrag in targets. |
terms[].targets | Übersetzungen pro Locale. Eine Locale nimmt ihren eigenen Eintrag, dann den ihrer Basissprache (de deckt de-AT ab), dann target; verglichen werden kanonische Codes ohne Groß- und Kleinschreibung. |
terms[].forbidden | Wiedergaben, die eine Locale nie verwenden darf; eine regionale Locale befolgt auch die Liste ihrer Basissprache. |
terms[].caseSensitive | Begriff, Übersetzungen und verbotene Wiedergaben mit Groß- und Kleinschreibung vergleichen. Default false. |
terms[].note, terms[].partOfSpeech | Freitext-Kontext, den ein LLM-Provider als Daten erhält. |
doNotTranslate | Begriffe, die unübersetzt übernommen werden. Ein reiner String behält seine Groß- und Kleinschreibung; bei { "term", "caseSensitive": false } darf sie sich ändern. |
Jeder Begriff braucht ein target, einen Eintrag in targets oder eine forbidden-Liste; er darf seine eigene Übersetzung nicht verbieten, keine Locale doppelt nennen und nicht in doNotTranslate stehen. Kein Text darf leer sein; ein Begriff hat höchstens 200 Zeichen, eine Übersetzung oder verbotene Wiedergabe 2000, eine note 500 und eine partOfSpeech 50. Der Cache-Fingerprint einer Locale deckt nur ihre eigenen Begriffe ab. Eine Datei der Version 1 behält ihren Fingerprint; updateGlossaryTerm schreibt sie nur für eine Änderung, die Version 1 nicht ausdrücken kann, als Version 2 neu.
Studio bearbeitet nur die Dateiform, Begriff für Begriff. Welche Provider das Glossar anwenden, steht unter Provider; jede Ausgabe wird danach geprüft.
generatePlurals
Wenn true, synthetisiert verbatra die CLDR-Pluralformen, die ein Ziel braucht, der Quelle aber fehlen, etwa few und many im Polnischen, und Ordinal-Keys (<key>_) nach den CLDR-Ordinalregeln. Nur i18next-json mit einem LLM-Provider unterstützt das; alles andere, und eine Locale ohne CLDR-Regeln, bekommt die Plural-Warnung (Ordinalzahlen nehmen other an). Es gibt kein CLI-Flag; translate() im SDK nimmt einen Override pro Lauf.
fuzzyCache
fuzzyCache: { enabled: true } lässt eine geänderte Quelle die Übersetzung ihrer früheren Form wiederverwenden, wenn beide mindestens threshold ähnlich sind, immer als FUZZY_ markiert (Unscharfe Wiederverwendung); --no-cache umgeht es.
humanEdits und pinnedKeys
humanEdits betrifft einen veralteten Key, dessen Wert ein Mensch geschrieben hat (Provenance human, import oder external); ein Key, der auf pinnedKeys passt (etwa ["legal.*", "brand.name"]), wird nie maschinell geschrieben, egal was humanEdits sagt. * passt auf jede Zeichenfolge, Punkte eingeschlossen, und der Abgleich unterscheidet Groß- und Kleinschreibung. Was jeder Modus macht, steht unter Menschliche Übersetzungen behalten.
network
Siehe Netzwerkzugriff einschränken.
network: { policy: "local-only", allowedHosts: ["gpu.lan"] }maxBatchSize
Fehlende und geänderte Einträge gehen in sequenziellen Sub-Batches von höchstens dieser Größe hinaus. Kein CLI-Flag überschreibt es.
maxLength
Längengrenzen pro Key, für Strings unter einer Layout-Vorgabe:
maxLength: {
"nav.dashboard": 18,
"cta.submit": 12,
}- Hier oder in
verbatra.limits.json. neu Keys werden wörtlich verglichen, nie als Globs; ein Key, der an keinem der beiden Orte gesetzt ist, wird nie gemessen.setKeyLengthLimitnimmt nur Keys der Quelle, also kann Studio für eine erzeugte Pluralform kein Limit setzen; ein von Hand geschriebener Eintrag für ihren eigenen Key gilt. Die eingecheckte Datei neben der Lock-Datei nimmt{ "version": 1, "maxLength": { ... } }, die Konfiguration gewinnt pro Key, undsetKeyLengthLimitschreibt sie. - Graphemcluster. Die Länge zählt wahrgenommene Zeichen, Leerraum eingeschlossen: Ein Emoji, ein Buchstabe mit Akzent oder ein CJK-Zeichen zählt als eines. Die Grenze ist inklusiv.
- Wann. Jeder Wert, den
translatefür den Key erzeugt, Cache-Wiederverwendungen eingeschlossen; ein aktueller Key wird erst gemessen, wenn sich seine Quelle ändert, oder durchexport --include-unchanged. - Beratend. Eine Überschreitung wird trotzdem geschrieben und gelockt und als
MAX_zum Review markiert.LENGTH_ EXCEEDED
maxTokens und budgetBehavior
maxTokens ist ein Budget für den ganzen Lauf: Eingabe- plus Ausgabe-Tokens über jeden Provider-Aufruf (Übersetzung und Pluralgenerierung) und jede Ziel-Locale. translate --max-tokens kann es für einen Lauf verschärfen, nie lockern, und zwar immer als hartes "stop".
budgetBehavior entscheidet, was dann passiert:
"warn", der Default, zählt die Ausgaben und vermerkt die Überschreitung, läuft aber weiter."stop"macht es zur Obergrenze für jeden Provider. Vor jeder Anfrage projiziert der Lauf ihre Kosten wie die Vorab-Schätzung und lehnt eine ab, diemaxTokensüberschreiten würde. Die Keys dieser Anfrage und jeder noch nicht versuchte Key (der Rest der Locale und jede spätere Locale, deren Vergleich und Verwaisten-Meldung trotzdem laufen) werden zurückgehalten, behalten ihren vorherigen Lock-Hash und werden beim nächsten Lauf erneut versucht.
Unter "stop":
- Nach jeder Anfrage wird die Summe auf den vom Provider gemeldeten Verbrauch abgeglichen, ein Lauf kann also knapp über
maxTokensenden (dasbudgetder Laufzusammenfassung). Maschinenübersetzungs-Provider melden keine Tokens und werden über die Projektion budgetiert. - Eine fehlgeschlagene oder abgeschnittene Anfrage wird voll belastet.
- Die Projektion enthält einen festen Aufschlag für den LLM-Prompt, auch für Maschinenübersetzungs-Provider, und umfasst einen ganzen Batch, ein
maxTokensunterhalb der Projektion eines Batches lehnt ihn also bei jedem Lauf ab; der Hinweis sagt,maxBatchSizezu senken odermaxTokenszu erhöhen. - Eine Locale mit zurückgehaltenen Keys ist
partial, oderfailed, wenn kein Key ankam, und beide enden mit Exit1. Unter"warn"ändert sich der Exit-Code nie.
Das Budget begrenzt einen translate-Lauf, nie eine Neuübersetzung eines einzelnen Eintrags; watch startet pro Lauf ein frisches. Ein Budget schließt --concurrency über 1 bei einem echten Lauf aus; ein Dry-Run ist ausgenommen. budgetBehavior ohne maxTokens hat keine Wirkung.
Zwei verschiedene maxTokens
Das Top-Level-maxTokens ist dieses Budget für den ganzen Lauf; Anthropics davon unabhängiges provider.options.maxTokens begrenzt eine Antwort (Provider).
rates
Wird nur von translate --estimate gelesen, das aus Raten Geld macht; ein echter Lauf ignoriert es.
rates: {
asOf: "2026-01-15",
currency: "USD",
table: {
// placeholders: copy the current rates from each provider's pricing page
"gemini/gemini-2.5-flash": { inputPerMillionTokens: 1, outputPerMillionTokens: 2 },
deepl: { perMillionCharacters: 1 },
},
}Ein table-Key ist provider/model, oder die blanke Id für einen Provider ohne Modell (deepl, google-translate). Ein Eintrag wird nach Tokens (inputPerMillionTokens, outputPerMillionTokens) oder nach Zeichen (perMillionCharacters) abgerechnet, nie beides, und eine negative Rate, eine Rate über 1.000.000.000 pro Million oder die falsche Einheit wird abgelehnt. openai-compatible und libretranslate stellen keine Rechnung, deshalb wird ihre Menge ohne Rate gemeldet.
extract
Wird nur von extract, doctor --literals und diff --unused gelesen.
extract: {
framework: "i18next",
roots: ["src", "app"],
exclude: ["generated"],
literals: { ignore: ["Acme Inc."] },
unused: { ignore: ["emails.*"] },
}frameworkmuss eines mit Extractor nennen (Aufrufformen); jedes andere wird abgelehnt.rootswerden gegen die Projektwurzel aufgelöst;excludeergänzt Verzeichnisnamen (keine Pfade), die überall unterhalb eines Roots passen, zur immer übersprungenen Menge.literals.ignoretrifft den ganzen Text eines Literals, Leerraum zusammengefasst und Referenzen aufgelöst (Nicht übersetzte Literale).unused.ignoregleicht den dekodierten Key ab (diff --unused).
Suchreihenfolge
verbatra sucht vom Arbeitsverzeichnis aus aufwärts und nimmt den ersten Treffer. Die Suche endet beim nächsten Verzeichnis mit .git, sonst bei deinem Home-Verzeichnis (oder beim Arbeitsverzeichnis, wenn Home nicht darüber liegt), greift also nie eine Konfiguration oberhalb deines Repositorys auf:
package.json(eine"verbatra"-Eigenschaft).verbatrarc.verbatrarc.json.verbatrarc.yaml.verbatrarc.yml.verbatrarc.js.verbatrarc.cjs.verbatrarc.tsverbatra.config.jsverbatra.config.cjsverbatra.config.ts
--config <path> lädt stattdessen genau eine Datei, relativ zum Arbeitsverzeichnis; den Vorrang im SDK beschreibt loadConfig.
Validierung im Editor
@verbatra/sdk liefert ein JSON Schema aus seinem zod-Schema aus, auch unter einer festen URL für jede Installation, npx, ein Monorepo mit Hoisting oder einen entfernten Agenten:
https://verbatra.kreitz-webdev.de/schema/v1/config.jsonOffline, oder um die installierte Version festzulegen, nimmst du die Kopie im Paket, ./node_ (alle Schemas).
.verbatrarc.json und .verbatrarc nehmen einen $schema-Key für VS Code:
{
"$schema": "https://verbatra.kreitz-webdev.de/schema/v1/config.json",
"sourceLocale": "en",
"targetLocales": ["de", "fr"],
"format": "i18next-json",
"files": { "pattern": "locales/{locale}.json" },
"provider": { "id": "gemini", "options": { "model": "gemini-2.5-flash", "maxOutputTokens": 4096 } }
}Das endungslose .verbatrarc braucht zusätzlich eine Workspace-Dateizuordnung:
{
"json.schemas": [
{
"fileMatch": [".verbatrarc", ".verbatrarc.json"],
"url": "https://verbatra.kreitz-webdev.de/schema/v1/config.json"
}
]
}Eine YAML-Konfiguration nimmt einen YAML-Language-Server-Kommentar:
# yaml-language-server: $schema=https://verbatra.kreitz-webdev.de/schema/v1/config.json
sourceLocale: en
targetLocales: [de, fr]
format: i18next-json
files:
pattern: "locales/{locale}.json"
provider:
id: gemini
options:
model: gemini-2.5-flash
maxOutputTokens: 4096package.json nimmt kein Schema; ausführbare Formen typisiert defineConfig.
Was das Dokument nicht prüfen kann
Nur das Laden der Konfiguration prüft diese Regeln: die Regeln für targetLocales und Locale-Codes, die Keys von localeMap und die Regeln für baseUrl und apiKeyEnvVar von openai-compatible, gültige Adressen und Bereiche in network.allowedHosts, sensitiveData.patterns innerhalb der Sicherheitsregeln für Muster und die Begriffsregeln eines Inline-Glossars der Version 2.
Das Arbeitsverzeichnis und .env
Das Arbeitsverzeichnis (wo du verbatra ausführst, oder --cwd) startet die Konfigurationssuche und hält die Umgebungsdateien. Locale-Pfade, verbatra.lock.json, der Cache, die Provenienzdatei und .verbatra-local/ werden gegen die Projektwurzel aufgelöst, das Verzeichnis der gefundenen Konfiguration, sodass ein Lauf aus einem Unterverzeichnis dieselben Dateien nutzt. Mit --config ist das Arbeitsverzeichnis die Wurzel, und eine Konfiguration an anderer Stelle bekommt einen Hinweis auf stderr, weil ihr Glossarpfad gegen ihr eigenes Verzeichnis aufgelöst wird. Ein Pfad, den du übergibst, etwa bei --out oder check --file, wird gegen das Arbeitsverzeichnis aufgelöst.
Wenn das Laden fehlschlägt
CONFIG_: an keinem der durchsuchten Orte eine Konfiguration, oder derNOT_ FOUND --config-Pfad existiert nicht.CONFIG_: Eine Konfiguration wurde gefunden, ist aber nicht verwendbar. Die Meldung listet jedes Problem mit seinem Key-Pfad; ein unbekannter Key bekommt den Hinweis, dass API-Schlüssel aus der Umgebung kommen. Ein Parse-Fehler nennt die Datei (bei YAML mit Zeile und Spalte), zitiert aber nie ihren Inhalt, und jede Meldung läuft durch die Schlüssel-Redaktion.INVALID
Die CLI endet in beiden Fällen mit Exit 2.