Blog
Feature
February 14, 20267 min

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.