Steven
Steven8 min lesing

Å shippe tretti språk uten sikkerhetsnett

GeekBye v1.8.3 koblet inn react-i18next og oversatte hele appen til 30 språk. Biblioteksdelen var rutine. Det interessante er hva som manglet: ingen kontroll av nøkkelparitet og ingen strengekstraktor — så hardkodet engelsk slapp gjennom, en dansk skrivefeil ble shippet, og endringsloggen klarte ikke å bli enig om appen snakket 28, 29 eller 30 språk.

Utvikling
i18n
Desktop
GeekBye-releaser
Å shippe tretti språk uten sikkerhetsnett

Noen releaser er en feature. GeekBye v1.8.3 ser ut som én — "appen snakker nå 30 språk" — men featuren var den enkle delen, og det er ikke det dette innlegget handler om. Mellom 17. og 19. februar 2026 koblet to pull requests react-i18next inn i appen, gjorde om hver hardkodet engelsk streng til et oversettelseskall og buntet sammen tretti lokalfiler. Det er ekte arbeid, men det er den typen arbeid et bibliotek er designet for å gjøre til rutine. Den interessante ingeniørkunsten er alt releasen ikke bygde — sikkerhetsnettet som ville ha fanget det den shippet i stykker.

Innkoblingen var de enkle 80 %

