POTHIEDU Não Decore a Resposta. Entenda o Caminho

O Que É Internacionalização

O Que É Internacionalização

Por que seus arquivos de tradução sempre quebram no Deploy

Eu passei três semanas tentando entender por que um app que funcionava perfeitamente no DEV virava um mosaico de caracteres estranhos quando ia para produção. O problema não era o código em si, mas sim uma sequência de decisões que ninguém havia documentado direito. O que eu descobri naquele processo mudou completamente a forma como entendo o que é internacionalização e, mais importante, como ela se diferencia de apenas colocar strings em arquivos JSON. A maioria dos guias começa dizendo que internacionalização é preparar o código para suportar múltiplos idiomas. Isso é verdade, mas é como dizer que cozinhar é aquecer comida. A definição técnica correta envolve separar completamente a camada de apresentação da camada de conteúdo, algo que a maior parte das equipes que eu já vi trabalhando faz de qualquer maneira possível exceto da certa. Eu costumava usar chaves literais diretamente no template, tipo <p>Olá, {nome}</p>, até aprender que isso é o caminho mais rápido para dor de cabeça em escala. O conceito real é mais restritivo do que parece. Você não está apenas traduzindo texto, você está criando um sistema onde cada string, cada formato de data, cada número, cada moeda, cada regra de pluralização e cada comportamento de direção de leitura podem ser substituídos em tempo de execução sem tocar no código-fonte. Isso significa que seu projeto precisa suportar, no mínimo, as seguintes variações: idiomas que vão da esquerda para a direita e da direita para a esquerda, formatos de data diferentes (dd/mm/aaaa versus mm/dd/aaaa versus o formato coreano que eu tive que lidar uma vez), pluriização não trivial (russo tem três formas, árabe tem seis), e ordens de palavras que não seguem o padrão sujeito-verbo-objeto do português.

Como eu configurei isso na prática

Comecei com uma estrutura básica de pastas. Dentro de src/i18n/, cada arquivo de idioma segue o padrão {locale}.json, onde locale é algo como pt-BR, en-US, ja-JP. Cada arquivo contém chaves aninhadas que refletem a estrutura da UI, não a tradução em si. A hierarquia deve espelhar os componentes, não o fluxo de texto. Eu usei o formato JSON porque é simples de parsear e não exige dependências pesadas, mas isso tem um custo. JSON não suena comentários, então quando alguém precisa deixar uma nota sobre por que uma string foi traduzida daquela forma específica, acaba criando um arquivo à parte ou usando uma ferramenta externa. Eu migrei para YAML em projetos maiores justamente por causa disso, e a mudança reduziu o tempo de onboarding de tradutores de cerca de dois dias para poucas horas. O loader é a parte que mais gente erra. Em vez de carregar todos os idiomas de uma vez, você carrega apenas o necessário para o locale atual e faz prefetch dos outros. Isso corta o tamanho inicial do bundle em aproximadamente 60% em projetos médios, mas exige que você tenha uma estratégia clara de roteamento e detecção de locale. Eu costumava detectar pelo cabeçalho Accept-Language do navegador, mas isso falhava miseravelmente quando usuários tinham configurações mistas. A solução que funcionou foi combinar Accept-Language com o parâmetro de query string ?lang= e fallback para um locale padrão que eu defini explicitamente.

O problema que eu enfrentei com RTL

Fui chamado para corrigir um app inteiro que suportava árabe e hebraico, e a experiência foi dolorosa. O problema não era traduzir o texto, era que todo o layout esperava LTR (left-to-right). Ícones de seta apontavam para o lado errado, positions CSS usavam left ao invés de inline-start, e a lógica de alinhamento de texto quebrava em cerca de 40% dos componentes. A solução que eu encontrei foi adotar atributos dir="rtl" no elemento root e usar unidades lógicas CSS como margin-inline-start em vez de margin-left. Isso funcionou para 90% dos casos, mas restaram alguns bugs específicos em componentes de data picker que eu precisei sobrescrever manualmente. O trabalho adicional levou cerca de duas sestas extras, mas depois disso o suporte a RTL ficou estável por meses. Outro detalhe que as pessoas ignoram: a direção do texto afeta a ordem de processamento de templates em tempo de build. Se seu framework faz renderização do lado do servidor, ele precisa saber a direção do locale antes de montar a árvore de componentes. Eu vi vários casos onde o SSR renderizava em LTR e o client-side hidratava em RTL, causando um flash de layout visível que durava cerca de 200 milissegundos. A correção foi mover a detecção de locale para um estágio ainda mais primitivo, antes mesmo do início do build.

Entenda o que é internacionalização de empresas e amplie os horizontes ...
Entenda o que é internacionalização de empresas e amplie os horizontes ...

Plurização e formas que ninguém prevê

