19. Internacionalización

NOTA PREVIA: Permitidme un salto intempestivo en la serie de artículos The WP Functions Library, pero os explico el porqué. Veréis, en mi estudio de los temas WP, me salté el apartado de internacionalización. Ahora, quiero que mi plugin esté disponible para cualquiera, sea de donde sea. Por tanto, debo permitir su traducción a cualquier idioma. Esto también será interesante, porque quien haya seguido la serie verá cuáles son los cambios y cómo se realizan.

 

Internacionalización

¿Qué es internacionalización?

Internacionalización es el proceso de desarrollar tus plugins de tal manera que sea fácilmente traducido a otros idiomas. La internacionalización, a menudo, se abrevia como i18n (porque hay 18 letras entre la i y la n en la palabra internationalization).

 

¿Por qué es importante la internacionalización?

WordPress se utiliza en todo el mundo. Entonces, las cadenas de los plugins de WordPress necesitan ser codificadas de una manera especial que permita traducirlas a otros lenguajes. Como desarrollador, no tienes tiempo en proveer localizaciones para todos tus usuarios; en cambio, cualquier traductor puede fácilmente localizar el plugin sin necesidad de modificar el código fuente.

 

Cómo internacionalizar tu plugin

Para hacer una cadena traducible en tu aplicación, debes envolver la cadena original en una llamada a una de las funciones especiales.

 

Introducción a gettext

WordPress utiliza las librerías y herramientas gettext para i18n. Si miras código, verás la función _() que refiere a la función nativa PHP de traducción. Con WordPress deberás utilizar la función __() PHP definida por WordPress. Si quieres tener un conocimiento más amplio y profundo de gettext, recomendamos la lectura del Manual online gettext.

 

Dominios de texto

Es importante utilizar un dominio de texto para indicar todo el texto del plugin. El dominio de texto es un identificador único, que asegura que WordPress puede distinguir entre todas las traducciones cargadas. Ello aumenta la portabilidad y funciona mejor con las herramientas de WordPress existentes. El dominio de texto debe hacer juego con el slug del plugin. Si tu plugin es un único fichero llamado my-plugin.php o está ubicado en un directorio llamado my-plugin, el nombre de dominio debe ser my-plugin. El nombre del dominio de texto utiliza guiones y no subrallados.

Ejemplo:

__( ‘String (texto traducible)’, ‘dominio-texto’ ); // donde dominio-texto se puede cambiar a my-plugin.

El dominio de texto también necesita ser añadido a la cabecera del plugin. WordPress lo utiliza para internacionalizar los metadatos de tu plugin cuando está desactivado. El dominio de texto debe ser el mismo que el utilizado cuando se carga el dominio de texto.

 

Ruta de dominio

La ruta de dominio se utiliza para que WordPress sepa donde encontrar la traducción cuando el plugin está desactivado. Sólo es útil si las traducciones están localizadas en un subdirectorio de idioma separado. Por ejemplo, si los ficheros .mo están localizados en el directorio local, entonces la ruta de dominio será “/languages” y debe tener el primer slash (/):

 

Cadenas básicas

El uso más frecuente es __(). Devuelve la traducción del argumento:

Otro uso simple es _e(), el cual muestra la traducción del argumento.

 

Variables

Si utilizas variables en cadenas como el siguiente ejemplo, debes utilizar marcadores de posición.

echo ‘Your city is $city.’;

La solución es utilizar la familia de funciones printf. Especialmente útiles son printf y sprintf. La solución correcta es como:

Observa que aquí la cadena de traducción es la plantilla “Your city is %s.”.

Si tienes más de un marcador de posición en una cadena, es recomendable que utilices intercambio de argumentos. En este caso, las comillas simples (‘) tienen prevalencia: las comillas dobles (“) indican a PHP a interpretar $s como la variable s, que no es lo que pretendemos.

NOTA: El siguiente código es incorrecto:

