Skip to content

feat(i18n): API responde erros no idioma do usuário - #36

Merged
janeiaraujo merged 2 commits into
mainfrom
feat/erros-backend-bilingues
Aug 3, 2026
Merged

feat(i18n): API responde erros no idioma do usuário#36
janeiaraujo merged 2 commits into
mainfrom
feat/erros-backend-bilingues

Conversation

@janeiaraujo

Copy link
Copy Markdown
Owner

O problema

A interface passou a atender pt e en, mas a API continuou respondendo no idioma
em que cada mensagem foi escrita — e estava escrita nos dois, sem critério.
Das 255 mensagens, 182 em inglês e 73 em português. Na prática: quem usa a
plataforma em inglês recebe Notificação não encontrada, e quem usa em
português recebe Record not found. Nenhum dos dois idiomas era servido de
forma consistente.

A abordagem

A tradução acontece na borda, num hook preSerialization, e não em cada
throw. Reescrever 255 pontos espalhados por 15 módulos seria um diff enorme e
arriscado para chegar no mesmo resultado — e cada throw novo voltaria a ser
uma chance de esquecer. Com o hook, a chave do catálogo é a própria mensagem
como já está escrita no código, então nenhuma rota muda.

Texto fora do catálogo passa intacto: pode ser conteúdo do próprio usuário
ou mensagem de biblioteca, e reescrever isso seria pior do que deixar como está.

Escolha do idioma, em ordem: a preferência salva no Perfil (usuário
autenticado) → Accept-Language, respeitando q-values → padrão pt. O
frontend passa a enviar o cabeçalho a partir do i18n; sem isso o backend
seguiria o idioma do navegador, que não acompanha a troca feita dentro da
plataforma.

A trava

backend/tests/i18n-mensagens.test.js. O modo como isso quebra é silencioso:
alguém escreve um throw novo, não registra a mensagem, e ela simplesmente sai
num idioma só — sem erro, sem aviso. O teste cobra que toda mensagem escrita nas
rotas esteja no catálogo, que nenhuma entrada tenha um lado vazio, e que o
catálogo não acumule órfãs (catálogo com lixo deixa de ser confiável).

Um detalhe do extrator que vale registrar: log.error('Auth middleware error:', error)
tem um error: dentro da string, e sem restringir a captura a uma única
linha ela corria até a aspa da linha seguinte, engolindo código como se fosse
mensagem.

Verificação

Subi um Fastify real com o mesmo hook e bati nele:

pedido resposta
/kb/1 com Accept-Language: pt-BR Registro não encontrado
/kb/1 com Accept-Language: en-US,en;q=0.9 Record not found
rota que estoura exceção, em pt Notificação não encontrada
a mesma, em en Notification not found
sem cabeçalho / idioma não suportado (fr-FR) cai no padrão pt
payload com message de conteúdo do usuário passa intacto

Cobre os dois caminhos: resposta montada na rota e exceção passando pelo
tratador global.

  • backend npm test38 passam (eram 25)
  • frontend npm test → 7 passam · vite build ok

A interface passou a atender pt e en, mas a API respondia sempre no
idioma em que a mensagem foi escrita - e estava escrita nos dois, sem
criterio: das 255 mensagens, 182 em ingles e 73 em portugues. Quem usava
em ingles recebia "Notificação não encontrada"; quem usava em portugues
recebia "Record not found".

A traducao acontece na borda, num hook preSerialization, e nao em cada
throw. Reescrever 255 pontos em 15 modulos seria um diff enorme e
arriscado para o mesmo resultado; com o hook, a chave do catalogo e a
propria mensagem como ja esta escrita. Texto fora do catalogo passa
intacto - pode ser conteudo do proprio usuario.

O idioma sai da preferencia salva no Perfil quando ha usuario
autenticado e, sem ela, do Accept-Language (respeitando q-values). O
frontend passa a mandar o cabecalho a partir do i18n, senao o backend
seguiria o idioma do navegador, que nao acompanha a troca feita dentro
da plataforma.

