Internacionalização
Internacionalização(internationalization), abreviada como i18n (porque há 18 letras entre “i” e “n”), é o processo de preparar um software para ser facilmente adaptado a diferentes idiomas e regiões, sem modificar o código-fonte.
Ou seja: você “internacionaliza” uma aplicação antes de traduzir, tornando-a multilíngue-ready.
Tip
i18n ≠ tradução. Tradução é uma parte da localização (l10n), que é o ato de adaptar o conteúdo a um idioma, cultura e convenções específicas.
| Conceito | Abreviação | O que é |
|---|---|---|
| Internacionalização | i18n | Tornar o software pronto para múltiplos idiomas. |
| Localização | l10n | Adaptar conteúdo (textos, datas, moedas) a um local específico. |
| Globalização | g11n | Processo geral de projetar, desenvolver e lançar um produto globalmente. |
- Acesso global: usuários de diferentes países entendem e usam seu app.
- Conformidade cultural: símbolos, unidades e formatos corretos.
- Expansão de mercado: facilita entrada em novos países.
- Experiência do usuário: o idioma nativo aumenta engajamento.
- Acessibilidade: mensagens e interfaces culturalmente coerentes.
| Locale | Data | Número | Moeda |
|---|---|---|---|
| pt-BR | 26/10/2025 | 1.234,56 | R$ 1.234,56 |
| en-US | 10/26/2025 | 1,234.56 | $ 1,234.56 |
| fr-FR | 26/10/2025 | 1 234,56 | 1 234,56 € |
Adicionando a API REST
Primeiro vamos instalar as dependências ao projeto.
Estrutura de pastas
Crie uma pasta locales/ com subpastas por idioma e arquivos por namespace.
Exemplo de conteúdo
Adicionando a API rest
Adicionar o import da biblioteca
Adicionar a configuração do i18n
i18n.configure({
locales: ['pt', 'en'], // Idiomas suportados
defaultLocale: 'pt', // Idioma padrão
directory: path.join(__dirname, 'locales'), // Pasta onde estão os arquivos .json
extension: '.json',
logDebugFn: function (msg) {
console.log('i18n debug:', msg);
},
autoReload: true, // Recarrega arquivos em desenvolvimento
updateFiles: false, // Não gera novos arquivos automaticamente
syncFiles: true, // Garante que todos os arquivos tenham as mesmas chaves
cookie: 'lang', // Nome do cookie que pode ser usado para setar a linguagem
queryParameter: 'lang', // Parâmetro de query que pode ser usado para setar linguagem
});
app.use(i18n.init);
healthcheck
+app.get('/health', (req, res) => {
const t = req.t || ((k) => k);
res.setHeader('Content-Language', req.language || 'en');
res.json({
ok: true,
service: req.__('SERVICE_NAME'),
message: req.__('MESSAGE'),
time: new Date().toISOString()
});
});
Ordem de prioridade:
Content-Language reflete o idioma detectado (por ?lang=pt-BR ou header Accept-Language).
- Querystring:
?lang=pt-BR|en|fr|es - Header:
Accept-Language: pt-BR,pt;q=0.9,en;q=0.8 - Fallback:
en
Testes
Alguns testes usando curl
# Saúde (fallback: en)
curl -s http://localhost:4000/health | jq
# Saúde em pt-BR (header)
curl -s -H "Accept-Language: pt-BR" http://localhost:4000/health | jq
# Saúde em fr (query)
curl -s "http://localhost:4000/health?lang=fr" | jq
# Validação ausente em espanhol
curl -s -X POST -H "Content-Type: application/json" -H "Accept-Language: es" \
-d '{}' http://localhost:4000/api/sign | jq
Localization
O jeito mais elegante e eficiente de integrar funções de formatação de data e moeda com o i18n é criar um middleware o locale injetado na requisição (req.locale) pelo i18n.
function mapLocale(i18nLocale) {
switch (i18nLocale.toLowerCase()) {
case 'pt':
return 'pt-BR';
case 'en':
return 'en-US';
default:
return 'en-US'; // Fallback
}
}
function formatCurrency(amount, i18nLocale, currencyCode) {
const locale = mapLocale(i18nLocale);
try {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currencyCode,
currencyDisplay: 'symbol',
}).format(amount);
} catch (e) {
console.error(`Erro ao formatar moeda para locale ${locale}:`, e.message);
return `${currencyCode} ${amount}`;
}
}
function formatDate(dateValue, i18nLocale, options = {}) {
const date = (dateValue instanceof Date) ? dateValue : new Date(dateValue);
const locale = mapLocale(i18nLocale);
const defaultOptions = {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
timeZone: 'UTC'
};
const finalOptions = Object.keys(options).length > 0 ? options : defaultOptions;
try {
return new Intl.DateTimeFormat(locale, finalOptions).format(date);
} catch (e) {
console.error(`Erro ao formatar data para locale ${locale}:`, e.message);
return date.toISOString();
}
}
module.exports = {
formatCurrency,
formatDate,
};
Adicionar a API
const { formatCurrency, formatDate } = require('./localeHelpers');
function localeMiddleware(req, res, next) {
const locale = req.locale;
res.locale = {
formatCurrency: (amount, currencyCode) => formatCurrency(amount, locale, currencyCode),
formatDate: (dateValue, options) => formatDate(dateValue, locale, options),
};
next();
}
app.use(i18n.init);
app.use(localeMiddleware);
Endpoint health atualizado para usar o localization