La build in arabo sembra rotta, ma la traduzione è corretta. La sidebar resta a sinistra, la freccia indietro punta a sinistra e l’avatar aderisce al bordo sinistro di ogni card: ogni parola è in arabo, ma ogni elemento resta dove lo aveva lasciato la versione inglese. La direzione del testo è un attributo HTML più una serie di decisioni CSS presenti nei componenti; nessun file di catalogo la contiene. globalize.now è un’infrastruttura di localizzazione basata su IA che estrae le stringhe e mantiene sincronizzati i cataloghi a ogni push Git.
Perché la mia app Lovable continua a leggere da sinistra a destra in arabo?
Perché la direzione è una proprietà del documento e del foglio di stile, non del messaggio.
I tuoi cataloghi sono coppie di stringhe sorgente e stringhe tradotte. Quando vengono compilati per ar e i tuoi componenti lo renderizzano, cambiano le parole e nient’altro. Che i cataloghi siano Lingui, su cui scrive globalize.now, o react-i18next non fa differenza. Nessuno dei due formati ha un posto dove mettere «e la sidebar passa dall’altro lato».
Devono cambiare due cose. L’attributo dir sull’elemento html indica al browser in quale direzione scorre l’asse inline. Il CSS deve smettere di nominare i lati e iniziare a nominare l’inizio e la fine di quell’asse. Tutto il resto deriva da questi due cambiamenti.
Cosa si rompe davvero quando aggiungi l’arabo a un’app Lovable?
I problemi si concentrano in punti prevedibili e riguardano tutti lo stile, non il testo.
- I drawer di navigazione e le sidebar restano fisicamente a sinistra.
- Chevron, frecce indietro e indicatori di avanzamento puntano nella direzione sbagliata.
- Il padding asimmetrico fissa il contenuto sul bordo sbagliato, così le etichette si ammassano da un lato.
text-leftsu un heading mantiene l’arabo ancorato a sinistra del suo contenitore.- Badge e pulsanti di chiusura con posizionamento assoluto finiscono nell’angolo opposto rispetto a quello che chi legge si aspetta.
- I bordi sinistri che indicano un elemento attivo dell’elenco compaiono sul bordo finale invece che su quello iniziale.
- Un codice prodotto inglese dentro una frase araba viene riordinato in modo imprevedibile senza un’indicazione di direzione.
Nessuno di questi è un difetto di traduzione. In ogni caso, la causa è una direzione fisica incorporata nel nome di una classe.
Come ricavo la direzione del testo dal tag della locale?
Chiedi alla locale quale direzione usa, poi scrivi il risultato nel documento.
Intl.Locale.prototype.getTextInfo() restituisce la direzione ltr o rtl per un tag di locale. Ha raggiunto lo stato Baseline newly available a luglio 2026: è quindi arrivato nella versione attuale di tutti i browser principali, ma non nella maggior parte dei browser usati davvero dai tuoi utenti. Considera il fallback qui sotto come il percorso attivo e l’API come ciò che lo sostituirà prima o poi.
// getTextInfo() is not yet in TypeScript's bundled Intl.Locale definitions.
declare global {
namespace Intl {
interface Locale {
getTextInfo?(): { direction: "ltr" | "rtl" };
}
}
}
// Stopgap for runtimes without getTextInfo(). This set is the one place where
// adding a new right-to-left language still costs you a code change.
const RTL_FALLBACK = new Set([
"ar", "he", "fa", "ur", "ps", "sd", "yi", "dv", "ckb", "ug", "nqo", "syr",
]);
export function directionFor(locale: string): "ltr" | "rtl" {
let tag: Intl.Locale | undefined;
try {
tag = new Intl.Locale(locale);
} catch {
// Malformed tag. Fall through to the string path below.
}
const direction = tag?.getTextInfo?.().direction;
if (direction === "ltr" || direction === "rtl") return direction;
const language = tag?.language ?? locale.split(/[-_]/)[0].toLowerCase();
return RTL_FALLBACK.has(language) ? "rtl" : "ltr";
}
Contano entrambi i punti in cui compare try. new Intl.Locale() genera un RangeError con un tag malformato, mentre getTextInfo() semplicemente non esiste nei runtime meno recenti. Gestire solo il secondo è il bug più comune: una locale come ar_EG proveniente da una query string manda la funzione nel proprio percorso di recupero e la fa uscire subito dopo come errore non gestito.
Imposta entrambi gli attributi nel punto in cui l’app conosce già la locale attiva. Lovable oggi genera due strutture: una single-page app Vite e un’app TanStack Start renderizzata sul server. Verifica quindi cosa ha generato davvero il tuo progetto. Con TanStack Start è il componente del documento root. Con Vite è il punto in cui monti il provider i18n.
<html lang={locale} dir={directionFor(locale)}>
Metti dir su html, non su un wrapper div. Portals, modals e toasts vengono renderizzati fuori dall’albero dei componenti e devono comunque ereditarlo.
Quali classi Tailwind devo modificare per il right-to-left?
Quelle che nominano un lato fisico. Le equivalenti logiche seguono l’asse inline e si invertono da sole quando cambia dir.
| Utility fisica | Sostituzione logica | Cosa imposta |
|---|---|---|
| ml-4 / mr-4 | ms-4 / me-4 | margin-inline-start / margin-inline-end |
| pl-6 / pr-6 | ps-6 / pe-6 | padding-inline-start / padding-inline-end |
| left-0 / right-0 | start-0 / end-0 | inset-inline-start / inset-inline-end |
| text-left / text-right | text-start / text-end | allineamento del testo sull’asse inline |
| border-l / border-r | border-s / border-e | bordo iniziale / bordo finale |
| rounded-l-lg / rounded-r-lg | rounded-s-lg / rounded-e-lg | angoli iniziali / angoli finali |
| float-left / float-right | float-start / float-end | float sull’asse inline |
| clear-left / clear-right | clear-start / clear-end | clear sull’asse inline |
| scroll-ml-4 / scroll-mr-4 | scroll-ms-4 / scroll-me-4 | margine scroll-snap |
La documentazione sui margini di Tailwind mostra direttamente l’effetto: ms-8 e me-8, renderizzati all’interno dei container dir="ltr" e dir="rtl", si trovano su lati opposti. Le utility per le proprietà logiche sono arrivate in Tailwind v3.3, quindi un progetto mantenuto da tempo potrebbe non includerle.
Due utility richiedono un secondo controllo, non una semplice rinomina. space-x-* e divide-x-* aggiungono spaziatura e bordi tra elementi fratelli, quindi in una riga invertita finiscono sul lato sbagliato di ogni elemento. Tailwind fornisce space-x-reverse e divide-x-reverse proprio per questo caso. Le utility verticali come mt-* e pb-* non richiedono modifiche, perché la direzione governa solo l’asse inline.
Cosa non possono risolvere le proprietà logiche?
Tutto ciò che codifica una direzione al di fuori del box model: è qui che le varianti rtl: e ltr: trovano il loro posto.
<ChevronRight className="rtl:rotate-180" />
<div className="bg-[url(/hero.svg)] bg-left rtl:bg-right" />
<div className="shadow-[4px_0_8px_rgba(0,0,0,.15)] rtl:shadow-[-4px_0_8px_rgba(0,0,0,.15)]" />
Trasformazioni, posizioni degli sfondi, offset delle ombre, angoli dei gradienti, calcoli dello scroll dei carousel e qualsiasi icona che indichi «avanti» rientrano in questa categoria. Mantieni breve l’elenco. Scrivere rtl: su un elemento ogni due significa che gli stili sottostanti sono ancora fisici e andrebbero convertiti invece.
I contenuti generati dagli utenti richiedono una gestione dedicata, e i due casi sono diversi. Per un intero blocco la cui lingua non controlli, dir="auto" permette al browser di impostare la direzione di base del blocco in base al primo carattere forte.
<p dir="auto">{review.body}</p>
Per una sequenza in uno script straniero all’interno di una frase con direzione già impostata — ad esempio uno SKU inglese inserito in un testo arabo — dir="auto" non fa nulla, perché imposta la direzione di base del paragrafo invece di isolare una sequenza inline. In quel caso devi racchiudere la sequenza.
<bdi>{product.sku}</bdi>
Perché un widget di traduzione non può invertire il layout al posto tuo?
Perché un widget opera sui nodi di testo della pagina renderizzata, mentre il problema di direzione vive negli stili che hanno prodotto quella pagina.
Un servizio runtime può aggiungere dir="rtl" al documento e tradurre le stringhe visibili dopo il rendering della versione inglese. Non può aprire i tuoi componenti e trasformare ml-4 in ms-4, perché ml-4 è già stato compilato in margin-left dentro un foglio di stile che il servizio non controlla. Il risultato è testo arabo dentro una struttura da sinistra a destra.
C’è anche un secondo costo. Lovalingo, un servizio di traduzione runtime usato in alcune app Lovable, ha annunciato la chiusura il 31 agosto 2026 alle 23:59 CEST. Le istruzioni per la migrazione invitano i clienti a passare a un’internazionalizzazione gestita nel progetto e a verificare nuovamente le route delle lingue, gli URL canonici e hreflang prima di rimuoverlo. Il lavoro sul layout fatto nel prodotto di un vendor se ne va insieme al vendor. Le utility logiche e un attributo dir nel tuo repository restano. Il nostro confronto con Lovalingo sostiene lo stesso principio per il livello delle stringhe.
Come impedisci al prossimo prompt di Lovable di annullare tutto?
Converti una volta, poi rendi visibili in review le utility fisiche.
- Installa il workflow. Nel workspace Lovable, aggiungi la skill
lovable-i18ne chiedi a Lovable di configurare l’i18n. Fuori dall’editor, l’installazione ènpx skills add globalize-now/globalize-skills, con--allda installare per ogni agent. - Imposta la direzione in base alla locale usando
directionForsopra, sull’elementohtml. - Converti le utility fisiche in utility logiche in tutti i componenti, iniziando da ciò che contiene le tue pagine.
- Aggiungi varianti
rtl:solo per trasformazioni, sfondi, ombre e icone direzionali, e usa<bdi>per le sequenze inline in script stranieri. - Aggiungi un controllo grep alla review, così una nuova utility fisica fallisce prima del merge.
rg -n '\b(ml|mr|pl|pr|scroll-m[lr])-|\b(float|clear|text)-(left|right)\b|\bborder-[lr]\b|\brounded-[lr]-|\b(left|right)-[0-9]' src/
Il quinto passaggio è quello che regge nel tempo. Lovable rigenera i componenti a ogni prompt e un modello a cui chiedi di aggiungere un pannello delle impostazioni userà ml-4, perché le utility fisiche dominano il codice React su cui questi modelli sono stati addestrati. Un controllo che blocca la pull request costa meno che scoprire il problema in uno screenshot arabo tre settimane dopo.
La metà del ciclo dedicata alle stringhe gira da sola. globalize.now estrae le nuove stringhe hardcoded da qualunque cosa abbia generato Lovable, le scrive nei tuoi cataloghi e sincronizza tutto a ogni push Git, così la build araba resta aggiornata mentre continui a usare i prompt. È lo stesso meccanismo descritto nella guida Vite e nella guida TanStack Start, ed è il motivo per cui le traduzioni smettono di rompersi a ogni push.
Aggiungere l’arabo non costa nulla in più con il piano. Il piano Starter costa 20 € al mese per workspace e include 20 € di credito per le traduzioni — circa 200.000 parole — con lo stesso costo per carattere anche dopo l’esaurimento del credito. Non ci sono costi per seat o per lingua e le lingue RTL sono incluse. Trovi maggiori dettagli sulla configurazione nella pagina dell’integrazione Lovable, i dettagli tecnici nella pagina per sviluppatori e un percorso più breve per chi costruisce senza un background tecnico nella pagina per vibe coder.
Una modifica al layout, poi resta a posto
La direzione è una decisione che il tuo repository prende una volta sola. Le stringhe, invece, cambiano ogni volta che usi un prompt, e quella parte gira da sola.
globalize.now trasforma il testo hardcoded dell'app in file di localizzazione pronti e li mantiene aggiornati mentre rilasci.
Prova globalize.now gratis