
Publicar treinta idiomas sin red de seguridad
La GeekBye v1.8.3 cableó react-i18next y tradujo toda la app a 30 idiomas. La parte de la librería fue rutina. Lo interesante es lo que faltaba: sin comprobación de paridad de claves ni extractor de strings — así que se coló inglés hardcodeado, se publicó una errata en danés y el changelog no lograba ponerse de acuerdo sobre si la app hablaba 28, 29 o 30 idiomas.
Algunas releases son una feature. La GeekBye v1.8.3 parece una — «la app ahora habla 30 idiomas» — pero la feature era la parte fácil, y no es de lo que trata este post. Entre el 17 y el 19 de febrero de 2026, dos pull requests cablearon react-i18next en la app, refactorizaron cada string en inglés hardcodeado en una llamada de traducción y empaquetaron treinta archivos de idioma. Eso es trabajo real, pero es del tipo de trabajo que una librería está diseñada para convertir en rutina. La ingeniería interesante es todo lo que la release no construyó — la red de seguridad que habría atrapado lo que publicó roto.
El cableado era el 80% fácil
El commit de infraestructura añadió react-i18next sobre i18next e hizo lo convencional: un i18n/index.ts que llama a initReactI18next, importa estáticamente los treinta JSON de idioma y los registra bajo un único objeto resources. Sin lazy loading — cada idioma va en el bundle. Los strings de traducción viven en src/i18n/locales/*.json como mapas planos con espacios de nombres por puntos: sidebar.meetings, startMeeting.start, una clave para un string.
La migración del inglés hardcodeado a t('key') se hizo de la manera honesta, grupo de componentes a grupo de componentes, y puedes leer el barrido en el log de commits: primero sidebar, navegación y chat; luego ajustes, login y onboarding; luego reuniones, dashboard, toasts y tooltips de la navbar. Cada commit reemplazó un lote de strings literales por llamadas de traducción. Veintinueve archivos no ingleses aterrizaron en un único commit — una caída de nueve mil líneas que tradujo la fuente en inglés a todo, del búlgaro al vietnamita.
Nada de eso es difícil de acertar, porque hacerlo mal es ruidoso: un provider que falta crashea, un JSON malformado no parsea, un import erróneo rompe la build. La librería te da barandillas para los errores mecánicos. Lo que no te da es una barandilla para el error que de verdad ocurrió.
Tres ajustes de idioma, y por qué eso es correcto
Antes de llegar a lo que se rompió, una cosa que la v1.8.3 acertó exactamente, porque es una distinción que muchas apps aplanan y no deberían. Después de esta release, GeekBye tiene tres ajustes de idioma independientes:
- Idioma de respuesta de la IA (
output_language) — en qué responde el asistente. El más antiguo de los tres, de noviembre de 2025. - Idioma de transcripción (
transcriptLanguage) — cómo el motor de voz transcribe tu micrófono. Conectado unas releases antes, en la v1.7.6. - Idioma de la app (
app_language) — el idioma de los botones, los menús y el onboarding. Nuevo en la v1.8.3.
Son ejes genuinamente distintos. Un desarrollador en Berlín podría querer una interfaz en alemán, respuestas de la IA en inglés (porque ese es el idioma de la documentación que está pegando) y transcripción en inglés (porque la reunión es en inglés). Colapsar eso en un único ajuste de «idioma» sería un error para esa persona, y el paso de onboarding añadido en esta release presenta los tres como desplegables separados precisamente por esa razón.
El detalle bonito es la ruta de actualización. Una migración de una sola vez copia el output_language existente de cada usuario en la nueva clave app_language en el primer arranque tras actualizar, y el provider resuelve el idioma mediante una cadena de fallback — app_language, luego output_language, luego 'en'. Así, los usuarios que ya le habían dicho a la app que querían respuestas de la IA en español se despertaron con una interfaz en español, en vez de ser reseteados a inglés y obligados a buscar el ajuste. Sembrar una preferencia nueva a partir de una vieja relacionada es una pequeña amabilidad que cuesta una migración y le ahorra a cada usuario que actualiza una pequeña molestia.
El re-render de onboarding que no quería
Hay un bug genuino que vale la pena sacar del trabajo de i18n, porque es una trampa de React con la que topará cualquiera que haga cambio de idioma en tiempo de ejecución. El asistente de onboarding construye algunas de sus listas de opciones dentro de un useMemo. Cuando cambias tu idioma en el propio paso de idioma, se supone que la UI debe redibujarse en el nuevo idioma de inmediato — y no lo hacía, porque t no estaba en el array de dependencias del memo. La función de traducción cambia de identidad cuando cambia el idioma; si tu valor memoizado se construye a partir de t pero no depende de él, React te entrega tan feliz la versión rancia, la del idioma anterior. El arreglo aparece dos veces en el log — una para el asistente, otra para la lista desplegable de ajustes — lo que te dice que es el tipo de error que cometes en cada sitio donde memoizas contenido traducido hasta que aprendes a listar siempre t.
El onboarding también arrastraba una arruga que el changelog no menciona: como este es un codebase white-label, hay dos árboles de onboarding que traducir — un asistente de GeekBye y uno de Pavleur — que viven bajo espacios de nombres de clave paralelos onboarding.* y onboarding.pavleur.*. Cada string de onboarding se tradujo dos veces, a dos voces de marca, en los veintinueve idiomas.
Lo que la build no sabía que le faltaba
Aquí está la parte que convierte esto en una lección en vez de en una checklist. La v1.8.3 no tenía manera automática de extraer strings hardcodeados ni comprobación en CI de la paridad de claves entre idiomas. Así que la definición de «terminado» era «alguien visitó los componentes que recordaba y envolvió los strings que vio». Esa definición hace agua.
Dos semanas después de la release, aterrizó un commit de limpieza con el mensaje «translate all hardcoded English strings across 10 components into 30 languages». Diez componentes — el formulario de reporte de incidencias, las pestañas de resumen y métricas, la pestaña de informe, el contenido de transcripción y chat, el aviso de límite de uso, los mensajes de chat, el botón de copiar código, los botones de acción flotantes — se habían publicado en la v1.8.3 todavía hablándole a todo el mundo en inglés, en todos los idiomas. La build de nadie falló. Ningún test se puso en rojo. Un usuario alemán simplemente vio inglés en la pestaña de informe, y el único detector fue un humano que acabó mirándolo.
La misma ausencia produjo las vergüenzas menores. Una traducción al danés se publicó con la palabra equivocada — Luk application helt en vez de Luk applikationen helt — y se parcheó en una release posterior, porque no había ni filtro de revisor nativo ni forma de que el pipeline lo supiera. Las claves de «cerrar app» faltaban en el lote inicial y se rellenaron días después. Y cada feature que se publicó después — un preset de tecla modificadora, silenciar el micro, chips de recap, opacidad del overlay — arrastró detrás su propio commit de «traducir las nuevas claves a 29 idiomas», para siempre, porque nada imponía el invariante automáticamente.
Incluso puedes medir el desorden en los mensajes de commit. Los mismos treinta archivos de idioma se describen, release tras release, como «30 idiomas», «29 idiomas» y — en un commit — «28 locales». No se añadió ni se quitó nada; hay treinta archivos en cada tag desde la v1.8.3 hasta la v2. El número deriva porque no había una única fuente de verdad para «cuántos idiomas publicamos», así que cada autor de commit contaba a mano, a veces incluyendo el inglés como un idioma y a veces como la fuente. Un changelog que no logra mantener firme su propia cuenta es una señal pequeña y honesta de que la cosa que describe no la está imponiendo una máquina.
La mina de la pluralización
Una cosa más que vale la pena señalar, no porque se rompiera ruidosamente sino porque es el tipo de bug que este montaje invita y sobre el que no te avisará. La pluralización en estos archivos de idioma se hace a mano, con claves emparejadas: meetings.meetingCount y meetings.meetingCountPlural, profiles.filesAttached y filesAttachedPlural. Es una división singular/plural limpia del inglés. También es incorrecta para una parte de los idiomas que la app ahora dice soportar. El ruso, el polaco y el checo tienen tres o cuatro formas plurales según el número — «1 archivo», «2 archivos», «5 archivos» y «21 archivos» pueden llevar terminaciones distintas. Un esquema de dos claves singular/plural no puede expresar eso, así que esos recuentos se renderizan gramaticalmente mal. i18next tiene maquinaria plural de verdad para exactamente esto; los pares manuales la esquivan, y ningún commit reconoce la brecha. Es el equivalente en pluralización a la fuga de strings hardcodeados: invisible para la build, visible solo para un hablante nativo.
Tres cosas que nos enseñó esta release
- La librería es el 80% fácil; las herramientas son el 20% difícil. Cablear
react-i18nexty traducir strings en masa es un problema resuelto. Mantener treinta archivos en paridad para siempre no lo es — y necesita un extractor de strings hardcodeados y una comprobación de claves ausentes en CI, ninguno de los cuales te da una librería. Constrúyelos antes de proclamar «i18n completa», o habrás proclamado un invariante que no tienes forma de sostener. - Separa los ajustes de idioma que de verdad son distintos. El idioma de la interfaz, el de transcripción y el de respuesta de la IA son tres ejes, y la v1.8.3 acertó al darle a cada uno su propia clave — y acertó aún más al sembrar el nuevo a partir de uno viejo relacionado mediante migración, de modo que actualizar no le costó nada a los usuarios.
- El número de tu changelog es una métrica de disciplina. Cuando el mismo conjunto de archivos es 28, 29 y 30 idiomas en tres commits, el número no es el problema — la fuente de verdad que falta lo es. Si una máquina no está contando tus idiomas, nadie los está contando igual dos veces.
Para el capítulo anterior de la historia v1, rediseñar el dashboard en torno a un gráfico de onda (v1.8.2); y para todo el arco, la anatomía de publicar software hasta la perfección.