Infrastruktur-commiten la react-i18next oppå i18next og gjorde det konvensjonelle: en i18n/index.ts som kaller initReactI18next, statisk importerer alle tretti lokal-JSON-ene og registrerer dem under ett enkelt resources-objekt. Ingen lat lasting — hvert språk shippes i bundelen. Oversettelsesstrengene bor i src/i18n/locales/*.json som flate, punktnavnrom-inndelte kart: sidebar.meetings, startMeeting.start, én nøkkel til én streng.

Migreringen fra hardkodet engelsk til t('key') ble gjort på den ærlige måten, komponentgruppe for komponentgruppe, og du kan lese forløpet i commit-loggen: sidepanel, navigasjon og chat først; så innstillinger, innlogging og onboarding; så møter, dashbord, toasts og navbar-tooltips. Hver commit erstattet en bunke bokstavelige strenger med oversettelseskall. Tjueni ikke-engelske filer landet i én enkelt commit — et nitusen-linjers slipp som oversatte den engelske kilden til alt fra bulgarsk til vietnamesisk.

Ingenting av det er vanskelig å få rett, fordi det er høylytt å få det feil: en manglende provider krasjer, en misformet JSON parser ikke, et dårlig import ødelegger buildet. Biblioteket gir deg rekkverk mot de mekaniske feilene. Det det ikke gir deg, er et rekkverk mot feilen som faktisk skjedde.

Tre språkinnstillinger, og hvorfor det er riktig

Før vi kommer til hva som gikk i stykker, én ting v1.8.3 fikk helt rett, fordi det er et skille mange apper flater ut selv om de ikke burde. Etter denne releasen har GeekBye tre uavhengige språkinnstillinger:

  • AI-svarspråk (output_language) — hvilket språk assistenten svarer på. Det eldste av de tre, fra november 2025.
  • Transkripsjonsspråk (transcriptLanguage) — hva talemotoren transkriberer mikrofonen din som. Trukket inn et par releaser tidligere, i v1.7.6.
  • App-språk (app_language) — språket i knapper, menyer og onboarding. Nytt i v1.8.3.

Dette er reelt forskjellige akser. En utvikler i Berlin vil kanskje ha et tysk grensesnitt, engelske AI-svar (fordi det er språket i dokumentasjonen de limer inn) og engelsk transkripsjon (fordi møtet er på engelsk). Å slå dem sammen til én enkelt "språk"-innstilling ville være feil for den personen, og onboarding-steget som ble lagt til i denne releasen presenterer alle tre som separate nedtrekksmenyer av nettopp den grunnen.

Den fine detaljen er oppgraderingsveien. En engangsmigrering kopierer hver eksisterende brukers output_language inn i den nye app_language-nøkkelen ved første oppstart etter oppdatering, og provideren løser opp språket via en fallback-kjede — app_language, så output_language, så 'en'. Så brukere som allerede hadde fortalt appen at de ville ha spanske AI-svar, våknet til et spansk grensesnitt, i stedet for å bli nullstilt til engelsk og satt til å lete etter innstillingen. Å så en ny preferanse fra en beslektet gammel er en liten vennlighet som koster én migrering og sparer hver oppgraderer for et lite irritasjonsmoment.

Onboarding-rerenderingen som ikke ville

Det finnes én ekte bug verdt å trekke ut av i18n-arbeidet, for det er en React-felle som alle som gjør språkbytte i runtime vil treffe. Onboarding-veiviseren bygger noen av valglistene sine inne i en useMemo. Når du bytter språk på selve språksteget, skal grensesnittet tegnes om på det nye språket med en gang — og det gjorde det ikke, fordi t ikke var i memoens dependency-array. Oversettelsesfunksjonen bytter identitet når språket endres; hvis den memoiserte verdien din er bygget fra t men ikke avhenger av den, rekker React deg blidt den utdaterte versjonen på det forrige språket. Fiksen dukker opp to ganger i loggen — én gang for veiviseren, én gang for innstillingenes nedtrekksliste — som forteller deg at det er den typen feil du gjør hvert sted du memoiserer oversatt innhold, helt til du lærer alltid å liste opp t.

Onboarding bar også på en krøll endringsloggen ikke nevner: fordi dette er en white-label-kodebase, finnes det to onboarding-trær å oversette — en GeekBye-veiviser og en Pavleur-veiviser — som bor under parallelle nøkkelnavnrom onboarding.* og onboarding.pavleur.*. Hver onboarding-streng ble oversatt to ganger, til to merkevarestemmer, på tvers av alle tjueni språk.

Hva buildet ikke visste at det manglet

Her er delen som gjør dette til en lærdom i stedet for en sjekkliste. v1.8.3 hadde ingen automatisk måte å trekke ut hardkodede strenger på og ingen CI-kontroll av nøkkelparitet på tvers av lokaler. Så definisjonen av "ferdig" var "noen besøkte komponentene de husket og pakket inn strengene de så." Den definisjonen er utett.

To uker etter releasen landet en opprydnings-commit med meldingen "translate all hardcoded English strings across 10 components into 30 languages." Ti komponenter — feilrapportskjemaet, sammendrags- og metrikkfanene, rapportfanen, transkripsjons- og chatinnhold, bruksgrenseprompten, chatmeldinger, kopier-kode-knappen, de flytende handlingsknappene — hadde blitt shippet i v1.8.3 og snakket fortsatt engelsk til alle, på hvert språk. Ingens build feilet. Ingen test ble rød. En tysk bruker så bare engelsk i rapportfanen, og den eneste detektoren var et menneske som til slutt kikket på det.

Det samme fraværet produserte de mindre pinlighetene. En dansk oversettelse ble shippet med feil ord — den lød Luk application helt, der den skulle ha lydt Luk applikationen helt — og ble lappet i en senere release, fordi det ikke fantes noen gate med en morsmålsgransker og ingen måte for pipelinen å vite det på. "Quit app"-nøkler manglet i den første bunken og ble etterfylt dager senere. Og hver eneste feature som ble shippet deretter — et forhåndsvalg for modifikatortast, mikrofon-mute, oppsummeringschips, overlay-gjennomsiktighet — slepte med seg sin egen "oversett de nye nøklene til 29 lokaler"-commit, for alltid, fordi ingenting håndhevet invarianten automatisk.

Du kan til og med måle uordenen i commit-meldingene. De samme tretti lokalfilene beskrives, release for release, som "30 språk", "29 språk" og — i én commit — "28 lokaler". Ingenting ble lagt til eller fjernet; det er tretti filer ved hver tag fra v1.8.3 til v2. Tallet driver fordi det ikke fantes én enkelt sannhetskilde for "hvor mange språk shipper vi", så hver commit-forfatter telte for hånd, noen ganger med engelsk som et språk og noen ganger som kilden. En endringslogg som ikke klarer å holde sitt eget tall stabilt er et lite, ærlig signal om at det den beskriver ikke håndheves av en maskin.

Flertallshåndteringens felle

Én ting til verdt å flagge, ikke fordi den gikk høylytt i stykker, men fordi det er den typen bug dette oppsettet inviterer til og ikke advarer deg om. Flertallshåndtering i disse lokalfilene gjøres manuelt, med parede nøkler: meetings.meetingCount og meetings.meetingCountPlural, profiles.filesAttached og filesAttachedPlural. Det er en ren engelsk entall/flertall-inndeling. Den er også feil for en del av språkene appen nå hevder å støtte. Russisk, polsk og tsjekkisk har tre eller fire flertallsformer avhengig av tallet — "1 fil", "2 filer", "5 filer" og "21 filer" kan ta ulike ordendelser. Et to-nøkkels entall/flertall-skjema kan ikke uttrykke det, så de tallene renderes grammatisk skjevt. i18next har ekte flertallsmaskineri for akkurat dette; de manuelle parene går utenom det, og ingen commit erkjenner gapet. Det er flertallspendanten til lekkasjen med hardkodede strenger: usynlig for buildet, synlig bare for en som har språket som morsmål.

Tre ting denne releasen lærte oss

  1. Biblioteket er de enkle 80 %; verktøyene er de vanskelige 20 %. Å koble inn react-i18next og masseoversette strenger er et løst problem. Å holde tretti filer i paritet for alltid er det ikke — og det trenger en ekstraktor for hardkodede strenger og en CI-kontroll for manglende nøkler, som et bibliotek ikke shipper noen av til deg. Bygg dem før du hevder "full i18n", ellers har du hevdet en invariant du ikke har noen måte å holde på.
  2. Del opp språkinnstillingene som faktisk er forskjellige. Grensesnittspråk, transkripsjonsspråk og AI-svarspråk er tre akser, og v1.8.3 hadde rett i å gi hver sin egen nøkkel — og enda mer rett i å så den nye fra en beslektet gammel via migrering, slik at oppgradering ikke kostet brukerne noe.
  3. Tallet i endringsloggen din er et disiplinmål. Når det samme filsettet er 28, 29 og 30 språk på tvers av tre commits, er tallet ikke problemet — den manglende sannhetskilden er det. Hvis en maskin ikke teller språkene dine, er det ingen som teller dem likt to ganger.

For det forrige kapittelet i v1-historien, å redesigne dashbordet rundt ett enkelt bølgediagram (v1.8.2); og for hele buen, anatomien i å shippe programvare til perfeksjon.