Internationalisatie-architectuur voor chat-gebouwde apps: definieer stabiele string-keys, pluralregels en één vertaalworkflow die consistent blijft op web en mobiel.

Het eerste dat kapotgaat is niet de code. Het zijn de woorden.
Chat-gegenereerde apps beginnen vaak als een snel prototype: je typt “Voeg een knop toe met Tekst Opslaan”, de UI verschijnt en je gaat door. Weken later wil je Spaans en Duits, en ontdek je dat die “tijdelijke” labels verspreid staan over schermen, componenten, e-mails en foutmeldingen.
Tekstwijzigingen komen ook vaker voor dan codewijzigingen. Productnamen worden aangepast, juridische teksten veranderen, onboarding wordt herschreven en support vraagt om duidelijkere foutmeldingen. Als tekst direct in UI-code leeft, wordt elke kleine formulering een risicovolle release en mis je plekken waar hetzelfde idee anders geformuleerd is.
Hier zijn vroege signalen dat je vertaalschuld opbouwt:
Een realistisch voorbeeld: je bouwt een eenvoudige CRM in Koder.ai. De webapp zegt “Deal stage”, de mobiele app zegt “Pipeline step” en een error toast zegt “Invalid status”. Zelfs als alle drie vertaald zijn, voelt het voor gebruikers inconsistent omdat de concepten niet overeenkomen.
“Consistent” betekent niet “overal dezelfde tekens”. Het betekent:
Zodra je tekst behandelt als productdata in plaats van decoratie, wordt talen toevoegen geen paniekactie meer maar routine.
Internationalisatie (i18n) is het werk dat je doet zodat een app meerdere talen kan ondersteunen zonder herschrijvingen. Lokalisatie (l10n) is de daadwerkelijke inhoud voor een specifieke taal en regio, zoals Frans (Canada) met de juiste woorden, datumformaten en toon.
Een eenvoudig doel: elk stuk gebruikerstekst wordt geselecteerd via een stabiele key, niet rechtstreeks in de UI-code getypt. Als je een zin kunt veranderen zonder een React-component of Flutter-widget te openen, zit je op het juiste spoor. Dit is de kern van een internationalisatie-architectuur voor chat-gebouwde apps, waar het makkelijk is per ongeluk hardgecodeerde copy te shippen die in een chat is gegenereerd.
Gebruikerstekst is breder dan teams vaak verwachten. Het omvat knoppen, labels, validatiefouten, lege staten, onboarding-tips, pushmeldingen, e-mails, PDF-exports en elk bericht dat een gebruiker kan zien of horen. Het omvat meestal geen interne logs, databasenaamgevingen, analytics-event-ID's, featureflags of admin-only debug-output.
Waar horen vertalingen te leven? In de praktijk is het vaak zowel frontend als backend, met een duidelijke grens.
De fout om te vermijden is verantwoordelijkheden door elkaar halen. Als de backend volledig geschreven Engelse zinnen terugstuurt voor UI-fouten, kan de frontend ze niet goed lokaliseren. Een beter patroon is: de backend stuurt een foutcode (en eventueel veilige parameters), en de client mappt die code naar een gelokaliseerd bericht.
Copy-eigenaarschap is een productbeslissing, geen technische detail. Beslis vroeg wie woorden kan veranderen en wie de toon goedkeurt.
Als product verantwoordelijk is voor copy, behandel vertalingen als content: versieer ze, review ze en geef product een veilige manier om wijzigingen aan te vragen. Als engineering verantwoordelijk is, stel dan de regel dat elke nieuwe UI-string met een key en standaardvertaling geleverd moet worden voordat het kan shippen.
Voorbeeld: als je aanmeldflow op drie verschillende schermen “Create account” zegt, maak er één key van die overal gebruikt wordt. Dat houdt de betekenis consistent, maakt vertalers sneller en voorkomt dat kleine woordwijzigingen later een grootschalige schoonmaak worden.
Keys zijn het contract tussen je UI en je vertalingen. Als dat contract blijft veranderen, krijg je ontbrekende tekst, gehaaste fixes en inconsistente woordkeuze tussen web en mobiel. Een goede internationalisatie-architectuur voor chat-gebouwde apps begint met één regel: keys moeten betekenis beschrijven, niet de huidige Engelse zin.
Gebruik stabiele IDs als keys (zoals billing.invoice.payNow) in plaats van de volledige copy (zoals “Pay now”). Zinskeys breken zodra iemand de bewoording aanpast, leestekens toevoegt of hoofdletters verandert.
Een praktisch patroon dat leesbaar blijft is: scherm (of domein) + component + intentie. Houd het saai en voorspelbaar.
Voorbeelden:
auth.login.titleauth.login.emailLabelbilling.checkout.payButtonnav.settingserrors.network.offlineBepaal wanneer je een key hergebruikt of een nieuwe maakt door te vragen: “Is de betekenis identiek op elke plek?” Hergebruik keys voor echt generieke acties, maar splits keys wanneer de context verandert. Bijvoorbeeld, “Save” in een profielscherm is een simpele actie, terwijl “Save” in een complexe editor in sommige talen een andere toon kan vereisen.
Houd gedeelde UI-tekst in dedicated namespaces zodat het niet over schermen wordt gedupliceerd. Handige buckets:
common.actions.* (save, cancel, delete)common.status.* (loading, success)common.fields.* (search, password)errors.* (validation, network)nav.* (tabs, menu items)Wanneer de bewoording verandert maar de betekenis gelijk blijft, behoud dan de key en update alleen de vertaalde waarden. Dat is precies het doel van stabiele IDs. Als de betekenis verandert (zelfs subtiel), maak dan een nieuwe key en laat de oude staan totdat je zeker weet dat die niet meer gebruikt wordt. Dit voorkomt “stille” mismatches waarbij een oude vertaling technisch aanwezig is maar nu onjuist.
Een klein voorbeeld uit een Koder.ai-achtige flow: je chat genereert zowel een React-webapp als een Flutter-mobile-app. Als beide common.actions.save gebruiken, krijg je overal consistente vertalingen. Maar als de webapp profile.save gebruikt en mobiel account.saveButton, zal je mettertijd uit elkaar drijven, ook al lijkt het Engels vandaag hetzelfde.
Behandel je brontaal (vaak Engels) als de enige bron van waarheid. Houd het op één plek, review het zoals code en voorkom dat strings in willekeurige componenten verschijnen “voor nu”. Dit is de snelste manier om hardgecodeerde UI-copy en latere herwerken te vermijden.
Een eenvoudige regel helpt: de app mag tekst alleen vanuit het i18n-systeem tonen. Als iemand nieuwe copy nodig heeft, voegen ze eerst een key en een default message toe, en gebruiken die key daarna in de UI. Dit houdt je internationalisatie-architectuur stabiel, zelfs wanneer features verschuiven.
Als je zowel web als mobiel levert, wil je één gedeeld catalogus van keys, plus ruimte voor featureteams om te werken zonder elkaar in de weg te zitten. Een praktische layout:
Houd keys identiek over platformen, ook als implementatie verschilt (React op web, Flutter op mobiel). Gebruik je een platform zoals Koder.ai om beide apps vanuit chat te genereren, dan is broncode exporteren makkelijker te onderhouden wanneer beide projecten naar dezelfde key-namen en hetzelfde berichtformaat verwijzen.
Vertalingen veranderen in de loop van de tijd. Behandel wijzigingen als productwijzigingen: klein, gereviewd en traceerbaar. Een goede review richt zich op betekenis en hergebruik, niet alleen op spelling.
Om te voorkomen dat keys tussen teams gaan drijven, maak keys eigendom van features (billing., auth.) en hernoem nooit keys alleen omdat de bewoording verandert. Update het bericht, behoud de key. Keys zijn identifiers, geen copy.
Pluralregels verschillen per taal, dus het eenvoudige Engelse patroon (1 vs alles) faalt snel. Sommige talen hebben aparte vormen voor 0, 1, 2-4, en anderen veranderen de hele zin, niet alleen het zelfstandig naamwoord. Als je plurale logica in de UI met if-else bakt, eindig je met gedupliceerde copy en gemiste randgevallen.
Een veiligere aanpak is één flexibel bericht per idee te houden en de i18n-laag de juiste vorm te laten kiezen. ICU-achtige berichten zijn hiervoor gemaakt. Ze houden grammaticale beslissingen bij de vertaling, niet in je componenten.
Hier is een klein voorbeeld dat de vergeten gevallen dekt:
itemsCount = "{count, plural, =0 {No items} one {# item} other {# items}}"
Die ene key dekt 0, 1 en alles daartussen. Vertalers kunnen het vervangen door de juiste pluralvormen voor hun taal zonder dat jij code hoeft aan te passen.
Wanneer je gender- of rol-gebaseerde woordkeuze nodig hebt, vermijd dan aparte keys zoals welcome_male en welcome_female tenzij het product het echt vereist. Gebruik select zodat de zin één geheel blijft:
welcomeUser = "{gender, select, female {Welcome, Ms. {name}} male {Welcome, Mr. {name}} other {Welcome, {name}}}"
Om te voorkomen dat je vastloopt met grammaticale gevallen, houd zinnen zo volledig mogelijk. Naai geen fragmenten aan elkaar zoals "{count} " + t('items') omdat veel talen woorden niet op die manier kunnen herschikken. Geef de voorkeur aan één bericht dat het getal, het zelfstandig naamwoord en omliggende woorden bevat.
Een simpele regel die goed werkt in chat-gebouwde apps (inclusief Koder.ai-projecten) is: als een zin een nummer, een persoon of een status bevat, maak het vanaf dag één een ICU-boodschap. Het kost iets meer werk vooraf en bespaart veel vertaalschuld later.
Als je React-webapp en Flutter-mobileapp elk hun eigen vertaalbestanden bijhouden, zullen ze uit elkaar drijven. Dezelfde knop krijgt andere woordkeuze, een key betekent het ene op web en iets anders op mobiel, en supporttickets beginnen te zeggen “de app zegt X maar de website zegt Y”.
De simpelste oplossing is ook de belangrijkste: kies één bron-van-waarheid-formaat en behandel het als code. Voor de meeste teams betekent dat één gedeelde set locale-bestanden (bijv. JSON met ICU-berichten) die zowel web als mobiel consumeren. Als je apps via chat en generators bouwt, is dit extra belangrijk omdat je makkelijk per ongeluk nieuwe tekst op twee plekken creëert.
Een praktisch opzet is een klein “i18n-pakket” of map die bevat:
React en Flutter worden dan consumenten. Zij zouden geen nieuwe keys lokaal moeten bedenken. In een Koder.ai-stijl workflow (React web, Flutter mobiel) kun je beide clients genereren uit dezelfde keyset en wijzigingen onder review houden zoals elke andere codewijziging.
Backend-alignement hoort bij hetzelfde verhaal. Fouten, notificaties en e-mails mogen niet als handgeschreven Engelse zinnen in Go staan. Stuur in plaats daarvan stabiele foutcodes terug (zoals auth.invalid_password) plus veilige parameters. Vervolgens mappen clients codes naar vertaalde tekst. Voor server-generated e-mails kan de server templates renderen met dezelfde keys en locale-bestanden.
Maak één klein regelboek en handhaaf het in code review:
Om dubbele keys met verschillende betekenissen te voorkomen, voeg een “description”-veld (of een commentaarbestand) toe voor vertalers en je toekomstige zelf. Voorbeeld: billing.trial_days_left moet uitleggen of het als banner, e-mail of beide wordt getoond. Dat ene zinnetje voorkomt vaak het “goed genoeg” hergebruik dat vertaalschuld creëert.
Deze consistentie is de ruggengraat van een internationalisatie-architectuur voor chat-gebouwde apps: één gedeeld vocabulaire, veel oppervlaktes en geen verrassingen bij de volgende taalrelease.
Een goede internationalisatie-architectuur voor chat-gebouwde apps begint simpel: één set berichtkeys, één bron van waarheid voor copy en dezelfde regels op web en mobiel. Als je snel bouwt (bijv. met Koder.ai), houdt deze structuur snelheid zonder vertaalschuld te veroorzaken.
Kies je locales vroeg en beslis wat er gebeurt als een vertaling ontbreekt. Een veelvoorkomende keuze: toon de voorkeurstaal van de gebruiker als die beschikbaar is, anders val terug op Engels en log ontbrekende keys zodat je ze kunt herstellen voor de volgende release.
Zet dit dan op:
billing.plan_name.pro of auth.error.invalid_password. Gebruik dezelfde keys overal.t("key") in componenten. In Flutter gebruik je een localization-wrapper en roep je dezelfde key-gebaseerde lookup aan in widgets. Het doel is dezelfde keys, niet dezelfde bibliotheek.if (count === 1) verspreid over schermen.Test ten slotte één taal met langere woorden (Duits is klassiek) en één met andere interpunctie. Dat onthult snel knoppen die overlopen, titels die slecht wrappen en lay-outs die op Engels vertrouwen.
Als je vertalingen in een gedeelde map (of gegenereerd pakket) houdt en copy-wijzigingen als code-wijzigingen behandelt, blijven web en mobiel consistent, zelfs wanneer features snel in chat worden gebouwd.
Vertaalde UI-strings zijn maar de helft van het probleem. De meeste apps tonen ook veranderende waarden zoals datums, prijzen, aantallen en namen. Als je die waarden als platte tekst behandelt, krijg je rare formaten, verkeerde tijdzones en zinnen die in veel talen “raar” klinken.
Begin met het formatteren van getallen, valuta en datums met locale-regels, niet met aangepaste code. Een gebruiker in Frankrijk verwacht “1 234,50 €”, terwijl een gebruiker in de VS “$1,234.50” verwacht. Hetzelfde geldt voor datums: “03/04/2026” is dubbelzinnig, maar locale-formattering maakt het duidelijk.
Tijdzones zijn de volgende valkuil. Servers zouden timestamps in een neutrale vorm moeten bewaren (meestal UTC), maar gebruikers verwachten tijden in hun eigen zone. Bijvoorbeeld: een order gemaakt om 23:30 UTC kan “morgen” zijn voor iemand in Tokio. Kies één regel per scherm: toon gebruikerslokale tijd voor persoonlijke gebeurtenissen, en gebruik een vaste bedrijfstijdzone voor zaken als afhaaltijden (en label het duidelijk).
Vermijd zinnen opbouwen door vertaalde fragmenten aan elkaar te plakken. Dat breekt grammatica omdat de woordvolgorde per taal kan verschillen. In plaats van:
"{count} " + t("items") + " " + t("in_cart")
gebruik één bericht met placeholders, zoals: “{count} items in your cart”. De vertaler kan dan woorden veilig herordenen.
RTL is niet alleen tekstrichting. Lay-outflow keert, sommige iconen moeten gespiegeld worden (zoals terug-pijlen) en gemixte content (Arabisch plus een Engelse productcode) kan in verrassende volgorde renderen. Test echte schermen, niet alleen één label, en zorg dat je UI-componenten richtingverandering ondersteunen.
Vertaal nooit wat de gebruiker zelf schreef (namen, adressen, supporttickets, chatberichten). Je kunt labels daaromheen vertalen en metadata (datums, getallen) formatteren, maar de content zelf moet onaangeroerd blijven. Als je later automatische vertaling toevoegt, maak er dan een expliciete feature van met een duidelijke “origineel/vertaald” schakelaar.
Een praktisch voorbeeld: een Koder.ai-bouwde app kan “{name} renewed on {date} for {amount}” tonen. Houd dit als één bericht, formatteer {date} en {amount} volgens locale en toon het in de tijdzone van de gebruiker. Dit ene patroon voorkomt veel vertaalschuld.
Korte regels die meestal bugs voorkomen:
Vertaalschuld begint meestal als “nog even één string” en verandert later in weken van opruimwerk. In chat-gebouwde projecten kan het nog sneller gebeuren omdat UI-tekst binnen components, formulieren en zelfs backend-berichten wordt gegenereerd.
De duurste issues verspreiden zich door de app en worden moeilijk te vinden.
Stel: een React-webapp en een Flutter-mobileapp tonen allebei een billing-banner: “You have 1 free credit left”. Iemand past de webtekst aan naar “You have one credit remaining” en houdt de key als volledige zin. Mobile gebruikt nog de oude key. Nu heb je twee keys voor één concept en zien vertalers beide.
Een beter patroon is stabiele keys (bijv. billing.creditsRemaining) en pluralisatie met ICU-berichten zodat grammatica in alle talen klopt. Gebruik je een vibe-coding tool zoals Koder.ai, voeg dan vroeg een regel toe: alle gebruikerstekst geproduceerd in chat moet in vertaalbestanden terechtkomen, niet in componenten of serverfouten. Deze kleine gewoonte beschermt je internationalisatie-architectuur naarmate het project groeit.
Als internationalisatie rommelig aanvoelt, komt dat meestal doordat de basisregels nooit op papier zijn gezet. Een kleine checklist en één concreet voorbeeld houden je team (en toekomstige jij) uit vertaalschuld.
Hier is een snelle checklist voor elk nieuw scherm:
billing.invoice.paidStatus, niet billing.greenLabel).Een eenvoudig voorbeeld: je lanceert een billing-scherm in Engels, Spaans en Japans. De UI heeft: “Invoice”, “Paid”, “Due in 3 days”, “1 payment method” / “2 payment methods” en een totaal zoals “$1,234.50”. Als je dit bouwt met een internationalisatie-architectuur voor chat-gebouwde apps, definieer je keys één keer (gedeeld tussen web en mobiel) en vult elke taal alleen waarden in. “Due in {days} days” wordt een ICU-bericht en valutaformattering komt uit een locale-aware formatter, niet uit hardgecodeerde komma’s.
Rol taalondersteuning feature voor feature uit, niet als een grote rewrite:
Documenteer twee dingen zodat nieuwe features consistent blijven: je key-namingregels (met voorbeelden) en een “definition of done” voor strings (geen hardgecodeerde copy, ICU voor pluralen, format voor datums/getallen, toegevoegd aan gedeelde catalogi).
Vervolgstappen: als je bouwt in Koder.ai, gebruik Planning Mode om schermen en keys te definiëren voordat je UI genereert. Gebruik snapshots en rollback om veilig te itereren op copy en vertalingen voor web en mobiel zonder risico op een kapotte release.