Keep your translation files honest. Point this agent at a folder of
per-language JSON catalogs (en.json, fr.json, es.json, …) and it tells you
exactly what a release blocker looks like: missing keys, stale/orphan keys, and
the placeholder drift ({name}, {count}, %s) that quietly breaks a UI in
one language while every test still passes in another.
The audit and validation core is 100% deterministic — no LLM, no API key, no
network. Connect a coding-agent CLI (Claude Code / Kimi Code / Codex) and the
translate command additionally drafts the missing strings for you, with every
placeholder verified before it's written. No LLM connected? You still get a full
audit and safe TODO: stubs that keep placeholders intact.
Who it's for
Anyone shipping a product, app, or docs in more than one language and tired of
"the French build shows {count} items literally" bug reports:
- Product/frontend teams maintaining
i18next/vue-i18n/ gettext-style JSON. - Docs and marketing sites with parallel language folders.
- CI pipelines that should fail when a translation catalog goes out of sync.
What it is honest about
| Capability | Without an LLM | With a connected CLI |
|---|---|---|
| Coverage report per language | ✅ full | ✅ full |
| Missing / orphan key detection | ✅ full | ✅ full |
| Placeholder drift detection | ✅ full | ✅ full |
| CI validate gate (exit code) | ✅ full | ✅ full |
| Draft the missing translations | TODO: stubs (placeholders kept) |
real translations, placeholder-verified |
The LLM only ever writes to a side-car <locale>.draft.json for human review —
it never overwrites your real translation files.
Quick start
# 1) install (Debian/Ubuntu VM) — deps + venv + optional systemd service
sudo bash install.sh # or: bash install.sh --no-service
# 2) try it on the bundled sample catalog (en base, fr + es partial)
python agent.py audit
python agent.py validate ; echo "exit=$?" # non-zero: catalog is out of sync
# 3) draft the strings missing from Spanish
python agent.py translate --locale es
cat catalog/es.draft.json # review, then merge into es.json
# 4) point it at your own catalog
python agent.py audit --dir /path/to/locales --base en
How it works (OODA drift monitor)
┌──────────────────────────────────────────────┐
│ OBSERVE load base + target JSON catalogs │
│ ORIENT flatten keys, diff, check │
│ placeholders → coverage per lang │
│ DECIDE OK | REPORT_DRIFT (worst language) │
│ ACT emit JSON event / exit code │
└──────────────────────────────────────────────┘
run once (CI) │ loop (systemd)
agent.py run runs this loop continuously (systemd unit provided by
install.sh) so a catalog that drifts after a merge shows up in your logs
without anyone remembering to check.
Commands
| Command | What it does |
|---|---|
agent.py init |
Scaffold a sample catalog/ (en + fr) to play with |
agent.py audit |
Full report: coverage, missing, orphan, placeholder drift |
agent.py validate |
Same checks, exit 1 if any language is broken (CI gate) |
agent.py translate --locale <code> |
Draft missing strings → <code>.draft.json |
agent.py status |
Catalog health + which LLM backend (if any) is connected |
agent.py run [--once] |
OODA drift monitor (JSON events) |
Configuration
All optional, via environment variables (see SPEC.md):
I18N_CATALOG_DIR— catalog folder (defaultcatalog).I18N_BASE_LOCALE— source-of-truth language (defaulten).I18N_COVERAGE_WARNING— coverage floor before "degraded" (default0.90).PACK_LLM— pin a backendclaude|kimi|codex(default: auto-detect).
See CUSTOMIZE.md to plug in your own catalog and PROMPTS.md to tune the
translation prompt.