Páginas multi idioma en PHP con gettext y POEdit

Los programadores que hacemos webs con programación a medida en PHP, cuando nos plantean hacer una página en múltiples idiomas, cada uno tiene una fórmula distinta para poner las palabras que salen en el interface de la web en los diferentes idiomas. La fórmula que yo utilizaba antes, es muy común entre los programadores: Los arrays. Se trata de tener arrays asociativos con todas las palabras que van en cada idioma, cada idioma en un fichero.

De esta forma si elijo el idioma "inglés", se carga el fichero "eng.php" que contiene un array con todas las palabras del interface en ese idioma, luego en la plantilla llamo al elemento del array correspondiente por su clave.
Este sistema está bien, pero recientemente me he cambiado a otro más efectivo y estándar: Los ficheros generados por POedit. Es el sistema que usa Wordpress y muchos otros CMS / Frameworks.

Lo primero es hacerse con el POEdit: http://www.poedit.net/ e instalarlo.
Ahora vamos a nuestra página multi idioma, y editamos todos los ficheros donde aparezcan cadenas de texto que van en múltiples idiomas.
Por ejemplo, encontramos en nuestro formulario de contacto algo como:

<label>Nombre y apellidos:</label>

Esto lo cambiamos por:

<label><?=_("Nombre y apellidos")?>:</label>

Efectivamente, toda cadena o conjunto de ellas que sean para traducir, las metemos en esta notación de PHP: _(), que es lo mismo que poner gettext(), vamos que si nos gusta más ponemos:

<label><?=gettext("Nombre y apellidos")?>:</label>

Una vez lo tenemos todo hecho, creamos un nuevo proyecto en POEdit: Archivo -> Nuevo catálogo.
Aquí especificamos varias cosas: En "Información del proyecto" rellenamos los datos. En "carpetas", escribimos en "directorio raíz" el path local raiz del proyecto de nuestra web, por ejemplo (en mac): "/Users/Enrique/Desktop/trabajos/nuevaweb/"
A continuación, debemos decir dentro de ese raíz, donde están las carpetas con los ficheros a ser traducidos. Para ello pinchamos en el botón "Nuevo" (el 2º icono) y ahí metemos las rutas a las carpetas, por ejemplo (en mac): "/Users/Enrique/Desktop/trabajos/nuevaweb/website".
En "Palabras Clave", lo dejamos como está porque por defecto ya trae el "_" o el "gettext" que hemos usado.
Le damos a Aceptar y ya tenemos el proyecto creado. Ahora pinchamos en "Actualizar", y POEdit escaneará las carpetas que hemos especificado buscando los "_()" o "gettext()".
Una vez termine, nos mostrará todas las palabras que ha encontrado. Ahora prodecemos a traducir una por una.
Una vez hemos terminado, le damos a "Guardar", y POEdit crea dos ficheros: "default.mo" (fichero compilado) y "default.po" (fichero editable).
El "mo" lo subimos a: "nuevaweb/website/locale/es_ES/LC_MESSAGES/" donde "es_ES" sería la cadena del idioma que corresponda. La carpeta "website" sería la raiz de la web en sí, donde estaría el "index.php".
Cada vez que pongamos nuevas cadenas o modifiquemos cadenas existentes en nuestra web, hay que volver a generar los "po" y "mo" y subir el "mo". Para ello abrimos el fichero "po" con el POEdit, escaneamos, el programa encuentra todas las palabras nuevas o modificadas, escribimos las nuevas traducciones, guardamos, y subimos el "mo" generado.

Ahora para que nuestra web en PHP cargue esos idiomas, debemos hacer lo siguiente:
Al inicio de cada script PHP, con un include o con la fórmula que se quiera, obtendremos el idioma escogido. El idioma lo debemos tener en este formato: "es_ES".
Entonces ponemos el siguiente código:

$locale="es_ES";
putenv("LC_ALL=$locale");
setlocale(LC_ALL, $locale);
bindtextdomain("default", "./locale");
textdomain("default");

Y ya está, en principio. Digo en principio, porque tenemos un problema: El caché del servidor.

El problema del caché

Nuestro fichero "mo" se queda en la memoria del servidor, esto está muy bien porque hace que las traducciones sean rapidísimas, pero nos crea un gran problema: si modificamos un fichero de idiomas y lo subimos, no veamos los cambios inmediatamente, puesto que el antiguo está "cacheado" en la memoria. Una solución es resetear el Apache, pero esto no lo podemos hacer la mayoría de las veces. Para solucionarlo, usamos este codigo en vez del anterior:

 function translation($lang) {
  $rname="default";
  if (GETTEXT_CACHING == 0) {
       $targetFolder = "locale/".$lang."/LC_MESSAGES/";
       $folder = opendir($targetFolder);
       while (false !== ($file = readdir($folder))) {
            $pathFiles = $targetFolder."/".$file;
            if ($file != ".." AND $file != "." AND !is_dir($file) AND strrchr($file,'.') == '.mo' AND $file != $rname.'.mo') {
              unlink($pathFiles);
            }
        }
        closedir($folder);
        $translatefile = $rname.time();
        copy('locale/'.$lang.'/LC_MESSAGES/'.$rname.'.mo','locale/'.$lang.'/LC_MESSAGES/'.$translatefile.'.mo');
        } else $translatefile = $rname;
      
        setlocale(LC_ALL, $lang);
        putenv("LC_ALL=".$lang);
        bindtextdomain($translatefile,'./locale');
        bind_textdomain_codeset($translatefile, 'utf8'); 
        textdomain($translatefile);
}

translation($locale);

Lo que hace es duplicar el fichero "mo" con otro nombre y usar ese nuevo fichero, con esto siempre usaremos una versión nueva no cacheada.
Hay que darle permisos de escritura a la carpeta "LC_MESSAGES". Evidentemente esto lo mantendremos solo en tiempo de desarrollo, una vez la web sea definitiva, el cache ya no nos importará y usaremos el primer código de 5 líneas.

4 comentarios
  • Pep
    20-08-2012 15:36
    I prefer assoc arrays, less job
  • Enrique Gonzalez
    09-09-2012 10:20
    Sí, la verdad es que al final los assoc dan menos trabajo al programador, pero los PO son mas faciles de modificar para el usuario
  • Sergi
    15-03-2013 10:22
    Hola buenas enrique, me preguntava si esto funciona igual en strings guardadas en bases de datos (mysql), pues estoy remodelando una web ya hecha en un solo idioma (básicamente los textos estan en el mysql) para soportar 4 idiomas diferentes, no se realmente como ponerlo en practica, los PO parecen mi solución, agradeceria cualquier ayuda prestada y gracias por su esfuerzo en esta web.
  • Summer
    30-10-2013 14:50
    ¡Hola! Si estan interesados en localizar cualqier tipo de software, reccomendo con calor este rápido y intuitiva herramienta de localización: http://poeditor.com/.
Deja un comentario
He leido y acepto la Política de Privacidad

Los comentarios enviados por cada usuario serán siempre visibles en el post correspondiente, y no serán utilizados para ningún otro fin. El usuario tiene derecho a acceder, rectificar y suprimir dichos comentarios, tal como se refleja en nuestra Política de Privacidad