Pular para o conteúdo principal

@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 safeinitI18n() é 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çãoDescriçã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

TipoDescrição
InitI18nOptions{ language: string; resources: NamespaceResources; overrides?: NamespaceResources }
NamespaceResourcesRecord<string, Record<string, unknown>> — chave livre, sem união de namespace
TranslationNamespaceUnião dos nomes em TRANSLATION_NAMESPACES
FullResourcesRecord<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ódigoIdiomaDiretório
pt-brPortuguês (Brasil)locales/pt-br/
ptPortuguês (Portugal)locales/pt/
enInglêslocales/en/
en-ngInglês (Nigéria)locales/en-ng/
esEspanhollocales/es/
es-clEspanhol (Chile)locales/es-cl/
fi-fiFinlandêslocales/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.

NamespaceArquivoConteúdo principal
commoncommon.jsonBotões, status, erros, tempo, paginação, estados vazios
authauth.jsonLogin, registro, esqueci a senha, reset de senha
casinocasino.jsonCassino, busca, filtros, detalhe de jogo, stats, carrossel
consentconsent.jsonBanner e preferências de consentimento de cookies
countriescountries.jsonNomes de países e nacionalidades por ISO alpha-3
gamificationgamification.jsonVIP, missões, torneios, loja, badges, mini-games, bônus, jackpots, raffles
layoutlayout.jsonHeader, sidebar, footer, nav mobile, busca, páginas de erro
paymentspayments.jsonDepósito, saque, PIX, SPEI, redirect, histórico
profilesprofiles.jsonPerfis oficiais / social
sportssports.jsonEsportes, ao vivo, pré-match, apostas
useruser.jsonConta, carteira, segurança, proteção, IRPF, notificações
validationvalidation.jsonSteps 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 gamescasino) é 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ênciaTipo
i18nextRuntime
reactPeer (opcional)
react-i18nextPeer (opcional)

React e react-i18next são opcionais — o pacote funciona standalone em contextos Node/Worker.