Saltar a contenido

Internacionalización

Adaptive Learner se entrega en 8 idiomas, todos completamente traducidos desde v1.13.0 / Fase 26: DE, EN, ES, FR, EL, PT, TR, JA. Según la regla del proyecto, el contenido en alemán usa umlauts reales (ä, ö, ü, ß) — el alemán con ASCII está prohibido en de.yaml y docs/help/de/**. test_i18n_translation_audit.py fija tanto la cobertura de umlauts como el umbral de ≥90% de divergencia respecto a EN para los catálogos que anteriormente eran pasaporte inglés.

Dónde viven las cadenas

Backend

Catálogos YAML en backend/config/i18n/{lang}.yaml — un archivo por idioma. El catálogo de referencia es EN; cada otro catálogo debe tener el mismo árbol de claves.

# backend/config/i18n/en.yaml
common:
  save: Save
  cancel: Cancel
settings:
  title: Settings
  section_language: Language
  ...

El backend expone GET /api/i18n/{lang} que devuelve el catálogo completo como JSON. El hook useI18n del frontend lo llama en el primer renderizado y almacena el resultado en caché.

Fallbacks del frontend

frontend/src/i18n/fallbacks.ts contiene un subconjunto en línea de cadenas codificadas en el bundle del frontend. Estas se muestran en el primer renderizado antes de que se haya cargado el catálogo del backend — y como fallback permanente si el endpoint del catálogo devuelve 5xx.

La cadena de resolución de fallback en useI18n:

  1. Catálogo del backend (cadenas en vivo desde /api/i18n/{lang}). Recorre la ruta de notación por puntos.
  2. Fallbacks del frontend codificados (resiliencia en el primer renderizado).
  3. Cadena de fallback proporcionada por el llamador.
  4. La clave misma.

Ayuda en la aplicación / sitio de documentación

docs/help/_meta.yaml declara el árbol de navegación con títulos en DE + EN por entrada. El panel de ayuda en la aplicación lo lee directamente; scripts/generate_mkdocs_nav.py regenera el bloque de navegación de mkdocs.yml desde la misma fuente.

El Markdown de las páginas de ayuda vive en docs/help/{lang}/... — una carpeta por idioma. El modo docs_structure: folder de mkdocs-static-i18n gestiona la resolución.

Añadir un nuevo idioma

  1. Catálogo del backend: copia backend/config/i18n/en.yaml a backend/config/i18n/{nuevoidioma}.yaml. Traduce cada valor de forma nativa (sin pasaporte EN — la prueba de auditoría de traducción fallará con <90% de divergencia). Luego ejecuta make sync-i18n para reflejar el catálogo en frontend/src/data/i18n/{nuevoidioma}.json (el bundle del modo Dexie que el hotfix v1.16.0 hizo resiliente en el primer renderizado).
  2. Constantes del frontend: añade el código del idioma a SUPPORTED_LANGUAGES en frontend/src/lib/constants.ts.
  3. Fallbacks del frontend: añade un bloque {nuevoidioma}: {...} a frontend/src/i18n/fallbacks.ts con el pequeño subconjunto de cadenas que usa la ruta de código del primer renderizado.
  4. Preguntas de la evaluación: edita plugins/adaptive-learner-plugin-assessment/adaptive_learner_assessment/questions.py — añade un campo text_{nuevoidioma} a cada entrada de QUESTIONS y a cada respuesta. Vuelve a exportar el JSON: poetry run python plugins/.../questions.py --export-json frontend/src/data/assessment-questions.json (este CLI no existe aún; el script en scripts/ necesitaría una pequeña adición).
  5. Prompts de sesión: edita plugins/adaptive-learner-plugin-session/adaptive_learner_session/prompts.py para añadir una tercera clave de idioma a cada celda (método, paso). Vuelve a exportar el JSON de la misma manera.
  6. Sitio de documentación: actualiza el bloque plugins.i18n.languages de mkdocs.yml y añade el directorio docs/help/{nuevoidioma}/ reflejando la estructura EN.

Pruebas de paridad

backend/tests/test_i18n_parity.py recorre cada catálogo y verifica que:

  • Cada idioma tiene cada clave que tiene la referencia (EN).
  • Ningún idioma tiene una clave extra que la referencia no tenga.

Esto detecta el bug de deriva más común: alguien añade una clave en EN y se olvida de añadirla en los otros 7 archivos. Ejecuta make test-backend después de cualquier cambio en el catálogo.

Plurales

Los catálogos actuales no usan la sintaxis de plural ICU. Los pocos recuentos que renderizamos (número de sesiones, días de racha) tienen la selección de forma codificada en TS:

const label = count === 1 ? t("session_single") : t("session_plural");

Las cadenas de plural ICU mediante mkdocs-i18n o una biblioteca ICU en tiempo de ejecución están en la lista de trabajo aplazado.

Idiomas RTL

El árabe, el hebreo, el persa, etc. necesitarían soporte de maquetación bidireccional en el CSS. El tema actual asume LTR; añadir RTL está en la lista de trabajo aplazado.

Cuándo NO añadir una cadena

Si una cadena solo aparece en superficies destinadas a desarrolladores (mensajes de error lanzados por AdaptiveLearnerError, líneas de registro, datos de fixtures de pruebas), déjala en inglés. La maquinaria de internacionalización es solo para cadenas visibles por el usuario final.

Las llamadas console.warn y console.error del frontend también se mantienen en inglés — son para desarrolladores que leen la consola del navegador, no para usuarios finales.