Internacionalización en PHP: crear aplicaciones multilingües robustas con Intl e ICU

Guía práctica para separar textos y código, seleccionar locales y gestionar catálogos, plurales, números, monedas y fechas con PHP Intl.

Internacionalización en PHP: crear aplicaciones multilingües robustas con Intl e ICU

Convertir una aplicación PHP en multilingüe no consiste solo en sustituir algunas cadenas por un array. Un sistema fiable separa el contenido de la lógica, elige idioma y región de forma predecible, formatea cantidades, monedas y fechas correctamente y mantiene un idioma alternativo cuando falta una traducción.

Internacionalización y localización

La internacionalización prepara el software para varios idiomas y convenciones culturales; la localización aporta textos y formatos para un público concreto. El código debe usar claves estables como checkout.total, mientras los catálogos contienen las traducciones. Una corrección editorial no debería exigir cambios en el código.

Activar Intl y definir locales

La extensión intl expone las funciones de ICU en PHP. En producción comprueba extension_loaded('intl') y la versión de ICU. Usa identificadores explícitos como es-ES. No dependas del valor predeterminado del servidor. Una lista permitida impide valores no compatibles y rutas manipuladas.

<?php
$supported = ['it-IT', 'en-GB', 'de-DE', 'fr-FR', 'es-ES', 'pt-PT'];
$requested = $_GET['lang'] ?? 'it-IT';
$locale = in_array($requested, $supported, true) ? $requested : 'it-IT';

$catalogue = require __DIR__ . '/lang/' . $locale . '.php';

function trans(string $key, array $vars = []): string {
    global $catalogue, $locale;
    $pattern = $catalogue[$key] ?? $key;
    $formatted = MessageFormatter::formatMessage($locale, $pattern, $vars);
    return $formatted === false ? $pattern : $formatted;
}

Los catálogos pueden ser archivos PHP o JSON, o datos de un servicio. Deben estar fuera de directorios públicos, validarse durante el despliegue y compartir un conjunto mínimo de claves. La estrategia de respaldo se define en un componente central.

Mensajes dinámicos y plurales

Concatenar fragmentos impone a todas las lenguas el mismo orden. MessageFormatter utiliza patrones ICU para que cada traducción controle el orden, las reglas de plural y los formatos insertados. Envía valores estructurados y registra los errores de Intl.

Números, monedas y fechas

Almacena datos neutrales: importes en unidades menores o formato decimal seguro, fechas en UTC y monedas como códigos ISO. La presentación se aplica solo al mostrar. NumberFormatter adapta separadores y símbolos, pero no convierte tipos de cambio. IntlDateFormatter aplica idioma, calendario y una zona horaria explícita.

<?php
$price = new NumberFormatter($locale, NumberFormatter::CURRENCY);
echo $price->formatCurrency(1299.90, 'EUR');

$date = new IntlDateFormatter(
    $locale,
    IntlDateFormatter::LONG,
    IntlDateFormatter::SHORT,
    'Europe/Rome'
);
echo $date->format(new DateTimeImmutable('2026-11-06 10:30:00'));

Elegir el idioma

Un orden práctico es preferencia guardada, ajuste de sesión validado, cookie firmada, Accept-Language y respaldo de la aplicación. La cabecera del navegador es una pista, no un segmento seguro para una ruta. Guarda el locale canónico y genera URL localizadas coherentes.

HTML, seguridad y SEO

Escapa los valores al renderizar según el contexto HTML, atributo o JavaScript. Las traducciones no son HTML confiable por defecto. Define el atributo lang, publica enlaces hreflang y mantén un mapa estable para slugs traducidos.

Pruebas y mantenimiento

Automatiza controles de JSON válido, claves ausentes, marcadores incompatibles y textos sin traducir. Prueba cero, uno y muchos elementos, además de una lengua con palabras largas. Fija la zona horaria en las pruebas y registra claves ausentes sin mostrarlas a los visitantes.

Conclusión

Una arquitectura multilingüe sólida separa lógica y contenido y delega en ICU el formato cultural. Con locales validados, catálogos versionados, respaldos explícitos y pruebas automáticas, añadir un idioma se convierte en una tarea editorial controlada.

Compartir

¿Cuál es tu reacción?

Me gusta Me gusta 0
No me gusta No me gusta 0
Me encanta Me encanta 0
Divertido Divertido 0
Enfadado Enfadado 0
Triste Triste 0
¡Vaya! ¡Vaya! 0