Skip to content

Internacionalizacion

Technomantus Corvi edited this page Sep 5, 2026 · 1 revision

Internacionalización (i18n)

Tanuki incluye un sistema de traducciones mínimo basado en archivos: un helper global t() más un diccionario JSON por idioma en lang/, y un selector de idioma por prefijo de URL opcional, conectado por defecto pero inerte hasta que se usa.

lang/
├── en.json
└── es.json

Cómo funcionan las traducciones

t('nav.home')                              // → "Inicio" (o "Home" si el idioma es en)
t('todo.count_summary', ['total' => 5])    // → sustituye ":total" dentro del string
  • El idioma activo viene de current_locale(): un override de sesión si existe, si no APP_LOCALE del .env.
  • Las claves usan notación de puntos para llegar a secciones anidadas del JSON (errors.404_title{"errors": {"404_title": "..."}}).
  • Si una clave falta en el idioma activo, t() cae al inglés; si tampoco existe ahí, devuelve la clave tal cual — una traducción faltante se ve en la interfaz en vez de romper la página.
  • Los diccionarios se cargan y cachean una vez por petición, así que llamar a t() muchas veces no tiene coste extra.
  • Los placeholders usan dos puntos delante (:nombre) y se sustituyen con el segundo argumento.

Añadir un nuevo idioma

cp lang/en.json lang/fr.json
# traduce los valores en lang/fr.json

Añade fr a ACCEPTED_LANGUAGES (ver abajo) si quieres que sea seleccionable por URL, y dale bandera/nombre en locale_meta() (utils.php) si quieres que aparezca en el selector.

Formatear fechas

format_date($datetime) // 'en' → m/d/Y H:i, 'es' → d/m/Y H:i

El selector de idiomas

A diferencia del sistema base de i18n, el selector viene conectado por defecto, pero permanece completamente inerte hasta que se visita una URL /xx/ o se pulsa una bandera — sin coste para un proyecto que nunca lo use.

# .env
APP_LOCALE=en
ACCEPTED_LANGUAGES=en,es
  • Visitar una URL con prefijo de 2 letras que tenga un diccionario correspondiente (/es/todo) fija ese idioma para la sesión y quita el prefijo antes de que continúe el enrutado normal — no hace falta duplicar rutas con segmento de idioma.
  • ACCEPTED_LANGUAGES controla qué prefijos están habilitados. Un prefijo cuyo diccionario existe pero no está en esa lista devuelve 404 — deliberado: puedes tener un diccionario en el que aún trabajas sin exponerlo.
  • Una vez elegido (prefijo de URL o bandera), el idioma se mantiene en sesión — el resto de enlaces no necesitan prefijo.
  • Si ACCEPTED_LANGUAGES no está definida en absoluto, el selector no se renderiza y los prefijos /xx/ no se tratan como prefijos de idioma — el proyecto es monolingüe, usando solo APP_LOCALE.
  • Las banderas no están escritas a fuego — includes/head.php recorre accepted_locales(), así que quitar un idioma del .env quita su bandera automáticamente. Añadir un idioma nuevo sigue necesitando una línea en locale_meta() (utils.php) para su emoji de bandera y nombre.

Limitación conocida: el primer segmento de una ruta no puede compartir nombre con un código de idioma habilitado que además tenga un archivo de diccionario correspondiente (por ejemplo, evita una ruta literal /es) — el router trata cualquier segmento de 2 letras con un lang/xx.json coincidente como prefijo de idioma antes del matching normal de rutas.

<html lang="...">

includes/head.php lo fija desde current_locale() automáticamente.

Clone this wiki locally