Internacionalização (i18n) e localização (l10n) em apps
1. Fundamentos de i18n e l10n: terminologia e escopo
A internacionalização (i18n) e a localização (l10n) são disciplinas complementares no desenvolvimento de aplicações globais. i18n refere-se ao processo de arquitetar o software para suportar múltiplos idiomas e regiões sem alterações no código-fonte. l10n é a adaptação cultural e linguística propriamente dita, incluindo tradução de textos, formatação de dados e adequação a normas locais. A tradução (t9n) é apenas um subconjunto da localização.
O ciclo de vida da localização compreende cinco etapas: planejamento (definição de locales-alvo), extração (coleta de strings do código), tradução (realizada por tradutores humanos ou automatizada), integração (incorporação ao build) e manutenção (atualização contínua). Um mito comum é acreditar que localizar resume-se a traduzir textos — na verdade, envolve adaptar formatos de data, moeda, pluralização, símbolos, cores e até imagens.
2. Arquitetura de strings e arquivos de tradução
Arquivos de tradução podem usar formatos como JSON, YAML, PO/MO ou ICU MessageFormat. A organização por namespace (ex: common, errors, checkout) facilita o carregamento sob demanda e evita arquivos monolíticos. É recomendável usar chaves semânticas ao invés de IDs numéricos:
// Exemplo de arquivo JSON com chaves semânticas
{
"checkout.title": "Finalizar compra",
"checkout.subtotal": "Subtotal",
"checkout.shipping": "Frete",
"errors.required": "Campo obrigatório",
"errors.invalidEmail": "E-mail inválido"
}
Para suporte a contexto e gênero, a estrutura deve prever variantes:
{
"greeting.male": "Bem-vindo, {name}",
"greeting.female": "Bem-vinda, {name}",
"greeting.neutral": "Bem-vinde, {name}"
}
3. Pluralização, gênero e regras linguísticas complexas
O ICU MessageFormat é o padrão mais robusto para pluralização. Cada locale define suas próprias regras (CLDR):
// Português: regras one/other
"items": "{count, plural, one {# item} other {# itens}}"
// Russo: regras one/few/many/other
"items_ru": "{count, plural, one {# товар} few {# товара} many {# товаров} other {# товара}}"
// Árabe: regras zero/one/two/few/many/other
"items_ar": "{count, plural, zero {# عنصر} one {# عنصر} two {# عنصران} few {# عناصر} many {# عنصرًا} other {# عنصر}}"
Para gênero em francês:
"member": "{gender, select, male {membre} female {membre} other {membre}}"
4. Detecção de idioma e fallback estratégico
A negociação de locale pode ser feita via cabeçalho Accept-Language, geolocalização (API do navegador) ou preferências salvas. A estratégia de fallback deve criar uma cadeia: pt-BR → pt → en:
// Exemplo de lógica de fallback
function resolveLocale(preferred, supported) {
const fallbackChain = [preferred, preferred.split('-')[0], 'en'];
for (const locale of fallbackChain) {
if (supported.includes(locale)) return locale;
}
return 'en';
}
A persistência da escolha deve usar cookie (com consentimento LGPD/GDPR), localStorage ou perfil do usuário no backend. Nunca confie apenas em geolocalização, pois usuários podem estar viajando ou usar VPN.
5. Formatação de dados localizados (datas, números, moedas)
As APIs nativas Intl oferecem formatação consistente sem dependências externas:
// Formatação de data
const dateFormatter = new Intl.DateTimeFormat('pt-BR', {
dateStyle: 'long',
timeStyle: 'short'
});
console.log(dateFormatter.format(new Date())); // "15 de janeiro de 2025 14:30"
// Formatação de moeda
const currencyFormatter = new Intl.NumberFormat('ja-JP', {
style: 'currency',
currency: 'JPY'
});
console.log(currencyFormatter.format(1234)); // "¥1,234"
// Formatação de número com pluralização relativa
const relativeFormatter = new Intl.RelativeTimeFormat('pt-BR', { numeric: 'auto' });
console.log(relativeFormatter.format(-3, 'day')); // "há 3 dias"
Para fusos horários, use o parâmetro timeZone:
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Tokyo',
dateStyle: 'full',
timeStyle: 'full'
});
6. Internacionalização no backend (Node.js/Express)
Um middleware de locale deve extrair o idioma do header, validar contra a lista de suporte e injetar no objeto req:
const supportedLocales = ['pt-BR', 'en', 'es'];
const i18nMiddleware = (req, res, next) => {
const header = req.headers['accept-language'] || 'en';
const preferred = header.split(',')[0].trim();
req.locale = supportedLocales.includes(preferred) ? preferred : 'en';
next();
};
Usando i18next com backend HTTP:
const i18next = require('i18next');
const Backend = require('i18next-http-backend');
i18next.use(Backend).init({
fallbackLng: 'en',
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
});
app.use((req, res, next) => {
req.t = i18next.getFixedT(req.locale);
next();
});
Cuidados: cache de traduções em produção, carregamento lazy de namespaces e SSR com locale correto para SEO.
7. Internacionalização no frontend (React, Vue, Angular)
Em React com react-i18next:
import { useTranslation, Trans } from 'react-i18next';
function Welcome() {
const { t } = useTranslation('common');
return (
<div>
<h1>{t('welcome.title')}</h1>
<Trans i18nKey="welcome.description">
Você tem <strong>{{ count }} notificações</strong> não lidas.
</Trans>
</div>
);
}
Carregamento assíncrono de namespaces:
const { t, ready } = useTranslation(['checkout', 'errors'], { useSuspense: true });
Para roteamento amigável (Next.js, Nuxt, Angular Universal):
// URLs: /pt/produto, /en/product
// Meta tags hreflang
<link rel="alternate" hreflang="pt-BR" href="https://exemplo.com/pt/produto" />
<link rel="alternate" hreflang="en" href="https://exemplo.com/en/product" />
8. Testes, manutenção e ferramentas de automação
Testes unitários devem verificar chaves ausentes, placeholders quebrados e pluralização incorreta:
// Teste com Jest
test('todas as chaves em pt-BR existem em en', () => {
const pt = require('./locales/pt-BR/common.json');
const en = require('./locales/en/common.json');
Object.keys(pt).forEach(key => {
expect(en).toHaveProperty(key);
});
});
test('placeholders não quebrados', () => {
const en = require('./locales/en/common.json');
Object.values(en).forEach(value => {
const placeholders = value.match(/\{\{(\w+)\}\}/g);
// Verificar se placeholders são consistentes
});
});
Ferramentas de gerenciamento: Crowdin (colaboração em equipe), Lokalise (API robusta), POEditor (simplicidade), Weblate (open source). Na CI/CD, adicione etapas de lint de arquivos, validação de formato ICU e sincronização automática com repositórios Git.
Referências
- Documentação oficial do ICU MessageFormat — Guia completo sobre sintaxe de mensagens internacionalizadas, incluindo pluralização e seleção de gênero.
- CLDR - Unicode Common Locale Data Repository — Repositório oficial de dados de localização, incluindo regras de pluralização para centenas de idiomas.
- i18next - Framework de internacionalização — Documentação do i18next com exemplos de backend, frontend, detecção de idioma e fallback.
- MDN Web Docs - Intl API — Referência completa sobre formatação de datas, números, moedas e tempo com APIs nativas do JavaScript.
- React i18next - Guia oficial — Tutorial e documentação para integrar internacionalização em aplicações React com hooks e componentes.
- Crowdin - Plataforma de localização — Ferramenta colaborativa para gerenciamento de traduções com integração contínua e automação.
- Artigo "Internationalization vs. Localization: The Difference" (Lokalise) — Explicação clara sobre as diferenças entre i18n, l10n e t9n com exemplos práticos.