Blog
Feature
14 febbraio 20269 min

i18n in solitaria: 1993 chiavi, 6 lingue, 9 passaggi LLM

Punti chiave da ricordare: Internazionalizzare un'app mobile con 1993 chiavi di traduzione in 6 lingue è fattibile da soli grazie all'automazione LLM. Lo script npm run translate rileva il delta, traduce solo le nuove chiavi tramite OpenRouter e mantiene la sincronizzazione quasi in tempo reale per pochi centesimi per passaggio. Risultato: un canale di acquisizione moltiplicato per 6 senza sforzo umano continuo.

TAMSIV era nato in francese. Ogni stringa, ogni messaggio di errore, ogni placeholder — tutto era codificato direttamente nel JSX. Quando ho deciso di supportare 6 lingue (FR, EN, DE, ES, IT, PT), non immaginavo che ciò avrebbe rappresentato 1993 chiavi di traduzione distribuite in 35 file. E ancor meno che avrei avuto bisogno di 9 passaggi successivi per arrivare a un risultato stabile.

Ecco il racconto completo di questa avventura i18n, dal primo t('key') alla pipeline automatizzata che gira oggi.

Spazio di lavoro dello sviluppatore con più schermi che mostrano file di traduzione in diverse lingue
L'internazionalizzazione significa molti schermi e molti file JSON aperti in parallelo.

Perché internazionalizzare un'app indie da soli?

La risposta breve: l'acquisizione di utenti. Un'app disponibile solo in francese si priva del 95% del mercato mondiale. Anche in Europa, limitare il proprio pubblico al francese significa ignorare la Germania (83M di abitanti), la Spagna (47M), l'Italia (60M), il Portogallo (10M) e naturalmente l'intero mondo anglofono.

Ma al di là del mercato, c'è una ragione tecnica: gli store (Google Play, App Store) indicizzano i metadati in ogni lingua. Una scheda tradotta in 6 lingue significa 6 volte più superficie di scoperta organica. È una leva raccomandata da Google stesso.

Per uno sviluppatore solitario come me, è anche un vantaggio competitivo rispetto alle grandi app che trascurano la localizzazione oltre l'inglese. Se un utente tedesco cerca "Aufgaben-App mit Spracheingabe" e TAMSIV è l'unica ad avere una scheda in tedesco, è fatta.

Come estrarre 1993 chiavi di traduzione senza perdere la testa?

Primo passo: sostituire ogni stringa francese hardcoded con una chiamata t('key'). In 35 file. A mano. Non esiste una scorciatoia affidabile per questa fase — ogni stringa ha un contesto, e la denominazione della chiave deve riflettere questo contesto.

Ho strutturato le chiavi per schermata e per sezione:

  • feed.empty_state — il messaggio quando il feed è vuoto
  • agenda.filter.participating — il filtro "Partecipa" nell'agenda
  • profile.settings.language — il selettore di lingua nelle impostazioni
  • dictaphone.recording.title — il titolo della schermata di registrazione
  • groups.hierarchy.depth_limit — il messaggio di limite di profondità dei gruppi

Questa convenzione schermata.sezione.elemento è cruciale. Senza di essa, ci si ritrova rapidamente con chiavi del tipo button_text_3 che non significano nulla tre mesi dopo. Ho seguito le raccomandazioni di i18next sulla denominazione gerarchica.

Consiglio pratico: ho iniziato con le schermate più visitate (Dittafono, Feed, Agenda) prima di affrontare le impostazioni e le schermate secondarie. Ciò consente di testare rapidamente la traduzione sui percorsi principali.

Quale strumento di traduzione automatica scegliere quando si è da soli?

Tradurre 1993 chiavi manualmente in 5 lingue? Ovvero quasi 10.000 traduzioni individuali. Impossibile per uno sviluppatore solitario. I servizi classici come Google Translate API o DeepL mancano di contesto applicativo — "Memo vocal" diventerebbe "Vocal memo" invece di "Voice memo".

Ho scritto uno script npm run translate che invia le chiavi francesi a OpenRouter (Gemini 2.5 Flash come modello principale). L'LLM comprende il contesto applicativo e produce traduzioni naturali:

  • "Memo vocal" → "Voice memo" (EN), "Sprachnotiz" (DE), "Nota de voz" (ES)
  • "Ajouter une tache" → "Add a task", "Aufgabe hinzufugen", "Agregar una tarea"
  • "Tout voir" → "See all", "Alle anzeigen", "Ver todo"

