Documentación de Mentat — en evolución continua.
IngenieríaInternacionalización (i18n)

Internacionalización (i18n)

Mentat es multilenguaje: inglés primario, español secundario. Esta página documenta la arquitectura, cómo traducir un componente y el estado de la migración.

Decisiones de arquitectura

La web (apps/web) es output: 'export' (estático puro, sin servidor ni middleware). Por eso el i18n es 100% del lado cliente y el idioma es una preferencia (no un prefijo de URL como /en /es).

  • Librería: react-i18next (+ i18next + i18next-browser-languagedetector).
  • Sin prefijo de URL: las rutas no cambian (/actions sigue siendo /actions).
  • Resolución del idioma: localStorage['mentat.locale']navigator.language → fallback. Persiste solo en el cliente (cache del language detector).
  • DEFAULT_LOCALE hoy es 'es' para no cambiar la experiencia actual durante la migración; se flipea a 'en' cuando los catálogos estén completos (Fase A4).
  • <html lang> se sincroniza con el idioma activo desde el I18nProvider.

Archivos clave

ArchivoRol
lib/i18n/config.tsinit de i18next (initI18n, idempotente), detección, SUPPORTED_LOCALES, DEFAULT_LOCALE
lib/i18n/locales/{en,es}/*.jsoncatálogos, un archivo por namespace
lib/i18n/locales/{en,es}/index.tsarma el objeto { namespace: json }
lib/i18n/format.tsformatDateTime(ts, locale) — fechas locale-aware (en→en-US, es→es-AR)
components/providers/i18n-provider.tsxmonta I18nextProvider + sincroniza <html lang>
components/app-shell/LanguageSwitcher.tsxselector en/es (persiste + emite languageChanged)

Cómo traducir un componente

1. Elegí/creá el namespace

Un namespace por feature (nav, dashboard, actions, …). Agregá el .json en locales/en/ y locales/es/ y registralo en los dos index.ts. Las keys son semánticas en inglés (stats.objects), no el texto español.

// locales/en/dashboard.json          // locales/es/dashboard.json
{ "title": "Dashboard",               { "title": "Dashboard",
  "subtitle": "Your organization…" }    "subtitle": "Estado de tu organización…" }

2. Usá t() en el componente

'use client';
import { useTranslation } from 'react-i18next';
 
export function Foo() {
  const { t } = useTranslation('dashboard');
  return <h1>{t('title')}</h1>;
}

Para keys compartidas (loading, save, cancel…) reusá el namespace common: useTranslation(['dashboard', 'common']) y t('common:states.loading').

3. Fechas y números

Nunca hardcodees 'es-AR'. Usá el helper con el idioma activo:

const { i18n } = useTranslation();
const locale = i18n.resolvedLanguage ?? i18n.language;
formatDateTime(ts, locale); // en→en-US, es→es-AR
⚠️

Gotchas:

  • Páginas prerenderizadas (ej. /login): traducirlas introduce hydration mismatch (la HTML estática queda en el idioma del build). Las páginas bajo (app)/* son client-only (el shell renderiza tras el auth) → seguras. El login se migra con un patrón SSG-safe (render con DEFAULT_LOCALE en SSR + detección diferida post-mount).
  • noUncheckedIndexedAccess está activo: arr[0] es T | undefined → defaulteá.
  • Los enums de estado (success/failed/partial) se muestran crudos por ahora.

Estado de la migración

FaseQuéEstado
A0Andamiaje (deps, config, provider inerte)
A1Selector de idioma + persistencia + <html lang> + nav
A2Extracción incremental de strings🔄 dashboard ✅ · pendientes: actions, datasources, ontology, objects, inbox, login
A3Fechas/números/plurales🔄 helper formatDateTime listo
A4Flip de DEFAULT_LOCALE a 'en'
BBackend (errores por código) + emails
CSitio de docs Nextra multilingüe

Alcance total acordado: frontend + backend/emails + este sitio de docs. La web queda operativa bilingüe al terminar A; B y C completan el “inglés primario” de punta a punta.