Lectura de archivos MO de GetText y simplificación de su uso con PHP

rosetta diskQue el mundo cada día es un lugar mas cercano y global es algo cada vez mas palpable, y que las traducciones de nuestras paginas son algo igualmente importante también es algo que cualquiera puede ver, así que últimamente he estado viendo como funcionan los sistemas de traducción que suelen usarse en uno de mis lenguajes de programación favoritos, el PHP. Y lo que he visto es que lo mas facil y rapido de usar es el sistema libre GetText.

Este sistema permite internacionalizar a partir de frases, que son buscadas par a par dentro de un archivo precomprimido. Este tipo de archivos son los de extensión MO, generados a partir de archivos que contienen los datos en texto plano (archivos con extensión PO).

Para que no se nos haga pesado usarlo he construido una función que simplifica bastante el trabajo.. se trata de la función gettext_simply().
Directorios de GetText en el FTPPara realizar traducciones correctamente tenemos que en primer lugar crear un árbol de directorios específico para nuestro proyecto. Esto quiere decir que tenemos que crear una carpeta que contenga las diferentes carpetas de lenguajes, (por defecto, lenguajes). Luego cada lenguaje debe tener una nomenclatura especifica indicada por el estándar ISO639, lo que quiere decir, si tu lenguaje es español e España, el código será es_ES, en cambio si es español de Argentina será es_AR
Dentro de esta carpeta debe haber una segunda carpeta llamada LC_MESSAGES y dentro de esta el archivo MO de traducción. Al final el directorio debería quedar como el de la derecha.. (lo de los archivos .PO es una manía mía para mantener el contenido ordenado para futuras actualizaciones..)

Una vez echo el árbol de directorios ejecutamos gettext_simply() para decidir con que archivo vamos a funcionar..
La función de gettext_simply() es la siguiente:
[PHP]
function gettext_simply($lang=’es_ES’, $mo_file=’traduccion’, $path_LC=’./lenguajes’, $debug=false){
$locale = setlocale(LC_ALL, $lang);
$deb[‘locale’] = $locale;
$deb[‘bindtextdomain’] = bindtextdomain($mo_file, $path_LC);
$deb[‘textdomain’] = textdomain($mo_file);
return $debug ? $deb : $locale;
}
[/PHP]

El primer valor es el acrónimo del lenguaje a usar (el nombre del primer directorio..).
El segundo, el nombre del archivo de traducciones, en nuestro caso y por defecto, traduccion.
El tercero, la localización del directorio que contiene las carpetas de lenguajes.
Y por ultimo lugar una variable boleana para ver si queremos la información de los diferentes pasos

Si usamos el mimo arbol de directorios del ejemplo de arriba y el mismo nombre en los archivos MO, cambiar el idioma de una pagina es realmente sencillo…
Para cambiar el idioma de una frase usamos la funcion _().
Aqui os dejo unos cuantos ejemplos de como traducir una frase simple “We cannot find a title on that page.” (teniendo esta frase traducida en los archivos MO necesarios y la estructura de directorios necesaria..)
[PHP]
//traduccion a español simple
gettext_simply(‘es_ES’);
echo _(‘We cannot find a title on that page.’);

//traduccion a argentino
gettext_simply(‘es_AR’);
echo _(‘We cannot find a title on that page.’);

//Carpetas personalizadas, nombres personalizados…
gettext_simply(‘es_ES’, ‘frases’, ‘/mis_idiomas_personalizados’);
echo _(‘We cannot find a title on that page.’);

gettext_simply(‘en_GB’, ‘frases’, ‘/mis_idiomas_en_ingles’);
echo _(‘We cannot find a title on that page.’);
[/PHP]

Otra de mis inquietudes al usar los archivos MO era que no se podían leer en tipo array, de manera que no se podía saber si una traducción existía a simple vista o contar cuantas traducciones hay cargadas..
Al buscar alguna respuesta por Google parece que lo que mas se usa es la clase PHP-Gettextque según vi es la misma que usa wordpress, y me aprecio un poco engorrosa, así que también me decidí a crear una función que lea simplemente los MO convirtiéndolos en array (alguien adivina como la he llamado?) la funcion es mo2array().
La función es simple.. se le introduce la dirección de un archivo .MO y da como resultado un array con todo el contenido del archivo estructurado.
[PHP]
function mo2array($archivo = ‘traduccion.mo’){
$file = file_get_contents($archivo, FILE_BINARY);

$i[‘magic’] = deco(substr($file, 0, 4), true); // extraigo el numero magico para verificar si es valido
if($i[‘magic’] != ‘DE 12 04 95’){ return array(‘error’=>’Archivo no valido, no contiene datos validos de internacionalizacion.’, ‘magic’=>$i[‘magic’]); }
$f=4; $ini=4;
$i[‘version’] = deco(substr($file,$ini,4),’d’); $ini+=$f;
$i[‘len’] = deco(substr($file,$ini,4),’d’); $ini+=$f; //total de frases traducidas
$i[‘ini_original’] = deco(substr($file,$ini,4),’d’); $ini+=$f;
$i[‘ini_traduccion’] = deco(substr($file,$ini,4),’d’); $ini+=$f;
$i[‘len_hash’] = deco(substr($file,$ini,4),’d’); $ini+=$f;
$i[‘ini_hash’] = deco(substr($file,$ini,4),’d’);

$o = str_split( substr($file, $i[‘ini_original’], $i[‘ini_traduccion’]), 4);
$t = str_split( substr($file, $i[‘ini_traduccion’], $i[‘ini_hash’] ), 4);

for($k=1; $k<=$i['len']; $k++){ if($k%2!=0){ #$i['table'][$k ]['o'] = substr( $file, deco($o[$k],'d'), deco($o[$k-1],'d') ); #$i['table'][$k ]['t'] = substr( $file, deco($t[$k],'d'), deco($t[$k-1],'d') ); $i['table'][substr( $file, deco($o[$k],'d'), deco($o[$k-1],'d') )] = substr( $file, deco($t[$k],'d'), deco($t[$k-1],'d') ); } } return $i; } function deco($data, $jumps=false){ //pasa el contenido binario a HEX y lo preformatea if($jumps==='h'){ $o = explode(' ', wordwrap(strtoupper(bin2hex($data)), 2, " ", 1) ); $o = $o[3].$o[2].$o[1].$o[0]; } elseif($jumps==='d'){ // bin2dec $o = explode(' ', wordwrap(strtoupper(bin2hex($data)), 2, " ", 1) ); $o = hexdec($o[3].$o[2].$o[1].$o[0]); } elseif($jumps===true){ $o = wordwrap(strtoupper(bin2hex($data)), 2, " ", 1); } else { $o = strtoupper(bin2hex($data)); } return $o; } [/PHP] La función mo2array() usa una segunda función de apollo llamada deco() que convierte entre material binario, hexadecimal y decimal..
En la salida de mo2array() puede dar un valor con key error si el archivo no ha sido encontrado o no es un archivo PO valido.. si todo va bien da como resultado varios valores de la estructura interna del archivo y un valor tabla, que contiene como keys en las traducciones originales, y como valores de esas key las traducciones resultantes.

Un ejemplo simple:
[PHP]
print_r( po2array(‘./lenguajes/es_ES/LC_MESSAGES/traduccion.mo’) );
[/PHP]

Para crear tu propio grupo de archivos PO y MO hay una infinidad de articulos que hablan sobre ello.. el programa mas utilizado es PoEdit y si solo quieres probar esto puedes experimentar con los que ya hay por la red.

4 opiniones en “Lectura de archivos MO de GetText y simplificación de su uso con PHP”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *