SDK-Rezepte
Vollständige, lauffähige Muster, um verbatra aus deinen eigenen Skripten, CI-Jobs und Tools anzusteuern.
Maschinell übersetzte Seite
Die SDK-Referenz katalogisiert jeden Einstiegspunkt. Diese Seite ist der praktische
Begleiter: End-to-End-Rezepte, die du in ein Skript kopieren und ausführen kannst. Eine Sache
vorab: das SDK lädt keine .env-Dateien (das macht die CLI), stelle also sicher, dass die
Umgebungsvariable des Providers gesetzt ist, bevor dein Skript läuft, zum Beispiel:
node --env-file=.env translate.mjsEinmal übersetzen in einem Skript
Lade die Konfiguration, führe den Flow aus, dann lies die Zusammenfassung: die Schlagzeile aus
succeeded und failed, den Token-Verbrauch aus usage und die Keys, die einen menschlichen
Blick verdienen, aus dem needsReview jedes Locales. Probleme des gesamten Laufs werfen einen
SdkError; Ergebnisse pro Locale sind Daten auf der Zusammenfassung.
import { loadConfig, translate } from "@verbatra/sdk";
const config = await loadConfig();
const summary = await translate({ config });
console.log(`${summary.succeeded.length} locales ok, ${summary.failed.length} failed`);
if (summary.usage !== undefined) {
console.log(`tokens: ${summary.usage.inputTokens} in, ${summary.usage.outputTokens} out`);
}
for (const locale of summary.locales) {
if (locale.status === "failed") {
console.error(`${locale.locale}: ${locale.error?.code} ${locale.error?.message}`);
continue;
}
for (const entry of locale.needsReview) {
console.warn(`review ${locale.locale}/${entry.key}: ${entry.reasons.join(", ")}`);
}
}
if (summary.failed.length > 0) {
process.exitCode = 1;
}Übergib dryRun: true, um ohne Provider-Aufruf und ohne Schreiben eine Vorschau zu bekommen, und
prune: true oder generatePlurals: true, um diese Konfigurationsoptionen für einen Lauf zu
übersteuern. Setze concurrency über 1, um Locales parallel zu übersetzen (bei einem Live-Lauf
nicht mit einem maxTokens-Budget erlaubt), und cache: false, um den
Translation-Memory-Cache zu umgehen.
In CI prüfen, ohne zu schreiben
check liest und vergleicht, ohne einen Provider aufzurufen oder irgendeine Datei anzufassen, und
sein inSync-Flag ist genau dann true, wenn nichts fehlt und nichts veraltet ist. Das macht es
zum natürlichen CI-Gate.
import { check, loadConfig } from "@verbatra/sdk";
const summary = await check({ config: await loadConfig() });
if (!summary.inSync) {
for (const locale of summary.locales) {
if (!locale.inSync) {
console.error(`${locale.locale}: ${locale.missing} missing, ${locale.stale} stale`);
}
}
process.exitCode = 1;
}Du brauchst die Key-Namen statt Zähler? Tausche diff ein, das die Key-Listen missing,
changed und orphaned pro Locale zurückgibt, mit demselben hasPendingChanges-Gate. Du führst
stattdessen die CLI in CI aus? Siehe CI und Exit-Codes.
Beobachten in einem langlaufenden Prozess
watch feuert beim Start sofort einen Lauf, danach einen pro entprellter Quelländerung, und
meldet jeden über onRun. Für ein sauberes Herunterfahren warte auf controller.stop(): es
schließt den Watcher und wartet auf den laufenden Lauf, bevor dein Prozess endet.
import { loadConfig, watch } from "@verbatra/sdk";
const config = await loadConfig();
const controller = await watch({
config,
onRun: (result) => {
if (result.status === "succeeded") {
console.log(`ran: ${result.summary.succeeded.length} ok, ${result.summary.failed.length} failed`);
} else {
console.error(`run failed: ${result.error.code} ${result.error.message}`);
}
},
});
process.on("SIGINT", () => {
void controller.stop().then(() => {
process.exit(0);
});
});Ein Lauf-Fehlschlag nach dem Start wirft nie; er kommt als { status: "failed" } an, und das
Beobachten geht weiter.
Einen einzelnen Key bearbeiten und neu übersetzen
Das sind die Nahtstellen, die Verbatra Studio antreibt; nutze sie, um deinen eigenen Review-Flow
zu bauen. Lies die aktuellen Werte mit keyValue, speichere eine menschliche Korrektur mit
editEntry oder lass den Provider für einen Key mit retranslateEntry neu laufen. Beide
Schreiber schicken den Kandidatenwert durch dieselben Platzhalter- und ICU-Prüfungen wie ein
voller Lauf und geben ein zweiarmiges Ergebnis zurück, statt bei einem abgelehnten Wert zu werfen.
import { editEntry, keyValue, loadConfig, retranslateEntry } from "@verbatra/sdk";
const config = await loadConfig();
// Read the live values feeding your edit UI.
const current = await keyValue({ config, locale: "de", key: "checkout.title" });
console.log(`source: ${current.source}, target: ${current.target ?? "(not yet translated)"}`);
// Save a human-typed correction. No provider call.
const edit = await editEntry({
config,
locale: "de",
key: "checkout.title",
value: "Zur Kasse",
});
if (!edit.accepted) {
console.error(`rejected (${edit.reason} check failed), nothing written`);
}
// Or ask the provider for a fresh translation of just this key.
const retry = await retranslateEntry({ config, locale: "de", key: "checkout.title" });
if (retry.accepted) {
console.log(`wrote: ${retry.value}`);
if (retry.reviewReasons.length > 0) {
console.warn(`flagged for review: ${retry.reviewReasons.join(", ")}`);
}
}Ein unbekanntes Locale oder ein unbekannter Key wirft UNKNOWN_LOCALE oder UNKNOWN_KEY, und
retranslateEntry wirft den eigenen ProviderError des Providers (zum Beispiel RATE_LIMITED),
wenn der Aufruf selbst fehlschlägt. Dieselben Nahtstellen hinter einer Oberfläche zeigt
Review in Studio.
Der Arbeitsmappen-Roundtrip
Exportiere die zu übersetzenden Strings in eine Excel-Arbeitsmappe, gib sie an eine Übersetzerin,
dann importiere die ausgefüllte Datei zurück. importWorkbook führt dieselben Drift-,
Platzhalter- und ICU-Prüfungen aus wie translate und gibt dieselbe RunSummary-Form zurück; eine
manuelle Übergabe fügt sich also in exakt das Reporting ein, das du für einen automatischen Lauf
nutzt.
import { exportWorkbook, importWorkbook, loadConfig } from "@verbatra/sdk";
const config = await loadConfig();
// Write a workbook of the missing and changed strings.
const exported = await exportWorkbook({ config });
for (const sheet of exported.locales) {
console.log(`${sheet.locale}: ${sheet.rows} rows`);
}
console.log(`wrote ${exported.path}`);
// ...later, after the translator returns the file, import it back.
const summary = await importWorkbook({ config, workbook: exported.path });
console.log(`${summary.succeeded.length} locales ok, ${summary.failed.length} failed`);Übergib dryRun: true an importWorkbook, um eine zurückgekommene Datei zu validieren, ohne
etwas zu schreiben. Was Übersetzerinnen bearbeiten dürfen und wie Zeilen validiert werden, steht
in Menschliche Übersetzung.
Eine Konfiguration ohne Datei
loadConfig akzeptiert ein configOverride im Speicher, validiert genau wie eine geladene Datei;
du kannst verbatra also komplett aus Code heraus fahren, ohne Konfigurationsdatei auf der Platte.
import { loadConfig, translate } from "@verbatra/sdk";
const config = await loadConfig({
configOverride: {
sourceLocale: "en",
targetLocales: ["de", "fr"],
format: "i18next-json",
files: { pattern: "locales/{locale}.json" },
provider: { id: "gemini", options: { model: "gemini-2.5-flash", maxOutputTokens: 4096 } },
},
});
await translate({ config });Jede Eingabeform, die vollständige RunSummary-Anatomie und die SdkError-Code-Tabelle stehen in
der SDK-Referenz.