
Spedire trenta lingue senza rete di sicurezza
GeekBye v1.8.3 ha cablato react-i18next e tradotto l'intera app in 30 lingue. La parte della libreria era routine. L'interessante è ciò che mancava: nessun controllo di parità delle chiavi né estrattore di stringhe — così l'inglese hardcoded è trapelato, un refuso danese è finito in produzione e il changelog non riusciva a mettersi d'accordo se l'app parlasse 28, 29 o 30 lingue.
Alcune release sono una feature. GeekBye v1.8.3 sembra una — «l'app ora parla 30 lingue» — ma la feature era la parte facile, e non è di questo che parla questo post. Tra il 17 e il 19 febbraio 2026, due pull request hanno cablato react-i18next nell'app, rifattorizzato ogni stringa inglese hardcoded in una chiamata di traduzione e impacchettato trenta file di lingua. È lavoro vero, ma è il tipo di lavoro che una libreria è progettata per rendere routine. L'ingegneria interessante è tutto ciò che la release non ha costruito — la rete di sicurezza che avrebbe intercettato ciò che ha spedito rotto.
Il cablaggio era l'80% facile
Il commit di infrastruttura ha aggiunto react-i18next sopra i18next e ha fatto la cosa convenzionale: un i18n/index.ts che chiama initReactI18next, importa staticamente tutti i trenta JSON di lingua e li registra sotto un unico oggetto resources. Nessun lazy loading — ogni lingua va nel bundle. Le stringhe di traduzione vivono in src/i18n/locales/*.json come mappe piatte con spazi dei nomi puntati: sidebar.meetings, startMeeting.start, una chiave per una stringa.
La migrazione dall'inglese hardcoded a t('key') è stata fatta nel modo onesto, gruppo di componenti dopo gruppo di componenti, e puoi leggere la spazzata nel log dei commit: prima sidebar, navigazione e chat; poi impostazioni, login e onboarding; poi riunioni, dashboard, toast e tooltip della navbar. Ogni commit ha sostituito un lotto di stringhe letterali con chiamate di traduzione. Ventinove file non inglesi sono atterrati in un unico commit — una caduta di novemila righe che ha tradotto la fonte inglese in tutto, dal bulgaro al vietnamita.
Niente di tutto ciò è difficile da azzeccare, perché sbagliarlo è rumoroso: un provider mancante crasha, un JSON malformato non fa il parsing, un import errato rompe la build. La libreria ti dà guardrail per gli errori meccanici. Ciò che non ti dà è un guardrail per l'errore che è davvero successo.
Tre impostazioni di lingua, e perché è corretto
Prima di arrivare a ciò che si è rotto, una cosa che la v1.8.3 ha azzeccato in pieno, perché è una distinzione che molte app appiattiscono e non dovrebbero. Dopo questa release, GeekBye ha tre impostazioni di lingua indipendenti:
- Lingua di risposta dell'IA (
output_language) — in che lingua risponde l'assistente. La più vecchia delle tre, da novembre 2025. - Lingua di trascrizione (
transcriptLanguage) — in che lingua il motore vocale trascrive il tuo microfono. Collegata qualche release prima, nella v1.7.6. - Lingua dell'app (
app_language) — la lingua dei pulsanti, dei menu e dell'onboarding. Nuova nella v1.8.3.
Sono assi genuinamente diversi. Uno sviluppatore a Berlino potrebbe volere un'interfaccia in tedesco, risposte dell'IA in inglese (perché è la lingua della documentazione che sta incollando) e trascrizione in inglese (perché la riunione è in inglese). Collassare tutto ciò in un'unica impostazione «lingua» sarebbe sbagliato per quella persona, e il passo di onboarding aggiunto in questa release presenta tutti e tre come dropdown separati esattamente per questa ragione.
Il tocco carino è il percorso di aggiornamento. Una migrazione una tantum copia l'output_language esistente di ogni utente nella nuova chiave app_language al primo avvio dopo l'aggiornamento, e il provider risolve la lingua tramite una catena di fallback — app_language, poi output_language, poi 'en'. Così, gli utenti che avevano già detto all'app di volere risposte dell'IA in spagnolo si sono svegliati con un'interfaccia in spagnolo, invece di essere resettati all'inglese e costretti a cercare l'impostazione. Seminare una nuova preferenza a partire da una vecchia correlata è una piccola gentilezza che costa una migrazione e risparmia a ogni utente che aggiorna una piccola seccatura.
Il re-render di onboarding che non voleva
C'è un bug genuino che vale la pena tirare fuori dal lavoro di i18n, perché è una trappola di React in cui inciamperà chiunque faccia cambio di lingua a runtime. L'assistente di onboarding costruisce alcune delle sue liste di opzioni dentro un useMemo. Quando cambi la tua lingua sul passo della lingua stesso, la UI dovrebbe ridisegnarsi nella nuova lingua immediatamente — e non lo faceva, perché t non era nell'array delle dipendenze del memo. La funzione di traduzione cambia identità quando cambia la lingua; se il tuo valore memoizzato è costruito a partire da t ma non dipende da esso, React ti porge tutto contento la versione stantia, quella della lingua precedente. Il fix compare due volte nel log — una per l'assistente, una per la lista dropdown delle impostazioni — il che ti dice che è il tipo di errore che fai in ogni punto in cui memoizzi contenuto tradotto finché non impari a elencare sempre t.
L'onboarding portava anche una piega che il changelog non menziona: siccome questa è una codebase white-label, ci sono due alberi di onboarding da tradurre — un assistente GeekBye e uno Pavleur — che vivono sotto gli spazi dei nomi di chiave paralleli onboarding.* e onboarding.pavleur.*. Ogni stringa di onboarding è stata tradotta due volte, in due voci di marca, attraverso tutte e ventinove le lingue.
Ciò che la build non sapeva che le mancava
Ecco la parte che rende questo una lezione invece che una checklist. La v1.8.3 non aveva alcun modo automatico di estrarre le stringhe hardcoded né alcun controllo in CI della parità delle chiavi tra le lingue. Quindi la definizione di «fatto» era «qualcuno ha visitato i componenti che ricordava e ha avvolto le stringhe che vedeva». Quella definizione fa acqua.
Due settimane dopo la release, un commit di pulizia è atterrato con il messaggio «translate all hardcoded English strings across 10 components into 30 languages». Dieci componenti — il form di segnalazione problemi, le schede di riepilogo e metriche, la scheda di report, il contenuto di trascrizione e chat, il prompt di limite d'uso, i messaggi di chat, il pulsante copia-codice, i pulsanti d'azione flottanti — erano usciti nella v1.8.3 parlando ancora inglese a tutti, in ogni lingua. La build di nessuno è fallita. Nessun test è diventato rosso. Un utente tedesco ha semplicemente visto l'inglese nella scheda di report, e l'unico rilevatore è stato un umano che alla fine l'ha guardato.
La stessa assenza ha prodotto gli imbarazzi minori. Una traduzione danese è uscita con la parola sbagliata — Luk application helt invece di Luk applikationen helt — ed è stata corretta in una release successiva, perché non c'era né un filtro di revisore madrelingua né un modo per la pipeline di saperlo. Le chiavi «chiudi app» mancavano nel lotto iniziale e sono state riempite giorni dopo. E ogni feature uscita in seguito — un preset di tasto modificatore, il muto del microfono, chip di recap, l'opacità dell'overlay — si è trascinata dietro il proprio commit «tradurre le nuove chiavi in 29 lingue», per sempre, perché niente imponeva l'invariante automaticamente.
Puoi persino misurare il disordine nei messaggi di commit. Gli stessi trenta file di lingua vengono descritti, release dopo release, come «30 lingue», «29 lingue» e — in un commit — «28 locale». Nulla è stato aggiunto o rimosso; ci sono trenta file a ogni tag dalla v1.8.3 fino alla v2. Il numero deriva perché non c'era un'unica fonte di verità per «quante lingue spediamo», così ogni autore di commit contava a mano, a volte includendo l'inglese come una lingua e a volte come la fonte. Un changelog che non riesce a tenere fermo il proprio conteggio è un segnale piccolo e onesto che la cosa che descrive non viene imposta da una macchina.
La mina della pluralizzazione
Un'ultima cosa da segnalare, non perché si sia rotta rumorosamente ma perché è il tipo di bug che questo assetto invita e su cui non ti avviserà. La pluralizzazione in questi file di lingua è fatta a mano, con chiavi appaiate: meetings.meetingCount e meetings.meetingCountPlural, profiles.filesAttached e filesAttachedPlural. È una pulita divisione singolare/plurale dell'inglese. È anche sbagliata per una fetta delle lingue che l'app ora dichiara di supportare. Il russo, il polacco e il ceco hanno tre o quattro forme plurali a seconda del numero — «1 file», «2 file», «5 file» e «21 file» possono prendere desinenze diverse. Uno schema a due chiavi singolare/plurale non può esprimerlo, così quei conteggi vengono resi grammaticalmente storti. i18next ha vera macchineria plurale esattamente per questo; le coppie manuali la aggirano, e nessun commit riconosce la lacuna. È l'equivalente, in pluralizzazione, della falla delle stringhe hardcoded: invisibile alla build, visibile solo a un madrelingua.
Tre cose che questa release ci ha insegnato
- La libreria è l'80% facile; gli strumenti sono il 20% difficile. Cablare
react-i18nexte tradurre le stringhe in massa è un problema risolto. Tenere trenta file in parità per sempre non lo è — e serve un estrattore di stringhe hardcoded e un controllo in CI di chiavi mancanti, nessuno dei quali una libreria ti spedisce. Costruiscili prima di proclamare «i18n completa», o avrai proclamato un invariante che non hai modo di reggere. - Separa le impostazioni di lingua che sono davvero diverse. Lingua dell'interfaccia, lingua di trascrizione e lingua di risposta dell'IA sono tre assi, e la v1.8.3 ha avuto ragione a dare a ciascuna la propria chiave — e ancor più ragione a seminare la nuova a partire da una vecchia correlata tramite migrazione, così che aggiornare non è costato nulla agli utenti.
- Il conteggio del tuo changelog è una metrica di disciplina. Quando lo stesso insieme di file è 28, 29 e 30 lingue attraverso tre commit, il numero non è il problema — la fonte di verità mancante lo è. Se una macchina non conta le tue lingue, nessuno le conta due volte allo stesso modo.
Per il capitolo precedente della storia v1, ridisegnare la dashboard attorno a un solo grafico a onda (v1.8.2); e per tutto l'arco, l'anatomia del pubblicare software fino alla perfezione.