Internacionalização (i18n): preparando seu produto para outros mercados
Expandir um produto para outros países parece, à primeira vista, um problema de tradução: basta pegar o texto e passar para outro idioma. Quem já tentou sabe que a realidade é bem mais funda. Datas viram uma bagunça, plurais quebram, moedas aparecem no formato errado, e um idioma escrito da direita para a esquerda desmonta o layout inteiro. Preparar um produto para o mundo é uma decisão de arquitetura, não um ajuste de última hora. Esse trabalho tem nome: internacionalização, ou i18n (a sigla vem das 18 letras entre o "i" e o "n").
Vale separar dois conceitos que costumam ser confundidos. Internacionalização (i18n) é preparar o código para suportar múltiplos idiomas e regiões, sem valores fixos no código. Localização (l10n) é o ato de adaptar o produto para um mercado específico, traduzindo textos e ajustando formatos. A i18n é a fundação de engenharia; a l10n é o preenchimento por idioma. Este guia foca na fundação, o que precisa estar certo no código antes de a primeira tradução chegar.
Externalizar strings: o primeiro passo
O pecado original da falta de i18n é o texto escrito direto na interface. Enquanto houver "Bem-vindo" no meio do componente, não há tradução possível sem mexer no código. A base de tudo é externalizar as strings: tirá-las do código e colocá-las em arquivos de recurso por idioma, referenciadas por uma chave.
// pt-BR.json
{
"welcome_title": "Bem-vindo de volta",
"cart_items": "{count, plural, =0 {Carrinho vazio} one {# item} other {# itens}}"
}
// en-US.json
{
"welcome_title": "Welcome back",
"cart_items": "{count, plural, =0 {Empty cart} one {# item} other {# items}}"
}
Boas práticas para as chaves:
- Use chaves semânticas, que descrevem o papel do texto (
checkout_button), não o conteúdo (comprar_agora). O conteúdo muda, o papel não. - Nunca concatene frases. Montar
"Você tem " + n + " mensagens"no código quebra em qualquer idioma com ordem de palavras diferente. Use interpolação com variáveis dentro da própria string traduzível. - Um arquivo por idioma, com a mesma estrutura de chaves, para que o que falta em uma tradução seja fácil de detectar.
Plurais e interpolação: a armadilha da gramática
Aqui mora o erro mais comum de quem "quase" fez i18n. Cada idioma tem regras de plural diferentes. O português tem duas formas (singular e plural); o inglês também; mas o russo tem quatro, o árabe tem seis, e o japonês não distingue plural. Um if (n === 1) no código assume a gramática do inglês e falha em boa parte do mundo.
A solução é delegar a pluralização à biblioteca de i18n, usando um padrão como o ICU MessageFormat (visível no exemplo acima). Em vez de decidir a forma no código, você descreve todas as formas na string traduzível, e a biblioteca escolhe a certa conforme o idioma e o número. O mesmo vale para interpolação de variáveis: nome, quantidade, gênero, tudo entra como parâmetro, deixando a estrutura da frase sob controle de quem traduz.
Datas, números e moedas por locale
Texto é só parte do problema. Os dados formatados variam muito mais do que se imagina, e formatá-los à mão é garantia de erro.
| Dado | Brasil (pt-BR) | Estados Unidos (en-US) | Alemanha (de-DE) |
|---|---|---|---|
| Data | 03/09/2026 | 9/3/2026 | 03.09.2026 |
| Número | 1.234,56 | 1,234.56 | 1.234,56 |
| Moeda | R$ 1.234,56 | $1,234.56 | 1.234,56 € |
| Primeiro dia da semana | domingo ou segunda | domingo | segunda |
Repare como a mesma data (03/09) significa 3 de setembro no Brasil e 9 de março nos Estados Unidos. Tratar isso na mão é receita para bug silencioso. A regra é usar as APIs de formatação sensíveis a locale, como a família Intl disponível nos navegadores modernos, que resolve datas, números, moedas e até ordenação alfabética conforme a região.
const preco = 1234.56;
new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' })
.format(preco); // "R$ 1.234,56"
new Intl.DateTimeFormat('de-DE').format(new Date(2026, 8, 3)); // "3.9.2026"
Um princípio de ouro para os bastidores: guarde os dados em formato neutro (datas em UTC no padrão ISO, valores monetários como inteiros na menor unidade) e formate apenas na hora de exibir. Misturar armazenamento e apresentação é o caminho mais curto para dados corrompidos.
RTL: quando o layout inverte
Idiomas como árabe, hebraico e persa são escritos da direita para a esquerda (RTL, de right-to-left). Isso não é só espelhar o texto: a interface inteira precisa inverter. O menu vai para a direita, o ícone de "voltar" aponta para o outro lado, o alinhamento se inverte.
A forma moderna de preparar o produto para RTL é evitar propriedades de layout presas a uma direção física e usar propriedades lógicas de CSS. Em vez de margin-left, use margin-inline-start; em vez de text-align: left, use text-align: start. Assim, ao trocar o atributo dir="rtl" no HTML, o layout se adapta sozinho, sem uma folha de estilo espelhada e duplicada.
- Use
direlangcorretos no elemento raiz por idioma. - Prefira propriedades lógicas (
inline-start,inline-end,padding-inline) a físicas. - Cuidado com ícones direcionais (setas, progresso), que precisam espelhar.
- Teste de verdade com conteúdo RTL real, não com texto invertido de mentira.
Roteamento e detecção de locale
Como o produto sabe qual idioma servir, e como o usuário troca? A estratégia de URL importa para experiência e para SEO. As abordagens comuns:
- Subpasta (
exemplo.com/pt/,exemplo.com/en/): simples, boa para SEO, fácil de gerenciar. É a escolha mais frequente. - Subdomínio (
pt.exemplo.com): separa bem, mas adiciona complexidade de infraestrutura. - Domínio próprio por país (
exemplo.com.br,exemplo.de): máximo sinal local, maior custo.
Seja qual for, o locale deve estar explícito na URL, para que cada versão de idioma seja indexável e compartilhável. A detecção automática (pelo cabeçalho Accept-Language do navegador) é útil como sugestão inicial, mas nunca deve prender o usuário: ofereça sempre um seletor de idioma visível e respeite a escolha dele. Para o SEO, o atributo hreflang informa aos buscadores quais são as versões equivalentes em cada idioma, evitando que sejam tratadas como conteúdo duplicado.
O fluxo de tradução que escala
Com o código pronto, falta o processo humano. Times que tratam tradução como uma tarefa manual de copiar e colar em planilhas sofrem com retrabalho e inconsistência. Um fluxo maduro se apoia em automação:
- Extração: as strings novas são coletadas dos arquivos de recurso automaticamente.
- Plataforma de gestão (TMS): as chaves vão para uma ferramenta onde tradutores trabalham com contexto, memória de tradução e glossário.
- Contexto para o tradutor: capturas de tela e notas evitam traduções erradas por falta de entendimento (a mesma palavra pode ser botão ou título).
- Reintegração e validação: as traduções voltam ao projeto e passam por checagens automáticas (chaves faltando, variáveis quebradas, texto que estoura o layout).
Tratar as traduções como parte do pipeline, versionadas junto do código, é o que permite lançar novos idiomas sem virar um projeto paralelo a cada vez.
Prepare a fundação antes de precisar dela
Internacionalização é daquelas decisões que custam pouco quando feitas cedo e muito quando adiadas. Externalizar strings, delegar plurais e formatos às APIs corretas, adotar propriedades lógicas para RTL e definir o roteamento de locale desde o início transforma a entrada em um novo mercado em uma questão de traduzir, não de reconstruir. Retrofitar i18n em um produto que nasceu com texto fixo no código, por outro lado, costuma ser um dos refactors mais dolorosos que existem.
Na ConstructorTI, projetamos produtos prontos para escalar globalmente: arquitetura de i18n desde o primeiro commit, formatação sensível a locale, suporte a RTL com CSS lógico, roteamento e hreflang corretos para SEO internacional e um fluxo de tradução automatizado. Se você planeja levar o seu produto para outros mercados, ou já sofre com uma internacionalização improvisada, fale com o nosso time e saia da conversa com um plano claro de i18n sob medida para a sua expansão.