
Publicar trenta llengües sense xarxa de seguretat
GeekBye v1.8.3 va connectar react-i18next i va traduir tota l'app a 30 llengües. La part de la library va ser rutina. La part interessant és el que faltava: cap comprovació de paritat de claus ni cap extractor de strings — així que l'anglès hardcoded es va colar, es va publicar una errada tipogràfica danesa, i el changelog no es posava d'acord sobre si l'app parlava 28, 29 o 30 llengües.
Algunes releases són una funcionalitat. La GeekBye v1.8.3 ho sembla — "l'app ara parla 30 llengües" — però la funcionalitat era la part fàcil, i no és de què va aquest article. Entre el 17 i el 19 de febrer de 2026, dos pull requests van connectar react-i18next a l'app, van refactoritzar cada string anglès hardcoded en una crida de traducció, i van empaquetar trenta fitxers de locale. Això és feina de veritat, però és la mena de feina que una library està dissenyada per fer rutinària. L'enginyeria interessant és tot el que la release no va construir — la xarxa de seguretat que hauria atrapat el que va publicar trencat.
La connexió era el 80% fàcil
El commit d'infraestructura va afegir react-i18next sobre i18next i va fer el que és convencional: un i18n/index.ts que crida initReactI18next, importa estàticament els trenta JSON de locale, i els registra sota un únic objecte resources. Sense lazy loading — cada llengua va dins del bundle. Els strings de traducció viuen a src/i18n/locales/*.json com a mapes plans amb espais de noms per punts: sidebar.meetings, startMeeting.start, una clau per a un string.
La migració d'anglès hardcoded a t('key') es va fer de la manera honesta, grup de components rere grup de components, i pots llegir l'escombrada al log de commits: primer sidebar, navegació i chat; després settings, login i onboarding; després meetings, dashboard, toasts i tooltips de la navbar. Cada commit va substituir un lot de strings literals per crides de traducció. Vint-i-nou fitxers no anglesos van aterrar en un sol commit — una descàrrega de nou mil línies que va traduir la font anglesa a de tot, del búlgar al vietnamita.
Res d'això és difícil de fer bé, perquè fer-ho malament és sorollós: un provider que falta peta, un JSON mal format no es parseja, un import dolent trenca la build. La library et dona baranes per als errors mecànics. El que no et dona és una barana per a l'error que realment va passar.
Tres configuracions de llengua, i per què això és correcte
Abans d'arribar al que es va trencar, una cosa que la v1.8.3 va encertar del tot, perquè és una distinció que moltes apps aplanen i no haurien de fer. Després d'aquesta release, GeekBye té tres configuracions de llengua independents:
- Llengua de resposta de la IA (
output_language) — la llengua en què respon l'assistent. La més antiga de les tres, del novembre de 2025. - Llengua de transcripció (
transcriptLanguage) — la llengua en què el motor de veu transcriu el teu micròfon. Instal·lada unes quantes releases abans, a la v1.7.6. - Llengua de l'app (
app_language) — la llengua dels botons, els menús i l'onboarding. Nova a la v1.8.3.
Aquests són eixos genuïnament diferents. Un desenvolupador a Berlín pot voler una interfície en alemany, respostes de la IA en anglès (perquè és la llengua dels docs que enganxa) i transcripció en anglès (perquè la reunió és en anglès). Fondre-ho tot en una sola configuració de "llengua" seria un error per a aquesta persona, i el pas d'onboarding afegit en aquesta release presenta les tres com a desplegables separats exactament per aquest motiu.
La bona pinzellada és el camí d'actualització. Una migració única copia el output_language de cada usuari existent a la nova clau app_language en el primer arrencament després d'actualitzar, i el provider resol la llengua a través d'una cadena de fallback — app_language, després output_language, després 'en'. Així, els usuaris que ja havien dit a l'app que volien respostes de la IA en espanyol es van despertar amb una interfície en espanyol, en lloc de ser reiniciats a l'anglès i haver de buscar la configuració. Sembrar una preferència nova a partir d'una de vella relacionada és una petita amabilitat que costa una migració i estalvia a cada usuari que actualitza una petita molèstia.
El re-render d'onboarding que no volia
Hi ha un bug genuí que val la pena treure del treball d'i18n, perquè és un parany de React amb què topa qualsevol que faci canvi de llengua en temps d'execució. El wizard d'onboarding construeix algunes de les seves llistes d'opcions dins d'un useMemo. Quan canvies la llengua al pas de la llengua mateix, la UI se suposa que ha de redibuixar-se en la nova llengua immediatament — i no ho feia, perquè t no era a l'array de dependències del memo. La funció de traducció canvia d'identitat quan canvia la llengua; si el teu valor memoïtzat es construeix a partir de t però no en depèn, React et lliura tan content la versió obsoleta, de la llengua anterior. La correcció apareix dues vegades al log — una per al wizard, una per a la llista desplegable de settings — cosa que et diu que és la mena d'error que fas a cada lloc on memoïtzes contingut traduït fins que aprens a llistar sempre t.
L'onboarding també arrossegava un plec que el changelog no menciona: com que això és una codebase white-label, hi ha dos arbres d'onboarding per traduir — un wizard de GeekBye i un de Pavleur — que viuen sota els espais de noms de claus paral·lels onboarding.* i onboarding.pavleur.*. Cada string d'onboarding es va traduir dues vegades, en dues veus de marca, a totes vint-i-nou llengües.
El que la build no sabia que li faltava
Aquí ve la part que fa d'això una lliçó en lloc d'una checklist. La v1.8.3 no tenia cap manera automatitzada d'extreure strings hardcoded ni cap comprovació a CI de paritat de claus entre locales. Així que la definició de "fet" era "algú va visitar els components que recordava i va embolcallar els strings que veia." Aquesta definició deixa escapar coses.
Dues setmanes després de la release, un commit de neteja va aterrar amb el missatge "translate all hardcoded English strings across 10 components into 30 languages." Deu components — el formulari de report d'errors, les pestanyes de resum i de mètriques, la pestanya de report, el contingut de transcripció i de chat, el prompt de límit d'ús, els missatges de chat, el botó de copiar codi, els botons d'acció flotants — s'havien publicat a la v1.8.3 encara parlant anglès a tothom, en totes les llengües. La build de ningú va fallar. Cap test es va posar en vermell. Un usuari alemany simplement va veure anglès a la pestanya de report, i l'únic detector era un humà que finalment hi posava els ulls.
La mateixa absència va produir els embaraços més petits. Una traducció danesa es va publicar amb la paraula equivocada — Luk application helt en lloc de Luk applikationen helt — i es va corregir en una release posterior, perquè no hi havia cap porta de revisor natiu ni cap manera que el pipeline ho sabés. Les claus de "quit app" faltaven al lot inicial i es van reomplir dies després. I cada funcionalitat que es va publicar després — un preset de tecla modificadora, silenciar el micròfon, chips de recap, opacitat de l'overlay — arrossegava el seu propi commit de "traduir les noves claus a 29 locales" al darrere, per sempre, perquè res feia complir l'invariant automàticament.
Fins i tot pots mesurar el desordre als missatges de commit. Els mateixos trenta fitxers de locale es descriuen, release rere release, com "30 llengües," "29 llengües," i — en un commit — "28 locales." No s'hi va afegir ni treure res; hi ha trenta fitxers a cada tag des de la v1.8.3 fins a la v2. El número deriva perquè no hi havia una única font de veritat per a "quantes llengües publiquem," així que cada autor de commit comptava a mà, de vegades incloent l'anglès com a llengua i de vegades com a font. Un changelog que no pot mantenir estable el seu propi recompte és un senyal petit i honest que la cosa que descriu no la fa complir cap màquina.
La trampa de la pluralització
Una cosa més que val la pena assenyalar, no perquè es trenqués sorollosament sinó perquè és la mena de bug que aquesta configuració convida i del qual no t'avisarà. La pluralització en aquests fitxers de locale es fa manualment, amb claus aparellades: meetings.meetingCount i meetings.meetingCountPlural, profiles.filesAttached i filesAttachedPlural. Aquesta és una divisió singular/plural neta a l'anglès. També és incorrecta per a una bona part de les llengües que l'app ara diu que suporta. El rus, el polonès i el txec tenen tres o quatre formes de plural segons el número — "1 file," "2 files," "5 files," i "21 files" poden agafar terminacions de paraula diferents. Un esquema de dues claus singular/plural no ho pot expressar, així que aquests recomptes es mostren gramaticalment torts. i18next té maquinària de plurals de veritat per a exactament això; els parells manuals l'esquiven, i cap commit reconeix el buit. És l'equivalent en pluralització de la fuita de strings hardcoded: invisible per a la build, visible només per a un parlant natiu.
Tres coses que aquesta release ens va ensenyar
- La library és el 80% fàcil; l'eina és el 20% difícil. Connectar
react-i18nexti traduir strings en massa és un problema resolt. Mantenir trenta fitxers en paritat per sempre no ho és — i necessita un extractor per als strings hardcoded i una comprovació a CI per a les claus que falten, i cap de les dues coses te les publica una library. Construeix-les abans de reclamar "i18n completa," o hauràs reclamat un invariant que no tens cap manera de sostenir. - Separa les configuracions de llengua que realment són diferents. La llengua de la UI, la de transcripció i la de resposta de la IA són tres eixos, i la v1.8.3 va fer bé de donar a cadascuna la seva pròpia clau — i encara més bé de sembrar la nova a partir d'una de vella relacionada mitjançant migració, així actualitzar no va costar res als usuaris.
- El recompte del teu changelog és una mètrica de disciplina. Quan el mateix conjunt de fitxers és 28, 29 i 30 llengües a través de tres commits, el número no és el problema — la font de veritat que falta ho és. Si una màquina no compta les teves llengües, ningú no les compta dues vegades de la mateixa manera.
Per al capítol anterior de la història de la v1, redissenyar el dashboard al voltant d'una sola gràfica d'ona (v1.8.2); i per a tot l'arc, l'anatomia de publicar programari fins a la perfecció.