Internacionalización con Smarty

El temita de la internacionalización (i18n para los amigos) en una aplicación web puede dar para largo y tendido, yo voy a hablar aquí concretamente de la forma de guardar y utilizar las cadenas de texto del site en varios idiomas utilizando el framework de templates Smarty.

Hay muchas formas de hacerlo, se pueden encontrar varias alternativas en este post de best way to build a multi-language site with smarty, que empezó en el ¡2003! (el frenético ritmo que lleva la tecnología hace que no me crea nada que no sea de ayer…). Todas tienen sus ventajas e inconvenientes, y dado que no hay nada en el core de Smarty para ésto ni una solución popularmente aceptada como la estándar, cada uno debe analizar lo que necesita para su proyecto y tener unas preferencias subjetivas, que en mi caso son:

  • Que no implique PHP: obviamente hay opciones mucho más sofisticadas, eficientes, etc… para solucionar el tema del i18n con programación (por ejemplo smarty-gettext), pero precisamente el uso de Smarty es para separar la lógica de la aplicación de la de presentación, y dado que considero que ésto es problema de front-end puro, yo quiero resolverlo únicamente con Smarty.
  • Que sea sencillo y eficiente: con el tiempo me estoy conviertiendo en un talibán del código, en el sentido bueno (creo) de intentar que las cosas se hagan de la forma más clara posible (KISS power!) y que note un pinchazo en el corazón con cada ciclo de reloj que se consume en código prescindible.

Por ejemplo, hay una solución muy buena y que he utilizado en otros proyectos, SmartyML, pero es una clase más en PHP (una capa más para procesar) y viendo tanto código pienso que debe haber una forma mejor de hacerlo.

Pero vamos al turrón. Al final encontré en el foro antes mencionado la solución que me parece más sencilla y adecuada para lo que necesito. Se trata de utilizar archivos de configuración (1ª ventaja: es una funcionalidad core de Smarty, con lo que no hay que añadir más complejidad). En estos ficheros se definen variables globales para los templates y se pueden aplicar de varias formas:

// desde el PHP
$smarty->config_load('fichero.conf');

// desde el template
{config_load file='fichero.conf'}

Yo prefiero hacerlo desde PHP porque la gestión del idioma de la web es lógica de aplicación y hay que poner cada cosa en su sitio. El directorio donde se almacenan estos ficheros debe ser definido en el código:

$smarty = new Smarty();
$smarty->config_dir   = '/path/al/directorio/';

Crearemos un archivo por idioma, en los que tendremos todas las cadenas de texto que vamos a utilizar traducidas, por ejemplo:

# /path/al/directorio/es.conf (español)
saludo = hola!
enviar = Enviar
cancelar = Cancelar
# /path/al/directorio/en.conf (inglés)
saludo = hi!
enviar = Send
cancelar = Cancel

Utilizar estas variables en los templates es tan sencillo como usar la sintaxis {#variable#}, por ejemplo:

<strong>{#saludo#}</strong>
<input type="button" name="enviar" value="{#enviar#}" />
<input type="button" name="cancelar" value="{#cancelar#}" />

¡Y ya está! desde el PHP se puede controlar qué archivo de configuración de idioma cargar y de esta manera tener montada nuestra web multilenguaje. Unos últimos apuntes:

  • Las cadenas también pueden llevar texto variable, como por ejemplo un saludo del tipo Hola Fernando, bienvenido! Ésto se puede solucionar de dos formas:
    # forma CUTRE: en el tpl
    </span>{#saludo1#}{$nombre_usuario}{#saludo2#}</strong>

    # en el archivo de configuración de idioma
    saludo1 = Hola
    saludo2 = bienvenido! </div> </div>
    # forma CORRECTA: en el tpl
    </span>{eval var=#saludo#}</strong>

    # en el archivo de configuración de idioma
    saludo = Hola {$nombre_usuario}, bienvenido! </div> </div> </li>
  • En lo que entiendo es una optimizacion (supongo que a nivel interno se controlará), se pueden utilizar secciones (referenciadas así [seccion]) en el archivo de configuración, de manera que si se especifica una en el config_load sólo esas variables se cargarán (la que estén fuera de una sección estarán siempre disponibles). Ésto nos puede servir para organizar las cadenas de texto por página y suponer que Smarty lo manejará mejor que el chorro completo de variables. Quedaría así:
    # cadenas globales
    saludo = Hola!
    enviar = Enviar

    # página de login
    [login]
    titulo = Introduce tu usuario y contraseña

    Y desde el PHP:

    $smarty->config_load('es.conf','login');
    $smarty->display('login.tpl');
  • Una desventaja es que no maneja de manera nativa los plurales como otras soluciones, de forma que hay que controlarlo a manubrio, por ejemplo:
    # en el tpl
    {eval var=#subida_fotos#}{if $num_fotos > 1}s{/if}

    # en el archivo de configuración de idioma
    subida_fotos = Has subido {$num_fotos} foto
  • </ul>