Cadenas metálicas entre postes sobre un fondo amarillo con el texto «JavaScript»

Traducir cadenas de JavaScript

Hace poco estaba trabajando en un proyecto en el que utilizaba cadenas de traducción en PHP, algo bastante habitual. También necesitaba traducir algunos textos de JavaScript, así que pensé que lo más cómodo sería tenerlos juntos en un mismo archivo de traducciones.

Un mensaje de validación, el texto de un botón o un aviso generado desde JavaScript también pueden necesitar traducción. Si el proyecto trabaja con catálogos gettext, podemos recoger esos textos en archivos .pot y .po, igual que hacemos con las cadenas de otros lenguajes.

En este artículo veremos cómo preparar las cadenas y extraerlas mediante comandos o desde Poedit, para que puedas elegir el método que prefieras. También explicaré cómo configurar un extractor manual si la aplicación no las detecta.

Eso sí, conviene distinguir la preparación de las traducciones de su uso en la aplicación. Aquí vamos a localizar los textos y traducirlos en un catálogo. Para que después aparezcan traducidos, tu proyecto debe disponer de un sistema que cargue esas traducciones o las convierta al formato que necesite.

Preparar las cadenas de JavaScript para traducirlas

Antes de extraer los textos, tenemos que indicar cuáles deben poder traducirse. Las herramientas de extracción no pueden distinguir por su cuenta entre un mensaje dirigido al usuario y una cadena que utilizamos internamente, como un selector CSS o el nombre de una propiedad.

En los proyectos que utilizan gettext, los textos traducibles suelen marcarse mediante funciones como gettext() o _(). Para este artículo utilizaremos gettext() en un archivo llamado avisos.js:

const mensaje = gettext( 'Los cambios se han guardado.' );

Esta función no forma parte de JavaScript. Debe proporcionarla la biblioteca o el sistema de traducción que utilice tu proyecto. El ejemplo muestra cómo marcar la cadena para que el extractor pueda reconocerla; no añade por sí mismo un sistema de traducción a la aplicación.

El texto se escribe directamente como argumento de la función. Evita pasarlo mediante una variable, porque los extractores analizan el código sin ejecutarlo y no pueden resolver cualquier valor que calcules durante la ejecución. Una llamada como esta no permite identificar el texto original:

const texto = 'Los cambios se han guardado.';
const mensaje = gettext( texto );

También conviene evitar construir las frases traducibles concatenando fragmentos. Cada idioma puede necesitar un orden diferente, por lo que es preferible traducir el mensaje completo y utilizar el mecanismo de sustitución de valores que ofrezca tu biblioteca.

Puede que tu proyecto utilice otra función, como __() o una función propia llamada traducir(). Eso no impide extraer las cadenas, pero tendremos que indicar a la herramienta qué nombre debe buscar y en qué argumento se encuentra el texto.

Si trabajas con WordPress, en el artículo sobre las diferencias entre sus funciones de traducción en PHP explico cómo se manejan las cadenas simples, el contexto y los plurales.

A partir de aquí puedes seguir el método de comandos o el de Poedit. Ambos parten del código fuente y permiten obtener un archivo de traducción .po.

Extraer y traducir las cadenas de JavaScript con comandos

Para extraer las cadenas desde la terminal podemos utilizar xgettext, una herramienta incluida en GNU gettext. Su trabajo consiste en analizar los archivos de código y recoger los textos marcados para traducirlos en un catálogo.

Necesitas tener la herramienta instalada y accesible desde la terminal. Puedes comprobarlo ejecutando:

xgettext --version

Si el sistema no reconoce el comando, tendrás que instalar GNU gettext o indicar la ruta del ejecutable antes de continuar.

Extraer las cadenas del código

Para nuestro ejemplo, abre la terminal en la carpeta raíz del proyecto. Supondremos que avisos.js se encuentra dentro de una carpeta js.

Ejecuta el siguiente comando. Está escrito en una sola línea para que puedas utilizarlo tanto en Windows como en otros sistemas:

xgettext --language=JavaScript --from-code=UTF-8 --keyword=gettext --output=mi-proyecto.pot js/avisos.js

Con --language=JavaScript indicamos el lenguaje del archivo y con --from-code=UTF-8, su codificación. La opción --keyword=gettext indica que debe recoger el texto del primer argumento de las llamadas a gettext().

En este caso, gettext ya es una palabra clave predeterminada para JavaScript, por lo que podríamos omitir esa opción. La dejamos explícita para que se vea dónde configurar la función que utiliza el proyecto. Si tus cadenas están marcadas con __(), sustituye esa parte por --keyword=__.

El resultado se guarda en mi-proyecto.pot, una plantilla con las cadenas originales que utilizaremos como base para crear las traducciones de cada idioma. Entre sus entradas debería aparecer el mensaje que preparamos antes:

msgid "Los cambios se han guardado."
msgstr ""

El campo msgstr está vacío porque todavía no hemos escrito ninguna traducción. La extensión .pot identifica esa plantilla; después crearemos archivos .po para los idiomas que necesitemos.

