Steven
Steven7 min. skaitymo

Trisdešimties kalbų išleidimas be apsauginio tinklo

GeekBye v1.8.3 prijungė react-i18next ir išvertė visą programą į 30 kalbų. Dalis su biblioteka buvo rutina. Įdomiausia tai, ko trūko: jokio raktų pariteto patikrinimo ir jokio eilučių ekstraktoriaus — tad į kodą įrašyta anglų kalba prasisunkė, į produkciją iškeliavo daniška rašybos klaida, o changelog negalėjo susitarti, ar programa kalba 28, 29 ar 30 kalbomis.

Inžinerija
i18n
Desktop
GeekBye leidimai
Trisdešimties kalbų išleidimas be apsauginio tinklo

Kai kurie leidimai yra funkcija. GeekBye v1.8.3 atrodo kaip vienas iš jų — „programa dabar kalba 30 kalbų" — bet funkcija buvo lengvoji dalis, ir šis įrašas ne apie ją. Tarp 2026 vasario 17 ir 19 du pull requestai prijungė react-i18next prie programos, refaktoravo kiekvieną į kodą įrašytą anglišką eilutę į vertimo iškvietimą ir supakavo trisdešimt lokalizacijos failų. Tai tikras darbas, bet tai toks darbas, kurį biblioteka iš principo turi paversti rutina. Įdomi inžinerija yra viskas, ko leidimas _ne_pastatė — apsauginis tinklas, kuris būtų pagavęs tai, ką jis išsiuntė sugadinta.

Prijungimas buvo lengvieji 80%

