Ir al contenido

Internacionalización (i18n)

CÉNIT soporta tres locales — español, inglés y portugués — vía next-intl 4.9.1. El español es la fuente de verdad y está al 100%. El inglés está completamente traducido. El portugués tiene ~471 keys con el texto idéntico al español: el archivo existe y el locale funciona, pero un usuario lusoparlante hoy ve mayormente castellano. Está identificado y hay tooling para resolverlo.

Player surface: transparente. El jugador hereda el locale de su sesión y ve la app en su idioma sin opción explícita salvo por su perfil.

Todos los roles ven el selector de idioma en su perfil. El locale se persiste en dos lugares:

  • Cookie locale — fuente inmediata para el render del próximo request.
  • user_profiles.locale — fuente de verdad persistente.

Un componente LocaleSync en el layout detecta desajustes entre cookie y DB, escribe db_locale y el middleware redirige al usuario con la cookie corregida.

  • Cliente (Client Components): useTranslations('namespace').
  • Server (Server Components / Actions): getTranslations('namespace').
  • Regla absoluta: toda string visible al usuario va por t(). No hardcodear en JSX.
LocaleCobertura real
es100% — fuente de verdad.
en100% — traducido y revisado.
pt~471 keys idénticas al ES. Existe el locale, falta traducir.

20 keys están intencionalmente bilingues y no cuentan como gap (ej: nav.brand, siglas como HMLD, ACWR).

  • messages/es.json — fuente de verdad.
  • messages/en.json — traducido humano.
  • messages/pt.json — incompleto, requiere traducción humana.
  • tsx scripts/i18n-detect-untranslated.ts --out=untranslated.csv — extrae todas las keys de pt.json que son idénticas al es.json (excluyendo la lista de bilingues intencionales).
  • tsx scripts/i18n-merge-translations.ts <csv> — re-importa el CSV después de que un humano lo complete.

El loop esperado es: detectar → CSV a traductor → recibir CSV traducido → merge → commit.

  • Wellness tokenizado (/w/[token]) — tiene strings hardcodeadas en español. Es un formulario público simple, legacy para Nacional; no pasa por next-intl. No se traducirá salvo decisión explícita.
  • Mensajes del staff al jugador — el texto libre del mensaje no se traduce. Lo que se traduce son las strings de la UI alrededor.
  • Multi-tenant — el locale es por-usuario, no por-organización. Una org con staff multilingüe funciona naturalmente.
  • Middleware — el bloque “Locale sync” en middleware.ts resuelve los desajustes cookie/DB con un redirect en GET.
  • pt.json traducción humana — bloqueado por contratar traductor PT-BR/PT-PT. Tooling listo.
  • Decisión PT-BR vs PT-PT — pendiente. Vender a clubes brasileños o portugueses puede requerir locales separados.
  • Wellness tokenizado en otros idiomas — solo si un cliente lo pide; no es prioridad.