Si utilizas otras funciones de traducción, puedes añadirlas al comando con las posiciones de sus argumentos. Por ejemplo, en un proyecto que emplee las funciones habituales de gettext para plurales y contexto:

xgettext --language=JavaScript --from-code=UTF-8 --keyword=gettext --keyword=ngettext:1,2 --keyword=pgettext:1c,2 --output=mi-proyecto.pot js/avisos.js

En ngettext:1,2, los números indican que el primer argumento contiene el singular y el segundo, el plural. La letra c identifica el argumento que aporta contexto. Por eso pgettext:1c,2 recoge el contexto del primer argumento y el texto del segundo.

Adapta estas definiciones a las funciones que realmente utilice tu proyecto. Que dos bibliotecas resuelvan la misma necesidad no significa que sus funciones tengan los mismos nombres o argumentos.

También puedes analizar varios archivos en una misma ejecución, añadiendo sus rutas al final del comando:

xgettext --language=JavaScript --from-code=UTF-8 --keyword=gettext --output=mi-proyecto.pot js/avisos.js js/formulario.js

Las rutas son relativas a la carpeta desde la que ejecutas el comando. Esta extracción recoge las cadenas de los archivos JavaScript indicados. Si el proyecto contiene textos traducibles en otros lenguajes, necesitarás analizarlos con la configuración correspondiente.

Crear y traducir el archivo de idioma

Una vez generado el .pot, podemos crear el archivo de traducción con msginit, otra herramienta de GNU gettext. Crea primero una carpeta languages en la raíz del proyecto y ejecuta:

msginit --input=mi-proyecto.pot --locale=en_US --no-translator --output-file=languages/mi-proyecto-en_US.po

En este ejemplo utilizamos inglés de Estados Unidos como idioma de destino. Sustituye en_US por el código del idioma que necesites. El nombre y la ubicación de los archivos deberán adaptarse a las convenciones del sistema de traducción de tu aplicación.

Abre el archivo .po con un editor de texto que respete la codificación UTF-8 y escribe las traducciones en los campos msgstr. Nuestro mensaje quedaría así:

msgid "Los cambios se han guardado."
msgstr "The changes have been saved."

Conserva las cabeceras y las referencias a los archivos de origen que ha generado la herramienta. Estas referencias ayudan a localizar cada texto dentro del proyecto y pueden ser necesarias en pasos posteriores de conversión.

Con esto tenemos un archivo .po traducido sin haber utilizado Poedit. El siguiente apartado explica la alternativa gráfica, por lo que no necesitas seguirlo si prefieres trabajar con comandos.

Extraer y traducir las cadenas de JavaScript con Poedit

Poedit permite extraer las cadenas del código y escribir sus traducciones desde una interfaz gráfica. Para seguir este método no necesitas ejecutar los comandos del apartado anterior ni disponer de una plantilla generada desde la terminal.

Si ya tienes un archivo .po del proyecto, ábrelo para incorporar las cadenas de JavaScript a esa traducción. Si estás empezando desde cero, utiliza Archivo > Nuevo, selecciona el idioma de destino y guarda el catálogo en una carpeta languages dentro de tu proyecto.

Para nuestro ejemplo elegiremos inglés de Estados Unidos y utilizaremos el nombre mi-proyecto-en_US.po. Puedes cambiar el nombre y la ubicación según lo que requiera tu aplicación.

Configurar la extracción desde el código fuente

Abre las propiedades de la traducción, dentro del menú Traducción, y revisa las rutas de las fuentes. Los nombres de las opciones pueden variar ligeramente según la versión o el idioma de Poedit.

Tenemos que indicar dónde está el código que queremos analizar. Si el archivo .po se encuentra dentro de languages, establece .. como ruta base para apuntar a la raíz del proyecto. Después añade js como ruta de las fuentes si tus archivos JavaScript están en esa carpeta.

En este ejemplo, Poedit debe poder llegar al archivo js/avisos.js desde la carpeta del proyecto. Si tu estructura es diferente, adapta las rutas. También puedes incluir otras carpetas del código para gestionar conjuntamente sus cadenas traducibles.

Conviene excluir las carpetas de dependencias y otros archivos ajenos a tu desarrollo, para no incorporar textos que no pertenecen al proyecto.

En la pestaña de palabras clave de las fuentes, comprueba que aparecen las funciones de traducción que utilizas. Para extraer una llamada como esta:

const mensaje = gettext( 'Los cambios se han guardado.' );

El extractor debe reconocer gettext. Poedit puede utilizar las palabras clave predeterminadas de los lenguajes admitidos, pero también puedes configurarlas expresamente. Si tu biblioteca utiliza las funciones habituales de gettext para plurales y contexto, las entradas serían:

gettext
ngettext:1,2
pgettext:1c,2

Introduce cada una como una entrada independiente. Los números indican qué argumentos contienen los textos y la letra c identifica el argumento que aporta contexto.