Lo script invia le chiavi in batch di 50, con il contesto dell'applicazione come system prompt: "Stai traducendo le stringhe di un'applicazione mobile di gestione delle attività vocali con IA." Questo contesto fa tutta la differenza in termini di qualità.

Mappa del mondo con puntine colorate che segnano i paesi coperti dalla traduzione
Sei lingue, sei mercati europei: la traduzione come strategia di acquisizione.

Perché 9 passaggi e non uno solo?

Il primo passaggio ha tradotto la maggior parte del corpus — circa 1600 chiavi. Ma un'app viva evolve. Ogni nuova funzionalità aggiunge chiavi:

  • Passaggio 2: gamification — 47 nuove chiavi (livelli, badge, streak)
  • Passaggio 3: agenda — 62 chiavi (eventi, filtri, ricorrenza)
  • Passaggio 4: gruppi collaborativi — 89 chiavi (gerarchia, permessi, assegnazioni)
  • Passaggio 5-7: correzioni contestuali e aggiustamenti dopo i feedback degli utenti
  • Passaggio 8: onboarding e schermate di primo utilizzo
  • Passaggio 9: sito web (tamsiv.com) — un secondo corpus di traduzione separato

Lo script rileva le chiavi mancanti confrontando fr.json (il riferimento) con ogni file di destinazione. Ritraduce solo il delta — le chiavi nuove o modificate. Nessuno spreco di token, nessun rischio di sovrascrivere una traduzione già validata.

Come funziona concretamente la pipeline di traduzione?