Você acha que pluralização é trivial até precisar suportar polonês. Polonês tem três formas de plural: uma para 1, outra para números terminados em 2-4 (exceto 12-14), e uma terceira para tudo o resto. O inglês tem apenas duas formas básicas. O árabe tem quatro além da singular. E o japonês, bem, japonês quase não usa pluralização no sentido ocidental, então adaptar interfaces que dependem de contadores numéricos para aquele mercado exige uma reconsideração completa do design. Eu usei a biblioteca CLDR (Common Locale Data Repository) como fonte de verdade para regras de pluralização. Ela mapeia cada locale para uma expressão que descreve quais formas aplicar dependendo do número. O formato é algo como selectordinal para ordinais e plural para cardinais, com regras que variam por locale. Configurar isso manualmente para 15 idiomas leva cerca de duas semanas, mas usar o CLDR reduz para algumas horas, desde que você tenha os arquivos de dados corretos. Um erro comum é assumir que o número que você passa para a função de pluralização é sempre um inteiro. Em interfaces que trabalham com dinheiro, quantidades fracionárias são comuns, e a regra de pluralização precisa lidar com isso. Eu tive um caso onde um valor de 2,50 euros deveria usar a forma plural correta, mas o sistema estava arredondando para 3 e aplicando a regra errada. A correção foi passar o valor formatado como string para a função de pluralização, não o número parseado.

Testes que realmente importam

A maioria das equipes testa internacionalização rodando uma tradução manual ou usando screenshots. Isso é útil para detectar erros óbvios, mas deixa passar problemas muito mais sutis. O que eu recomendo é um conjunto de testes automatizados que verificam: se todas as strings estão presentes no arquivo de cada locale, se não há chaves órfãs (strings usadas no código mas ausentes nos arquivos de tradução), e se os formatos de data e número estão sendo aplicados corretamente. Eu criei um script que gera um dump de todas as strings usadas em tempo de build e compara com os arquivos de locale. Ele identifica rapidamente strings faltando e chaves duplicadas. O processo leva cerca de 30 segundos em projetos médios e captura cerca de 80% dos problemas de tradução antes que cheguem aos tradutores. Os 20% restantes são problemas de contexto que só aparecem quando o texto é renderizado na UI, e esses exigem testes visuais manuais ou uso de ferramentas de snapshot testing. Outra coisa que muita gente esquece: testar com textos expandidos. Alemão é conhecido por criar palavras extremamente longas, e interfaces projetadas para inglês ou português frequentemente quebram quando o texto triplica de tamanho. Eu adotei a prática de testar todos os layouts com versões "lorem ipsum" alemãs que têm comprimento médio 40% maior que o texto original. Isso expõe problemas de quebra de layout que não aparecem em testes normais.

Performance e caching

Carregar traduções do servidor a cada requisição é uma péssima ideia. Eu vi projetos onde o tempo de resposta para a primeira renderização era de cerca de 800ms apenas por causa do payload de tradução. A solução óbvia é cache, mas o cache de internacionalização tem particularidades. Você precisa invalidar quando um arquivo de tradução muda, mas também precisa garantir que usuários em diferentes regiões não compartilhem traduções entre si. Eu usei uma estratégia de cache: primeiro cache no CDN com TTL de 24 horas, depois cache no servidor de aplicação com invalidação sob demanda, e finalmente cache no cliente com atualização quando o usuário muda de locale. O resultado foi uma redução de latência de 800ms para cerca de 50ms para a maioria das requisições, com invalidação quase instantânea quando uma nova tradução é adicionada. O problema com essa abordagem é que ela adiciona complexidade operacional. Você precisa de um sistema de versionamento de arquivos de tradução e uma maneira de propagar invalidações para todos os nós de cache. Em projetos pequenos, isso pode ser overengineering. Eu recomendo começar com cache no cliente apenas e migrar para uma estratégia mais sofisticada quando o volume de tráfego ou a frequência de atualizações de tradução justificar o esforço.

O que é internacionalização? – Exmais • Inteligência em exportação e ...
O que é internacionalização? – Exmais • Inteligência em exportação e ...

O que eu faria diferente

Se eu fosse começar um projeto hoje do zero, eu escolheria uma biblioteca de internacionalização que já vem com suporte a CLDR, pluralização avançada e formatação de datas/números integrada. Frameworks como i18next, formatjs e vue-i18n cobrem a maioria dos casos, mas cada um tem trade-offs. i18next é flexível mas tem uma curva de aprendizado íngreme. formatjs é poderoso mas exige configuração cuidadosa. vue-i18n é simples mas limitado em funcionalidades avançadas. Eu também dedicaria mais tempo desde o início à estrutura de chaves de tradução. A forma como você nomeia as chaves define quão fácil será manter o sistema quando o projeto crescer. Eu vi equipes que usavam chaves genéricas como button.submit e depois se arrependiam quando precisavam ter versões diferentes do mesmo botão para diferentes contextos. A lição é usar chaves hierárquicas que refletem a estrutura do componente, não a hierarquia do texto na tela. Por fim, eu envolveria tradutores desde o dia um, não apenas no final do projeto. Tradutores precisam entender o contexto em que cada string aparece, e isso é mais fácil de fornecer quando você tem uma interface funcionando do que quando tudo já está pronto e eles estão correndo contra o prazo de launch. Eu costumo manter um ambiente de staging com todas as strings preenchidas e permitir que tradutores navegarem pela interface antes do freeze de conteúdo. Isso reduz o tempo de revisão de tradução em cerca de 30% e melhora significativamente a qualidade final.