
Livrer trente langues sans filet de sécurité
GeekBye v1.8.3 a câblé react-i18next et traduit toute l'app en 30 langues. La partie librairie était de la routine. L'intéressant, c'est ce qui manquait : pas de contrôle de parité des clés ni d'extracteur de chaînes — si bien que de l'anglais codé en dur a fui, une coquille danoise est passée en prod, et le changelog n'arrivait pas à se mettre d'accord sur si l'app parlait 28, 29 ou 30 langues.
Certaines releases sont une feature. GeekBye v1.8.3 en a l'air — « l'app parle maintenant 30 langues » — mais la feature était la partie facile, et ce n'est pas le sujet de ce post. Entre le 17 et le 19 février 2026, deux pull requests ont câblé react-i18next dans l'app, refactorisé chaque chaîne anglaise codée en dur en un appel de traduction, et empaqueté trente fichiers de langue. C'est du vrai travail, mais c'est le genre de travail qu'une librairie est conçue pour rendre routinier. L'ingénierie intéressante, c'est tout ce que la release n'a pas construit — le filet de sécurité qui aurait rattrapé ce qu'elle a livré cassé.
Le câblage était les 80 % faciles
Le commit d'infrastructure a ajouté react-i18next par-dessus i18next et a fait la chose conventionnelle : un i18n/index.ts qui appelle initReactI18next, importe statiquement les trente JSON de langue, et les enregistre sous un unique objet resources. Pas de lazy loading — chaque langue part dans le bundle. Les chaînes de traduction vivent dans src/i18n/locales/*.json sous forme de maps plates à espaces de noms pointés : sidebar.meetings, startMeeting.start, une clé pour une chaîne.
La migration de l'anglais codé en dur vers t('key') a été faite honnêtement, groupe de composants par groupe de composants, et on peut lire le balayage dans le log des commits : d'abord la sidebar, la navigation et le chat ; puis les réglages, le login et l'onboarding ; puis les réunions, le dashboard, les toasts et les tooltips de la navbar. Chaque commit a remplacé un lot de chaînes littérales par des appels de traduction. Vingt-neuf fichiers non anglais ont atterri dans un seul commit — une chute de neuf mille lignes qui a traduit la source anglaise en tout, du bulgare au vietnamien.
Rien de tout cela n'est difficile à réussir, parce que le rater est bruyant : un provider manquant plante, un JSON malformé ne parse pas, un import erroné casse la build. La librairie vous donne des garde-fous pour les erreurs mécaniques. Ce qu'elle ne vous donne pas, c'est un garde-fou pour l'erreur qui s'est réellement produite.
Trois réglages de langue, et pourquoi c'est correct
Avant d'en venir à ce qui a cassé, une chose que la v1.8.3 a parfaitement réussie, parce que c'est une distinction que beaucoup d'apps aplatissent et ne devraient pas. Après cette release, GeekBye a trois réglages de langue indépendants :
- Langue de réponse de l'IA (
output_language) — dans quelle langue l'assistant répond. La plus ancienne des trois, depuis novembre 2025. - Langue de transcription (
transcriptLanguage) — dans quelle langue le moteur vocal transcrit votre micro. Branchée quelques releases plus tôt, en v1.7.6. - Langue de l'app (
app_language) — la langue des boutons, des menus et de l'onboarding. Nouvelle en v1.8.3.
Ce sont de véritables axes distincts. Un développeur à Berlin pourrait vouloir une interface en allemand, des réponses de l'IA en anglais (parce que c'est la langue de la doc qu'il colle) et une transcription en anglais (parce que la réunion est en anglais). Fondre tout ça en un seul réglage « langue » serait une erreur pour cette personne, et l'étape d'onboarding ajoutée dans cette release présente les trois comme des menus déroulants distincts précisément pour cette raison.
La jolie touche, c'est le chemin de mise à jour. Une migration unique copie le output_language existant de chaque utilisateur dans la nouvelle clé app_language au premier lancement après mise à jour, et le provider résout la langue via une chaîne de repli — app_language, puis output_language, puis 'en'. Ainsi, les utilisateurs qui avaient déjà dit à l'app qu'ils voulaient des réponses de l'IA en espagnol se sont réveillés avec une interface en espagnol, au lieu d'être réinitialisés en anglais et forcés de chercher le réglage. Amorcer une nouvelle préférence à partir d'une ancienne apparentée est une petite gentillesse qui coûte une migration et épargne à chaque utilisateur qui met à jour une petite contrariété.
Le re-render d'onboarding qui refusait
Il y a un vrai bug qui vaut la peine d'être extrait du travail d'i18n, parce que c'est un piège React que rencontrera quiconque fait du changement de langue à l'exécution. L'assistant d'onboarding construit certaines de ses listes d'options à l'intérieur d'un useMemo. Quand vous changez de langue sur l'étape de langue elle-même, l'UI est censée se redessiner dans la nouvelle langue immédiatement — et ça ne le faisait pas, parce que t n'était pas dans le tableau de dépendances du memo. La fonction de traduction change d'identité quand la langue change ; si votre valeur mémoïsée est construite à partir de t mais n'en dépend pas, React vous tend joyeusement la version périmée, celle de la langue précédente. Le correctif apparaît deux fois dans le log — une fois pour l'assistant, une fois pour la liste déroulante des réglages — ce qui vous dit que c'est le genre d'erreur qu'on fait à chaque endroit où l'on mémoïse du contenu traduit, jusqu'à ce qu'on apprenne à toujours lister t.
L'onboarding traînait aussi un pli que le changelog ne mentionne pas : comme c'est une codebase white-label, il y a deux arbres d'onboarding à traduire — un assistant GeekBye et un Pavleur — vivant sous les espaces de noms de clés parallèles onboarding.* et onboarding.pavleur.*. Chaque chaîne d'onboarding a été traduite deux fois, en deux voix de marque, à travers les vingt-neuf langues.
Ce que la build ne savait pas qu'il lui manquait
Voici la partie qui fait de ceci une leçon plutôt qu'une checklist. La v1.8.3 n'avait aucun moyen automatique d'extraire les chaînes codées en dur ni aucun contrôle CI de parité des clés entre les langues. Donc la définition de « terminé » était « quelqu'un a visité les composants dont il se souvenait et emballé les chaînes qu'il a vues ». Cette définition prend l'eau.
Deux semaines après la release, un commit de nettoyage a atterri avec le message « translate all hardcoded English strings across 10 components into 30 languages ». Dix composants — le formulaire de signalement d'incident, les onglets de résumé et de métriques, l'onglet de rapport, le contenu de transcription et de chat, l'invite de limite d'usage, les messages de chat, le bouton de copie de code, les boutons d'action flottants — étaient sortis en v1.8.3 en parlant encore anglais à tout le monde, dans toutes les langues. La build de personne n'a échoué. Aucun test n'est passé au rouge. Un utilisateur allemand a simplement vu de l'anglais dans l'onglet de rapport, et le seul détecteur a été un humain qui a fini par le regarder.
La même absence a produit les plus petites gênes. Une traduction danoise est sortie avec le mauvais mot — Luk application helt au lieu de Luk applikationen helt — et a été corrigée dans une release ultérieure, parce qu'il n'y avait ni filtre de relecteur natif ni moyen pour le pipeline de le savoir. Les clés « fermer l'app » manquaient dans le lot initial et ont été rajoutées quelques jours plus tard. Et chaque feature sortie ensuite — un préréglage de touche modificatrice, la coupure du micro, des chips de récap, l'opacité de l'overlay — a traîné derrière elle son propre commit « traduire les nouvelles clés en 29 langues », pour toujours, parce que rien n'imposait l'invariant automatiquement.
On peut même mesurer le désordre dans les messages de commit. Les mêmes trente fichiers de langue sont décrits, de release en release, comme « 30 langues », « 29 langues » et — dans un commit — « 28 locales ». Rien n'a été ajouté ni retiré ; il y a trente fichiers à chaque tag, de la v1.8.3 à la v2. Le nombre dérive parce qu'il n'y avait pas de source de vérité unique pour « combien de langues livrons-nous », alors chaque auteur de commit comptait à la main, incluant parfois l'anglais comme une langue et parfois comme la source. Un changelog qui n'arrive pas à tenir son propre compte est un petit signal honnête que la chose qu'il décrit n'est pas imposée par une machine.
La mine de la pluralisation
Une dernière chose à signaler, non pas parce qu'elle a cassé bruyamment mais parce que c'est le genre de bug que cette configuration invite et dont elle ne vous préviendra pas. La pluralisation dans ces fichiers de langue est faite à la main, avec des clés appariées : meetings.meetingCount et meetings.meetingCountPlural, profiles.filesAttached et filesAttachedPlural. C'est un partage singulier/pluriel propre de l'anglais. C'est aussi faux pour une partie des langues que l'app prétend désormais supporter. Le russe, le polonais et le tchèque ont trois ou quatre formes plurielles selon le nombre — « 1 fichier », « 2 fichiers », « 5 fichiers » et « 21 fichiers » peuvent prendre des terminaisons différentes. Un schéma à deux clés singulier/pluriel ne peut pas exprimer ça, donc ces décomptes s'affichent grammaticalement de travers. i18next a une vraie machinerie de pluriel pour exactement ça ; les paires manuelles la contournent, et aucun commit ne reconnaît le manque. C'est l'équivalent, côté pluralisation, de la fuite des chaînes codées en dur : invisible pour la build, visible seulement par un locuteur natif.
Trois choses que cette release nous a apprises
- La librairie, ce sont les 80 % faciles ; l'outillage, les 20 % difficiles. Câbler
react-i18nextet traduire les chaînes en masse est un problème résolu. Garder trente fichiers en parité pour toujours ne l'est pas — et ça demande un extracteur de chaînes codées en dur et un contrôle CI de clés manquantes, dont aucune librairie ne vous livre. Construisez-les avant de proclamer « i18n complète », sinon vous aurez proclamé un invariant que vous n'avez aucun moyen de tenir. - Séparez les réglages de langue qui sont réellement différents. Langue de l'interface, langue de transcription et langue de réponse de l'IA sont trois axes, et la v1.8.3 a eu raison de donner à chacun sa propre clé — et plus raison encore d'amorcer le nouveau à partir d'un ancien apparenté via une migration, pour que mettre à jour ne coûte rien aux utilisateurs.
- Le compte de votre changelog est une métrique de discipline. Quand le même ensemble de fichiers est 28, 29 puis 30 langues à travers trois commits, le nombre n'est pas le problème — c'est la source de vérité manquante. Si une machine ne compte pas vos langues, personne ne les compte deux fois de la même façon.
Pour le chapitre précédent de l'histoire v1, repenser le dashboard autour d'un seul graphique en vague (v1.8.2) ; et pour tout l'arc, l'anatomie de livrer un logiciel jusqu'à la perfection.