跳转到主要内容

Agent Guide: Using i18n-rosetta

i18n-rosetta is a CLI tool that translates your app's locale files with one command. This guide is for AI agents (or developers working with AI agents) who want to go from zero to translated locale files quickly.

:::tip Already familiar? If you just need commands, jump to the CLI Reference. If you want to build and benchmark a translation method, see the Arena Agent Guide. :::


Environment Setup

# No global install needed — npx runs it directly
npx i18n-rosetta sync

Requirements:

  • Node.js 18+
  • An API key for your translation provider

API key setup — rosetta needs at least one key depending on which methods you use:

# Option 1: export (session only)
export OPENROUTER_API_KEY="sk-or-..." # for llm / llm-coached methods
export GOOGLE_TRANSLATE_API_KEY="AIza..." # for google-translate method

# Option 2: .env file in your project root (persistent, gitignored)
echo 'OPENROUTER_API_KEY=sk-or-...' > .env

Rosetta reads .env automatically. Get an OpenRouter key at openrouter.ai/keys.


First Sync

Rosetta auto-detects your locale files, their format (JSON, TOML, YAML, PO), and your target languages:

npx i18n-rosetta sync

What happens:

  1. Loads i18n-rosetta.config.json (or auto-detects settings)
  2. Scans your source locale file, flattens nested keys
  3. Compares against .i18n-rosetta.lock (SHA-256 hashes of previously translated values)
  4. Checks .rosetta/tm.json for cached translations (Translation Memory)
  5. Translates only changed, missing, or stale keys via the configured method
  6. Runs the quality gate (5 checks) on every translation
  7. Writes passing translations to the target locale file
  8. Updates the lock file and TM cache

On a typical re-run after changing one key, step 4 serves 142 keys from cache and step 5 translates 1 key. This is why subsequent syncs are fast and cheap.


Configuration

Create i18n-rosetta.config.json in your project root:

{
"inputLocale": "en",
"pairs": {
"en-fr": { "method": "llm-coached" },
"en-ja": { "method": "google-translate" },
"en-crk": { "method": "api", "endpoint": "http://localhost:3000/translate" }
}
}

Key fields:

FieldPurposeDefault
inputLocaleSource languageen
pairsMap of source→target with method config(required)
localesDirWhere locale files live(auto-detected)
modelLLM model for llm/llm-coached methodsgoogle/gemini-2.5-flash
batchSizeKeys per API call30 (LLM), 128 (Google)
concurrencyParallel API calls for content translation12

Full reference: Configuration


Translation Methods

MethodWhen to useCostAPI key needed
llmGeneral-purpose, good for well-resourced languagesPer-token (model-dependent)OPENROUTER_API_KEY
llm-coachedWhen you have grammar rules/dictionary for the target languagePer-token + coaching contextOPENROUTER_API_KEY
google-translateHigh-resource languages where GT works well$20/million charsGOOGLE_TRANSLATE_API_KEY
apiCustom pipeline hosted behind an HTTP endpointServer-determinedNone (endpoint handles auth)
pluginPre-packaged method installed locallyVariesVaries

Details: Translation Methods


Coaching Data

For llm-coached pairs, coaching data steers the LLM with explicit linguistic knowledge. Create a coaching file:

coaching/fr.json
{
"grammar_rules": [
"Use formal register (vous) for all UI text",
"Adjectives agree in gender and number with the noun"
],
"dictionary": {
"dashboard": "tableau de bord",
"settings": "paramètres"
},
"style_notes": "Prefer active voice. Avoid anglicisms."
}

Reference it in your pair config:

"en-fr": { "method": "llm-coached", "coachingFile": "coaching/fr.json" }

The quality gate verifies that dictionary terms actually appear in the output — violations are logged as [TERM] warnings.

Details: Coaching Data


Quality Gate

Every translation passes through five automated checks before it's written to disk:

CheckWhat it catchesExample
Empty/blankModel returned nothing""
Source echoModel returned the English input unchanged"Welcome" for Japanese
Hallucination loopRepeated trigrams"Qo' Qo' Qo' Qo'"
Length inflationOutput is 4×+ longer than source10-char source → 50-char output
Script complianceWrong script for the localeLatin text for Arabic locale

Failures are logged with [GATE] prefix. No silent fallbacks — if a translation fails, it's reported, not quietly accepted.

Details: Quality Gate


Translation Memory

Rosetta caches translations in .rosetta/tm.json, keyed by source text + locale + method. On subsequent syncs, unchanged keys are served from cache — no API call, no cost.

[TM] 142 key(s) served from cache
Translating 3 key(s) to French (llm)... [OK]

To bypass the cache for one run: npx i18n-rosetta sync --no-tm

Details: Translation Memory


Generated Files

Rosetta creates several files in your project. Know what they are so you don't accidentally delete or commit the wrong ones:

FilePurposeGit?
.i18n-rosetta.lockSHA-256 hashes of translated source values (change detection)Yes — commit this
.i18n-rosetta-content.lockSame, but for Markdown/MDX content filesYes — commit this
.rosetta/tm.jsonTranslation Memory cacheYes — commit this (saves API costs for the team)
.rosetta/coaching/Coaching data directoryYes — this is your linguistic knowledge
i18n-rosetta.config.jsonProject configurationYes — commit this

Common Patterns

Translate one language pair:

npx i18n-rosetta sync --pair en-fr

Translate all configured pairs:

npx i18n-rosetta sync

Rosetta processes pairs sequentially. With TM caching, only changed keys hit the API.

Content mode (Markdown/MDX for Docusaurus, Hugo, etc.):

npx i18n-rosetta sync --content

Translates docs, blog posts, and content files alongside locale JSON. Uses parallel concurrency (default: 12 simultaneous API calls).

Dry run (preview without writing):

npx i18n-rosetta sync --dry-run

Force re-translate specific keys:

npx i18n-rosetta sync --force-keys "hero.title,nav.about"

Force re-translate all content files:

npx i18n-rosetta sync --force-content

Check translation status:

npx i18n-rosetta status

Shows coverage, quality tiers, and plugin info for each pair.

Audit for untranslated fallbacks:

npx i18n-rosetta audit

Lists all [EN] fallback values that need translation.


Troubleshooting

ProblemFix
OPENROUTER_API_KEY not setExport the key or add it to .env in your project root
No locale files foundSet localesDir in config, or ensure your locale files match standard naming (en.json, fr.json)
[GATE] Script compliance failedYour target locale got Latin text instead of the expected script — try a different model or add coaching data
[GATE] Source echoThe model returned English unchanged — coaching data or a different model usually fixes this
All translations cachedRun with --no-tm to bypass the cache, or --force-keys for specific keys
Lock file conflicts.i18n-rosetta.lock uses SHA-256 hashes — merge conflicts are safe to resolve by keeping either version, then re-running sync

What's Next