Las cadenas para traducir se extraen de las fuentes, luego los traductores tomarán esta frase para traducir: “Your city is $city.”.

 

Plurales

Pluralización básica.

Si tienes una cadena que cambia cuando el número de ítems cambia. En inglés tienes “One comment” y “Two comments”. En otros idiomas tienes múltiples formas de plural. Para gestionar esto en WordPress puedes utilizar la función _n().

_n() acepta 4 argumentos:

  • singular – la forma singular de la cadena
  • plural – la forma plural de la cadena
  • count – el número de objetos, lo que determina si se devolverá la forma singular o plural.
  • dominio de texto del plugin.

El valor devuelto de las funciones es la forma correcta traducida, correspondiente al cómputo dado.

 

Pluralización posterior

Primero selecciona las cadenas plurales con _n_noop()_nx_noop().

Luego, en un punto posterior del código, puedes utilizar la función translate_nooped_plural() para cargar las cadenas.

 

Desambiguación por contexto

En ocasiones un término es utilizado en varios contextos y a pesar de que es uno y la misma palabra en inglés, tiene diferentes traducciones en otros idiomas. Por ejemplo, la palabra post puede utilizarse como enviar (“Click here to post your comment”) y como artículo (“Edit this post”). En ambos casos las funciones _x()_ex() se pueden utilizar. Son similares a __()_e(), pero tienen un argumento adicional, el contexto:

Utilizar este método en ambos casos tendremos la cadena Comment para la versión original, pero los traductores verán dos cadenas Comment para traducir, cada una en diferentes contextos.

Observa que parecidamente  a __()_x() tiene una versión echo: _ex(). El ejemplo anterior se puede escribir así también:

 

Descripciones

Para que los traductores sepan cómo traducir una cadena como __( ‘g:i:s a’ ) puedes añadir un comentario clarificador en el código fuente. Debe empezar con la palabra translators: y ser el último comentario PHP antes de la llamada gettext. Veamos un ejemplo:

También se utilizan para explicar marcadores de posición en una cadena como:

 

Caracteres de línea nueva

Gettext no quiere \r (código ASCII 13) en cadenas a traducir, por ello emplea en su lugar \n.

 

Cadenas vacías

Las cadenas vacías están reservadas para usop interno de gettext y debes evitar internacionalizar cadenas vacías. No tiene sentido, porque los traductores no encuentran el contexto.

Si tienes un caso válido para internacionalizar una cadena vacía, añade contexto tanto a la ayuda a traductores y estate en paz con el sistema gettext.

 

Manejo de ficheros JavaScript

Utiliza wp_localize_script() para añadir cadenas traducibles a scripts previamente añadidos.

 

Caracteres de escape

Es recomendable escapar todas tus cadenas, de esta manera los traductores no podrán ejecutar código malicioso. Hay unas cuantas funciones de escapado que están integradas con las funciones de internacionalización.

 

Funciones de localización

Más adelante se explican.

 

Funciones básicas

__()
_e()
_x()
_ex()
_n()
_nx()
_n_noop()
_nx_noop()
translate_nooped_plural()

 

Funciones de escape

esc_html__()
esc_html_e()
esc_html_x()
esc_attr__()
esc_attr_e()
esc_attr_x()

 

Funciones de fecha y numéricas

number_format_i18n()
date_i18n()

 

Buenas prácticas para escribir cadenas

Aquí están las buenas prácticas para escribir cadenas:

  • Utilizar un estilo decente.
  • Emplear sentencias enteras.
  • Divide con párrafos. Une sentencias relacionadas, pero no incluyas una página entera de texto en una sola cadena.
  • Asume que las cadenas pueden doblar su longitud al ser traducidas.
  • Evita marcados o caracteres de control no habituales.
  • No incluyas marcado HTML innecesario en la cadena a traducir.
  • No incluyas URLs en cadenas a traducir. Pueden tener una versión en otro lenguaje.
  • Añade las variables como marcadores de posición en la cadena como en algunos idiomas los marcadores de posición cambian de posición.
  • Utiliza formatos de cadenas en lugar de concatenar cadenas – traduce frases, no palabras.
  • Intenta utilizar las mismas palabras y los mismos símbolos, para no necesitar traducir múltiples cadenas.

 

