Seu app Lovable pode estar totalmente traduzido e ainda exibir "5 элемент" ou mostrar um preço como "1,234.50" para um usuário alemão. A causa é uma lógica de plural e de formatação escrita para o inglês e esquecida nos seus componentes. O globalize.now é uma infraestrutura de localização com IA que mantém suas strings em catálogos versionados, e a correção abaixo coloca as regras de plural dentro desses catálogos, e não no seu código.
Por que os plurais quebram quando o Lovable traduz meu app?
Porque a regra de plural fica no componente, e não na tradução. Um padrão gerado com frequência é assim:
<p>{count} {count === 1 ? "item" : "items"} in your cart</p>
Essa linha deixa a gramática do inglês fixa no código. Um tradutor pode mudar as palavras, mas não o teste === 1, e a ordem das palavras também fica travada, com o número sempre em primeiro lugar. O inglês tem duas formas. O russo tem quatro, então "1 элемент", "2 элемента", "5 элементов" e "1,5 элемента" exigem terminações diferentes.
Se você configurou o roteamento primeiro (veja como tornar um app Lovable multilíngue), os plurais são o próximo bug que os usuários notam, porque nada quebra. A frase simplesmente sai errada.
Passo 1: Como escrever um plural que os tradutores possam alterar?
Use uma mensagem de plural ICU, para que a regra passe para a tradução. Com o Lingui, tanto o componente Plural quanto a macro plural geram uma.
import { Plural } from "@lingui/react/macro";
<p>
<Plural value={count} one="# item in your cart" other="# items in your cart" />
</p>
Para strings fora do JSX, use a macro do pacote core:
import { plural } from "@lingui/core/macro";
const label = plural(count, { one: "# file", other: "# files" });
# é substituído pelo número. Segundo a documentação do Lingui, você escreve apenas as formas que o seu idioma de origem exige, e other é obrigatório. Correspondências exatas como _0="Your cart is empty" tratam o caso zero em inglês sem um ramo separado.
As stacks padrão do Lovable diferem na configuração, mas não nessa API, então o mesmo componente funciona nos projetos Vite SPA e TanStack Start.
Como fica o catálogo depois que os plurais são extraídos?
Uma mensagem por string, em sintaxe ICU padrão, armazenada como arquivo PO no seu repositório. O tradutor, humano ou IA, fornece as formas que o idioma dele exige.
msgid "{count, plural, one {# item in your cart} other {# items in your cart}}"
msgstr "{count, plural, one {# товар в корзине} few {# товара в корзине} many {# товаров в корзине} other {# товара в корзине}}"
A forma other do russo cobre frações como 1,5, e é por isso que ela coincide com o genitivo singular. Como o catálogo é um arquivo versionado, você revisa mudanças de plural em um pull request, como qualquer outro código. Para entender a sintaxe em si, veja o que é o ICU MessageFormat.
Quais idiomas precisam de mais do que one e other?
O Unicode CLDR define seis categorias de plural: zero, one, two, few, many e other. Cada idioma usa um subconjunto, e other é obrigatório em todos.
| Idioma | Categorias usadas |
|---|---|
| Japonês, chinês | other |
| Inglês | one, other |
| Russo | one, few, many, other |
| Árabe | zero, one, two, few, many, other |
A especificação do CLDR lista o galês como um idioma que também usa as seis categorias. Você não precisa decorar isso. Você precisa de um formato de catálogo que permita a cada idioma preencher as próprias formas, e o PO com mensagens ICU faz isso.
Como evitar que o Lovable volte a escrever plurais com ternários?
Coloque a regra onde o Lovable lê as instruções. O Lovable regenera componentes quando você pede novas funcionalidades, e uma funcionalidade nova escrita sem a regra traz o ternário de volta.
Adicione isto ao seu Knowledge ou ao AGENTS.md:
Never hand-build plurals or number formats. Use the Lingui Plural component
or plural() macro for every count. Use Intl with the active locale for every
date, number and price.
A skill lovable-i18n escreve regras desse tipo para você. Como ela faz isso está em o que a skill de i18n do Lovable faz. Depois da próxima funcionalidade gerada, procure no diff por === 1 e !== 1 para confirmar que a regra foi respeitada.
Passo 2: Como formatar datas e moeda por locale?
Use Intl com o locale ativo. O objeto i18n do Lingui também oferece i18n.date() e i18n.number(), mas chamar Intl diretamente com i18n.locale mantém as opções de formatação visíveis no seu próprio código.
import { useLingui } from "@lingui/react";
export function Price({ amount, currency }: { amount: number; currency: string }) {
const { i18n } = useLingui();
return (
<span>
{new Intl.NumberFormat(i18n.locale, { style: "currency", currency }).format(amount)}
</span>
);
}
export function OrderDate({ date }: { date: Date }) {
const { i18n } = useLingui();
return <time>{new Intl.DateTimeFormat(i18n.locale, { dateStyle: "long" }).format(date)}</time>;
}
Os mesmos 1234,5 euros são renderizados como uma string diferente em cada locale, com separadores e posição de símbolo próprios. A formatação nunca converte dinheiro. Um preço armazenado em euros continua em euros em todos os idiomas, então mantenha o código da moeda ao lado de cada preço no seu banco de dados.
Passo 3: Como testar todas as categorias de plural antes de publicar?
Pergunte ao navegador em qual categoria um número se enquadra e depois verifique se cada uma tem uma forma.
const ru = new Intl.PluralRules("ru");
[1, 2, 5, 21, 1.5].map((n) => ru.select(n));
// ["one", "few", "many", "one", "other"]
const ar = new Intl.PluralRules("ar");
[0, 1, 2, 3, 11, 100].map((n) => ar.select(n));
// ["zero", "one", "two", "few", "many", "other"]
Adicione um teste por idioma publicado: renderize o componente com um número de cada categoria e verifique que nenhum cai em uma forma ausente. Depois, leia as páginas em russo e em árabe no navegador pelo menos uma vez. Um few ausente em um catálogo aparece como a terminação errada, não como um erro.
O que muda se eu usava um widget de runtime antes?
Widgets de runtime traduzem o texto já renderizado depois do carregamento, então não enxergam uma regra de plural no seu código. Esse é um dos motivos pelos quais mover as traduções para o repositório importa. O Lovalingo encerrou as atividades em 31/08/2026; o guia de migração e a página de comparação explicam o que muda quando os catálogos ficam no seu repositório.
Com o globalize.now, você conecta o repositório uma vez e a base de código é convertida no app. Depois disso, as tarefas de push traduzem as novas unidades do catálogo e o resultado chega como um pull request. O Lovable sincroniza a partir do GitHub, então as formas de plural chegam ao seu app como qualquer outra mudança. A configuração está na página de integração com o Lovable, e o fluxo sem painel está em como localizar um app Lovable sem painel.
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