Local-first, AI-assisted i18n CLI for XLIFF, ARB, and nested JSON.
Engo fills in the missing strings of your translation catalogs with the LLM of your choice, while preserving file structure, placeholders, metadata, and your reviewers' work. It runs on your machine β your API key, your files, no third-party servers in the default path.
cd my-app/
export ANTHROPIC_API_KEY=sk-ant-...
engo init # detects your i18n setup and writes engo.toml
engo translate # fills in every missing target stringMost i18n-with-AI tools are either SaaS (your strings leave your machine) or generic LLM wrappers (they mangle placeholders and lose XLIFF metadata). Engo is built around three beliefs:
- Your translation files are source of truth. Engo never reformats, reorders, or strips metadata. It only writes the target values you asked it to translate, and it writes them atomically with a
.baknext to each file. - Review state is sacred. Engo never overwrites XLIFF
final/signed-offunits. Your translators' sign-offs survive every run. - Placeholders are invariants. Every translation is checked: if the source has
{name},%s, or an ICU plural, the target must too. Mismatches are rejected, not written.
brew install valerymelou/engo/engocargo install engo-clicargo install engo-cliDownload the archive for your platform from the latest release and put engo on your PATH.
git clone https://github.com/valerymelou/engo
cd engo
cargo build --release
./target/release/engo --helpMinimum Rust version: 1.88.
# 1. Provide your API key (BYOK)
export ANTHROPIC_API_KEY=sk-ant-...
# 2. Initialize β detects Flutter/Angular/i18next/etc. and writes engo.toml
cd my-app/
engo init
# 3. Preview what's missing (no AI call, no writes)
engo translate --list
# 4. See the translations the AI would produce (no writes)
engo translate --dry-run
# 5. Apply. Writes atomically with a .bak next to each file.
engo translate- Three formats, zero guesswork. XLIFF 1.2 and 2.0 (Angular, Symfony, Java, Qtβ¦), ARB (Flutter), and nested JSON (i18next, next-intl, vue-i18n). Key order and surrounding XML are preserved byte-for-byte on unchanged units.
- State-aware.
finalandsigned-offXLIFF units are never touched.--forcere-translatestranslatedunits only. - Placeholder validation. Rejects translations that drop, rename, or reorder
{name},%s/%d, or ICU plural/select keys. - Context-aware prompts. ARB
@key.description, XLIFF<note>, and your[glossary]table are sent to the model so short, ambiguous strings ("Log in"β verb or noun?) get translated correctly. - Persistent cache.
.engo/cache.db(SQLite). The same source + context + model + glossary hash never calls the AI twice. Safe to delete; safe to commit. - Safety by default. Refuses to run on a dirty git tree without
--allow-dirty. Every overwrite leaves a.bak. Writes use atomicrename(2). - Bring your own key. Uses your
ANTHROPIC_API_KEYdirectly. No telemetry, no proxy, no account. - Fast. Concurrent batched requests (default 15 strings/request, 4 parallel) with Anthropic prompt caching on the system prompt.
| Format | Ecosystems | Source of locale | Key shape |
|---|---|---|---|
| XLIFF 1.2 / 2.0 | Angular, Symfony, Java ResourceBundle, Qt Linguist | target-language / trgLang attribute |
trans-unit id |
| ARB | Flutter, Dart intl |
@@locale attribute |
top-level key |
| JSON | i18next, next-intl, vue-i18n, FormatJS | filename stem (fr.json, app_fr.json, messages-fr.json) |
dot-path auth.login.button |
Source and target files are paired by filename stem (so app_en.arb β app_fr.arb, messages-en.json β messages-fr.json). XLIFF files carry their own language tags, so no pairing is required.
Written by engo init. Edit freely.
[project]
format = "arb" # "xliff" | "arb" | "json"
files_glob = "lib/l10n/*.arb" # relative to engo.toml
description = "Consumer banking app, casual tone."
[languages]
source = "en"
targets = ["fr", "de", "es-419"]
[ai]
provider = "anthropic" # "anthropic" today; "openai" / "engo-cloud" on the roadmap
model = "claude-haiku-4-5"
batch_size = 15
# Domain glossary. Keys are the canonical source term; values are the required
# translation (or a short note). Sent to the model on every batch.
[glossary]
"Engo" = "Engo"
"Log in" = "Se connecter"Interactive. Detects your project format (Flutter's pubspec.yaml β ARB, package.json with i18next β JSON, *.xlf files β XLIFF), prompts for source + target languages, and writes engo.toml.
Flags:
--yesβ accept detected defaults, non-interactive.--format <xliff|arb|json>β override detection.--source <tag>,--target <tag>β skip prompts.
Flags:
--listβ print every pending unit per file. No AI call, no writes.--dry-runβ translate and validate, but don't write.--forceβ also re-translate units intranslatedstate. Never touchesfinal.--target <tag>β restrict to one target language.--concurrency <N>β max parallel AI calls (default 4).--allow-dirtyβ skip the git-clean check..bakfiles are still written.--no-cacheβ bypass reads from.engo/cache.db. Writes still happen.
Every translation is keyed by a SHA-256 over (source, source_lang, target_lang, context, model, glossary_version). Change any one of those and the entry misses β so swapping a glossary term, upgrading the model, or editing the source text all correctly force a retranslation. Everything else is a free local lookup.
The cache lives at .engo/cache.db. It's a self-contained SQLite file: safe to delete, safe to commit, safe to share with teammates.
Translation tools write to your source files, so Engo is paranoid about it:
- Clean-tree check.
engo translaterefuses to run ifgit status --porcelainis non-empty, so a bad run is always recoverable withgit checkout. Override with--allow-dirty. .bakfiles. Every overwrite creates apath.baknext to the file with the previous bytes.- Atomic writes. Files are written to a sibling temp file and renamed β a crash mid-write leaves the original untouched.
- Placeholder validation. The AI's output is parsed against the source; missing or renamed placeholders reject the translation before it's written.
Engo fits naturally into a pre-merge check that fails when translations are missing:
# .github/workflows/i18n.yml
name: i18n
on: pull_request
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cargo install engo-cli
- run: engo translate --list
# Or: translate and open a PR
# - run: engo translate
# env:
# ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}A GitHub Action wrapper is on the roadmap.
- v0.1 β Foundation. XLIFF 1.2 / 2.0 parser and state-aware patcher.
engo initwith project auto-detection. - v0.1 β Translation. Anthropic provider with prompt caching and tool-use-backed structured output. Tokio-based concurrent batching. Placeholder / ICU validator.
engo translate --list / --dry-run / --force. - v0.1 β Multi-format + safety. ARB and JSON parsers and patchers. SQLite translation cache. Atomic writes with
.bak. Git-clean enforcement.
- v0.2 β OpenAI provider.
provider = "openai"with GPT-4o-mini as the default, using the same structured-output contract. - v0.2 β Engo Cloud (optional). A thin Vercel proxy for teams that don't want to distribute per-developer API keys. Still opt-in; BYOK stays the default.
- v0.2 β
engo stats. Cost estimate (tokens in / out) and pending-unit counts per language. - v0.3 β More formats. PO / gettext, Android
strings.xml, Apple.stringsand.stringsdict, YAML (Railsconfig/locales). - v0.3 β GitHub Action.
valerymelou/engo-action@v1for drop-in CI usage. - v0.3 β Pluralization-aware validation. Language-specific plural category checks (Arabic's six forms, Russian's three, etc.).
- VS Code extension: inline "translate this" code action.
- Translation-memory export to TMX and XLIFF TM.
- Glossary linting: detect candidate glossary terms from your existing translations.
- Bundle-size analysis: warn when translations materially inflate a ship bundle.
Want to shape one of these? Open an issue or a discussion.
Contributions welcome β see CONTRIBUTING.md for the build / test / commit workflow.
Quick loop:
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --allGood first issues are tagged good first issue. Questions live in Discussions; security reports go to the address in SECURITY.md.
Dual-licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option. Unless you explicitly state otherwise, any contribution submitted for inclusion in Engo by you shall be dual-licensed as above, without any additional terms or conditions.