Verificado num Fastify real: mesma rota devolvendo pt e en, pelo caminho
normal e pelo tratador global de excecao, com conteudo do usuario
preservado. 38 testes passando (eram 25).
Copilot AI review requested due to automatic review settings August 3, 2026 20:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Este PR padroniza as mensagens de erro da API no idioma do usuário, movendo a tradução para a borda (hook preSerialization) e garantindo consistência via catálogo e testes, além de fazer o frontend enviar o Accept-Language conforme o i18n da plataforma.

Changes:

  • Frontend passa a enviar Accept-Language em todas as requisições via interceptor do Axios.
  • Backend adiciona módulo de i18n com resolução de idioma (perfil → header → padrão) e tradução de error/message no preSerialization.
  • Adiciona catálogo bilíngue de mensagens e teste “guarda” para evitar regressões.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
frontend/src/services/api.js Inclui Accept-Language baseado no i18n.language em todas as requisições.
backend/src/server.js Adiciona hook preSerialization para traduzir error/message no payload de resposta.
backend/src/i18n/index.js Implementa parsing de Accept-Language, resolução de idioma e tradução de payloads.
backend/src/i18n/messages.js Introduz catálogo bilíngue (pt/en) com chaves baseadas no texto literal existente.
backend/tests/i18n-mensagens.test.js Adiciona testes para cobertura/consistência do catálogo e lógica de idioma/tradução.
Suppressed comments (1)

backend/src/i18n/messages.js:112

  • Para chaves em inglês, o valor em inglês deveria coincidir com a chave para evitar que respostas em en sejam reescritas/encurtadas pela tradução (aqui some "successfully").
    'KB access control updated successfully': ['Controle de acesso do KB atualizado', 'KB access control updated'],

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

'Invalid group ID': ['ID de grupo inválido', 'Invalid group ID'],
'Invalid parent group ID': ['ID de grupo pai inválido', 'Invalid parent group ID'],
'Parent group not found': ['Grupo pai não encontrado', 'Parent group not found'],
'User added to group successfully': ['Usuário adicionado ao grupo', 'User added to the group'],
'Serviço de email não configurado. Entre em contato com o administrador.': ['Serviço de email não configurado. Entre em contato com o administrador.', 'Email service is not configured. Please contact an administrator.'],
'Serviço de e-mail não configurado. Peça ao administrador para redefinir sua senha.': ['Serviço de e-mail não configurado. Peça ao administrador para redefinir sua senha.', 'Email service is not configured. Ask an administrator to reset your password.'],
'Falha ao conectar com o servidor de e-mail. Tente novamente mais tarde.': ['Falha ao conectar com o servidor de e-mail. Tente novamente mais tarde.', 'Could not reach the email server. Please try again later.'],
'Falha ao conectar com servidor de email. Tente novamente mais tarde.': ['Falha ao conectar com servidor de e-mail. Tente novamente mais tarde.', 'Could not reach the email server. Please try again later.'],
Comment on lines +22 to +24
// O backend traduz as mensagens de erro pelo idioma do pedido. Sem
// isto ele cairia no Accept-Language do navegador, que nao acompanha
// a troca de idioma feita dentro da plataforma.
Comment on lines +44 to +57
const padrao = /\b(?:error|message)\s*:\s*'((?:[^'\\\n]|\\.)*)'/g;

for (const file of collectFiles(SRC)) {
const relativo = path.relative(SRC, file).replace(/\\/g, '/');
if (relativo.startsWith('i18n/')) continue;

const code = fs.readFileSync(file, 'utf8');
padrao.lastIndex = 0;
let match;
while ((match = padrao.exec(code)) !== null) {
const texto = match[1].replace(/\\'/g, "'");
if (!encontradas.has(texto)) encontradas.set(texto, relativo);
}
}
@janeiaraujo
janeiaraujo merged commit 13e7fc7 into main Aug 3, 2026
3 checks passed
@janeiaraujo janeiaraujo mentioned this pull request Aug 3, 2026
@janeiaraujo
janeiaraujo deleted the feat/erros-backend-bilingues branch August 3, 2026 23:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants