Your first translation
Install the CLI, scaffold a config with verbatra init, set your API key, and run your first translate. Under ten minutes from nothing to a translated locale file.
Four 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.
1. Install
verbatra is a dev dependency:
pnpm add -D @verbatra/clinpm and yarn work too. The package ships the verbatra binary; run it through your package manager (pnpm verbatra ..., npx verbatra ..., or yarn verbatra ...). The code blocks below use the bare name.
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, and.verbatra-local/, created or appended so a real key or local state 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. 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=. verbatra loads .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.
4. Translate
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."
}
}Then run:
verbatra translateverbatra reads the source locale, sees that every key is missing from de, sends them to the provider in batches, checks each result for placeholder and ICU integrity, 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 succeeded and 1 when one failed; 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. To preview any run without a provider call or a single write, use verbatra translate --dry-run.
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.