
Κυκλοφορώντας τριάντα γλώσσες χωρίς δίχτυ ασφαλείας
Το GeekBye v1.8.3 συνέδεσε το react-i18next και μετέφρασε ολόκληρη την εφαρμογή σε 30 γλώσσες. Το κομμάτι της library ήταν ρουτίνα. Το ενδιαφέρον κομμάτι είναι αυτό που έλειπε: κανένας έλεγχος parity κλειδιών και κανένας extractor strings — έτσι hardcoded αγγλικά ξέφυγαν, ένα δανέζικο τυπογραφικό λάθος κυκλοφόρησε, και το changelog δεν μπορούσε να συμφωνήσει αν η εφαρμογή μιλούσε 28, 29 ή 30 γλώσσες.
Κάποιες εκδόσεις είναι μια λειτουργία. Το GeekBye v1.8.3 μοιάζει με μία — «η εφαρμογή τώρα μιλά 30 γλώσσες» — αλλά η λειτουργία ήταν το εύκολο μέρος, και δεν είναι αυτό το θέμα αυτού του κειμένου. Μεταξύ 17 και 19 Φεβρουαρίου 2026, δύο pull requests συνέδεσαν το react-i18next στην εφαρμογή, αναδιαμόρφωσαν κάθε hardcoded αγγλικό string σε μια κλήση μετάφρασης, και πακετάρισαν τριάντα locale αρχεία. Αυτό είναι πραγματική δουλειά, αλλά είναι το είδος δουλειάς που μια library είναι σχεδιασμένη να κάνει ρουτίνα. Η ενδιαφέρουσα μηχανική είναι όλα όσα η έκδοση δεν έχτισε — το δίχτυ ασφαλείας που θα είχε πιάσει αυτό που κυκλοφόρησε σπασμένο.
Η σύνδεση ήταν το εύκολο 80%
Το commit υποδομής πρόσθεσε το react-i18next πάνω στο i18next και έκανε το συμβατικό: ένα i18n/index.ts που καλεί το initReactI18next, εισάγει στατικά και τα τριάντα locale JSON, και τα καταχωρεί κάτω από ένα αντικείμενο resources. Κανένα lazy loading — κάθε γλώσσα ταξιδεύει στο bundle. Τα strings μετάφρασης ζουν στο src/i18n/locales/*.json ως επίπεδοι χάρτες με namespace μέσω τελείας: sidebar.meetings, startMeeting.start, ένα κλειδί για ένα string.
Η μετάβαση από τα hardcoded αγγλικά στο t('key') έγινε με τον τίμιο τρόπο, ομάδα components μετά από ομάδα components, και μπορείς να διαβάσεις τη σάρωση στο log των commits: πρώτα sidebar, navigation και chat· μετά settings, login και onboarding· μετά meetings, dashboard, toasts, και τα tooltips του navbar. Κάθε commit αντικατέστησε μια παρτίδα κυριολεκτικών strings με κλήσεις μετάφρασης. Είκοσι εννέα μη αγγλικά αρχεία προσγειώθηκαν σε ένα μόνο commit — μια πτώση εννέα χιλιάδων γραμμών που μετέφρασε την αγγλική πηγή σε τα πάντα, από βουλγαρικά μέχρι βιετναμέζικα.
Τίποτα από αυτά δεν είναι δύσκολο να γίνει σωστά, επειδή το να τα κάνεις λάθος είναι θορυβώδες: ένας provider που λείπει κάνει crash, ένα κακοσχηματισμένο JSON δεν parse-άρει, ένα κακό import σπάει το build. Η library σου δίνει κιγκλιδώματα για τα μηχανικά λάθη. Αυτό που δεν σου δίνει είναι ένα κιγκλίδωμα για το λάθος που πραγματικά συνέβη.
Τρεις ρυθμίσεις γλώσσας, και γιατί αυτό είναι σωστό
Πριν φτάσουμε σε αυτό που έσπασε, ένα πράγμα που το v1.8.3 πέτυχε ακριβώς, επειδή είναι μια διάκριση που πολλές εφαρμογές ισοπεδώνουν ενώ δεν θα έπρεπε. Μετά από αυτή την έκδοση, το GeekBye έχει τρεις ανεξάρτητες ρυθμίσεις γλώσσας:
- Γλώσσα των AI απαντήσεων (
output_language) — σε τι απαντά ο assistant. Η παλαιότερη από τις τρεις, από τον Νοέμβριο του 2025. - Γλώσσα της μεταγραφής (
transcriptLanguage) — σε τι μεταγράφει το speech engine το μικρόφωνό σου. Περάστηκε μερικές εκδόσεις νωρίτερα, στο v1.7.6. - Γλώσσα της εφαρμογής (
app_language) — η γλώσσα των κουμπιών, των μενού και του onboarding. Νέα στο v1.8.3.
Αυτές είναι πραγματικά διαφορετικοί άξονες. Ένας developer στο Βερολίνο μπορεί να θέλει γερμανικό interface, αγγλικές AI απαντήσεις (επειδή αυτή είναι η γλώσσα της τεκμηρίωσης που κάνει paste) και αγγλική μεταγραφή (επειδή η συνάντηση είναι στα αγγλικά). Το να συμπτύξεις αυτά σε μία ρύθμιση «γλώσσας» θα ήταν λάθος για αυτό το άτομο, και το βήμα onboarding που προστέθηκε σε αυτή την έκδοση παρουσιάζει και τα τρία ως ξεχωριστά dropdowns ακριβώς για αυτόν τον λόγο.
Η ωραία πινελιά είναι η διαδρομή αναβάθμισης. Μια εφάπαξ migration αντιγράφει το output_language κάθε υπάρχοντος χρήστη στο νέο κλειδί app_language κατά την πρώτη εκκίνηση μετά την ενημέρωση, και ο provider επιλύει τη γλώσσα μέσω μιας αλυσίδας fallback — app_language, μετά output_language, μετά 'en'. Έτσι οι χρήστες που είχαν ήδη πει στην εφαρμογή ότι ήθελαν ισπανικές AI απαντήσεις ξύπνησαν με ισπανικό interface, αντί να επαναφερθούν στα αγγλικά και να αναγκαστούν να ψάξουν τη ρύθμιση. Το να θρέφεις μια νέα προτίμηση από μια σχετική παλιά είναι μια μικρή καλοσύνη που κοστίζει μία migration και γλιτώνει κάθε χρήστη που αναβαθμίζει από έναν μικρό εκνευρισμό.
Το re-render του onboarding που δεν γινόταν
Υπάρχει ένα γνήσιο bug που αξίζει να τραβηχτεί έξω από τη δουλειά του i18n, επειδή είναι μια παγίδα React που θα χτυπήσει όποιον κάνει runtime αλλαγή γλώσσας. Ο onboarding wizard χτίζει κάποιες από τις λίστες επιλογών του μέσα σε ένα useMemo. Όταν αλλάζεις τη γλώσσα σου στο ίδιο το βήμα της γλώσσας, το UI υποτίθεται ότι επανασχεδιάζεται αμέσως στη νέα γλώσσα — και δεν το έκανε, επειδή το t δεν ήταν στον πίνακα εξαρτήσεων του memo. Η συνάρτηση μετάφρασης αλλάζει ταυτότητα όταν αλλάζει η γλώσσα· αν η μεμοϊζευμένη τιμή σου είναι χτισμένη από το t αλλά δεν εξαρτάται από αυτό, το React σου παραδίδει ευχαρίστως την ξεπερασμένη εκδοχή της προηγούμενης γλώσσας. Το fix εμφανίζεται δύο φορές στο log — μία για τον wizard, μία για τη λίστα dropdown των settings — που σου λέει ότι είναι το είδος του λάθους που κάνεις σε κάθε σημείο όπου μεμοϊζεύεις μεταφρασμένο περιεχόμενο, μέχρι να μάθεις να βάζεις πάντα το t στη λίστα.
Το onboarding κουβαλούσε επίσης μια πτυχή που το changelog δεν αναφέρει: επειδή αυτό είναι ένα white-label codebase, υπάρχουν δύο δέντρα onboarding προς μετάφραση — ένας wizard GeekBye και ένας Pavleur — που ζουν κάτω από τα παράλληλα namespaces κλειδιών onboarding.* και onboarding.pavleur.*. Κάθε string onboarding μεταφράστηκε δύο φορές, σε δύο φωνές brand, σε όλες τις είκοσι εννέα γλώσσες.
Αυτό που το build δεν ήξερε ότι του έλειπε
Να το κομμάτι που κάνει αυτό μάθημα αντί για checklist. Το v1.8.3 δεν είχε κανέναν αυτόματο τρόπο να εξάγει hardcoded strings ούτε CI έλεγχο για parity κλειδιών ανάμεσα στα locales. Έτσι ο ορισμός του «έτοιμο» ήταν «κάποιος επισκέφτηκε τα components που θυμόταν και τύλιξε τα strings που είδε». Αυτός ο ορισμός αφήνει πράγματα να ξεφεύγουν.
Δύο εβδομάδες μετά την έκδοση, προσγειώθηκε ένα commit καθαρισμού με το μήνυμα «translate all hardcoded English strings across 10 components into 30 languages» (μετάφρασε όλα τα hardcoded αγγλικά strings σε 10 components σε 30 γλώσσες). Δέκα components — η φόρμα αναφοράς προβλήματος, τα tabs σύνοψης και μετρικών, το tab αναφοράς, το περιεχόμενο μεταγραφής και chat, το prompt ορίου χρήσης, τα μηνύματα chat, το κουμπί copy-code, τα floating action buttons — είχαν κυκλοφορήσει στο v1.8.3 μιλώντας ακόμη αγγλικά σε όλους, σε κάθε γλώσσα. Κανενός το build δεν απέτυχε. Κανένα test δεν κοκκίνισε. Ένας Γερμανός χρήστης απλώς έβλεπε αγγλικά στο tab αναφοράς, και ο μόνος ανιχνευτής ήταν ένας άνθρωπος που τελικά το κοίταξε.
Η ίδια απουσία παρήγαγε τις μικρότερες αμηχανίες. Μια δανέζικη μετάφραση κυκλοφόρησε με τη λάθος λέξη — Luk application helt αντί για Luk applikationen helt — και διορθώθηκε σε μια μεταγενέστερη έκδοση, επειδή δεν υπήρχε πύλη native reviewer ούτε τρόπος να το ξέρει το pipeline. Τα κλειδιά «quit app» έλειπαν από την αρχική παρτίδα και συμπληρώθηκαν μέρες αργότερα. Και κάθε μία λειτουργία που κυκλοφόρησε μετά — ένα preset modifier-key, mic mute, recap chips, opacity του overlay — έσερνε πίσω της το δικό της commit «μετάφρασε τα νέα κλειδιά σε 29 locales», για πάντα, επειδή τίποτα δεν επέβαλλε την αναλλοίωτη ιδιότητα αυτόματα.
Μπορείς μάλιστα να μετρήσεις την αταξία στα μηνύματα των commits. Τα ίδια τριάντα locale αρχεία περιγράφονται, έκδοση με έκδοση, ως «30 γλώσσες», «29 γλώσσες» και — σε ένα commit — «28 locales». Τίποτα δεν προστέθηκε ή αφαιρέθηκε· υπάρχουν τριάντα αρχεία σε κάθε tag από το v1.8.3 ως το v2. Ο αριθμός παρασύρεται επειδή δεν υπήρχε μία μοναδική πηγή αλήθειας για το «πόσες γλώσσες κυκλοφορούμε», οπότε κάθε συντάκτης commit μετρούσε με το χέρι, άλλοτε συμπεριλαμβάνοντας τα αγγλικά ως γλώσσα και άλλοτε ως πηγή. Ένα changelog που δεν μπορεί να κρατήσει σταθερό το ίδιο του το πλήθος είναι ένα μικρό, τίμιο σήμα ότι αυτό που περιγράφει δεν επιβάλλεται από μηχανή.
Η νάρκη της πληθυντικοποίησης
Ένα ακόμη πράγμα που αξίζει να επισημανθεί, όχι επειδή έσπασε θορυβωδώς αλλά επειδή είναι το είδος bug που αυτή η διάταξη προσκαλεί και δεν θα σε προειδοποιήσει γι' αυτό. Η πληθυντικοποίηση σε αυτά τα locale αρχεία γίνεται χειροκίνητα, με ζευγαρωμένα κλειδιά: meetings.meetingCount και meetings.meetingCountPlural, profiles.filesAttached και filesAttachedPlural. Αυτός είναι ένας καθαρός αγγλικός διαχωρισμός ενικού/πληθυντικού. Είναι επίσης λάθος για ένα κομμάτι των γλωσσών που η εφαρμογή τώρα ισχυρίζεται ότι υποστηρίζει. Τα ρωσικά, πολωνικά και τσεχικά έχουν τρεις ή τέσσερις μορφές πληθυντικού ανάλογα με τον αριθμό — «1 αρχείο», «2 αρχεία», «5 αρχεία» και «21 αρχεία» μπορούν να πάρουν διαφορετικές καταλήξεις. Ένα σχήμα δύο κλειδιών ενικού/πληθυντικού δεν μπορεί να το εκφράσει αυτό, οπότε αυτά τα πλήθη αποδίδονται γραμματικά λάθος. Το i18next έχει πραγματικό μηχανισμό πληθυντικού ακριβώς γι' αυτό· τα χειροκίνητα ζεύγη τον παρακάμπτουν, και κανένα commit δεν αναγνωρίζει το κενό. Είναι το ανάλογο της διαρροής hardcoded strings στην πληθυντικοποίηση: αόρατο στο build, ορατό μόνο σε έναν φυσικό ομιλητή.
Τρία πράγματα που μας δίδαξε αυτή η έκδοση
- Η library είναι το εύκολο 80%· τα εργαλεία είναι το δύσκολο 20%. Η σύνδεση του
react-i18nextκαι η μαζική μετάφραση strings είναι λυμένο πρόβλημα. Το να κρατάς τριάντα αρχεία σε parity για πάντα δεν είναι — και χρειάζεται έναν extractor για hardcoded strings και έναν CI έλεγχο για ελλείποντα κλειδιά, κανένα από τα οποία δεν σου παραδίδει μια library. Χτίσε τα πριν ισχυριστείς «πλήρες i18n», αλλιώς έχεις ισχυριστεί μια αναλλοίωτη ιδιότητα που δεν έχεις τρόπο να κρατήσεις. - Χώρισε τις ρυθμίσεις γλώσσας που πραγματικά διαφέρουν. Η γλώσσα του UI, η γλώσσα της μεταγραφής και η γλώσσα των AI απαντήσεων είναι τρεις άξονες, και το v1.8.3 σωστά έδωσε στην καθεμία το δικό της κλειδί — και ακόμη σωστότερα έθρεψε τη νέα από μια σχετική παλιά μέσω migration, ώστε η αναβάθμιση να μην κοστίσει τίποτα στους χρήστες.
- Το πλήθος στο changelog σου είναι μια μετρική πειθαρχίας. Όταν το ίδιο σύνολο αρχείων είναι 28, 29 και 30 γλώσσες σε τρία commits, ο αριθμός δεν είναι το πρόβλημα — η ελλείπουσα πηγή αλήθειας είναι. Αν μια μηχανή δεν μετρά τις γλώσσες σου, κανείς δεν τις μετρά με τον ίδιο τρόπο δύο φορές.
Για το προηγούμενο κεφάλαιο της ιστορίας του v1, επανασχεδιάζοντας το dashboard γύρω από ένα wave chart (v1.8.2)· και για ολόκληρη την πορεία, η ανατομία της παράδοσης λογισμικού στην τελειότητα.