Traducir plugins y themes en WordPress

traduzioniEs una práctica muy sana el preparar nuestros plugins y themes para el multilenguaje, sobre todo si nuestra intención es liberarlos al público, o, con más importancia aun, venderlos.

Probablemente, has encontrado esta información “tarde“, y ahora te toca un laborioso trabajo para adaptar todo tu plugin a un sistema multilenguaje, pero afortunadamente, la próxima vez ya lo tendrás en cuenta.

Lo primero de todo es saber que no hay ningún sistema milagroso que detecte todas y cada una de las cadenas de texto simple que aparecen en tu plugin sin tratarlas primero. Lo dudo mucho. Si existe, lo desconozco, y si además funciona bien, mis respetos y mi admiración al creador porque es un currazo.

Lo que sí existe es un sistema de “detección y traducción” al que podemos ayudar. ¿Cómo? Escribiendo correctamente todas las cadenas simples del plugin. He aquí el problema: si este tutorial te lo has encontrado con tu plugin ya terminado, mentalizate de que tienes que cambiar todas las cadenas de texto simple que tengas. Me refiero al texto “fijo”: encabezados, botones, formularios, nombres de tipos personalizados… todo aquello que sea fijo, que no sea contenido dinámico que se recupera desde la base de datos de WordPress.

Estas cadenas se llaman como parámetros de una función. Por eso es tan importante que lo hagamos directamente mientras estamos desarrollando, porque si no luego hay que encontrar todas las cadenas y aplicarles la función, y es un engorro.

Funciones de traducción

Vamos a preparar el entorno de traducción. Ahora vamos a definir qué cadenas se traducen, no la traducción de cada cadena; ese es el paso final. Dentro de nuestro plugin, cualquier cadena que deba ser traducida hay que pasarla como parámetro a una de estas funciones:

  • __( “Cadena de texto”, “ambito” ): La función es “__” (dos guiones bajos). El primer parámetro es el que queremos traducir. El segundo es el ámbito de la traducción. Esto es importante: todas las cadenas de nuestro plugin deben tener el mismo ámbito. Se recomienda que el ámbito sea una cadena simple, única, fácil de recordar, en minúsculas, sin espacios ni caracteres especiales.
  • _e( “Cadena de texto”, “ambito” ): Esta función es igual que la anterior, pero además, imprime la cadena con un echo.
  • _x( “Cadena de texto”, “Contexto”, “ambito”): La diferencia de esta función es que añade un contexto. Este contexto es información para el traductor. En ocasiones, puede ocurrir, que nos encontremos una palabra que según el contexto signifique una cosa u otra. Sin embargo es la misma palabra, y su traducción va a ser única. ¿Cómo remediamos esto? Con _x, añadiendo el contexto de la palabra, para que su traducción sea la correcta. La función _ex funciona igual, pero imprime la cadena.
  • _ex( “Cadena de texto”, “Contexto”, “ambito”): Por simple combinación, si _e imprime la cadena traducida, y _x añade un contexto a la cadena, _ex imprime la cadena en el contexto adecuado.
  • _n( $singular, $plural, $numero, “ambito”): Muy útil si tenemos texto del tipo “1 comentario” – “7 comentarios”, que según si es singular o plural muestra un texto u otro. El parámetro $singular se compone de la cadena a mostrar si $numero es 1; $plural tiene la cadena a mostrar si $numero es distinto de 1; $numero es el número a comparar, y ámbito funciona igual que antes.
  • _nx( $singular, $plural, $numero, “Contexto”, “ambito”): Esto ya debería ser muy fácil de comprender, simplemente añade un contexto a estas traducciones. Ni _n ni _nx imprimen la cadena.

Lo bueno de estas funciones es que buscan dentro de las traducciones disponibles si existe una traducción para la cadena de texto dentro del ámbito establecido (y dentro del contexto si fuese necesario). Si no la encuentra, imprime directamente la cadena de texto tal y como aparece en la función. De forma que si no hay traducción para “Cadena de texto”, imprimirá “Cadena de texto” en todos los idiomas (lo que nos facilita la búsqueda de cadenas sin traducir).

Ejemplos

Vale, vale, ya prometo que dejo de hablar y empezamos a verlo en práctica, sé que todo esto al principio es muy lioso, pero al final lo más probable es que sólo uséis una o dos de las funciones de arriba, como mucho (__ y _e).

<?php

$traduccion = __("Hola Mundo","miplugin");
// Ahora podemos mostrar $traduccion con echo:

echo $traduccion;

// También podíamos haber hecho esto directamente con el mismo resultado:
echo __("Hola Mundo","miplugin");

// O usar la funcion _e
_e("Hola Mundo","miplugin");

?>

Recordad que de la traducción nos ocuparemos luego. Ahora estamos preparando el entorno.