Añade dominio de texto a las cadenas

Debes añadir tu dominio de texto como un argumento a cada llamada __()_e()__n(), de otra manera tus traducciones no funcionarán.

Ejemplos:

Si hay cadenas en tu plugin que también son empleadas por el núcleo de WordPress (p.ej., ‘settings’), debes también añadir tu dominio de texto a las mismas, o de otra manera serán intraducibles si las cadenas del núcleo cambian (que pasa).

Añadir el dominio de texto a mano puede ser una carga si no se hace continuamente mientras se escribe el código y esto es porque puedes hacerlo automáticamente:

  • Si tu plugin está en el repositorio oficial de plugins, ve a tu página de “Administración” y baja a “Add Domain to Gettext Calls”.
  • Sube el fichero para el dominio de texto que quieres añadir.
  • Clica en “Get domainified file”.

De otra manera:

  • Descarga el escript add-textdomain.php a la carpeta donde está el fichero al que quieres añadir el dominio de texto.
  • En línea de comandos, ve al directorio en el que está el fichero.
  • Ejecuta este comando para crear un nuevo fichero con el dominio de texto añadido:
  • Si quieres tener el archivo add-textdomain.php en una carpeta diferente, debes definir la localización en el comando.
  • Utiliza este comando y no quieres crear un nuevo fichero.
  • Si quieres modificar múltiples ficheros en un directorio, puedes también pasar un directorio al script.

Después de hacerlo, el dominio de texto se añadirá al final de las llamadas gettext en el fichero. Si hay un dominio de texto previo, se mantendrá.

 

Cargar el dominio de texto

Necesitas cargar el fichero MO con tus traducciones del plugin. Puedes cargarlo mediante la función load_plugin_textdomain(). Esta llamada carga {text-domain}-{locale}.mo del directorio base de tu plugin. El ‘locale’ es el codigo de lenguaje y/o código de país del lenguaje seleccionado en ‘Ajustes generales’. Para más información acerca de idiomas y códigos de país, mira WordPress en tu idioma.

En el siguiente ejemplo, el dominio de texto es my-plugin, por lo tanto los ficheros alemanes MO y PO deben llamarse my-plugin-de_DE.mo y my-plugin-de_DE.po:

 

Packs de lenguaje

Si estás interesado en packs de lenguaje, lee Meta Handbook page about y Translations.

 

Localización

¿Qué es localización?

Localización describe el proceso siguiente de traducir un plugin internacionalizado. Localización se abrevia como l10n.

 

Ficheros de localización

  • Ficheros POT (Portable Object Template)

Este fichero contiene las cadenas originales (¿en inglés?) de tu plugin. He aquí una entrada ejemplo de un fichero POT:

  • Ficheros PO (Portable Object)

Cada traductor tomará el fichero POT y traducirá las secciones msgstr a su propio idioma. El resultado es un fichero PO con el mismo formato que un fichero POT, pero con traducciones y algunas cabeceras específicas. Existe un fichero PO por idioma.

  • Ficheros MO (Machine Object)

De cada fichero PO traducido, se crea un fichero MO. Son ficheros binarios, legibles por la máquina, que las funciones gettext actualmente utilizan (no se preocupan acerca de los ficheros POT o PO), y son una versión “compilada” de los ficheros PO. La conversión se da utilizando la herramienta msgfmt. En general, una aplicación puede utilizar más de un gran módulo lógico traducible y un fichero diferente MO acorde. Un dominio de texto se encarga de cada módulo, que es un fichero MO diferente.

 

