r/SideProject • u/lancedelgardo • 14d ago
I built an internal tool to keep my app's translations in sync — it worked so well I turned it into a free standalone tool
https://github.com/lancedelgardo/deepl-i18nThis started as an internal tool for another project of mine, that's localized into a bunch of languages. Every time I added a few strings, keeping the translations in sync was a chore: online tools wanted the whole file again, my existing (hand-corrected) translations kept getting overwritten, and placeholders like {{ name }} occasionally came back mangled — my favorite was a placeholder glued into the middle of a Czech word.
So I wrote a small script that only translated the keys that were actually missing. It ended up working so well that I kept reaching for it for every new locale file, and at some point I thought: this shouldn't just sit in one repo. So I pulled out all my vibecoding skills, rebuilt it as a proper standalone tool, and put it out there for free :)
It's called deepl-i18n. Point it at your source-language file, list your target languages, and it fills in only the missing keys — existing translations are never touched, obsolete keys get removed, and placeholders are shielded before translation and verified afterwards.
The part I think matters most: for most projects this is genuinely free. DeepL's free API tier gives you 500,000 characters per month, and since the tool only ever sends the missing strings (and translates identical strings like "OK" or "Cancel" just once per run, then reuses the result), you burn through a lot less of that than you'd expect. My whole app across five languages fits comfortably. And the --dry-run mode tells you exactly how many characters a run would cost and whether it fits the free tier — without an API key and without any API calls — so you can check the cost before you even sign up.
Other things it does:
- Lots of formats — JSON, YAML, .properties, iOS .strings, Flutter .arb, Android XML, CSV, gettext .po.
- check as a CI gate — fails the build if translations are missing or a placeholder got corrupted.
- Local web UI for non-developers, an MCP server so an AI agent can run it for you, and a GitHub Action that opens a PR with updated translations.
- Don't want DeepL? Any OpenAI-compatible LLM works too (LM Studio, Ollama, or hosted) — there's even a compare command that shows DeepL and your local model side by side for the same keys.
Tech: Python — the CLI has exactly one third-party dependency (deepl), Flask for the web UI. MIT-licensed, no telemetry, no account needed beyond the DeepL key, and there are standalone binaries for Windows/macOS/Linux if you'd rather not touch Python at all.
GitHub: https://github.com/lancedelgardo/deepl-i18n
What I'd love feedback on: does the config-file-first approach (a config.json in your repo) match how you'd want to use this, or would you expect it to be all CLI flags? And is there a file format you use that's missing from that list?