Antes de seguir con los ejemplos, os daré un consejo. Algunos pensaréis que es de lógica, pero creedme cuando os digo que he visto casos peores. Es muy recomendable que las etiquetas HTML se queden fuera de las funciones de traducción. De lo contrario, probablemente tengamos más de un error.

<?php

// No hagáis esto:
__("<span class='miclase'>Cadena para traducir</span>","miplugin");

// Es mejor que hagáis esto:
echo("<span class='miclase'>".__("Cadena para traducir","miplugin")."</span>");

// O incluso salirnos del PHP:
?>
<span class="miclase"><?=__("Cadena para traducir","miplugin");?></span>

Hay veces en que es inevitable traducir cadenas en las que una parte (usualmente, un número) nos lo da el código de forma dinámica. ¿Cómo traducimos entonces esto? En ese caso, usaremos sprintf:

<?php

sprintf( _n("%s voto","%s votos",$num,"miplugin"), $num);

?>

Analicemos esto por partes, que puede conducirnos a error. La función sprintf va a imprimir por pantalla el contenido de su primer parámetro, es decir, la función _n; a su vez, la función _n, según el contenido de $num, decidirá si se imprime la cadena en singular o en plural. Estas cadenas llevan un valor dinámico: %s. Este valor se toma desde el segundo parámetro de la función sprintf, que también es $num.

En definitiva, que $num se usa dos veces: la primera, dentro de la función _n, para decidir si se imprime la cadena en singular o en plural, y la segunda, como parámetro de sprintf, para reemplazar a %s en las cadenas dentro de _n. Es un poco lioso, pero leedlo de nuevo y lo veréis más claro.

Ya os lo he dicho, y supongo que os lo imagináis, pero sí: hay que pasar todas las cadenas de texto por las funciones anteriores. Es un trabajo tedioso que podemos evitar en parte si al crear el código por primera vez ya establecemos estas funciones (por eso digo que para el próximo que tengáis que hacer no se os olvidará). Nos guste o no nos guste, si no hacemos esto, ya podemos tener a C-3PO y sus más de seis millones de formas de comunicación, que no podremos traducir nuestro plugin o Theme.

Traduciendo

Bien, supongamos que hemos terminado de aplicar las funciones anteriores a todas las cadenas. Ahora querremos traducirlas, claro.

La mejor manera de hacer esto es usando un programa gratuito llamado Poedit. Es ligero, multiplataforma, fácil de instalar y no tiene mucha complicación. Podéis descargarlo aquí.

Ahora vamos a trabajar con nuestro plugin en local, en nuestro equipo, así que si tienes tu plugin en desarrollo subido al hosting donde tienes tu WordPress, deberías descargartelo por FTP.

Abrimos Poedit y pulsamos en Archivo > Catálogo Nuevo. Si ya tenemos el plugin en WordPress.org, podemos generar el archivo .pot con todas las cadenas para traducir. En ese caso, pulsaremos en Archivo > Catalogo Nuevo desde un archivo POT. Se abrirá una ventana con tres pestañas:

  • La primera, Propiedades de Traducción, es referente a la información del traductor. Puedes rellenarla con tus datos o no hacerlo.
  • La siguiente es “Directorios fuente”. En esta pestaña definimos qué archivos origen tiene que leer poedit para extraer las traducciones. Nosotros solemos guardar los archivos de traducción en una carpeta /lang dentro del directorio de nuestro plugin, de modo que tenemos que subir un nivel en el árbol de directorios. Lo más sencillo es pulsar el botón “Elemento Nuevo” (el segundo botón de los cinco que hay, y al principio el único disponible, tiene forma de cuadrado con linea de puntos) y añadir . y ..
    Captura01
  • Finalmente, en la pestaña “Palabras clave originales”, debemos establecer las funciones de traducción que hemos usado en nuestro plugin. ¿Por qué debemos hacerlo? Porque WordPress las reconoce como propias para aplicar traducciones, pero Poedit no, es un programa de uso general, y de esta forma le indicamos qué debe considerar “traducible”. En el mismo botón de antes, “Elemento Nuevo”, añadimos “__”, “_e”, “_x”, “_ex”, “_n” y “_nx”.
    Captura02

Tras pulsar Aceptar veremos el cuadro de “Guardar como”. Navegaremos hasta la carpeta /lang de nuestro plugin (si no existe, la creamos) y estableceremos de nombre el mismo ámbito que hemos puesto hasta ahora en todas las funciones (en nuestro caso de ejemplo, “miplugin.po”). Es recomendable guardarlo de forma que sepamos que este nuestro archivo “limpio”, para poder luego hacer copias de él (por ejemplo, “miplugin-default.po”).

Esto genera dos archivos: miplugin.po y miplugin.mo. El .po podemos editarlo. El .mo contiene información importante: si se edita el .po, Poedit guarda información en el .mo automáticamente, de forma que uno sin el otro no pueden funcionar. Cada idioma tiene un .po y un .mo. Podemos hacer copias de los archivos limpios para cada idioma, sustituyendo “default” por el codigo de idioma correspondiente. Tenéis una lista de códigos de idioma justo aquí.