Generar el fichero POT

El fichero POT es el único que necesitas para encargarte de los traductores, para que realicen su faena. Los ficheros POT y PO pueden ser fácilmente intercambiados renombrados para cambiar los tipos de fichero sin problemas. Es una buena idea ofrecer el fichero POT con tu plugin, para que los traductores no tengan que buscar las cadenas. Existen unas cuantas maneras de generar un fichero POT en tu plugin:

  • Herramientas del Admin del Repositorio del Plugin

Si tu plugin está registrado en el repositorio oficial de plugins, ve a tu página de “administración” y clica en el botón “Continue” de la sección “Generate POT file”. Después, simplemente, clica en el botón “Get POT” para descargar el fichero POT.

  • Herramientas WordPress i18n

Si tu plugin no está en el repositorio oficial, puedes revisar el directorio WordPress Trunk de SVN (ver Using Subversion para aprender acerca de SVN). Necesitas revisar el paquete entero porque las herramientas wordpress-i18n utilizan código del núcleo de WordPress para generar el fichero POT. Necesitas que los paquetes gettext y PHP estén instalados en tu servidor u ordenador antes de que ejecutes el comando.

Abre la línea de comandos y cambia el directorio a la carpeta de idioma. Entonces, puedes ejecutar el scrip makepot.php en la línea de comandos así:

Cuando termine, puedes ver el fichero POT en el directorio actual.

  • Poedit

También puedes utilizar Poedit localmente para traducir. Es una herramienta de código abierto para todos los sistemas operativos mayoritarios. La versión por defecto gratuita de Poedit soporta escaneo manual para todos el código fuente con funciones gettext. Está disponible una versión profesional que ofrece una característica de escaneo en un click para los plugins WordPress. Después de generar el fichero PO, puedes renombrarlo a POT. Si creara un fichero MO, puedes eliminarlo, pues no es necesario. Si no tienes la versión pro, puedes tener fácilmente el Blank POT y utilizarlo como base de tu fichero POT. Una vez tienes el POT vacío en el directorio de idiomas, puedes clicar “Update” en Poedit para actualizar el fichero POT con tus cadenas.

  • Otro

Con grunt-wp-i18n y grun-pot, necesitas instalar node.js. Es una instalación sencilla. Más información en https://developer.wordpress.org/plugins/internationalization/localization/#grunt-tasks.

 

Traducir el fichero PO

Existen múltiples maneras de traducir un fichero PO.

Puedes utilizar un editor de textos para introducir la traducción.

También puedes utilizar Poedit.

Una tercera opción es utilizar un servicio de traducción en línea. La idea general es que subes el fichero POT y das permiso a los usuarios o traductores para traducir tu plugin. Esto permite seguir la trayectoria de los cambios, tener siempre la última traducción y evitar hacer la faena dos veces.

WP-Translations, una comunidad “donde los traductores conocen a desarrolladores”, corre en Transifex. Puedes enviar proyectos para traducir como desarrollador, o traducir plugins y temas existentes a tu idioma.

Unas cuantas herramientas que puedes emplear para traducir online tus ficheros PO (aparte de las ya descritas):

También puedes utilizar plugins, como Codestyling Localization.

El fichero traducido se guarda como my-plugin-{locale}.mo. ‘Locale’ es el código de idioma y/o país que defines en la constante WPLANG en el fichero wp-config.php.

 

Generar el fichero MO

  • Línea de comandos

El programa msgfmt se utiliza para crear el fichero MO. msgfmt es parte del paquete Gettext. Para ello:

Si tienes un lote de ficheros PO, puedes hacerlo como un proceso batch:

Para UNIX:

Para WINDOWS (previamente necesitas instalar Cygwin):

  • Poedit

msgfmt está integrado en Poedit, permitiendo generar el fichero MO.

  • Otros

grunt-po2mo convierte todos los ficheros.

 