La pipeline completa segue 4 fasi:

  1. Diff: lo script confronta fr.json con en.json, de.json, es.json, it.json, pt.json. Ogni chiave presente in FR ma assente in una destinazione viene contrassegnata come "da tradurre".
  2. Batch: le chiavi da tradurre vengono raggruppate in pacchetti di 50 (per rimanere entro i limiti di token dell'LLM).
  3. Traduzione: ogni batch viene inviato a OpenRouter con un prompt strutturato. L'LLM restituisce un JSON valido con le traduzioni.
  4. Merge: le traduzioni vengono unite nei file di destinazione, preservando l'ordine alfabetico e la struttura esistente.

Il costo? Circa 0.02 a 0.05 EUR per passaggio con Gemini 2.5 Flash tramite OpenRouter. Per 5 lingue. È quasi gratuito rispetto ai servizi di traduzione professionale (che fatturano 0.10-0.20 EUR per parola).

Come gestire il rilevamento automatico della lingua al primo avvio?

Al primo avvio, TAMSIV rileva la lingua del sistema tramite getLocales() di React Native. Se la lingua è supportata (FR, EN, DE, ES, IT, PT), la si usa direttamente. Altrimenti, si ricade sull'inglese.

La scelta viene poi salvata nel database (Supabase) nel profilo utente. Ciò consente di ritrovare la preferenza anche cambiando telefono. E si evita il problema classico: l'utente cambia la lingua del suo OS per una ragione X, e tutte le sue app cambiano senza preavviso.

Ho anche aggiunto un selettore di lingua nelle impostazioni, in modo che l'utente possa scegliere indipendentemente dalla lingua del sistema. È un dettaglio, ma fa la differenza per i bilingue o gli espatriati.

Quali sono le insidie della traduzione automatica tramite LLM?

La traduzione tramite LLM non è perfetta. Ecco le insidie che ho incontrato:

  • I falsi amici: "Classeur" tradotto in "Binder" invece di "Folder" — l'LLM all'inizio mancava di contesto applicativo.
  • Il genere grammaticale: in tedesco, "die Aufgabe" (femminile) vs "der Memo" (maschile) — gli articoli devono corrispondere.
  • I plurali: alcune lingue hanno regole di plurale complesse (portoghese, tedesco). Bisogna fornire esplicitamente le forme singolare/plurale.
  • La lunghezza: il tedesco produce parole il 30-50% più lunghe del francese. "Einstellungen" vs "Parametres". Questo rompe i layout se il design UI non è flessibile.
  • I caratteri speciali: le virgolette variano a seconda delle lingue (" " in inglese, « » in francese, „ “ in tedesco).

La soluzione: un system prompt dettagliato che precisa il dominio applicativo, esempi di traduzioni validate in few-shot, e un passaggio di rilettura automatica in cui l'LLM verifica la coerenza delle proprie traduzioni.

Catena di produzione automatizzata che simboleggia la pipeline di traduzione continua
L'automazione trasforma un processo di traduzione faticoso in una pipeline quasi istantanea.

Come mantenere la sincronizzazione nel tempo?

La parte più difficile non è la traduzione iniziale, ma la manutenzione. Ogni modifica in francese deve essere propagata. Se cambio "Ajouter une tache" in "Creer une tache", le altre 5 lingue devono seguire.

Il mio sistema di sincronizzazione:

  1. Rilevamento del cambiamento: viene memorizzato un hash SHA256 di ogni valore FR. Se l'hash cambia, la chiave viene contrassegnata per la ritraduzione.
  2. CI automatica: tramite GitHub Actions, ad ogni push su main che modifica fr.json, lo script viene eseguito e crea un commit con le traduzioni aggiornate.
  3. Verifica manuale: esamino i diff di traduzione nella PR per rilevare errori evidenti.

6 lingue mantenute quasi in tempo reale per pochi centesimi per passaggio. Il rapporto sforzo/impatto è imbattibile.

Quale impatto concreto sull'acquisizione di utenti?

I numeri parlano da soli. Dopo l'internazionalizzazione:

  • Superficie di scoperta x6: la scheda del Play Store è indicizzata in 6 lingue, il che moltiplica le query di ricerca coperte.
  • Tasso di conversione dello store: un utente che vede una descrizione nella sua lingua ha 2 o 3 volte più probabilità di installare.
  • Retention: l'esperienza in-app nella lingua madre riduce drasticamente il churn dei primi 7 giorni.
  • SEO del sito web: il sito tamsiv.com è disponibile in 6 lingue con URL localizzati (/fr/, /en/, /de/, ecc.), il che potenzia il referenziamento internazionale.

Per uno sviluppatore solitario, è la leva di acquisizione con il miglior rapporto costo/efficacia. Nessun budget pubblicitario, nessun oscuro growth hacking — solo una traduzione automatizzata intelligente.

Come applicare questo approccio al tuo progetto?

Se stai sviluppando un'app e esiti a internazionalizzare, ecco il mio consiglio: fallo presto. Più aspetti, più stringhe ci saranno da estrarre. Ecco i passaggi chiave:

  1. Struttura le tue chiavi fin dall'inizio con una convenzione schermata.sezione.elemento.
  2. Scegli le tue lingue target in base al tuo mercato. Per l'Europa, FR/EN/DE/ES/IT/PT coprono la maggior parte.
  3. Automatizza la traduzione con un LLM tramite API (OpenRouter, OpenAI, ecc.). Il costo è trascurabile.
  4. Integra nella tua CI in modo che la sincronizzazione sia automatica ad ogni push.
  5. Testa con utenti reali nativi se possibile — anche una rapida occhiata è meglio di niente.

Il percorso build in public di TAMSIV dimostra che anche da soli si può raggiungere un livello di localizzazione paragonabile a quello di grandi team.

FAQ

Quanto tempo richiede l'internazionalizzazione di un'app React Native?

Per TAMSIV, l'estrazione iniziale delle 1993 chiavi ha richiesto circa 3 giorni di lavoro concentrato. L'implementazione dello script di traduzione automatica, mezza giornata. I passaggi successivi richiedono pochi minuti ciascuno grazie all'automazione. Il grosso del lavoro è nell'estrazione — non nella traduzione.

OpenRouter è affidabile per la traduzione automatica?

Sì, con le dovute precauzioni. Il system prompt deve includere il contesto applicativo, esempi di traduzioni validate e istruzioni sul tono. Gemini 2.5 Flash tramite OpenRouter produce traduzioni di qualità superiore a Google Translate per le stringhe dell'interfaccia, perché comprende il contesto. Il fallback tra modelli garantisce la disponibilità.

Bisogna tradurre i contenuti generati dall'utente?

No. Le attività, i memo e gli eventi rimangono nella lingua dell'utente. Solo l'interfaccia (pulsanti, etichette, messaggi di sistema, notifiche) viene tradotta. Tradurre il contenuto utente sarebbe sia costoso che fonte di confusione.

Come gestire le lingue con alfabeti diversi (arabo, giapponese)?

TAMSIV si concentra per ora sulle lingue latine. Le lingue RTL (arabo, ebraico) o gli ideogrammi (cinese, giapponese) richiedono adattamenti di layout aggiuntivi (direzione del testo, font, spaziatura). Questo è previsto per una fase successiva, ma ogni alfabeto è un cantiere a sé stante.

Lo script di traduzione può essere riutilizzato su un altro progetto?

Assolutamente. Lo script è generico: prende un file JSON sorgente, un elenco di lingue target e un endpoint LLM. Basta adattare il system prompt al contesto della tua applicazione. Il pattern delta-only (tradurre solo le nuove chiavi) funziona per qualsiasi progetto che utilizzi file JSON di traduzione.