@cactus-agents/i18n
SDK de internacionalização modular com seleção de idioma em build-time e suporte a overrides de fork. Baseado no i18next.
Instalação
pnpm add @cactus-agents/i18n
Conceitos
- Um idioma por build — sem language detector, sem fallback (
fallbackLng: false). O idioma é definido em build-time via env var (BRAND_LANGUAGE). - Um namespace por arquivo JSON — cada domínio da plataforma tem o seu.
- Fork overrides — brands sobrescrevem só as chaves que precisam, via
deepMerge. O resto mantém o valor do SDK. - Sem Suspense — evita flash de conteúdo não traduzido.
- SSR/HMR safe —
initI18n()é idempotente (não reinicializa se já foi chamado). defaultNSécommon;escapeValueéfalse(o React já cuida do XSS);lowerCaseLng: true.
Uso básico
O caminho recomendado é o alias ~i18n, resolvido pelo Vite no build para o diretório do idioma ativo — assim o arquivo de bootstrap não precisa saber qual idioma está compilando. É como o front-web-base faz (app/context/i18n.tsx):
import { initI18n } from "@cactus-agents/i18n";
import auth from "~i18n/auth.json";
import casino from "~i18n/casino.json";
import common from "~i18n/common.json";
import consent from "~i18n/consent.json";
import countries from "~i18n/countries.json";
import gamification from "~i18n/gamification.json";
import layout from "~i18n/layout.json";
import payments from "~i18n/payments.json";
import profiles from "~i18n/profiles.json";
import sports from "~i18n/sports.json";
import user from "~i18n/user.json";
import validation from "~i18n/validation.json";
const i18n = initI18n({
language: "pt-br",
resources: {
auth, casino, common, consent, countries, gamification,
layout, payments, profiles, sports, user, validation,
},
});
Fora do Vite (Node/Worker, testes), importe direto do pacote pelo export path ./locales/*:
import casino from "@cactus-agents/i18n/locales/pt-br/casino.json";
import common from "@cactus-agents/i18n/locales/pt-br/common.json";
:::caution Não existe games.json
O namespace de cassino é casino, e o arquivo é casino.json em todos os 7 idiomas. Um import ... from "@cactus-agents/i18n/locales/pt-br/games.json" falha na resolução. Ver a nota em Namespaces sobre a constante TRANSLATION_NAMESPACES.
:::
Com overrides de fork
const overrides = {
common: { button: { save: "Gravar" } },
layout: { header: { deposit: "Adicionar saldo" } },
};
const i18n = initI18n({
language: "pt-br",
resources: { /* ...os 12 namespaces... */ },
overrides,
});
Apenas as chaves fornecidas em overrides são substituídas — todas as outras mantêm o valor original do SDK.
Fora do React
import { getI18n } from "@cactus-agents/i18n";
const i18n = getI18n();
const label = i18n.t("common:button.save");
API pública
Funções
| Função | Descrição |
|---|---|
initI18n(options) | Inicializa i18next com idioma, resources e overrides opcionais. Idempotente. |
getI18n() | Retorna o singleton i18next. Deve ser chamado depois de initI18n(). |
deepMerge(base, override) | Merge recursivo. O override sobrescreve apenas as chaves fornecidas. |
Tipos
| Tipo | Descrição |
|---|---|
InitI18nOptions | { language: string; resources: NamespaceResources; overrides?: NamespaceResources } |
NamespaceResources | Record<string, Record<string, unknown>> — chave livre, sem união de namespace |
TranslationNamespace | União dos nomes em TRANSLATION_NAMESPACES |
FullResources | Record<TranslationNamespace, Record<string, unknown>> |
initI18n aceita NamespaceResources (chave string), não FullResources. Ou seja: passar casino funciona e não gera erro de tipo.
Idiomas suportados
7 diretórios em packages/i18n/locales/, cada um com os 12 arquivos de namespace — 84 arquivos JSON no total.
| Código | Idioma | Diretório |
|---|---|---|
pt-br | Português (Brasil) | locales/pt-br/ |
pt | Português (Portugal) | locales/pt/ |
en | Inglês | locales/en/ |
en-ng | Inglês (Nigéria) | locales/en-ng/ |
es | Espanhol | locales/es/ |
es-cl | Espanhol (Chile) | locales/es-cl/ |
fi-fi | Finlandês | locales/fi-fi/ |
Essa é exatamente a lista que o docblock de InitI18nOptions.language declara como suportada.
Namespaces
12 namespaces — um arquivo JSON por namespace, por idioma.
| Namespace | Arquivo | Conteúdo principal |
|---|---|---|
common | common.json | Botões, status, erros, tempo, paginação, estados vazios |
auth | auth.json | Login, registro, esqueci a senha, reset de senha |
casino | casino.json | Cassino, busca, filtros, detalhe de jogo, stats, carrossel |
consent | consent.json | Banner e preferências de consentimento de cookies |
countries | countries.json | Nomes de países e nacionalidades por ISO alpha-3 |
gamification | gamification.json | VIP, missões, torneios, loja, badges, mini-games, bônus, jackpots, raffles |
layout | layout.json | Header, sidebar, footer, nav mobile, busca, páginas de erro |
payments | payments.json | Depósito, saque, PIX, SPEI, redirect, histórico |
profiles | profiles.json | Perfis oficiais / social |
sports | sports.json | Esportes, ao vivo, pré-match, apostas |
user | user.json | Conta, carteira, segurança, proteção, IRPF, notificações |
validation | validation.json | Steps de validação (docs, endereço, telefone, email, KYC, limites, termos) |
:::warning Divergência conhecida em TRANSLATION_NAMESPACES
A constante exportada (packages/i18n/src/types.ts) lista:
["common", "auth", "consent", "payments", "games", "user",
"validation", "sports", "gamification", "layout", "countries", "profiles"]
Note o "games": nenhum idioma tem games.json — o arquivo é casino.json. TranslationNamespace e FullResources derivam dessa união, então um objeto tipado como FullResources exigiria uma chave games e rejeitaria casino.
Na prática isso é inerte hoje, porque initI18n recebe NamespaceResources (chave string) e nunca FullResources. Mas a constante está fora de sincronia com o disco — provável bug de source no core, e a correção (renomear games → casino) é no core, não aqui.
:::
Adicionar uma tradução
Traduções novas vão para o @cactus-agents/i18n do core — não para app/locales/overrides/ do base, a menos que seja override explícito de uma brand.
Ao adicionar um namespace novo, os 7 diretórios de idioma precisam do arquivo: packages/i18n/src/__tests__/locale-contract.test.ts compara as chaves entre idiomas e falha se um locale ficar para trás.
Dependências
| Dependência | Tipo |
|---|---|
i18next | Runtime |
react | Peer (opcional) |
react-i18next | Peer (opcional) |
React e react-i18next são opcionais — o pacote funciona standalone em contextos Node/Worker.