Ideas para buenas traducciones

  • No traduzcas literalmente, traduce orgánicamente.

Al ser bi o multi-idiomas, indudablemente sabes que los idiomas que hablas tienen diferentes estructuras, ritmos, tonos e inflexiones. Los mensajes traducidos no necesitan estructurarse de la misma forma que los originales: toma las ideas presentes y utiliza un mensaje que exprese la misma cosa en una manera natural para el idioma final. Es la diferencia entre crear un mismo mensaje y crear un lenguaje equivalente: no iguales, reemplaza. Lo mismo con más ítems estructurales en los mensajes, tienes licencia creativa para adaptar y cambiar si sientes que será más lógico o más adaptado para tu audiencia.

  • Intenta guardar el mismo nivel de formalidad o informalidad.

Cada mensaje tiene un nivel diferente de formalidad o informalidad. Qué nivel exactamente de formalidad utilizar para cada mensaje en tu idioma final es algo que tienes definir tú (o tu equipo), pero los mensajes de WordPress (particularmente los de información) tienden a tener un tono políticamente informal en inglés. Intenta conseguir lo mismo en el idioma final, dentro de tu contexto cultural.

  • No utilices argot o términos específicos de audiencia.

Alguna terminología se espera en un blog, pero rehúsa utilizar coloquialismos que sólo conocen ciertas personas. Si el blogger no iniciado instala WordPress en tu idioma, ¿conocerá lo que significan ciertos términos? Palabras como pingback, trackback y feed son excepciones.

  • Lee otras traducciones en tu idioma.

Al hacerlo, observarás cuáles son los términos comúnmente utilizados, qué formalidad, etc.

 

Utilizar localizaciones

A partir de WordPress 4.0, puedes cambiar el idioma en “Ajustes generales”. Pero, si no ves una opción o el idioma que quieres, haz lo siguiente:

  • Define WPLANG entro del archivo wp-config.php:
  • Ve a wp-admin/options-general.php o ‘Ajustes’ > ‘General’.
  • Selecciona tu idioma en ‘Idioma del sitio’
  • Ve a wp-admin/update-core.php.
  • Clica ‘Update translations’, cuando esté habilitado.
  • Se descargan los archivos de traducción, cuando estén disponibles.

 

Seguridad en la internacionalización

La seguridad, a menudo, se pasa por alto al hablar de internacionalización, pero hay unas pocas cosas importantes a tener en mente.

 

Chequea el spam y otras cadenas maliciosas

Cuando un traductor te envía una traducción, asegúrate siempre que no incluye spam o palabras maliciosas en su traducción. Puedes utilizar Google Translate para traducir su traducción inversamente a tu idioma natural, para poder comparar entonces el original y la traducción.

 

Utiliza marcadores de posición para las URLs

No incluyas URLs en cadenas internacionalizadas, porque un traductor malicioso puede cambiarlas a una dirección diferente. En su lugar, utiliza marcadores de posición para printf()sprintf().

No seguro:

Seguro:

 

Compila tus propios binarios .mo

Frecuentemente los traductores envían los ficheros compilados .mo junto con el fichero de texto .po, pero debes descartar su fichero .mo y compilarlo tú mismo, porque no tienes manera de saber si se ha compilado del correspondiente archivo .po o de otro diferente. En este último caso, podría contener spam y otras palabras maliciosas si tu conocimiento.

Utilizando Poedit para generar el binario, sobreescribirá las cabeceras en el fichero .po, por lo que es mejor compilarlo desde la línea de comandos:

msgfmt -cv -o /path/to/output.mo /path/to/input.po

 

Recursos

 

 

Conclusiones

Si alguien ha terminado de leer este artículo, ¡enhorabuena!. De todas formas, en el próximo artículo de la serie haremos un resumen práctico en el que internacionalizaremos nuestro plugin para su distribución a escala mundial (caray, qué bien me ha quedado).

Deja un comentario

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