Desenvolvimento· Arrumando a casa · parte 20
O idioma certo na tela pode virar o errado no servidor.
Veja como organizo URLs e mensagens com Paraglide JS e guardo o idioma escolhido para respostas e comunicações posteriores.

Uma tela no idioma certo não garante que o e-mail enviado depois saia no mesmo idioma. O idioma precisa acompanhar o pedido e continuar gravado quando o processamento sai do navegador.
Defini esse padrão em 5 de novembro, quando um produto que ainda não apresentei aqui ganhou a versão brasileira. Ele vale para todo produto com mais de um idioma, inclusive os novos. A regra cobre desde a URL pública até o texto produzido no servidor.
As mensagens curtas ficam juntas no pacote
Nos produtos web em TypeScript, uso Paraglide JS 2 num pacote packages/i18n. A regra vale para Astro, SPA React e API Hono em Workers. As mensagens ficam em JSON por idioma e são compiladas no build com chaves tipadas.
Cada build do navegador leva um idioma só. Essa organização define com clareza onde mora o texto curto de interface: telas, moldura do site, botões e erros entram no catálogo de mensagens. Cada chave tipada representa o texto exato que a interface deve renderizar.
Para documentos longos ou com estrutura, o tratamento é diferente. Textos de jurídico, blog, metodologia, e-mails com blocos e prompts de IA ficam em arquivos de conteúdo por idioma. Essa separação respeita o ciclo de vida de cada documento sem sobrecarregar o catálogo de interface.
Essa divisão também orienta a manutenção contínua do código. Ajustar um botão exige alterar apenas o arquivo JSON do catálogo; atualizar termos de uso mexe apenas nos arquivos de conteúdo. A separação mantém os arquivos de tradução limpos e fáceis de auditar.
A URL mostra o idioma da página pública
As páginas públicas levam prefixo do idioma, slug traduzido, hreflang e link de troca de idioma. O visitante encontra um caminho claro e previsível para a versão que deseja abrir. Não adoto redirecionamento automático por país ou navegador.
O português do Brasil usa pt-BR como código e /pt-br como prefixo de URL quando não é o idioma base. A base de onde nascem os produtos novos já vem configurada com en-US na raiz e pt-BR em /pt-br.
A estrutura da URL faz parte da arquitetura de conteúdo público. O slug traduzido mantém a semântica da página em cada língua, e o link de troca dá acesso imediato à outra opção. As tags hreflang informam aos buscadores a relação direta entre as versões equivalentes.
A revisão de uma página pública confere a rota completa junto com o conteúdo visível. Uma tradução correta no corpo da página perde utilidade se faltar o cabeçalho de idioma ou o link alternativo. O padrão reúne esses elementos para validar a experiência inteira do visitante.
O servidor recebe o idioma em cada chamada
No servidor, passo sempre { locale } explícito em cada mensagem, nunca um idioma global. O isolate do Worker atende pedidos simultâneos de origens diferentes. O app manda o idioma num cabeçalho em toda chamada para garantir o contexto certo na resposta.
Usar variáveis globais de idioma causaria vazamento de contexto entre requisições concorrentes. No modelo de execução dos Workers, um pedido em inglês e outro em português podem compartilhar a mesma instância. O parâmetro explícito em cada função elimina qualquer risco de contaminação cruzada.
O trabalho com idiomas não termina quando a resposta chega ao navegador. O idioma fica gravado no registro (tentativa, lead, usuário) para tudo o que sai depois: e-mail, PDF, lembrete. Essa informação precisa continuar persistida para acompanhar o ciclo de vida da conta.
Uma tela traduzida não encerra a localização do produto. Se a verificação parar na interface web, comunicações disparadas horas depois podem sair no idioma padrão errado. O padrão exige persistir a preferência no banco desde o primeiro contato.
A revisão alcança os produtos existentes
No gate, bun i18n:check falha com chave ausente ou variável perdida. Tradução feita por modelo é revisada antes de publicar. A validação automatizada impede que chaves esquecidas quebrem a interface em produção.
Apps nativos usam o catálogo da plataforma: Localizable.xcstrings no iOS e i18next no React Native, como no Garfim. O padrão respeita os ecossistemas nativos onde eles já possuem convenções estabelecidas.
A situação em 5 de novembro mostra o estado da transição: dois produtos que ainda não apresentei aqui já seguem o padrão. O site do ClipLeve tem páginas duplicadas em /en/, sem mensagens compiladas. O app do Garfim segue o item dos nativos, e o site dele ainda vai ser conferido. A padronização dos que divergem fica para outra sessão.
O erro mais custoso de idioma aparece horas depois no servidor ou na caixa de entrada do usuário. Guardar a escolha no registro e exigir { locale } explícito em cada chamada resolve o problema na raiz. Para outra decisão técnica compartilhada, veja como organizei o roteamento de IA pelo OpenRouter.