Your first translation
Install the CLI, scaffold a config with verbatra init, preview the run with no API key, then set your key and run your first translate. Under ten minutes from nothing to a translated locale file.
Five steps take you from an empty project to a translated locale file. All you need is Node.js >=22.14.0, and an API key for one provider once you are ready to translate for real. The first three steps cost nothing and need no account.
1. Install
verbatra is a dev dependency:
npm install --save-dev @verbatra/clipnpm and yarn work too. The package ships the verbatra binary; run it through your package manager (npx verbatra ..., pnpm verbatra ..., or yarn verbatra ...). The code blocks below use the bare name.
pnpm takes one extra step. pnpm add -D @verbatra/cli installs correctly but exits 1 with ERR_PNPM_IGNORED_BUILDS, and leaves an unanswered pnpm-workspace.yaml behind that makes every later pnpm command in the project fail the same way. Run pnpm approve-builds once and approve neither entry, or see Troubleshooting for the non-interactive fix.
2. Scaffold a config
verbatra init --provider geminiWith a terminal attached, init prompts for anything you did not pass as a flag, each with a default:
- the provider: one of
anthropic,openai,gemini, ordeepl(flag--provider, the only input without a default) - the source locale, default
en(--source) - the target locales, comma-separated, default
de(--targets) - the locale file pattern, default
locales/{locale}.json(--path)
Pass --yes to skip the prompts and take the defaults. Without a terminal (in CI, for example) init never prompts; it uses the defaults and only requires --provider.
init writes three things:
verbatra.config.ts: your project config, validated against the real schema before it is written.env.example: names the provider's key variable, never a key value.gitignoreentries for.env,.env.local,.verbatra-local/, andverbatra.cache.json, created or appended so a real key, local state, or the regenerable cache never lands in a commit
Re-running init skips files that already exist; --force overwrites them. It also pre-fills format by looking at your dependencies: a project using i18next, vue-i18n, next-intl, or @ngx-translate/core gets the matching JSON format, anything else defaults to i18next-json with a TODO comment to change it. For Gemini the scaffold looks like this:
import { defineConfig } from "@verbatra/cli";
export default defineConfig({
sourceLocale: "en",
targetLocales: ["de"],
format: "i18next-json",
files: {
pattern: "locales/{locale}.json",
},
provider: {
id: "gemini",
options: {
model: "gemini-2.5-flash",
maxOutputTokens: 4096,
},
},
});3. Preview without an API key
If you do not have a source file yet, create one at the configured pattern, for example locales/en.json:
{
"greeting": "Hello, {{name}}!",
"cart": {
"empty": "Your cart is empty."
}
}Now preview the run:
verbatra translate --dry-runA dry run constructs no provider at all, so it needs no API key and cannot spend anything. It reads your files, diffs them, and prints the same per-locale summary a real run would, without sending a single string or writing a single file. Use it to confirm your format, file pattern, and locales are right before you sign up for anything.
verbatra check and verbatra diff are provider-free in the same way: check reports per-locale counts, diff lists the exact keys.
4. Set your API key
Keys come from the environment, never from the config file. Each hosted provider reads exactly one variable; for Gemini that is GEMINI_API_KEY. Copy the scaffolded example and fill in your key:
cp .env.example .envThen open .env and paste your key after GEMINI_API_KEY=. translate, watch, and studio load .env.local and then .env from the working directory before a run, and a variable already set in your shell always wins. See Providers for every provider's variable.
No key yet? Gemini has a free tier.
Gemini's API has a genuinely free tier, which makes it the cheapest way to try verbatra. Grab a key from Google AI Studio and set GEMINI_API_KEY. The free tier has per-minute and per-day request limits, so pace a large first-time translation.
5. Translate
With the key in place, run the same command for real:
verbatra translateverbatra reads the source locale, sees that every key is missing from de, sends them to the provider in batches, runs each result through the integrity gate, and writes locales/de.json. The run ends with a per-locale summary: keys translated, keys unchanged, orphaned keys, and any notices. The exit code is 0 when every locale came out complete and 1 when one failed or came out partial (written, but with keys still missing); add --json for a machine-readable summary.
What just happened
Three things are now on disk:
-
locales/de.json: the target locale file. It has the same keys as your source, in the same document order, and{{name}}survived translation intact; a result that dropped it would have been withheld, not written. -
verbatra.lock.json: the lock file. For each target locale it maps every translated key to a hash of the source string that translation came from:{ "version": 1, "locales": { "de": { "cart.empty": "<source content hash>", "greeting": "<source content hash>" } } } -
.verbatra-local/: process-local state (the run-status snapshot and per-locale write locks).initgitignored it; never commit it.
The lock file is the baseline for every future run. Run verbatra translate again without editing anything and nothing is sent: every key is already current. Edit one source string and only that key is retranslated.
Commit the lock file
Commit verbatra.lock.json alongside your locale files, so every machine and your CI diff against the same baseline. See The lock file.
Next
- Add a language: drop a new target locale into this setup.
- How it works: the full pipeline behind a run.
- Configuration: everything
verbatra.config.tscan hold.
Introduction
What verbatra is, the problem it solves, and how the CLI, SDK, and Studio fit together. Translate only the keys that changed, with integrity checks on every result.
Add a language
Add a new target locale to an existing verbatra project with a one-line config change and one translate run, while your existing locales stay untouched.