A versão em árabe parece quebrada, e a tradução está correta. A barra lateral fica à esquerda, a seta de voltar aponta para a esquerda, o avatar gruda na borda esquerda de cada card: cada palavra está em árabe, mas cada caixa está exatamente onde a versão em inglês a deixou. A direção do texto é um atributo HTML somado a um conjunto de decisões de CSS que vivem nos seus componentes, e nenhum arquivo de catálogo carrega essa informação. globalize.now é uma infraestrutura de localização com IA que extrai as strings e mantém os catálogos sincronizados a cada push no Git.
Por que meu app do Lovable continua sendo lido da esquerda para a direita em árabe?
Porque a direção é uma propriedade do documento e da folha de estilos, não da mensagem.
Seus catálogos são pares de strings de origem e strings traduzidas. Depois que são compilados para ar e seus componentes os renderizam, só as palavras mudam, e nada mais. Não faz diferença se esses catálogos são do Lingui, para onde o globalize.now escreve, ou do react-i18next. Nenhum dos dois formatos tem onde colocar “e a barra lateral vai para o outro lado”.
Duas coisas precisam mudar. O atributo dir no elemento html diz ao navegador para que lado o eixo inline corre. Seu CSS precisa parar de nomear lados e começar a nomear o início e o fim desse eixo. Tudo o mais decorre dessas duas mudanças.
O que realmente quebra quando você adiciona árabe a um app do Lovable?
As falhas se concentram em lugares previsíveis, e todas são de estilo, não de conteúdo traduzido.
- Gavetas de navegação e barras laterais permanecem na esquerda física.
- Chevrons, setas de voltar e indicadores de progresso apontam para o lado errado.
- O padding assimétrico prende o conteúdo à borda errada, fazendo os rótulos se amontoarem de um lado.
text-leftem um título mantém o árabe preso à esquerda do contêiner.- Selos e botões de fechar posicionados de forma absoluta acabam no canto oposto ao que o leitor espera.
- Bordas à esquerda que marcam um item de lista ativo aparecem na borda final, em vez da inicial.
- Um código de produto em inglês dentro de uma frase em árabe muda de ordem de forma imprevisível sem uma indicação de direção.
Nenhum desses é defeito de tradução. Todos são direções físicas fixadas no nome de uma classe.
Como derivo a direção do texto a partir da tag de idioma?
Pergunte à localidade qual direção ela usa e escreva a resposta no documento.
Intl.Locale.prototype.getTextInfo() retorna a direção como ltr ou rtl para uma tag de localidade. Atingiu o status Baseline newly available em julho de 2026, o que significa que chegou à versão atual de todo navegador principal, mas não à maioria dos navegadores que seus usuários realmente estão usando. Trate o fallback abaixo como o caminho ativo e a API como algo que, eventualmente, vai substituí-lo.
// 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";
}
Os dois pontos de atenção com try importam. new Intl.Locale() lança um RangeError com uma tag malformada, e getTextInfo() simplesmente não existe em ambientes mais antigos. Tratar apenas o segundo caso é o erro mais comum: uma localidade como ar_EG vinda de uma query string faz a função entrar no próprio caminho de recuperação e sair direto como um erro não tratado.
Defina os dois atributos onde seu app já conhece a localidade ativa. Atualmente o Lovable gera dois formatos: um app de página única com Vite e um app renderizado no servidor com TanStack Start, então verifique o que seu projeto realmente gerou. No TanStack Start, é o componente raiz do documento. No Vite, é onde você monta o provedor de i18n.
<html lang={locale} dir={directionFor(locale)}>
Coloque dir em html em vez de em um wrapper div. Portais, modais e toasts são renderizados fora da árvore de componentes e ainda assim precisam herdar essa configuração.
Quais classes do Tailwind eu preciso mudar para suportar a direita para a esquerda?
As que nomeiam um lado físico. Seus equivalentes lógicos seguem o eixo inline e se invertem sozinhos quando dir muda.
| Utilitário físico | Substituto lógico | O que ele define |
|---|---|---|
| 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 | alinhamento de texto no eixo inline |
| border-l / border-r | border-s / border-e | borda inicial / final |
| rounded-l-lg / rounded-r-lg | rounded-s-lg / rounded-e-lg | cantos inicial / final |
| float-left / float-right | float-start / float-end | float no eixo inline |
| clear-left / clear-right | clear-start / clear-end | clear no eixo inline |
| scroll-ml-4 / scroll-mr-4 | scroll-ms-4 / scroll-me-4 | margem de scroll-snap |
A documentação de margens do Tailwind mostra o efeito diretamente: ms-8 e me-8 renderizados dentro de contêineres dir="ltr" e dir="rtl" ficam em lados opostos. Os utilitários de propriedades lógicas chegaram no Tailwind v3.3, então um projeto mais antigo pode não os ter.
Dois utilitários merecem uma segunda análise em vez de uma simples renomeação. space-x-* e divide-x-* adicionam espaçamento e bordas entre elementos irmãos, então, em uma linha invertida, eles acabam no lado errado de cada filho. O Tailwind oferece space-x-reverse e divide-x-reverse exatamente para esse caso. Utilitários verticais como mt-* e pb-* não precisam de nada, já que a direção só afeta o eixo inline.
O que as propriedades lógicas não conseguem resolver?
Qualquer coisa que codifique uma direção fora do modelo de caixa, que é exatamente onde as variantes rtl: e ltr: têm seu lugar.
<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)]" />
Transformações, posições de plano de fundo, deslocamentos de sombra, ângulos de gradiente, cálculos de scroll de carrossel e qualquer ícone que signifique “avançar” se encaixam aqui. Mantenha a lista curta. Se você estiver escrevendo rtl: em quase todos os elementos, os estilos de base ainda são físicos e deveriam ser convertidos.
O conteúdo gerado pelo usuário precisa de um tratamento próprio, e os dois casos são diferentes. Para um bloco inteiro cujo idioma você não controla, dir="auto" deixa o navegador definir a direção base do bloco a partir do primeiro caractere de direção forte.
<p dir="auto">{review.body}</p>
Para um trecho em script estrangeiro dentro de uma frase que já tem direção definida, como um SKU em inglês em um texto em árabe, dir="auto" não resolve nada, porque ele define a direção base do parágrafo, e não isola um trecho inline. Em vez disso, envolva esse trecho.
<bdi>{product.sku}</bdi>
Por que um widget de tradução não consegue inverter seu layout para você?
Porque um widget atua sobre nós de texto na página renderizada, e o seu problema de direção está nos estilos que geraram essa página.
Um serviço em tempo de execução pode injetar dir="rtl" no documento e traduzir as strings visíveis depois que a versão em inglês já foi renderizada. Ele não consegue abrir seus componentes e transformar ml-4 em ms-4, porque ml-4 já foi compilado para margin-left em uma folha de estilos que o serviço não controla. O resultado é um texto em árabe dentro de um layout da esquerda para a direita.
Há um segundo custo envolvido. O Lovalingo, um serviço de tradução em tempo de execução usado em alguns apps do Lovable, anunciou que vai encerrar as atividades em 31 de agosto de 2026 às 23h59 CEST. As orientações de migração dizem aos clientes para migrarem para uma internacionalização própria do projeto e para reverificarem rotas de idioma, URLs canônicas e hreflang antes de removê-lo. O trabalho de layout feito dentro do produto de um fornecedor vai embora junto com o fornecedor. Os utilitários lógicos e um atributo dir no seu próprio repositório, não. Nossa comparação com o Lovalingo apresenta o mesmo argumento sobre a camada de strings.
Como impedir que o próximo prompt no Lovable desfaça tudo isso?
Converta uma vez e depois torne os utilitários físicos visíveis na revisão.
- Instale o fluxo de trabalho. No workspace do Lovable, adicione a skill
lovable-i18ne peça ao Lovable para configurar o i18n. Fora do editor, a instalação énpx skills add globalize-now/globalize-skills, com--allpara instalar em todos os agentes. - Defina a direção a partir do idioma usando
directionForacima, no elementohtml. - Converta os utilitários físicos em lógicos em todos os seus componentes, começando pelo que envolve suas páginas.
- Adicione variantes
rtl:apenas para transformações, planos de fundo, sombras e ícones direcionais, e use<bdi>em trechos inline com script estrangeiro. - Adicione uma verificação com grep na revisão para que um novo utilitário físico gerado falhe antes de ser mesclado.
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/
A quinta etapa é a que realmente se mantém. O Lovable regenera componentes a cada prompt, e um modelo solicitado a adicionar um painel de configurações vai recorrer a ml-4, porque os utilitários físicos dominam o código React com o qual esses modelos foram treinados. Uma verificação que reprova o pull request custa muito menos do que descobrir o problema em uma captura de tela em árabe três semanas depois.
A metade do processo relacionada às strings roda sozinha. O globalize.now extrai as novas strings fixas geradas pelo Lovable, grava-as nos seus catálogos e sincroniza a cada push no Git, mantendo a versão em árabe atualizada enquanto você continua criando prompts. É o mesmo mecanismo descrito no guia do Vite e no guia do TanStack Start, e é por isso que as traduções param de quebrar a cada push.
Adicionar o árabe não custa nada a mais no plano. O plano Starter custa €20 por mês por workspace, com €20 de crédito de tradução incluído (cerca de 200.000 palavras), e o uso continua na mesma tarifa por caractere depois disso, sem cobrança por usuário nem por idioma, e com os idiomas da direita para a esquerda incluídos. Há mais detalhes sobre a configuração na página de integração com o Lovable, detalhes técnicos na página para desenvolvedores, e um caminho mais rápido para quem não é técnico na página para vibe coders.
Uma mudança de layout, e ela permanece resolvida
A direção é uma decisão que seu repositório toma uma única vez. As strings são a parte que muda a cada prompt, e essa parte roda por conta própria.
O globalize.now transforma textos fixos do app em arquivos de locale prontos para tradução e os mantém atualizados a cada novo lançamento.
Experimente o globalize.now grátis