Si tu proyecto utiliza __(), añade __ como palabra clave. Si emplea una función propia, configura su nombre y las posiciones de sus argumentos. No necesitas duplicar las entradas que ya estén configuradas ni eliminar las demás funciones que utilice el proyecto.

Comprueba también que la codificación del código fuente sea UTF-8, siempre que tus archivos utilicen esa codificación, y guarda los cambios.

Actualizar y traducir el catálogo

Utiliza la opción Actualizar desde código fuente. Poedit analizará las rutas indicadas e incorporará las cadenas detectadas al catálogo.

Debería aparecer «Los cambios se han guardado.». Selecciónala y escribe «The changes have been saved.» en el campo de traducción. Guarda el archivo mi-proyecto-en_US.po cuando hayas terminado.

Cuando añadas o modifiques textos en el código, vuelve a actualizar desde las fuentes y revisa las entradas nuevas o modificadas. Así puedes mantener la traducción desde Poedit sin realizar la extracción mediante comandos.

Si las cadenas aparecen correctamente, ya puedes gestionarlas en el catálogo. En mi caso seguían sin aparecer y tuve que configurar un extractor manualmente, como explico a continuación.

Si Poedit no detecta las cadenas de JavaScript

Puede que, después de actualizar desde el código fuente, las cadenas de JavaScript sigan sin aparecer en el catálogo. Es lo que me ocurrió utilizando Poedit 3.6, en su edición gratuita, sobre Windows 10. Aunque la aplicación dispone de soporte para este lenguaje, en mi instalación tuve que añadir un extractor manualmente para poder recogerlas.

Antes de hacerlo, revisa que las rutas de las fuentes incluyan los archivos .js que quieres analizar y que no haya una exclusión que los deje fuera. Comprueba también las palabras clave y que los textos estén escritos directamente dentro de las funciones de traducción. Un extractor nuevo no resolverá una ruta incorrecta ni podrá recuperar el contenido de una variable calculada durante la ejecución.

Añadir un extractor para JavaScript

Abre la configuración de Poedit y busca la sección Extractores. La ubicación puede cambiar según la versión y el sistema operativo; en Windows puedes encontrarla dentro de Archivo > Preferencias.

Pulsa el botón para añadir un extractor y ponle el nombre JavaScript. A continuación, completa los campos con estos valores.

Lista de extensiones

*.js

Orden para extraer las funciones

xgettext --language=JavaScript --force-po -o %o %C %K %F

Un elemento de la lista de palabras clave

-k%k

Un elemento de la lista de archivos de entrada

%f

Conjunto de caracteres del código fuente

--from-code=%c

Los elementos que comienzan con % son marcadores que Poedit sustituye al ejecutar el extractor. Representan el archivo de salida, la codificación, las palabras clave y los archivos que debe analizar. Debes dejarlos tal como aparecen en el ejemplo.

La opción --language=JavaScript indica expresamente cómo interpretar los archivos. Las palabras clave se toman de la configuración del catálogo mediante %K, por lo que necesitamos conservar allí las funciones de traducción que utilice el proyecto, con sus correspondientes argumentos.

En las propiedades de la traducción, comprueba que la codificación del código fuente esté configurada como UTF-8, siempre que tus archivos utilicen esa codificación. El marcador %C incorporará esa información al comando mediante el último campo del extractor.

Guarda los cambios, comprueba que el extractor esté habilitado y vuelve a actualizar la traducción desde el código fuente. Para verificar el resultado, busca el mensaje «Los cambios se han guardado.» del archivo avisos.js.

Si sigue sin aparecer, revisa los errores que muestre Poedit. Que el archivo esté incluido en las rutas no garantiza que el analizador pueda procesarlo correctamente; un error de sintaxis o una construcción que la versión de gettext utilizada no admita también puede impedir la extracción.

Utilizar las traducciones en el proyecto

Una vez recogidas y traducidas las cadenas, tenemos un catálogo que podemos mantener junto al código del proyecto. Incluso podemos gestionar en el mismo .po textos procedentes de JavaScript y de otros lenguajes, si forman parte del mismo sistema de traducción.

El siguiente paso depende de la aplicación. Algunas bibliotecas trabajan con catálogos gettext y otras necesitan que las traducciones se conviertan a JSON o a un formato propio. Generar un .mo tampoco garantiza que el código JavaScript pueda utilizarlo; debe existir una integración que cargue esas traducciones.

Por ejemplo, WordPress utiliza archivos JSON para sus traducciones de JavaScript. Incorporar las cadenas al .po resulta útil para gestionarlas y generar esos archivos, pero no basta con guardar el catálogo o compilarlo en un .mo.

Antes de integrar las traducciones, comprueba qué formato admite la biblioteca de tu proyecto y cómo se cargan sus archivos de idioma. La extracción nos permite localizar y mantener los textos; el sistema de traducción es el que debe hacerlos llegar a la aplicación.


Referencias:

Jesús Tovar - Desarrollador web freelance Sevilla

Deja una respuesta

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