Respostas de erro multilíngues da API pública

TLDR: As mensagens de erro e de sucesso da API pública são traduzíveis e devolvidas no idioma do chamador (pt-BR, en, es), com pt-BR como padrão. O Admin/ActiveAdmin fica fora — não é multilíngue.

Visão geral

Toda mensagem user-facing da API pública vive em arquivo de locale sob o namespace api.*. A resolução do idioma da resposta acontece uma vez por request, em ApplicationController, via around_action :switch_locale — que envolve a ação em I18n.with_locale para o locale thread-local nunca vazar entre requests.

Somente locales presentes em I18n.available_locales (pt-BR, en, es, definidos em config/application.rb) são aceitos; qualquer outro valor cai no fallback. Com config.i18n.fallbacks = true, uma chave ausente em en/es cai no pt-BR em vez de quebrar.

Fluxo

mermaid flowchart TD R["Request"] --> P{"param ?locale= <br/>está em available_locales?"} P -->|sim| USE["usa esse locale"] P -->|não| H{"header Accept-Language<br/>tem match?"} H -->|sim| USE H -->|não| D["I18n.default_locale (pt-BR)"] D --> USE USE --> A["I18n.with_locale { action }"]

Precedência, em ordem:

  1. Param ?locale= — override explícito, mantido por compatibilidade.
  2. Header Accept-Language — melhor match respeitando os pesos q; tag regional cai no idioma base (en-US → en, pt-PT → pt-BR).
  3. I18n.default_locale (pt-BR) como fallback.

Contratos

Convenção de chaves

Namespace Uso Domínios em uso
api.errors.<domínio>.<chave> mensagens de erro common, auth, membership, apolo, citrg, onboarding, webhook, webhook_email
api.messages.<chave> mensagens de sucesso ex.: profile_updated

Tradução obrigatória nos três arquivos: config/locales/pt-BR.yml, config/locales/en.yml e config/locales/es.yml.

Mensagens de validação

As mensagens do ActiveRecord (blank, taken, …) e do Devise são localizadas por outra via: pt-BR pelo pt-BR.yml/devise.pt-BR.yml, en pelos defaults nativos do Rails, e es pelos gems rails-i18n/devise-i18n (já presentes). Nome de atributo sem tradução é humanizado automaticamente.

Regra ao adicionar uma mensagem nova

  1. Usar I18n.t("api.errors....") / I18n.t("api.messages....") — nunca string literal.
  2. Ter entrada em pt-BR, en e es.
  3. Manter o shape de resposta do endpoint ({ error: ... }, { message: ... } ou { errors: [...] }) — a mudança é só na origem do texto.

Referências

  • app/controllers/application_controller.rb — switch_locale, resolved_locale, locale_from_header, best_available_locale
  • config/application.rb — I18n.available_locales, default_locale, fallbacks
  • config/locales/pt-BR.yml, config/locales/en.yml, config/locales/es.yml
  • Spec: respostas de erro multilíngues