Steven
Steven6 min lukuaika

Kolmenkymmenen kielen toimittaminen ilman turvaverkkoa

GeekBye v1.8.3 kytki react-i18next-kirjaston ja käänsi koko sovelluksen 30 kielelle. Kirjasto-osuus oli rutiinia. Kiinnostava osa on se mikä puuttui: ei avainten pariteettitarkistusta eikä merkkijonojen poimijaa — joten kovakoodattu englanti pääsi läpi, tanskalainen kirjoitusvirhe toimitettiin, eikä muutosloki päässyt yksimielisyyteen siitä puhuiko sovellus 28, 29 vai 30 kieltä.

Ohjelmistokehitys
i18n
Desktop
GeekBye-julkaisut
Kolmenkymmenen kielen toimittaminen ilman turvaverkkoa

Jotkut julkaisut ovat ominaisuus. GeekBye v1.8.3 näyttää sellaiselta — "sovellus puhuu nyt 30 kieltä" — mutta ominaisuus oli helppo osa, eikä tämä kirjoitus ole siitä. 17.–19. helmikuuta 2026 kaksi pull requestia kytki react-i18next-kirjaston sovellukseen, muunsi jokaisen kovakoodatun englanninkielisen merkkijonon käännöskutsuksi ja niputti kolmekymmentä lokalisaatiotiedostoa. Se on oikeaa työtä, mutta sellaista työtä jonka kirjasto on suunniteltu tekemään rutiiniksi. Kiinnostava insinöörityö on kaikki se mitä julkaisu ei rakentanut — turvaverkko joka olisi napannut sen mitä se toimitti rikkinäisenä.

Kytkentä oli helppo 80 %

Infrastruktuuricommit lisäsi react-i18next-kirjaston i18next-kirjaston päälle ja teki sen tavanomaisen asian: i18n/index.ts joka kutsuu initReactI18next, tuo staattisesti kaikki kolmekymmentä lokalisaatio-JSONia ja rekisteröi ne yhden resources-objektin alle. Ei laiskaa latausta — jokainen kieli toimitetaan paketissa. Käännösmerkkijonot elävät src/i18n/locales/*.json-tiedostoissa litteinä, pisteillä nimiavaruudellisina karttoina: sidebar.meetings, startMeeting.start, yksi avain yhtä merkkijonoa kohti.

Migraatio kovakoodatusta englannista muotoon t('key') tehtiin rehellisesti, komponenttiryhmä kerrallaan, ja sen kulun voi lukea committilokista: ensin sivupalkki, navigointi ja chat; sitten asetukset, kirjautuminen ja onboarding; sitten palaverit, kojelauta, ilmoitukset ja navbarin työkaluvihjeet. Jokainen commit korvasi erän kirjaimellisia merkkijonoja käännöskutsuilla. Kaksikymmentäyhdeksän ei-englanninkielistä tiedostoa laskeutui yhdessä commitissa — yhdeksäntuhannen rivin pudotus joka käänsi englanninkielisen lähteen kaikeksi bulgariasta vietnamiin.

Mitään tuosta ei ole vaikea saada oikein, koska sen saaminen väärin on äänekästä: puuttuva provider kaatuu, viallinen JSON ei jäsenny, huono import rikkoo buildin. Kirjasto antaa sinulle suojakaiteet mekaanisia virheitä vastaan. Mitä se ei anna sinulle, on suojakaide virheelle joka oikeasti tapahtui.

Kolme kieliasetusta, ja miksi se on oikein

Ennen kuin pääsemme siihen mikä meni rikki, yksi asia jonka v1.8.3 sai täsmälleen oikein, koska se on erottelu jonka moni sovellus litistää vaikka ei pitäisi. Tämän julkaisun jälkeen GeekByella on kolme itsenäistä kieliasetusta:

  • AI-vastauskieli (output_language) — kieli jolla assistentti vastaa. Kolmesta vanhin, marraskuulta 2025.
  • Transkriptiokieli (transcriptLanguage) — kieli joksi puhemoottori litteroi mikrofonisi. Putkitettu muutama julkaisu aiemmin, versiossa v1.7.6.
  • Sovelluksen kieli (app_language) — painikkeiden, valikoiden ja onboardingin kieli. Uusi versiossa v1.8.3.

Nämä ovat aidosti eri akseleita. Berliinissä oleva kehittäjä saattaa haluta saksankielisen käyttöliittymän, englanninkieliset AI-vastaukset (koska se on liittämänsä dokumentaation kieli) ja englanninkielisen transkription (koska palaveri on englanniksi). Näiden latistaminen yhdeksi "kieli"-asetukseksi olisi väärin sille henkilölle, ja tässä julkaisussa lisätty onboarding-vaihe esittää kaikki kolme erillisinä pudotusvalikkoina juuri siitä syystä.

Mukava yksityiskohta on päivityspolku. Kertaluonteinen migraatio kopioi jokaisen olemassa olevan käyttäjän output_language-arvon uuteen app_language-avaimeen ensimmäisellä käynnistyksellä päivityksen jälkeen, ja provider ratkaisee kielen varaketjun kautta — app_language, sitten output_language, sitten 'en'. Niinpä käyttäjät jotka olivat jo kertoneet sovellukselle haluavansa espanjankieliset AI-vastaukset, heräsivät espanjankieliseen käyttöliittymään sen sijaan että heidät nollattaisiin englantiin ja pantaisiin etsimään asetusta. Uuden asetuksen siementäminen sukua olevasta vanhasta on pieni ystävällisyys joka maksaa yhden migraation ja säästää jokaisen päivittäjän pieneltä harmilta.

Onboardingin uudelleenrenderöinti joka jäi tekemättä

Yksi aito bugi kannattaa nostaa i18n-työstä esiin, koska se on React-ansa johon jokainen ajonaikaista kielenvaihtoa tekevä törmää. Onboarding-velho rakentaa osan valintalistoistaan useMemo-kutsun sisällä. Kun vaihdat kieltä kieliaskeleella itsellään, käyttöliittymän on tarkoitus piirtyä uudelleen uudella kielellä heti — eikä se piirtynyt, koska t ei ollut memon riippuvuustaulukossa. Käännösfunktio vaihtaa identiteettiä kun kieli muuttuu; jos memoisoitu arvosi on rakennettu t:stä mutta ei riipu siitä, React ojentaa sinulle iloisesti vanhentuneen, edellisen kielen version. Korjaus esiintyy lokissa kahdesti — kerran velholle, kerran asetusten pudotusvalikkolistalle — mikä kertoo että se on sellainen virhe jonka teet joka paikassa jossa memoisoit käännettyä sisältöä, kunnes opit aina luettelemaan t:n.

Onboarding kantoi myös mutkan jota muutosloki ei mainitse: koska tämä on white-label-koodikanta, käännettäviä onboarding-puita on kaksi — GeekBye-velho ja Pavleur-velho — jotka elävät rinnakkaisten onboarding.*- ja onboarding.pavleur.*-avainnimiavaruuksien alla. Jokainen onboarding-merkkijono käännettiin kahdesti, kahteen brändiääneen, kaikilla kahdellakymmenelläyhdeksällä kielellä.

Mitä build ei tiennyt sen puuttuvan

Tässä on se osa joka tekee tästä opetuksen tarkistuslistan sijaan. v1.8.3:lla ei ollut automaattista tapaa poimia kovakoodattuja merkkijonoja eikä CI-tarkistusta avainten pariteetille lokalisaatioiden välillä. Niinpä "valmis" määriteltiin näin: "joku kävi läpi ne komponentit jotka muisti ja kääri ne merkkijonot jotka näki." Tuo määritelmä vuotaa.

Kaksi viikkoa julkaisun jälkeen laskeutui siivouscommit viestillä "translate all hardcoded English strings across 10 components into 30 languages." Kymmenen komponenttia — ongelmailmoituslomake, yhteenveto- ja mittarivälilehdet, raporttivälilehti, transkription ja chatin sisältö, käyttörajakehote, chat-viestit, koodinkopiointipainike, kelluvat toimintopainikkeet — oli toimitettu v1.8.3:ssa puhuen yhä englantia kaikille, joka kielellä. Kenenkään build ei epäonnistunut. Yksikään testi ei mennyt punaiseksi. Saksalainen käyttäjä vain näki englantia raporttivälilehdellä, ja ainoa tunnistin oli ihminen joka lopulta katsoi sitä.

Sama poissaolo tuotti pienemmät nolostukset. Tanskankielinen käännös toimitettiin väärällä sanalla — Luk application helt eikä Luk applikationen helt — ja se paikattiin myöhemmässä julkaisussa, koska ei ollut äidinkielisen tarkastajan porttia eikä keinoa jolla putki olisi tiennyt. "Quit app" -avaimet puuttuivat alkuperäisestä erästä ja täydennettiin jälkikäteen päiviä myöhemmin. Ja jokainen ainoa ominaisuus joka toimitettiin sen jälkeen — muunnosnäppäimen esiasetus, mikrofonin mykistys, tiivistelmäsirut, peittokuvan läpinäkyvyys — laahasi perässään omaa "käännä uudet avaimet 29 lokalisaatioon" -committiaan, ikuisesti, koska mikään ei valvonut invarianttia automaattisesti.

Epäjärjestyksen voi jopa mitata committiviesteistä. Samoja kolmeakymmentä lokalisaatiotiedostoa kuvataan julkaisusta toiseen "30 kielenä", "29 kielenä" ja — yhdessä commitissa — "28 lokalisaationa". Mitään ei lisätty tai poistettu; jokaisessa tagissa v1.8.3:sta v2:een on kolmekymmentä tiedostoa. Luku ajelehtii koska ei ollut yhtä totuuden lähdettä sille "montako kieltä me toimitamme", joten jokainen committin tekijä laski käsin, joskus lukien englannin kieleksi ja joskus lähteeksi. Muutosloki joka ei pysty pitämään omaa lukuaan vakaana on pieni, rehellinen signaali siitä että kone ei valvo sitä mitä loki kuvaa.

Monikkomuodostuksen ansa

Vielä yksi asia jota kannattaa merkitä, ei siksi että se meni äänekkäästi rikki vaan siksi että se on sellainen bugi jonka tämä rakenne houkuttelee esiin eikä varoita sinua. Monikkomuodostus näissä lokalisaatiotiedostoissa tehdään manuaalisesti, parillisilla avaimilla: meetings.meetingCount ja meetings.meetingCountPlural, profiles.filesAttached ja filesAttachedPlural. Se on siisti englannin yksikkö/monikko-jako. Se on myös väärin osalle kieliä joita sovellus nyt väittää tukevansa. Venäjässä, puolassa ja tšekissä on kolme tai neljä monikkomuotoa luvusta riippuen — "1 tiedosto", "2 tiedostoa", "5 tiedostoa" ja "21 tiedostoa" voivat saada eri sananpäätteitä. Kahden avaimen yksikkö/monikko-malli ei voi ilmaista tuota, joten nuo lukumäärät renderöityvät kieliopillisesti pieleen. i18next-kirjastossa on aito monikkokoneisto juuri tähän; manuaaliset parit kiertävät sen, eikä yksikään commit myönnä tätä aukkoa. Se on kovakoodattujen merkkijonojen vuodon monikkovastine: buildille näkymätön, näkyvä vain äidinkieliselle puhujalle.

Kolme asiaa jotka tämä julkaisu opetti meille

  1. Kirjasto on helppo 80 %; työkalut ovat vaikea 20 %. react-i18next-kirjaston kytkeminen ja merkkijonojen massakääntäminen on ratkaistu ongelma. Kolmenkymmenen tiedoston pitäminen pariteetissa ikuisesti ei ole — ja se tarvitsee poimijan kovakoodatuille merkkijonoille ja CI-tarkistuksen puuttuville avaimille, joista kumpaakaan kirjasto ei toimita sinulle. Rakenna ne ennen kuin väität "täyttä i18n:ää", tai olet väittänyt invarianttia jota et voi mitenkään pitää.
  2. Erottele kieliasetukset jotka ovat oikeasti eri asioita. Käyttöliittymän kieli, transkriptiokieli ja AI-vastauskieli ovat kolme akselia, ja v1.8.3 teki oikein antaessaan kullekin oman avaimen — ja vielä oikeammin siementäessään uuden sukua olevasta vanhasta migraation kautta, niin ettei päivittäminen maksanut käyttäjille mitään.
  3. Muutoslokisi lukumäärä on kurinalaisuusmittari. Kun sama tiedostojoukko on 28, 29 ja 30 kieltä kolmessa commitissa, luku ei ole ongelma — puuttuva totuuden lähde on. Jos kone ei laske kieliäsi, kukaan ei laske niitä kahdesti samalla tavalla.

Edellinen luku v1-tarinassa: kojelaudan uudelleensuunnittelu yhden aaltokaavion ympärille (v1.8.2); ja koko kaarelle, ohjelmiston toimittamisen anatomia täydellisyyteen asti.