i18n: 1993 keys, 6 languages, 9 machine translation passes
TAMSIV was born in French. The entire interface, every error message — all hardcoded. When I decided to support 6 languages (FR, EN, DE, ES, IT, PT), I counted the strings to extract: 1993 translation keys.
Extraction
First step: replace each French string with a t('key') call. In 35 files. Manually. I structured the keys by screen: feed.empty_state, agenda.filter.participating, profile.settings.language.
The automatic translation script
Translating 1993 keys manually into 5 languages? Impossible. I wrote an npm run translate script that sends the French keys to OpenRouter (Gemini 2.5 Flash). The LLM understands the application context — "Mémo vocal" becomes "Voice memo" and not "Vocal memo".
The 9 passes
The first pass translated the bulk of the corpus. Then, each new feature added keys: gamification (47 keys), agenda (62), groups (89). The script detects missing keys and only retranslates the delta.
Automatic language detection
On first launch, TAMSIV detects the system language. If supported, it's used. Otherwise, it falls back to English. The choice is saved in the database.
Keeping in sync
The hardest part: maintenance. Every modification in French must be propagated. If fr.json is modified, the script runs automatically via GitHub Actions. 6 languages maintained in near real-time for a few cents per pass.