
Wysłanie trzydziestu języków bez siatki asekuracyjnej
GeekBye v1.8.3 podpiął react-i18next i przetłumaczył całą aplikację na 30 języków. Część z biblioteką była rutyną. Ciekawe jest to, czego zabrakło: żadnego sprawdzania parytetu kluczy i żadnego ekstraktora stringów — więc zaszyty na sztywno angielski przeciekł, do produkcji trafiła duńska literówka, a changelog nie potrafił się zgodzić, czy aplikacja mówi w 28, 29 czy 30 językach.
Niektóre wydania to feature. GeekBye v1.8.3 wygląda jak jedno z nich — „aplikacja mówi teraz w 30 językach" — ale feature był łatwą częścią i nie o nim jest ten wpis. Między 17 a 19 lutego 2026 dwa pull requesty podpięły react-i18next do aplikacji, przerobiły każdy zaszyty na sztywno angielski string na wywołanie tłumaczenia i spakowały trzydzieści plików lokalizacji. To prawdziwa praca, ale to rodzaj pracy, którą biblioteka ma z założenia czynić rutyną. Ciekawa inżynieria to wszystko, czego to wydanie nie zbudowało — siatka asekuracyjna, która wyłapałaby to, co wysłało zepsute.
Okablowanie było łatwym 80%
Commit infrastrukturalny dodał react-i18next na wierzchu i18next i zrobił rzecz konwencjonalną: i18n/index.ts, który wywołuje initReactI18next, statycznie importuje wszystkie trzydzieści JSON-ów lokalizacji i rejestruje je pod jednym obiektem resources. Żadnego leniwego ładowania — każdy język jedzie w bundlu. Stringi tłumaczeń żyją w src/i18n/locales/*.json jako płaskie mapy z przestrzenią nazw po kropce: sidebar.meetings, startMeeting.start, jeden klucz na jeden string.
Migracja z zaszytego na sztywno angielskiego do t('key') została zrobiona uczciwie, grupa komponentów po grupie, a cały przemiał można wyczytać z logu commitów: najpierw pasek boczny, nawigacja i czat; potem ustawienia, logowanie i onboarding; potem spotkania, dashboard, toasty i podpowiedzi w navbarze. Każdy commit zastępował partię dosłownych stringów wywołaniami tłumaczeń. Dwadzieścia dziewięć nieangielskich plików wylądowało w jednym commicie — dziewięciotysięczna zrzutka linii, która przetłumaczyła angielskie źródło na wszystko, od bułgarskiego po wietnamski.
Nic z tego nie jest trudne do zrobienia dobrze, bo zrobienie tego źle jest głośne: brakujący provider się wywala, źle sformowany JSON nie parsuje się, zły import psuje build. Biblioteka daje ci barierki na błędy mechaniczne. Czego nie daje, to barierka na błąd, który naprawdę się wydarzył.
Trzy ustawienia języka i dlaczego to słuszne
Zanim dojdziemy do tego, co się zepsuło, jedna rzecz, którą v1.8.3 zrobił dokładnie dobrze, bo to rozróżnienie, które wiele aplikacji spłaszcza, a nie powinno. Po tym wydaniu GeekBye ma trzy niezależne ustawienia języka:
- Język odpowiedzi AI (
output_language) — język, w którym odpowiada asystent. Najstarszy z trójki, z listopada 2025. - Język transkrypcji (
transcriptLanguage) — język, na jaki silnik mowy transkrybuje twój mikrofon. Podpięty kilka wydań wcześniej, w v1.7.6. - Język aplikacji (
app_language) — język przycisków, menu i onboardingu. Nowość w v1.8.3.
To naprawdę różne osie. Programista w Berlinie może chcieć niemieckiego interfejsu, angielskich odpowiedzi AI (bo to język dokumentacji, którą wkleja) i angielskiej transkrypcji (bo spotkanie jest po angielsku). Zwinięcie tego w jedno ustawienie „język" byłoby dla tej osoby błędne, a krok onboardingu dodany w tym wydaniu prezentuje wszystkie trzy jako osobne listy rozwijane dokładnie z tego powodu.
Miłym akcentem jest ścieżka aktualizacji. Jednorazowa migracja kopiuje output_language każdego istniejącego użytkownika do nowego klucza app_language przy pierwszym uruchomieniu po aktualizacji, a provider rozwiązuje język przez łańcuch fallbacków — app_language, potem output_language, potem 'en'. Więc użytkownicy, którzy już powiedzieli aplikacji, że chcą hiszpańskich odpowiedzi AI, obudzili się z hiszpańskim interfejsem, zamiast zostać zresetowanymi do angielskiego i zmuszonymi do polowania na to ustawienie. Zasianie nowej preferencji z pokrewnej starej to drobna uprzejmość, która kosztuje jedną migrację i oszczędza każdemu aktualizującemu drobnej niedogodności.
Przerysowanie onboardingu, które nie chciało nastąpić
Jest jeden prawdziwy bug wart wyciągnięcia z pracy nad i18n, bo to pułapka Reacta, w którą wpadnie każdy, kto robi przełączanie języka w czasie działania. Kreator onboardingu buduje część swoich list opcji wewnątrz useMemo. Kiedy zmieniasz język na samym kroku wyboru języka, interfejs powinien natychmiast przerysować się w nowym języku — a nie robił tego, bo t nie było w tablicy zależności memo. Funkcja tłumacząca zmienia tożsamość, gdy zmienia się język; jeśli twoja zmemoizowana wartość jest zbudowana z t, ale od niego nie zależy, React z radością wręcza ci nieaktualną wersję w poprzednim języku. Poprawka pojawia się w logu dwa razy — raz dla kreatora, raz dla listy rozwijanej w ustawieniach — co mówi ci, że to rodzaj błędu, który popełniasz w każdym miejscu, gdzie memoizujesz przetłumaczoną treść, dopóki nie nauczysz się zawsze wpisywać t.
Onboarding niósł też zmarszczkę, o której changelog nie wspomina: ponieważ to baza kodu typu white-label, są dwa drzewa onboardingu do przetłumaczenia — kreator GeekBye i kreator Pavleur — żyjące pod równoległymi przestrzeniami nazw kluczy onboarding.* i onboarding.pavleur.*. Każdy string onboardingu został przetłumaczony dwa razy, na dwa głosy marki, we wszystkich dwudziestu dziewięciu językach.
Czego build nie wiedział, że mu brakuje
Oto część, która czyni z tego lekcję zamiast listy kontrolnej. v1.8.3 nie miał żadnego automatycznego sposobu na wyciągnięcie zaszytych na sztywno stringów ani sprawdzania parytetu kluczy między lokalizacjami w CI. Więc definicją „gotowe" było „ktoś odwiedził komponenty, które pamiętał, i opakował stringi, które zobaczył". Ta definicja przecieka.
Dwa tygodnie po wydaniu wylądował commit porządkujący z komunikatem „translate all hardcoded English strings across 10 components into 30 languages." Dziesięć komponentów — formularz zgłaszania problemu, zakładki podsumowania i metryk, zakładka raportu, treść transkrypcji i czatu, monit o limicie użycia, wiadomości czatu, przycisk kopiowania kodu, pływające przyciski akcji — trafiło w v1.8.3 wciąż mówiąc do wszystkich po angielsku, w każdym języku. Niczyj build się nie wywalił. Żaden test nie zaświecił się na czerwono. Niemiecki użytkownik po prostu widział angielski w zakładce raportu, a jedynym detektorem był człowiek, który w końcu na to spojrzał.
Ta sama nieobecność wyprodukowała mniejsze wstydy. Duńskie tłumaczenie trafiło z błędnym słowem — Luk application helt zamiast Luk applikationen helt — i zostało załatane w późniejszym wydaniu, bo nie było bramki rodzimego recenzenta ani sposobu, by pipeline to wiedział. Klucze „zamknij aplikację" brakowały w początkowej partii i zostały uzupełnione dni później. A każdy pojedynczy feature, który trafił potem — preset klawiszy modyfikujących, wyciszenie mikrofonu, chipy podsumowania, przezroczystość nakładki — ciągnął za sobą własny commit „przetłumacz nowe klucze na 29 lokalizacji", na zawsze, bo nic nie egzekwowało tego niezmiennika automatycznie.
Nieporządek można nawet zmierzyć w komunikatach commitów. Te same trzydzieści plików lokalizacji opisywane jest, wydanie po wydaniu, jako „30 języków", „29 języków" i — w jednym commicie — „28 lokalizacji". Nic nie dodano ani nie usunięto; jest trzydzieści plików przy każdym tagu od v1.8.3 aż po v2. Liczba dryfuje, bo nie było jednego źródła prawdy o tym, „ile języków wysyłamy", więc każdy autor commita liczył ręcznie, czasem wliczając angielski jako język, a czasem jako źródło. Changelog, który nie potrafi utrzymać stabilnej własnej liczby, to drobny, uczciwy sygnał, że rzecz, którą opisuje, nie jest egzekwowana przez maszynę.
Mina pluralizacji
Jeszcze jedna rzecz warta zaznaczenia, nie dlatego, że zepsuła się głośno, ale dlatego, że to rodzaj buga, który ta konfiguracja zaprasza i przed którym cię nie ostrzeże. Pluralizacja w tych plikach lokalizacji robiona jest ręcznie, przez pary kluczy: meetings.meetingCount i meetings.meetingCountPlural, profiles.filesAttached i filesAttachedPlural. To czysty angielski podział pojedyncza/mnoga. Jest też błędny dla sporej części języków, które aplikacja teraz twierdzi, że wspiera. Rosyjski, polski i czeski mają trzy lub cztery formy liczby mnogiej w zależności od liczby — „1 plik", „2 pliki", „5 plików" i „21 plików" mogą przyjmować różne końcówki słów. Schemat dwóch kluczy pojedyncza/mnoga nie potrafi tego wyrazić, więc te liczby renderują się gramatycznie krzywo. i18next ma prawdziwą maszynerię liczby mnogiej dokładnie do tego; ręczne pary ją omijają, a żaden commit nie przyznaje się do tej luki. To pluralizacyjny odpowiednik przecieku zaszytych na sztywno stringów: niewidoczny dla builda, widoczny tylko dla rodzimego użytkownika.
Trzy rzeczy, których nauczyło nas to wydanie
- Biblioteka to łatwe 80%; narzędzia to trudne 20%. Podpięcie
react-i18nexti hurtowe tłumaczenie stringów to rozwiązany problem. Utrzymanie trzydziestu plików w parytecie na zawsze — nie, a wymaga ekstraktora zaszytych na sztywno stringów i sprawdzania brakujących kluczy w CI, z których biblioteka nie dostarczy ci żadnego. Zbuduj je zanim ogłosisz „pełne i18n", inaczej ogłosiłeś niezmiennik, którego nie masz jak utrzymać. - Rozdziel ustawienia języka, które naprawdę się różnią. Język interfejsu, język transkrypcji i język odpowiedzi AI to trzy osie, a v1.8.3 słusznie dał każdej własny klucz — i jeszcze słuszniej zasiał nowy z pokrewnego starego przez migrację, więc aktualizacja nic użytkowników nie kosztowała.
- Liczba w twoim changelogu to metryka dyscypliny. Kiedy ten sam zestaw plików to 28, 29 i 30 języków w trzech commitach, liczba nie jest problemem — brakujące źródło prawdy jest. Jeśli maszyna nie liczy twoich języków, nikt nie liczy ich dwa razy tak samo.
Po poprzedni rozdział historii v1 — przeprojektowanie dashboardu wokół jednego wykresu falowego (v1.8.2); a po cały łuk — anatomia dostarczania oprogramowania do perfekcji.