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/cli

pnpm 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 gemini

With 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, or deepl (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
  • .gitignore entries for .env, .env.local, .verbatra-local/, and verbatra.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-run

A 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 .env

Then 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 translate

verbatra 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). init gitignored 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

Edit on GitHub