Contexto
O hcf-painel é uma SPA Vite + React 18 que utiliza Ant Design e React Router v4. Toda a interface está em Português Brasileiro hardcoded em aproximadamente 182 arquivos-fonte (uma mistura de componentes de classe .jsx legados e componentes funcionais .tsx mais novos). Não existe nenhuma infraestrutura de i18n atualmente.
Objetivo: Suportar pt-BR, es e en com URLs prefixadas por locale (ex: /pt/tombos, /es/tombos, /en/tombos).
Solução Proposta
Biblioteca
Utilizar react-i18next + i18next. É o padrão da indústria para SPAs React, suporta componentes de classe (HOC withTranslation), componentes funcionais (hook useTranslation), carregamento lazy de namespaces e tem suporte de primeira classe ao TypeScript.
Pacotes adicionais necessários:
i18next-browser-languagedetector — detecta o locale a partir da URL, localStorage ou configuração do navegador
i18next-http-backend — carrega os arquivos JSON de tradução sob demanda (evita empacotar todos os locales no bundle)
Estrutura dos Arquivos de Tradução
public/
locales/
pt/
common.json
navigation.json
tombos.json
taxonomia.json
...
en/
common.json
navigation.json
tombos.json
...
es/
common.json
navigation.json
tombos.json
...
Cada namespace corresponde a uma funcionalidade/tela. Exemplo de common.json:
{
"loading": "Carregando...",
"save": "Salvar",
"cancel": "Cancelar",
"error": {
"generic": "Ocorreu um erro, tente novamente."
}
}
Inicialização do i18n (src/i18n.ts)
Inicializar o i18next com o LanguageDetector configurado para ler o locale a partir do caminho da URL primeiro, com fallback para localStorage e depois para as configurações do navegador.
// src/i18n.ts (ilustrativo, não completo)
i18n
.use(HttpBackend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'pt',
supportedLngs: ['pt', 'en', 'es'],
detection: {
order: ['path', 'localStorage', 'navigator'],
},
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
},
ns: ['common', 'navigation', 'tombos'],
defaultNS: 'common',
});
Roteamento Baseado em URL
Envolver todas as rotas existentes dentro de um prefixo /:locale. O segmento de locale na URL define o idioma ativo. Um redirecionamento de / para /<locale-detectado> deve ser adicionado.
Antes: /tombos, /taxonomia/familias
Depois: /pt/tombos, /en/tombos, /es/taxonomia/familias
Um componente wrapper LocaleRoute deve:
- Ler
:locale da URL
- Validar contra
['pt', 'en', 'es'] — redirecionar para /pt/... se inválido
- Chamar
i18n.changeLanguage(locale) e definir o locale do ConfigProvider do Ant Design correspondente
// LocaleRoute ilustrativo
function LocaleRoute() {
const { locale } = useParams();
const { i18n } = useTranslation();
useEffect(() => {
if (SUPPORTED_LOCALES.includes(locale)) {
i18n.changeLanguage(locale);
} else {
navigate(`/pt${window.location.pathname}`);
}
}, [locale]);
return <Outlet />; // ou <Switch> no RR v4
}
Locale do Ant Design
Conectar o locale ao ConfigProvider existente em src/index.tsx de forma dinâmica com base no idioma ativo:
const antdLocaleMap = { pt: ptBR, en: enUS, es: esES };
<ConfigProvider locale={antdLocaleMap[i18n.language] ?? ptBR}>
Extração de Strings nos Componentes
Componentes funcionais (padrão mais novo, ex: feature de login):
const { t } = useTranslation('common');
// Antes: <span>Carregando...</span>
// Depois: <span>{t('loading')}</span>
Componentes de classe (padrão legado, maioria das telas):
export default withTranslation('tombos')(ListaTombosScreen);
// Dentro do render():
const { t } = this.props;
// Antes: <Button>Salvar</Button>
// Depois: <Button>{t('save')}</Button>
Componente de Troca de Idioma
Um novo componente LanguageSwitcher no cabeçalho do layout principal que substitui o segmento de locale na URL atual ao trocar de idioma, sem recarregar a página.
Arquivos Afetados (não exaustivo)
src/index.tsx — adicionar I18nextProvider, atualizar ConfigProvider com locale dinâmico
src/App.tsx — adicionar prefixo /:locale em todas as rotas, adicionar wrapper LocaleRoute
src/layouts/MainLayout.jsx — extrair todos os rótulos de navegação, adicionar LanguageSwitcher
src/features/login/LoginLayout.tsx — extrair strings do formulário de login
src/pages/ — todas as ~50 telas (maior esforço; deve ser feito em lotes por namespace)
src/components/SimpleTableComponent.jsx — remover strings PT hardcoded; substituir por i18next + locale do antd
- Todas as 7 telas de relatório com imports de
dateLocale por tela — centralizar via ConfigProvider
Estratégia de Migração (Recomendada)
- Fase 1 — Infraestrutura: Instalar bibliotecas, criar
src/i18n.ts, atualizar ConfigProvider, adicionar wrapper de roteamento por locale, criar arquivos de tradução vazios para os 3 idiomas
- Fase 2 — Layout e componentes compartilhados:
MainLayout, SimpleTableComponent, mensagens de erro comuns, componente LanguageSwitcher
- Fase 3 — Telas de funcionalidades (em lotes): Login, tombos, taxonomia, locais, remessas, usuários — um namespace por PR
- Fase 4 — Tradução do conteúdo: Entregar os arquivos JSON de
en e es para tradução humana ou automática; pt é preenchido primeiro como fonte primária de verdade
Critérios de Aceite
- Navegar para
/en/tombos renderiza a tela de tombos em inglês; /es/tombos em espanhol
- Navegar para
/ redireciona para /<locale-preferido-do-navegador>/... (fallback: /pt/)
- As strings internas do Ant Design (date picker, paginação de tabela, modais) correspondem ao locale ativo
- O
LanguageSwitcher atualiza a URL e re-renderiza as strings sem recarregar a página
- Todas as chaves de tradução
pt-BR estão presentes; os arquivos en e es estão estruturados (podem ter strings placeholder inicialmente)
- Nenhuma string em Português permanece hardcoded fora dos arquivos JSON de tradução
Contexto
O
hcf-painelé uma SPA Vite + React 18 que utiliza Ant Design e React Router v4. Toda a interface está em Português Brasileiro hardcoded em aproximadamente 182 arquivos-fonte (uma mistura de componentes de classe.jsxlegados e componentes funcionais.tsxmais novos). Não existe nenhuma infraestrutura de i18n atualmente.Objetivo: Suportar pt-BR, es e en com URLs prefixadas por locale (ex:
/pt/tombos,/es/tombos,/en/tombos).Solução Proposta
Biblioteca
Utilizar
react-i18next+i18next. É o padrão da indústria para SPAs React, suporta componentes de classe (HOCwithTranslation), componentes funcionais (hookuseTranslation), carregamento lazy de namespaces e tem suporte de primeira classe ao TypeScript.Pacotes adicionais necessários:
i18next-browser-languagedetector— detecta o locale a partir da URL, localStorage ou configuração do navegadori18next-http-backend— carrega os arquivos JSON de tradução sob demanda (evita empacotar todos os locales no bundle)Estrutura dos Arquivos de Tradução
Cada namespace corresponde a uma funcionalidade/tela. Exemplo de
common.json:{ "loading": "Carregando...", "save": "Salvar", "cancel": "Cancelar", "error": { "generic": "Ocorreu um erro, tente novamente." } }Inicialização do i18n (
src/i18n.ts)Inicializar o i18next com o
LanguageDetectorconfigurado para ler o locale a partir do caminho da URL primeiro, com fallback paralocalStoragee depois para as configurações do navegador.Roteamento Baseado em URL
Envolver todas as rotas existentes dentro de um prefixo
/:locale. O segmento de locale na URL define o idioma ativo. Um redirecionamento de/para/<locale-detectado>deve ser adicionado.Um componente wrapper
LocaleRoutedeve::localeda URL['pt', 'en', 'es']— redirecionar para/pt/...se inválidoi18n.changeLanguage(locale)e definir olocaledoConfigProviderdo Ant Design correspondenteLocale do Ant Design
Conectar o locale ao
ConfigProviderexistente emsrc/index.tsxde forma dinâmica com base no idioma ativo:Extração de Strings nos Componentes
Componentes funcionais (padrão mais novo, ex: feature de login):
Componentes de classe (padrão legado, maioria das telas):
Componente de Troca de Idioma
Um novo componente
LanguageSwitcherno cabeçalho do layout principal que substitui o segmento de locale na URL atual ao trocar de idioma, sem recarregar a página.Arquivos Afetados (não exaustivo)
src/index.tsx— adicionarI18nextProvider, atualizarConfigProvidercom locale dinâmicosrc/App.tsx— adicionar prefixo/:localeem todas as rotas, adicionar wrapperLocaleRoutesrc/layouts/MainLayout.jsx— extrair todos os rótulos de navegação, adicionarLanguageSwitchersrc/features/login/LoginLayout.tsx— extrair strings do formulário de loginsrc/pages/— todas as ~50 telas (maior esforço; deve ser feito em lotes por namespace)src/components/SimpleTableComponent.jsx— remover strings PT hardcoded; substituir por i18next + locale do antddateLocalepor tela — centralizar viaConfigProviderEstratégia de Migração (Recomendada)
src/i18n.ts, atualizarConfigProvider, adicionar wrapper de roteamento por locale, criar arquivos de tradução vazios para os 3 idiomasMainLayout,SimpleTableComponent, mensagens de erro comuns, componenteLanguageSwitchereneespara tradução humana ou automática;pté preenchido primeiro como fonte primária de verdadeCritérios de Aceite
/en/tombosrenderiza a tela de tombos em inglês;/es/tombosem espanhol/redireciona para/<locale-preferido-do-navegador>/...(fallback:/pt/)LanguageSwitcheratualiza a URL e re-renderiza as strings sem recarregar a páginapt-BRestão presentes; os arquivoseneesestão estruturados (podem ter strings placeholder inicialmente)