Infrastruktūros commitas pridėjo react-i18next ant i18next viršaus ir padarė įprastą dalyką: i18n/index.ts, kuris kviečia initReactI18next, statiškai importuoja visus trisdešimt lokalizacijos JSON-ų ir registruoja juos po vienu resources objektu. Jokio tingaus krovimo — kiekviena kalba važiuoja bandle. Vertimų eilutės gyvena src/i18n/locales/*.json kaip plokščios, taškais atskirtos mapos: sidebar.meetings, startMeeting.start, vienas raktas vienai eilutei.

Migracija iš į kodą įrašytos anglų kalbos į t('key') buvo padaryta sąžiningai, komponentų grupė po grupės, ir visą tą perėjimą gali perskaityti commitų loge: pirma šoninė juosta, navigacija ir chatas; paskui nustatymai, prisijungimas ir onbordingas; paskui susitikimai, dashboardas, tostai ir navbaro paaiškinimai. Kiekvienas commitas pakeitė partiją tiesioginių eilučių vertimo iškvietimais. Dvidešimt devyni neangliški failai nusileido viename commite — devynių tūkstančių eilučių išmetimas, kuris išvertė anglišką šaltinį į viską nuo bulgarų iki vietnamiečių.

Nieko iš to nesunku padaryti teisingai, nes padaryti tai neteisingai garsu: trūkstamas provideris griūva, sugadintas JSON neparsinamas, blogas importas laužo build. Biblioteka duoda tau turėklus mechaninėms klaidoms. Ko ji neduoda — tai turėklas klaidai, kuri iš tikrųjų nutiko.

Trys kalbos nustatymai, ir kodėl tai teisinga

Prieš pereidami prie to, kas sulūžo, vienas dalykas, kurį v1.8.3 padarė būtent teisingai, nes tai skirtis, kurią daug programų suplokština, o neturėtų. Po šio leidimo GeekBye turi tris nepriklausomus kalbos nustatymus:

  • AI atsakymų kalba (output_language) — kokia kalba atsako asistentas. Seniausias iš trijų, nuo 2025 lapkričio.
  • Transkripcijos kalba (transcriptLanguage) — kokia kalba kalbos variklis transkribuoja tavo mikrofoną. Prijungta keliais leidimais anksčiau, v1.7.6.
  • Programos kalba (app_language) — mygtukų, meniu ir onbordingo kalba. Naujiena v1.8.3.

Tai iš tikrųjų skirtingos ašys. Kūrėjas Berlyne gali norėti vokiškos sąsajos, angliškų AI atsakymų (nes tokia yra dokumentacijos, kurią jis įklijuoja, kalba) ir angliškos transkripcijos (nes susitikimas vyksta angliškai). Suplakti tai į vieną „kalbos" nustatymą būtų neteisinga tam žmogui, ir onbordingo žingsnis, pridėtas šiame leidime, pateikia visus tris kaip atskirus išskleidžiamus sąrašus būtent dėl šios priežasties.

Malonus prisilietimas — atnaujinimo kelias. Vienkartinė migracija nukopijuoja kiekvieno esamo naudotojo output_language į naują app_language raktą per pirmą paleidimą po atnaujinimo, o provideris išsprendžia kalbą per fallbackų grandinę — app_language, paskui output_language, paskui 'en'. Tad naudotojai, kurie jau buvo pasakę programai, kad nori ispaniškų AI atsakymų, pabudo su ispaniška sąsaja, užuot buvę atstatyti į anglų kalbą ir priversti medžioti to nustatymo. Užsėti naują nustatymą iš giminingo seno — maža malonė, kuri kainuoja vieną migraciją ir kiekvienam, kas atsinaujina, sutaupo smulkaus dūrio.

Onbordingo perpiešimas, kuris nenorėjo

Yra vienas tikras bug, kurį verta ištraukti iš i18n darbo, nes tai React spąstai, į kuriuos pateks kiekvienas, kas daro kalbos perjungimą veikimo metu. Onbordingo vediklis kuria dalį savo parinkčių sąrašų useMemo viduje. Kai pakeiti kalbą pačiame kalbos pasirinkimo žingsnyje, sąsaja turėtų nedelsiant persipiešti nauja kalba — o ji to nedarė, nes t nebuvo memo priklausomybių masyve. Vertimo funkcija keičia tapatybę, kai keičiasi kalba; jei tavo memoizuota reikšmė sukurta iš t, bet nuo jos nepriklauso, React linksmai paduoda tau pasenusią, ankstesnės kalbos versiją. Pataisa lange pasirodo du kartus — kartą vedikliui, kartą nustatymų išskleidžiamam sąrašui — o tai tau sako, kad tai tokia klaida, kokią darai kiekvienoje vietoje, kur memoizuoji išverstą turinį, kol neišmoksti visada nurodyti t.

Onbordingas nešė ir raukšlę, apie kurią changelog neužsimena: kadangi tai white-label kodo bazė, yra du onbordingo medžiai, kuriuos reikia išversti — GeekBye vediklis ir Pavleur vediklis — gyvenantys po lygiagrečiomis raktų vardų erdvėmis onboarding.* ir onboarding.pavleur.*. Kiekviena onbordingo eilutė buvo išversta du kartus, į du prekės ženklo balsus, visomis dvidešimt devyniomis kalbomis.

Ko build nežinojo, kad jam trūksta

Štai dalis, kuri paverčia tai pamoka, o ne kontroliniu sąrašu. v1.8.3 neturėjo jokio automatinio būdo ištraukti į kodą įrašytas eilutes nei CI raktų pariteto tarp lokalizacijų patikros. Tad „atlikta" apibrėžimas buvo „kažkas apsilankė komponentuose, kuriuos prisiminė, ir apgaubė eilutes, kurias pamatė". Tas apibrėžimas prakiūra.

Po dviejų savaičių nuo leidimo nusileido tvarkymo commitas su žinute „translate all hardcoded English strings across 10 components into 30 languages." Dešimt komponentų — problemos pranešimo forma, santraukos ir metrikų kortelės, ataskaitos kortelė, transkripto ir chato turinys, naudojimo limito raginimas, chato žinutės, kodo kopijavimo mygtukas, plaukiojantys veiksmų mygtukai — iškeliavo v1.8.3 vis dar kalbėdami visiems angliškai, kiekviena kalba. Niekam build nesugriuvo. Joks testas nepasidarė raudonas. Vokiškas naudotojas tiesiog matė anglų kalbą ataskaitos kortelėje, o vienintelis detektorius buvo žmogus, galiausiai į tai pažvelgęs.

Tas pats nebuvimas pagimdė mažesnes gėdas. Daniškas vertimas iškeliavo su neteisingu žodžiu — Luk application helt vietoj Luk applikationen helt — ir buvo pataisytas vėlesniame leidime, nes nebuvo nei gimtakalbio recenzento vartų, nei būdo pipeline apie tai sužinoti. „Uždaryti programą" raktai trūko pradinėje partijoje ir buvo užpildyti dienomis vėliau. Ir kiekviena be išimties funkcija, kuri iškeliavo vėliau — modifikavimo klavišų presetas, mikrofono nutildymas, santraukos žetonai, perdangos permatomumas — vilko paskui save savo pačios commitą „išversk naujus raktus į 29 lokales", amžinai, nes niekas neprižiūrėjo invarianto automatiškai.

Netvarką gali net išmatuoti commitų žinutėse. Tie patys trisdešimt lokalizacijos failų aprašomi, leidimas po leidimo, kaip „30 kalbų", „29 kalbos" ir — viename commite — „28 lokalės". Nieko nepridėta ir nepašalinta; yra trisdešimt failų prie kiekvieno tago nuo v1.8.3 iki v2. Skaičius dreifuoja, nes nebuvo vienintelio tiesos šaltinio, „kiek kalbų mes išleidžiame", tad kiekvienas commito autorius skaičiavo ranka, kartais įskaičiuodamas anglų kaip kalbą, o kartais kaip šaltinį. Changelog, kuris negali išlaikyti savo paties skaičiaus stabilaus, yra mažas, sąžiningas signalas, kad dalykas, kurį jis aprašo, nėra prižiūrimas mašinos.

Pluralizacijos mina

Dar vienas dalykas, vertas pažymėti, ne todėl, kad jis sulūžo garsiai, o todėl, kad tai toks bug, kurį ši sąranga pakviečia ir apie kurį tavęs neįspės. Pluralizacija šiuose lokalizacijos failuose daroma ranka, poriniais raktais: meetings.meetingCount ir meetings.meetingCountPlural, profiles.filesAttached ir filesAttachedPlural. Tai švarus angliškas vienaskaitos/daugiskaitos padalijimas. Jis taip pat neteisingas daliai kalbų, kurias programa dabar teigia palaikanti. Rusų, lenkų ir čekų kalbos turi tris ar keturias daugiskaitos formas priklausomai nuo skaičiaus — „1 failas", „2 failai", „5 failų" ir „21 failas" gali imti skirtingas žodžių galūnes. Dviejų raktų vienaskaitos/daugiskaitos schema to negali išreikšti, tad tie skaičiai atvaizduojami gramatiškai kreivai. i18next turi tikrą daugiskaitos mechaniką būtent tam; rankinės poros ją apeina, ir joks commitas šios spragos nepripažįsta. Tai į kodą įrašytų eilučių nutekėjimo pluralizacijos atitikmuo: nematomas build, matomas tik gimtakalbiui.

Trys dalykai, kurių mus išmokė šis leidimas

  1. Biblioteka yra lengvieji 80%; įrankiai yra sunkieji 20%. Prijungti react-i18next ir masiškai išversti eilutes yra išspręsta problema. Amžinai laikyti trisdešimt failų paritete — ne, ir tam reikia į kodą įrašytų eilučių ekstraktoriaus bei CI trūkstamų raktų patikros, kurių nė vienos biblioteka tau neišleidžia. Pastatyk juos prieš tai, kai paskelbi „pilną i18n", kitaip paskelbei invariantą, kurio niekaip negali išlaikyti.
  2. Atskirk kalbos nustatymus, kurie iš tikrųjų skiriasi. Sąsajos kalba, transkripcijos kalba ir AI atsakymų kalba yra trys ašys, ir v1.8.3 buvo teisus, davęs kiekvienai savo raktą — ir dar teisesnis, užsėjęs naują iš giminingo seno per migraciją, tad atnaujinimas naudotojams nieko nekainavo.
  3. Skaičius tavo changelog yra disciplinos metrika. Kai tas pats failų rinkinys yra 28, 29 ir 30 kalbų per tris commitus, problema ne skaičius — o trūkstamas tiesos šaltinis. Jei mašina neskaičiuoja tavo kalbų, niekas jų neskaičiuoja dukart vienodai.

Ankstesniam v1 istorijos skyriui — dashboardo perprojektavimas aplink vieną banguotą grafiką (v1.8.2); o visai istorijos linijai — programinės įrangos tobulybės pristatymo anatomija.