Automate i18n translation and keep your locale files in sync across languages with AI and machine-translation providers.
verbatra translates your application's locale files for you. You maintain the source locale by hand, and as strings are added or change, verbatra fills in every target locale through the AI or machine-translation provider you choose. It records what it has already translated, so each run touches only what actually changed.
It ships in two packages. @verbatra/cli gives you a verbatra command for the terminal and CI, and @verbatra/sdk is the same engine as a programmatic API. verbatra is built SDK-first: the CLI is a thin wrapper over the SDK, so anything the command line does, you can also do in code.
- Many locale formats. JSON for i18next, vue-i18n, next-intl, and ngx-translate, plus XLIFF, YAML, and ARB.
- Five providers behind one interface: Anthropic, OpenAI, Gemini, and openai-compatible (a local or self-hosted server such as LM Studio, Ollama, or vLLM) as LLMs, plus DeepL (machine translation).
- Incremental by default. A lock file records what has been translated, so each run sends only new or changed strings to the provider.
- Project scaffolding.
verbatra initwrites a config and a.env.examplefor your project. - Dry runs.
--dry-runpreviews what would change without calling a provider or writing files. - Read-only status and diff.
verbatra checkreports per-locale missing, stale, and up-to-date counts, andverbatra difflists the exact keys that would be added, re-translated, or are orphaned. Both write nothing and exit non-zero when locales are out of sync, so they slot into CI. - Watch mode.
verbatra watchre-translates automatically on every source change. - Manual translation.
verbatra exportwrites the strings that need translating to a styled Excel workbook for a human translator, andverbatra importreads the filled file back with the same safety checks as an automated run. - Placeholder integrity. Every translation is checked after the fact; a result that drops or alters a placeholder is withheld and reported rather than written.
- Lossless key round-trip. Literal dotted leaf keys (such as
"foo.bar"used as a single leaf) and real nested paths each keep their on-disk shape. A genuine collision, where one file expresses the same effective path both as a literal dotted leaf and as a real nested path, errors withINVALID_STRUCTURErather than guessing or corrupting data. See the Formats page for the full behavior. - Opt-in cleanup and plural generation. Orphan pruning (
--prune/prune) and CLDR plural-category generation (generatePlurals) are off by default and documented on the Configuration page. - Keys stay in your environment. API keys are read only from environment variables, never from the config.
Node.js >=22.14.0.
verbatra is a development dependency:
pnpm add -D @verbatra/cli
# npm
npm install -D @verbatra/cli
# yarn
yarn add -D @verbatra/cli# 1. Install as a dev dependency
pnpm add -D @verbatra/cli
# 2. Scaffold verbatra.config.ts and .env.example (choose your provider)
verbatra init --provider anthropic
# 3. Provide the provider's API key. init created .env.example and gitignored
# .env, so you can set it in .env, or export it (Anthropic shown):
export ANTHROPIC_API_KEY=your-key-here
# 4. Translate every target locale once
verbatra translateInvoke the binary through your package manager: pnpm verbatra ..., npx verbatra ..., or yarn verbatra ....
verbatra looks for its configuration upward from the working directory: a verbatra.config.ts, a .verbatrarc.json (and the other .verbatrarc.* variants), or a "verbatra" key in package.json. The quickest way to get a valid one is verbatra init. A minimal verbatra.config.ts:
import { defineConfig } from "@verbatra/sdk";
export default defineConfig({
sourceLocale: "en",
targetLocales: ["de", "fr"],
format: "i18next-json",
files: {
pattern: "locales/{locale}.json",
},
provider: {
id: "anthropic",
options: {
model: "claude-sonnet-4-6", // replace with your provider's model id
maxTokens: 4096,
},
},
});files.pattern must contain the {locale} token, and targetLocales must not include sourceLocale. The supported format values are i18next-json, vue-i18n-json, next-intl-json, ngx-translate-json, xliff, yaml, and arb. The optional glossary (a term map) and tone ("formal", "informal", or "neutral") refine the output.
The provider block is selected by id. The LLM providers take a model and a token limit; DeepL needs no model:
// OpenAI / Gemini
provider: { id: "openai", options: { model: "gpt-5.4-mini", maxOutputTokens: 4096 } }
// DeepL (machine translation)
provider: { id: "deepl", options: {} }Each provider reads its API key from one environment variable:
| Provider id | Environment variable |
|---|---|
anthropic |
ANTHROPIC_API_KEY |
openai |
OPENAI_API_KEY |
gemini |
GEMINI_API_KEY |
deepl |
DEEPL_API_KEY |
openai-compatible is not in this table: most local servers need no key at all, and when one is required you name your own environment variable for it. See the Providers page for its key resolution.
| Command | What it does | Common flags |
|---|---|---|
verbatra init |
Create a verbatra config and .env example for this project | --provider <id>, --source, --targets, --path, --yes, --force |
verbatra translate |
Translate every target locale once, then exit | --cwd, --config, --dry-run, --prune, --json |
verbatra watch |
Re-translate on every source change until interrupted | --cwd, --config, --debounce <ms>, --json |
verbatra check |
Report per-locale missing, stale, and up-to-date counts without writing (read-only) | --cwd, --config, --locales, --json |
verbatra diff |
List the keys per locale that would be added, re-translated, or are orphaned, without writing (read-only) | --cwd, --config, --locales, --json |
verbatra export |
Export untranslated strings into a styled Excel workbook for a human translator | --out, --locales, --include-unchanged, --cwd, --config, --json |
verbatra import <workbook> |
Import a filled workbook back into the locale files, with the same safety checks | --dry-run, --cwd, --config, --json |
Run verbatra <command> --help for the full option list. The complete command reference - every flag, examples, and the exit-code contract - lives on the documentation site.
verbatra studio starts Verbatra Studio, a local, read-only web dashboard over your project: translation status, the diff explorer, your resolved config and glossary, lock-file drift, and locale-file history, all read from the same computations as check and diff. It binds to 127.0.0.1 only, gates every request behind a per-session token, and never calls a provider or writes a file.
verbatra studioVerbatra Studio is currently available only as a prerelease. @verbatra/cli needs the next tag explicitly; @verbatra/studio has not had a stable release yet, so its own latest tag already carries the current prerelease build:
pnpm add -D @verbatra/cli@next @verbatra/studioA plain pnpm add -D @verbatra/cli (no tag) resolves to the current stable release, which does not include the studio command.
See the Verbatra Studio docs for the full command reference and security model.
Everything the CLI does is available from @verbatra/sdk:
import { loadConfig, translate } from "@verbatra/sdk";
// Discovers and validates verbatra.config.ts (or .verbatrarc.json, or a package.json "verbatra" key).
const config = await loadConfig();
// The provider reads its API key from the environment (e.g. ANTHROPIC_API_KEY). No key is passed.
const summary = await translate({ config });
console.log(`${summary.succeeded.length} locale(s) translated, ${summary.failed.length} failed`);The manual-translation workflow is available too, with exportWorkbook and importWorkbook:
import { exportWorkbook, importWorkbook, loadConfig } from "@verbatra/sdk";
const config = await loadConfig();
// Export the strings that need translating to an Excel workbook.
const { path } = await exportWorkbook({ config });
// ...a human fills the Translation column, then import the file back.
const summary = await importWorkbook({ config, workbook: path });See the @verbatra/sdk README for the full API.
| Package | Description |
|---|---|
@verbatra/cli |
The verbatra command-line tool. |
@verbatra/sdk |
The programmatic API. |
API keys are read only from environment variables, never from the config file. The config schema rejects unknown keys, so a key cannot hide there by accident, and verbatra init adds .env and .env.local to your .gitignore. To report a vulnerability, see SECURITY.md.
The hosted documentation site at verbatra.kreitz-webdev.de is the canonical reference, including the full CLI command reference. The @verbatra/sdk README documents the programmatic API. At the terminal, verbatra <command> --help prints the same command reference.
Contributions are welcome. Please read CONTRIBUTING.md and our Code of Conduct first.
MIT (c) Mario Kreitz
