El soporte para múltiples idiomas o lenguajes es una gran característica a la hora de desarrollar aplicaciones web con PHP, pues así podemos tener sitios traducidos sin mucho esfuerzo.
Aunque PHP soporta de manera nativa opciones como gettext
para aplicaciones multilenguaje siempre vamos a necesitar una opción más robusta y simple.
Por ello es que hoy vengo a exponer un enfoque de cómo desarrollar aplicaciones multiidoma con PHP utilizando un componente de Symfony (no el framework, solo un componente que se puede integrar a cualquier proyecto) llamado translation
.
Para traducir mensajes podemos instalar el módulo de Symfony que funciona de manera aislada. Para ello utiliza Composer. Si no usas composer mira aquí cómo instalarlo, y aquí cómo adaptarlo.
Una vez que lo tengas, instala con:
composer require symfony/translation
Espera a que se instale y luego, en el archivo en donde vas a usar la app, carga el autoload:
<?php
include_once "vendor/autoload.php";
Estamos listos.
Comenzamos creando un traductor, instanciando un Translator
pasándole el locale o idioma en el que se mostrarán los mensajes.
Es decir, aquí es en donde indicamos el idioma del usuario (puedes obtenerlo como se ve en este post o usar tus propios métodos)
<?php
$idioma = "es";
$traductor = new Translator($idioma);
Ahora a ese traductor le vamos a agregar recursos de traducción usando catálogos. Para comenzar vamos a usar el loader de arreglo, es decir, nuestras traducciones estarán en un arreglo así que lo agregamos así: (más tarde veremos cómo cargar las traducciones desde un fichero)
$traductor->addLoader("array", new ArrayLoader());
Eso agregó el loader, pero no ha agregado catálogos. Para agregarlos usamos:
<?php
$traductor->addResource("array", [
"saludo" => "Hola, mundo",
], "es");
$traductor->addResource("array", [
"saludo" => "Hello world!",
], "en");
Los argumentos de addResource
son(en orden): tipo de loader, datos y locale o idioma. En este caso estoy usando es
y en
pero podrían ser más específicos como es_US
, en_US
, es_MX
, etcétera.
Solo estoy definiendo el mensaje de saludo, que en inglés es Hello world! y en español es Hola mundo.
Finalmente definimos los idiomas fallback, es decir, los idiomas que se usarán en caso de que el idioma del traductor (el que especificamos al inicio) no encuentre una traducción:
<?php
$traductor->setFallbackLocales(["es"]); // Si no se encuentra el idioma, utilizamos es por defecto
Finalmente obtenemos el mensaje traducido accediendo a su clave:
<?php
$saludoTraducido = $traductor->trans("saludo");
echo $saludoTraducido;
Así que el código completo queda así:
<?php
include_once "vendor/autoload.php";
use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\Translator;
$idioma = "es";
$traductor = new Translator($idioma);
$traductor->addLoader("array", new ArrayLoader());
$traductor->addResource("array", [
"saludo" => "Hola, mundo",
], "es");
$traductor->addResource("array", [
"saludo" => "Hello world!",
], "en");
$traductor->setFallbackLocales(["es"]); // Si no se encuentra el idioma, utilizamos es por defecto
$saludoTraducido = $traductor->trans("saludo");
echo $saludoTraducido;
Lo que realmente cambiará la salida es el idioma que especificamos en $idioma
en la línea 5. Si uso es
, la salida es:
En cambio, cuando uso en
, la salida es:
Así que en resumen hay que indicar el idioma al instanciar el traductor, este idioma lo puedes obtener de las preferencia de idioma del usuario, usar algún método de detección, ver la ip, etcétera.
Además de los arreglos podemos usar ficheros yaml, csv, json, entre otros. Mostraré un ejemplo para usar JSON. Voy a definir mis catálogos de idiomas.
El de español queda así:
{
"saludo": "Hola mundo",
"bienvenido": "Bienvenido, {usuario}"
}
El de inglés así:
{
"saludo": "Hello world",
"bienvenido": "Welcome, {usuario}"
}
Si te fijas es un objeto o diccionario en donde la clave es el identificador del mensaje y el valor es el mensaje.
Ahora vamos a ver el código que es muy parecido al anterior, aunque un poco más corto. En lugar de cargar un loader de Array, cargamos un JsonFileLoader
:
<?php
$traductor->addLoader("json", new JsonFileLoader());
Y agregamos los catálogos:
<?php
$traductor->addResource("json", "idioma_es.json", "es");
$traductor->addResource("json", "idioma_en.json", "en");
Los argumentos son los mismos: el loader, los datos y el locale.
Nota importante: los archivos deben existir en el mismo directorio, y si no, asegúrate de escribir su ruta absoluta. Si la ruta es errónea fallará silenciosamente, es decir, no reportará errores y por lo tanto usará el idioma fallback.
Así que el código completo para tener varios idiomas en PHP usando archivos de JSON es el siguiente:
<?php
include_once "vendor/autoload.php";
use Symfony\Component\Translation\Loader\JsonFileLoader;
use Symfony\Component\Translation\Translator;
$idioma = "en";
$traductor = new Translator($idioma);
$traductor->addLoader("json", new JsonFileLoader());
$traductor->addResource("json", "idioma_es.json", "es");
$traductor->addResource("json", "idioma_en.json", "en");
$traductor->setFallbackLocales(["es"]); // Si no se encuentra el idioma, utilizamos es por defecto
$saludoTraducido = $traductor->trans("saludo");
echo $saludoTraducido;
Como ves, todo es igual, excepto la carga de recursos, pero fuera de eso seguimos invocando al método trans
.
Si te fijas bien, en los ejemplos he definido un mensaje de bienvenida. En inglés es:
Welcome, {usuario}
Y en español es:
Bienvenido, {usuario}
Al traducir mensajes con este componente podemos especificar variables. He definido la variable en llaves {} porque quiero, pero no es obligatorio (solo que así se entiende mejor la intención del mensaje) ya que se hace un remplazo completo.
Para pasar argumentos se hace lo siguiente:
<?php
$saludoTraducido = $traductor->trans("bienvenido", [
"{usuario}" => "parzibyte",
]);
Como ves, en el segundo argumento indicamos un arreglo con los valores que se van a remplazar dentro de la cadena. Ahora sí, en inglés se ve así:
Y en español:
Por cierto, el código completo es el siguiente:
<?php
include_once "vendor/autoload.php";
use Symfony\Component\Translation\Loader\JsonFileLoader;
use Symfony\Component\Translation\Translator;
$idioma = "es";
$traductor = new Translator($idioma);
$traductor->addLoader("json", new JsonFileLoader());
$traductor->addResource("json", "idioma_es.json", "es");
$traductor->addResource("json", "idioma_en.json", "en");
$traductor->setFallbackLocales(["es"]); // Si no se encuentra el idioma, utilizamos es por defecto
$saludoTraducido = $traductor->trans("bienvenido", [
"{usuario}" => "parzibyte",
]);
echo $saludoTraducido;
Aunque lo veas complejo, no lo es tanto; pues podrías encerrar esto en una función o servicio y llamarlo desde otro lugar.
Recuerda: es tu deber identificar el idioma del usuario, puedes leerlo desde una base de datos, un parámetro GET, detectarlo, etcétera.
Esto que te mostré es un panorama general, si quieres ver la documentación oficial aquí la tienes.
Puedes ver el proyecto completo con todos los ejemplos en mi GitHub.
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.