Architettura di internazionalizzazione per app create tramite chat: definisci chiavi stringa stabili, regole sui plurali e un workflow di traduzione coerente per web e mobile.

La prima cosa che si rompe non è il codice. Sono le parole.
Le app create tramite chat spesso nascono come prototipi rapidi: scrivi “Aggiungi un pulsante con scritto Salva”, l'interfaccia appare e vai avanti. Settimane dopo vuoi spagnolo e tedesco e scopri che quelle etichette “temporanee” sono sparse tra schermate, componenti, email e messaggi di errore.
Le modifiche al testo sono anche più frequenti delle modifiche al codice. I nomi dei prodotti vengono rinominati, i testi legali cambiano, l'onboarding viene riscritto e l'assistenza chiede messaggi di errore più chiari. Se il testo vive direttamente nel codice UI, ogni piccola modifica diventa un rilascio rischioso e ti sfuggiranno luoghi in cui la stessa idea è espressa con parole diverse.
Ecco i primi sintomi che indicano che stai accumulando debito di traduzione:
Un esempio realistico: costruisci un semplice CRM in Koder.ai. L'app web dice “Deal stage”, l'app mobile dice “Pipeline step” e un toast di errore dice “Invalid status”. Anche se tutte e tre sono tradotte, gli utenti percepiranno incoerenza perché i concetti non corrispondono.
“Coerente” non significa “stessi caratteri ovunque”. Significa:
Quando tratti il testo come dati di prodotto, non come decorazione, aggiungere lingue smette di essere una corsa e diventa una routine di sviluppo.
Internationalization (i18n) è il lavoro che fai affinché un'app possa supportare molte lingue senza riscritture. Localization (l10n) è il contenuto specifico per una lingua e una regione, per esempio francese (Canada) con le parole, i formati di data e la tonalità corretti.
Un obiettivo semplice: ogni testo rivolto all'utente è selezionato tramite una chiave stabile, non digitato direttamente nel codice UI. Se puoi cambiare una frase senza aprire un componente React o un widget Flutter, sei sulla giusta strada. Questo è il nucleo di un'architettura di internazionalizzazione per app create in chat, dove è facile pubblicare per errore copy hard-coded generati durante una sessione chat.
Il testo rivolto agli utenti è più ampio di quanto molte squadre pensino. Include pulsanti, etichette, errori di validazione, stati vuoti, suggerimenti di onboarding, notifiche push, email, esportazioni PDF e qualsiasi messaggio che un utente può vedere o ascoltare. Di solito non include log interni, nomi di colonne del DB, ID di eventi analytics, feature flag o output di debug riservato agli admin.
Dove dovrebbero vivere le traduzioni? In pratica spesso stanno sia nel frontend sia nel backend, con un confine chiaro.
L'errore da evitare è mescolare le responsabilità. Se il backend restituisce frasi inglesi complete per gli errori UI, il frontend non può localizzarle correttamente. Un pattern migliore è: il backend restituisce un codice di errore (e magari parametri sicuri) e il client mappa quel codice su un messaggio localizzato.
La proprietà del copy è una decisione di prodotto, non un dettaglio tecnico. Decidi presto chi può cambiare le parole e approvare la tonalità.
Se il prodotto possiede il copy, tratta le traduzioni come contenuti: versionale, revisionale e dai al prodotto un modo sicuro per richiedere modifiche. Se l'ingegneria possiede il copy, stabilisci la regola che ogni nuova stringa UI deve avere una chiave e una traduzione di default prima di essere rilasciata.
Esempio: se il tuo flusso di registrazione dice “Create account” in tre schermate diverse, fallo diventare un'unica chiave usata ovunque. Questo mantiene il significato coerente, velocizza i traduttori e impedisce che piccole modifiche si trasformino in una pulizia multi-schermata in futuro.
Le chiavi sono il contratto tra la tua UI e le traduzioni. Se quel contratto cambia continuamente, avrai testo mancante, fix affrettati e wording incoerente tra web e mobile. Una buona architettura di internazionalizzazione per app generate in chat comincia con una regola: le chiavi dovrebbero descrivere il significato, non la frase inglese corrente.
Usa ID stabili come chiavi (per esempio billing.invoice.payNow) invece della copia completa (per esempio "Paga ora"). Le chiavi basate sulla frase si rompono quando qualcuno cambia una parola, aggiunge punteggiatura o cambia maiuscole/minuscole.
Un pattern pratico e leggibile è: schermata (o dominio) + componente + intento. Mantienilo noioso e prevedibile.
Esempi:
auth.login.titleauth.login.emailLabelbilling.checkout.payButtonnav.settingserrors.network.offlineDecidi quando riusare una chiave o crearne una nuova chiedendoti: “Il significato è identico in ogni contesto?” Riusa le chiavi per azioni veramente generiche, ma spezzale quando cambia il contesto. Per esempio, “Save” in una schermata profilo può essere semplice, mentre “Save” in un editor complesso potrebbe richiedere una sfumatura diversa in alcune lingue.
Tieni il testo UI condiviso in namespace dedicati così non viene duplicato tra schermate. Bucket comuni che funzionano bene:
common.actions.* (save, cancel, delete)common.status.* (loading, success)common.fields.* (search, password)errors.* (validation, network)nav.* (tabs, menu items)Quando il wording cambia ma il significato resta lo stesso, mantieni la chiave e aggiorna solo i valori tradotti. Questo è il senso degli ID stabili. Se il significato cambia (anche leggermente), crea una nuova chiave e lascia la vecchia finché non sei sicuro che non sia più usata. Così eviti discrepanze “silenziose” in cui una vecchia traduzione è presente ma ormai sbagliata.
Un piccolo esempio nello stile Koder.ai: la tua chat genera sia un'app React web che un'app Flutter mobile. Se entrambe usano common.actions.save, ottieni traduzioni coerenti ovunque. Ma se il web usa profile.save e il mobile account.saveButton, con il tempo divergerete, anche se l'inglese sembra lo stesso oggi.
Tratta la lingua di origine (spesso l'inglese) come unica fonte di verità. Tienila in un posto, revisionala come codice ed evita che le stringhe appaiano in componenti sparsi “giusto per ora”. Questo è il modo più veloce per evitare copy hard-coded e lavoro di rifacimento dopo.
Una regola semplice aiuta: l'app può mostrare testo solo dal sistema i18n. Se qualcuno ha bisogno di nuovo copy, aggiunge prima una chiave e un messaggio di default, poi usa quella chiave nell'UI. Questo mantiene l'architettura di i18n stabile anche quando le funzionalità si spostano.
Se rilasci web e mobile, vuoi un catalogo condiviso di chiavi, più spazio per i team di feature per lavorare senza pestarsi i piedi. Un layout pratico:
Mantieni le chiavi identiche tra le piattaforme, anche se l'implementazione differisce (React sul web, Flutter su mobile). Se usi una piattaforma come Koder.ai per generare entrambe le app da chat, esportare il codice sorgente è più semplice quando entrambi i progetti puntano agli stessi nomi di chiave e allo stesso formato di messaggio.
Le traduzioni cambiano col tempo. Tratta le modifiche come cambi di prodotto: piccole, revisionate e tracciabili. Una buona review si concentra sul significato e sul riuso, non solo sull'ortografia.
Per evitare che le chiavi divergano tra team, assegna le chiavi alle feature (billing., auth.) e non rinominarle solo perché cambia il testo. Aggiorna il messaggio, mantieni la chiave. Le chiavi sono identificatori, non copy.
Le regole sui plurali variano molto tra le lingue, quindi il semplice pattern inglese (1 vs tutto il resto) salta subito. Alcune lingue hanno forme separate per 0, 1, 2-4, e molte. Altre cambiano l'intera frase, non solo il sostantivo. Se metti la logica dei plurali nell'UI con if-else, finirai per duplicare copy e perdere casi limite.
Un approccio più sicuro è tenere un messaggio flessibile per ogni idea e lasciare che lo strato i18n scelga la forma giusta. I messaggi in stile ICU sono fatti per questo: tengono le decisioni grammaticali nella traduzione, non nei componenti.
Ecco un piccolo esempio che copre i casi che spesso si dimenticano:
itemsCount = \"{count, plural, =0 {No items} one {# item} other {# items}}\"
Quella singola chiave copre 0, 1 e il resto. I traduttori possono sostituirla con le forme plurali adeguate per la loro lingua senza che tu tocchi il codice.
Quando hai bisogno di formulazioni basate su genere o ruolo, evita di creare chiavi separate come welcome_male e welcome_female a meno che il prodotto non lo richieda davvero. Usa select così la frase resta un'unica unità:
welcomeUser = \"{gender, select, female {Welcome, Ms. {name}} male {Welcome, Mr. {name}} other {Welcome, {name}}}\"
Per non metterti in un vicolo cieco con i casi grammaticali, mantieni le frasi il più complete possibile. Non cucire insieme frammenti tipo "{count} " + t('items') perché molte lingue non possono riordinare le parole in quel modo. Preferisci un unico messaggio che includa numero, sostantivo e parole circostanti.
Una regola semplice che funziona bene nelle app create in chat (inclusi i progetti Koder.ai) è: se una frase contiene un numero, una persona o uno stato, falla in ICU fin dal primo giorno. Costa un po' di più all'inizio e risparmia tanto debito di traduzione dopo.
Se la tua app React web e la tua app Flutter mobile tengono ciascuna i propri file di traduzione, divergeranno. Lo stesso pulsante finirà con wording diverso, una chiave avrà un significato su web e un altro su mobile e i ticket di supporto inizieranno a dire “l'app dice X ma il sito dice Y”.
La soluzione più semplice e importante: scegli un formato fonte di verità e trattalo come codice. Per la maggior parte dei team significa un insieme condiviso di file di locale (per esempio JSON con messaggi in stile ICU) che sia consumato sia dal web sia dal mobile. Quando costruisci app tramite chat e generatori, questo conta ancora di più perché è facile creare testo nuovo in due posti diversi.
Una configurazione pratica è un piccolo “pacchetto i18n” o una cartella che contiene:
React e Flutter diventano consumatori. Non dovrebbero inventare nuove chiavi localmente. In un workflow stile Koder.ai (React web, Flutter mobile) puoi generare entrambi i client dallo stesso set di chiavi e mantenere le modifiche sotto controllo come qualsiasi altra modifica di codice.
Allineare il backend fa parte della stessa storia. Errori, notifiche ed email non dovrebbero essere frasi inglesi scritte a mano in Go. Meglio restituire codici di errore stabili (per esempio auth.invalid_password) più parametri sicuri. Poi i client mappano i codici su testo tradotto. Per le email inviate dal server, il server può renderizzare i template usando le stesse chiavi e gli stessi file di locale.
Crea un piccolo regolamento e applicalo nelle code review:
Per prevenire chiavi duplicate con significati diversi, aggiungi un campo “description” (o un file di commento) per i traduttori e per il te futuro. Esempio: billing.trial_days_left dovrebbe spiegare se è mostrato in un banner, in un'email o in entrambi. Quella singola frase spesso evita il riuso “va bene così” che crea debito di traduzione.
Questa coerenza è la spina dorsale di un'architettura di internazionalizzazione per app create in chat: un vocabolario condiviso, molte superfici e nessuna sorpresa quando lanci la prossima lingua.
Una buona architettura di internazionalizzazione per app create in chat parte semplice: un set di chiavi messaggi, una fonte di verità per il copy e le stesse regole su web e mobile. Se costruisci in fretta (per esempio con Koder.ai), questa struttura mantiene la velocità senza creare debito di traduzione.
Scegli le tue localizzazioni presto e decidi cosa succede quando manca una traduzione. Una scelta comune: mostra la lingua preferita dell'utente quando disponibile, altrimenti ritorna all'inglese e registra le chiavi mancanti così da poterle correggere prima del prossimo rilascio.
Poi metti in pratica:
billing.plan_name.pro o auth.error.invalid_password. Mantieni le stesse chiavi ovunque.t("key") nei componenti. In Flutter usa un wrapper di localizzazione e chiama la stessa lookup basata su chiavi nei widget. L'obiettivo sono le stesse chiavi, non la stessa libreria.if (count === 1) sparsi nelle schermate.Infine, testa una lingua con parole più lunghe (il tedesco è classico) e una con punteggiatura diversa. Questo rivela rapidamente pulsanti che overflowano, titoli che vanno a capo e layout che presumono la lunghezza inglese.
Se tieni le traduzioni in una cartella condivisa (o in un pacchetto generato) e tratti le modifiche al copy come modifiche di codice, le tue app web e mobile resteranno coerenti anche quando le funzionalità vengono costruite rapidamente in chat.
Le stringhe UI tradotte sono solo metà del problema. La maggior parte delle app mostra anche valori che cambiano: date, prezzi, contatori e nomi. Se tratti quei valori come testo semplice, otterrai formati strani, fusi orari sbagliati e frasi che suonano “strane” in molte lingue.
Inizia formattando numeri, valute e date con le regole della locale, non con codice ad hoc. Un utente in Francia si aspetta “1 234,50 €”, mentre un utente negli USA si aspetta “$1,234.50”. Lo stesso vale per le date: “03/04/2026” è ambiguo, ma la formattazione locale chiarisce.
I fusi orari sono la trappola successiva. I server dovrebbero memorizzare i timestamp in una forma neutra (di solito UTC), ma gli utenti si aspettano di vedere gli orari nel proprio fuso. Per esempio: un ordine creato alle 23:30 UTC può essere “domani” per qualcuno a Tokyo. Decidi una regola per ogni schermata: mostra l'orario locale dell'utente per eventi personali e usa un fuso aziendale fisso per cose come finestre di ritiro (etichettandolo chiaramente).
Evita di costruire frasi concatenando frammenti tradotti. Rompe la grammatica perché l'ordine delle parole cambia tra le lingue. Invece di:
"{count} " + t("items") + " " + t("in_cart")
usa un unico messaggio con placeholder, come: “{count} items in your cart”. Il traduttore può riordinare le parole in modo sicuro.
RTL non è solo direzione del testo. Il flusso del layout si inverte, alcune icone devono essere specchiate (come le frecce indietro) e i contenuti misti (arabo più un codice prodotto in inglese) possono apparire in ordine sorprendente. Testa schermate reali, non solo una singola etichetta, e assicurati che i componenti UI supportino i cambi di direzione.
Non tradurre mai ciò che l'utente ha scritto (nomi, indirizzi, ticket di supporto, messaggi chat). Puoi tradurre le etichette intorno ad esso e formattare i metadati circostanti (date, numeri), ma il contenuto stesso deve restare così com'è. Se aggiungi la traduzione automatica in seguito, fallo come funzionalità esplicita con un toggle “originale/tradotto”.
Un esempio pratico: un'app costruita con Koder.ai potrebbe mostrare “{name} renewed on {date} for {amount}”. Mantienila come un unico messaggio, formatta {date} e {amount} per la locale e mostra l'orario nel fuso dell'utente. Questo pattern evita molto debito di traduzione.
Regole veloci che spesso prevengono bug:
Il debito di traduzione inizia spesso come “solo una stringa veloce” e si trasforma in settimane di rifacimento. Nei progetti creati in chat può succedere ancora più in fretta perché il testo UI viene generato dentro componenti, form e persino messaggi backend.
Gli issue più costosi sono quelli che si diffondono nell'app e diventano difficili da trovare.
Immagina un'app React web e una Flutter mobile che mostrano entrambe un banner billing: “You have 1 free credit left”. Qualcuno cambia il testo web in “You have one credit remaining” e mantiene la chiave come frase completa. Mobile usa ancora la vecchia chiave. Ora hai due chiavi per lo stesso concetto e i traduttori vedono entrambe.
Un pattern migliore è usare chiavi stabili (tipo billing.creditsRemaining) e pluralizzazione con messaggi ICU così la grammatica è corretta in tutte le lingue. Se usi uno strumento di generazione come Koder.ai, aggiungi una regola presto: qualsiasi testo rivolto all'utente prodotto in chat deve finire nei file di traduzione, non dentro componenti o errori server. Questa piccola abitudine protegge la tua architettura i18n man mano che il progetto cresce.
Quando l'internazionalizzazione sembra caotica, di solito è perché le basi non sono state scritte. Una piccola checklist e un esempio concreto possono mantenere il tuo team (e il te futuro) lontano dal debito di traduzione.
Ecco una checklist rapida da eseguire per ogni nuova schermata:
billing.invoice.paidStatus, non billing.greenLabel).Un esempio semplice: stai lanciando una schermata billing in inglese, spagnolo e giapponese. L'UI ha: “Invoice”, “Paid”, “Due in 3 days”, “1 payment method” / “2 payment methods” e un totale come “$1,234.50”. Se costruisci questo con un'architettura i18n per app create in chat, definisci le chiavi una volta (condivise tra web e mobile) e ogni lingua compila solo i valori. “Due in {days} days” diventa un messaggio ICU e il formato del denaro viene dal formatter locale, non da virgole hard-coded.
Rilascia il supporto linguistico funzionalità per funzionalità, non come grande riscrittura:
Documenta due cose così le nuove feature restano coerenti: le regole di naming delle chiavi (con esempi) e una “definition of done” per le stringhe (niente copy hard-coded, ICU per plurali, formattazione per date/numeri, aggiunta al catalogo condiviso).
Prossimi passi: se costruisci in Koder.ai, usa Planning Mode per definire schermate e chiavi prima di generare l'UI. Poi usa snapshot e rollback per iterare in sicurezza su copy e traduzioni tra web e mobile senza rischiare un rilascio compromesso.