Lingue e localizzazione
OnlyTons parla inglese e italiano. L'interfaccia, le etichette di stato, i suggerimenti e il testo delle notifiche cambiano tutti insieme.
Cambiare lingua (per gli utenti)
- Nelle pagine pubbliche — la home,
/explore, le pagine dei terreni e la documentazione — usa i link 🇬🇧 EN / 🇮🇹 IT nell'intestazione. Cambiano l'indirizzo (?lang=it), quindi la pagina che stai leggendo in italiano è un link che puoi inviare a qualcun altro. - Nella pagina di accesso, usa il selettore di lingua sotto il modulo.
- Una volta effettuato l'accesso, usa il selettore in fondo alla barra laterale.
La tua scelta viene ricordata: viene salvata nel tuo account (così ti segue tra i diversi dispositivi) e memorizzata nella cache del browser. La pagina viene ridisegnata immediatamente nella lingua scelta.
La bandiera accanto a ogni abbreviazione è solo decorativa: una bandiera indica un paese, non una lingua. Gli screen reader annunciano il nome completo della lingua ("IT — Italiano") e l'etichetta effettiva del link è l'abbreviazione di due lettere.
Cosa viene tradotto
- Pulsanti, etichette dei moduli, stati vuoti/di errore, toast e suggerimenti.
- Etichette di stato ed enum — fasi delle trattative, stati di offerta/richiesta, schemi di certificazione, stati di verifica, unità , ruoli e così via.
- Testo delle notifiche — assemblato da modelli, così arriva nella tua lingua.
I dati di prodotto che inserisci — nomi delle organizzazioni, nomi dei prodotti, note in testo libero — vengono archiviati come li hai scritti e mostrati così come sono; non vengono tradotti automaticamente.
Come funziona (per chi mantiene il codice)
OnlyTons usa una localizzazione basata sulle preferenze, non un routing
basato sull'URL. Non c'è deliberatamente nessun segmento /[lang]/… — ogni
route mantiene il suo percorso e il locale attivo viene risolto da un cookie
(inizializzato dal valore users.locale salvato dall'utente). Questo si adatta
a un'app B2B autenticata dove ogni utente ha una preferenza archiviata, ed evita
di ristrutturare l'intero albero delle route.
?lang= sulle pagine pubbliche
Il livello pubblico ha richiesto proprio l'intervento additivo previsto nel
paragrafo precedente, limitato alle sole route pubbliche. Un crawler non invia
cookie, quindi senza un override l'unica versione indicizzabile di / e
/explore sarebbe stata quella inglese — su un prodotto il cui mercato
principale è l'Italia.
?lang=itè un override indicizzabile e condivisibile. Ha la precedenza sul cookie: la query string è l'espressione di intento più specifica, e un alternatehreflangche mostrasse in silenzio la lingua del cookie sarebbe una bugia detta al crawler.Accept-Languageè il fallback quando non ci sono né?langné cookie: il caso della prima visita (negotiate.ts, puro e testato).- Il proxy risolve tutto questo e lo passa a valle come HEADER DI RICHIESTA
(
x-onlytons-locale), mai comeSet-Cookie. Un Set-Cookie su ogni risposta pubblica la rende non memorizzabile in nessuna cache condivisa, e il livello pubblico è costruito attorno alla cache. È anche l'unico meccanismo che arriva al layout radice, dove viene scritto<html lang>: nell'App Router i layout non possono leggeresearchParams. - Il proxy rimuove l'header su ogni percorso non pubblico, così non può essere falsificato per sovrascrivere la preferenza salvata di un utente registrato.
- Il selettore di lingua pubblico è un insieme di link, non un select, così gli URL alternativi sono nel markup e funziona anche senza JavaScript.
L'albero dell'app non cambia: dentro (app) il cookie resta l'unica fonte.
Gli elementi, tutti sotto apps/web/src/lib/i18n/:
| File | Ruolo |
|---|---|
config.ts | Locale supportati, default, nome del cookie, helper |
messages/en.ts | Il catalogo canonico dei messaggi (l'insieme delle chiavi) |
messages/it.ts | Catalogo italiano — tipizzato rispetto a en, così una chiave mancante non compila |
messages/index.ts | Lookup nel dizionario + interpolazione dei {placeholder} |
enum-labels.ts | Override in italiano dei *_LABELS colocati nello schema; l'inglese è il fallback |
client.tsx | I18nProvider + hook: useTranslate(), useLabels(), useLocale() |
server.ts | getLocale(), getServerT(), getServerLabels() per i server component |
actions.ts | setLocaleAction — la server action che il selettore chiama (imposta il cookie + persiste users.locale) |
negotiate.ts | negotiateLocale(Accept-Language) + resolvePublicLocale({lang, cookie, acceptLanguage}) — puro; il proxy lo usa per i percorsi pubblici |
Regole pratiche quando aggiungi UI:
- Client component →
const { t } = useTranslate()e/oconst labels = useLabels(). - Server component →
const t = await getServerT()/const labels = await getServerLabels(). - Non scrivere mai una stringa rivolta all'utente fissa nel codice della
logica. Aggiungi una chiave a
messages/en.ts(e il suo valore italiano inmessages/it.ts) e usat("your.key"). I router e le librerie restituiscono codici leggibili dalla macchina; l'interfaccia li mappa in testo. - I nomi visualizzati degli enum restano colocati nel loro file di schema
(inglese) e ricevono un override italiano in
enum-labels.ts.
Aggiungere una lingua
- Aggiungi il codice a
LOCALESinconfig.tse un nome nativo aLOCALE_LABELS. - Aggiungi un
messages/<locale>.tstipizzato: Messages(il compilatore elenca ogni chiave che ancora manca). - Aggiungi un blocco di override in
enum-labels.ts(oppure lascia che l'inglese faccia da fallback).
Tutto qui — il selettore, il cookie e il provider la riconoscono automaticamente.