Hecho esto, abrimos Poedit con el archivo .po (si es que no se nos ha abierto el .po al crearlo) y ya podemos empezar a editar.

Implementando localización en el plugin

Evidentemente todo esto no funciona sin un nexo entre código y traducciones. Es muy simple. Solo hay que añadir estas lineas a nuestro archivo principal del plugin, o a functions.php si estas traduciendo un Theme:

<?php

function miplugin_aplicar_traduccion(){
	load_plugin_textdomain( 'ambito', false, dirname( plugin_basename(__FILE__) ) ."/lang" );
}
add_action('plugins_loaded','miplugin_aplicar_traduccion');

?>

Es importante establecer el ámbito correcto, así como dirigir a la carpeta de traducciones correcta (la nuestra es /lang), y que en el add_action se llame a la función que acabamos de crear.

Actualizando nuestra traducción

Estupendo, ya tienes tu plugin traducido. Pero de pronto un buen día surgen bugs, o una nueva funcionalidad, o varias funcionalidades. En fin, cosas que te llevan a tener que incluir o eliminar cadenas de texto (esta vez usando correctamente desde un principio las funciones de traducción), que evidentemente no aparecen por arte de magia en los archivos .po y .mo.

Captura03Esto es muy simple de arreglar. Basta con volver a descargar nuestro plugin a nuestro ordenador y abrir los archivos .po (sí, uno por uno, pero al fin y al cabo, también tienes que aplicar las traducciones de las cadenas nuevas una a una, ¿No?). En esta ocasión utilizaremos el botón “Actualizar” de Poedit. Rastreará los archivos en busca de nuevas cadenas y mostrará una ventana con las nuevas cadenas y las cadenas obsoletas. Tras aceptar, ya podremos ponernos a traducirlas.

Y aquí concluye este tutorial sobre la implementación de traducciones en plugins y Themes de WordPress. No es un proceso complicado en sí mismo, es tedioso sólo cuando no aplicas las reglas desde el principio. Para cualquier duda, siempre estamos disponibles en nuestro cuadro de comentarios, justo debajo de esta entrada.

“No me culpes. Solo soy un intérprete. No sabia que el
enchufe de energía llegaba a esta terminal de computadora”

.- C3PO, después de freír a R2D2

¿Te ha gustado el artículo?
Sé el primero en calificar esta publicación.
1 estrella2 estrellas3 estrellas4 estrellas5 estrellas
Loading...
Artículo escrito por

¡Exprésate! Dejanos tu comentario

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

2 Comentarios

Tomás Guiloff

Estimado, estoy traduciendo una página web con WPML.  No se de programación, por lo que necesito ayuda más puntual, por lo que ruego que pueda ayudarme. He leído atentamente el artículo pero no he podido realizar lo que debo hacer.  Esto es, traducir ciertas cosas que están "incrustadas" en el tema de mi wordpress.  Son ciertos botones y plantillas (cómo aquellos de contacto, trabaja con nosotros, etc) que si bien los puedo localizar en los temas, no veo como traducirlos.  Me complica la parte que debo agregar funciones al tema para luego aíslar las cadenas. Espero tu ayuda.  Muchas gracias de antemano, Tomás

Juan Antonio

Buenas tardes, He leido detenidamente su articulo, ya que ando intentando traducir un plugin que tengo instalado en mi wordpress. Este plugin contiene una carpeta /language y en ella leagueengine.po He seguido sus pasos para poder actualizar el idioma. Desactivo el plugin, lo vuelvo activar y sigue sin hacerme ninguna traducción. tengo un archivo principal que se llama index.php  en el cual se carga: /***************************************************************/ /* SETUP /***************************************************************/ $plugin = plugin_basename(__FILE__); add_action('init','leagueengine_init'); add_action('admin_menu','leagueengine_menu'); register_activation_hook(__FILE__,'leagueengine_install'); /***************************************************************/ /* INCLUDE FILES /***************************************************************/ include('assets/includes/install.php'); include('assets/includes/functions.php'); include('assets/includes/shortcodes.php'); include('assets/includes/widgets.php'); /***************************************************************/ /* INIT /***************************************************************/ function leagueengine_init() { load_plugin_textdomain('leagueengine', false, dirname(plugin_basename(__FILE__)) . '/assets/languages/'); }   En principio entiendo yo que el "add_action('init','leagueengine_init'); " ya lo hace correctamente y carga la función, pero no entiendo porque cuando modifico el archivo "leagueengine.po" no funciona.   Lo he intentado tanto con el programa Poedit (subiendo a la carpeta del FTP los dos *.po y *.mo) y con uno Plugin llamado Translation Management (Powered by Loco)   No sé si me puedes echar una mano.   Gracias de antemano un saludo!!!   P.D: Muy bueno tu articulo