Saltar a contenido

Implementación de i18n

KanjiIQ utiliza el framework de internacionalización integrado de Flutter con archivos ARB (Application Resource Bundle) para la localización de UI y JSONB de PostgreSQL para la localización de contenido.

Localización de UI con ARB

Configuración

El sistema de localización se configura en frontend/l10n.yaml:

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

Estructura de archivos ARB

Cada idioma tiene un archivo ARB siguiendo el formato de mensajes ICU:

// lib/l10n/app_en.arb (template)
{
  "@@locale": "en",
  "appTitle": "KanjiIQ",
  "studyButton": "Start Study",
  "selectLanguage": "Select Language",
  "selectLevel": "Select JLPT Level",
  "cardCount": "{count} cards",
  "@cardCount": {
    "placeholders": {
      "count": { "type": "int" }
    }
  },
  "noConnection": "No internet connection. Using cached data.",
  "showAllLanguages": "Show all 51 languages"
}

Generación de código

Ejecutar flutter gen-l10n genera:

  • app_localizations.dart — Clase base abstracta con todas las cadenas localizadas
  • app_localizations_en.dart — Implementación en inglés
  • app_localizations_ja.dart — Implementación en japonés
  • ... (51 archivos generados)

Uso en widgets de Flutter:

// Access localized strings
Text(AppLocalizations.of(context)!.studyButton)

// With parameters
Text(AppLocalizations.of(context)!.cardCount(10))

Registro de locales soportadas

Las locales se registran en MaterialApp:

MaterialApp(
  localizationsDelegates: AppLocalizations.localizationsDelegates,
  supportedLocales: AppLocalizations.supportedLocales,
  locale: selectedLocale,  // From LocalePreferences
)

Localización de contenido

El contenido de las tarjetas (significados de kanji, definiciones de vocabulario) se almacena como JSONB en PostgreSQL y se sirve a través de la API:

graph LR
    A[API Request<br/>?lang=pt] --> B[Backend]
    B --> C[PostgreSQL<br/>JSONB lookup]
    C --> D[Return meaning<br/>in Portuguese]

Pipeline de traducción

El contenido nuevo se traduce a través de la OpenAI API:

  1. El contenido fuente se escribe en inglés
  2. El backend envía solicitudes de traducción a OpenAI con contexto sobre el nivel JLPT y uso
  3. Las traducciones se almacenan en la columna JSONB junto a las traducciones existentes
  4. Cada idioma puede actualizarse independientemente sin afectar a los demás

Traducción autoalojada

Los manifiestos de Kubernetes incluyen un despliegue de LibreTranslate (k8s/04-libretranslate-deployment.yaml) como alternativa a OpenAI. Esto proporciona traducción completamente autoalojada sin dependencias de APIs externas, aunque la calidad varía según el par de idiomas.

Idioma de UI vs. idioma de contenido

KanjiIQ permite que el idioma de la UI y el idioma de estudio sean diferentes:

  • Idioma de UI: El idioma de botones, etiquetas y navegación (ej., francés)
  • Idioma de estudio: El idioma para los significados de las tarjetas (ej., portugués)

Un usuario francófono que estudia japonés puede tener la interfaz en francés mientras ve los significados de las tarjetas en portugués si lo prefiere.

Añadir un nuevo idioma

Para añadir un nuevo (52°) idioma:

  1. UI: Crear lib/l10n/app_XX.arb con todas las cadenas traducidas
  2. Contenido: Añadir traducciones a las columnas JSONB mediante el pipeline de traducción
  3. Configuración de locale: Añadir el código de idioma a las entradas relevantes de locale_configs mediante la API de administración
  4. Compilación: Ejecutar flutter gen-l10n para regenerar los archivos de localización

No se requieren migraciones de esquema ni cambios en el código del backend — el almacenamiento JSONB y el sistema ARB están diseñados para ser extensibles.