If you've ever built a multilingual application, you've probably started with something like this:
{
"welcome": "Welcome",
"logout": "Logout"
}
A few weeks later you add another language:
{
"welcome": "Witamy",
"logout": "Wyloguj"
}
Everything still looks manageable.
Then the product grows.
More features are added.
More developers join.
More languages appear.
Suddenly your repository contains:
locales/
├── en.json
├── pl.json
├── de.json
├── fr.json
├── es.json
├── it.json
└── ...
And that's when localization starts becoming a maintenance problem rather than a translation problem.
The Hidden Cost of Translation Files
Most teams focus on translating text.
In practice, the harder problem is keeping translations synchronized.
Questions start appearing:
- Which keys are missing in German?
- Which translations are outdated?
- Which strings were recently changed?
- Why is production showing a fallback language?
- Which developer forgot to update all language files?
The larger the application becomes, the harder these questions are to answer.
Git Was Never Designed for Localization Workflows
Git is excellent for source code.
It is much less effective for managing hundreds or thousands of translations.
Imagine changing:
{
"subscription_expired_message": "Your subscription has expired"
}
Now that same change needs to be reflected across:
- Polish
- German
- French
- Spanish
- Italian
And every translation file must stay synchronized.
Developers often end up spending more time managing translation files than writing features.
AI Makes Translation Easier, But Doesn't Solve Management
Today we can generate translations using:
- OpenAI
- DeepL
- Google Translate
Generating translations is easy.
Managing them is still difficult.
You still need to:
- store them
- version them
- review them
- serve them efficiently
- keep languages synchronized
AI solves translation generation.
It doesn't solve translation infrastructure.
What Changed My Approach
Instead of treating translations as files, I started treating them as data.
The workflow became:
- Store translations in a centralized system.
- Manage translation keys through an API.
- Generate missing translations automatically.
- Serve translations through cached endpoints.
- Keep source code focused on business logic.
Instead of:
Git Repository
└── Translation Files
The architecture becomes:
Application
↓
Translation API
↓
Translation Store
↓
AI Translation Providers
Benefits
Easier Collaboration
Developers, translators, and product owners can work independently.
Better Visibility
You can immediately see:
- missing translations
- outdated strings
- approval status
Faster Releases
Adding a new language no longer requires editing dozens of files.
Reduced Merge Conflicts
Translation changes stop polluting source control.
Is File-Based Localization Always Wrong?
No.
For small projects, translation files are perfectly fine.
If your application has:
- one developer
- two languages
- a few dozen strings
there is little reason to introduce additional infrastructure.
But once localization becomes an ongoing process, centralization starts paying off quickly.
Final Thoughts
Localization is often treated as a translation problem.
In my experience it's actually a workflow problem.
The challenge isn't generating text.
The challenge is managing thousands of translations across multiple languages while keeping development fast and predictable.
The earlier you solve the workflow problem, the easier localization becomes as your application grows.
How are you managing translations today?
Still using JSON files in Git, or have you moved to a centralized localization workflow?
I ended up building the tool this post describes. It is called i18nme: a CLI and GitHub Action that translates changed strings in CI and fails the build when a language is incomplete. There is a free tier. I am a solo developer, so if you try it and something is rough, email contact@cod3lab.com and I will fix it.
Top comments (2)
The missing-key question is a diff. A CI check that fails the build when a language file is incomplete answers it, and the string still ships in the same commit as the feature. The API store in the middle of the post is for someone else: a translator who never opens a pull request. i18nme, as described at the end, is the CI check. The diagram is the other system. Mixing them hides the fallback bug that shows up when the key arrives in the store after the deploy, not in it.
that is a really good point ! i18nme.com is also storing history of changes so you don't need to store it on your end, that simplifys CI /CD.