Cómo hacer que Gettext funcione con PHP en Windows 11

Gettext con PHP en Windows 11

Sí, lo he conseguido, después de pasar sólo 8 horas entre pruebas y depuración, ¡he habilitado con éxito gettext en Windows para mi entorno PHP local! Si estás leyendo esto, supongo que también estarás luchando con ello como lo he hecho yo hoy.

Premisa importante: En mi caso he usado Laragon y PHP 8.1.10 con Apache como Servidor Web. Puede funcionar de forma diferente con otros entornos como XAMP o WAMP, o con Nginx y otras versiones de PHP.

Preparando el entorno PHP

En primer lugar, habilitemos la extensión gettext dentro de PHP, editando el php.ini

# php.ini extension=gettext

Luego verificamos que ha sido habilitada con phpinfo:

// index.php phpinfo();

La salida debe mostrarnos que gettext está efectivamente habilitado:

Gettext enabled with phpinfo
Gettext habilitado con phpinfo

Formato de idioma regional de Windows

Sé que debe sonar increíble, pero el uso de gettext depende del Formato Regional para el idioma que tengamos en Windows. Pasé horas para llegar a esta conclusión, pero así es, al menos para mí.

Comprobemos entonces nuestro formato regional :

Formato regionale della lingua su Windows 11
Formato regional para el idioma en Windows 11

También podemos encontrar los códigos de idioma desde los docs de Microsoft:

Por lo general, sólo necesitamos los códigos de dos letras, "it" para italiano, "en" para inglés, etcétera. Puede que necesites utilizar un código más específico, como "it-IT" o "en-US". Ten en cuenta que Windows utiliza el guión , mientras que Unix utiliza el guión bajo. En Linux tienes "fr_FR" mientras que Windows tiene "fr-FR".

Usaremos este código con setlocale en php y, como vamos a ver, para el textdomain.

Cómo funciona gettext con PHP en Windows

En pocas palabras: Supongamos que tenemos "Italiano" como Idioma Regional (ver arriba). Gettext buscará archivos MO dentro de la carpeta "it" en la ruta que especificaremos en PHP con las funciones en el textdomain:

$language = 'fr'; // Francés, o también podría usar fr-FR $domain = 'midominio_' . $language; // El dominio será midominio_fr, por lo tanto PHP buscará el archivo midominio_fr.mo setlocale(LC_ALL, $language); bindtextdomain($domain, 'C:\laragon\usr\locale'); // PHP buscará en esta ruta la carpeta "it" basándose en el código de idioma del Formato Regional de nuestro Sistema textdomain($domain); bind_textdomain_codeset($domain, 'UTF-8'); # Por lo tanto, todos mis archivos .mo deben estar dentro de C:\laragon\usr\locale\it

Tenga en cuenta que en Windows usamos LC_ALL, no tenemos LC_MESSAGES ni LC_TIME que se usan en Unix. LC_ALL se usa para todas las locales, como se indica en la documentación de PHP para setlocale.

Después de todo, gettext es un software GNU.

Obteniendo el idioma del usuario desde el navegador

Si queremos conocer el idioma en el que está navegando nuestro usuario, podemos hacerlo de la siguiente manera:

$language = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);

Esto es sólo una cabecera enviada por el navegador, puede que no esté configurada en absoluto, así que asegúrate de definir una locale por defecto como "en".

Con esta configuración, debemos tener todos los archivos .mo siguiendo esta estructura:

C:\laragon\ - usr - locale - it (código de idioma para mi formato regional en Windows) .\mydomain_fr.mo ---> este es el archivo que se va a cargar porque tengo setlocale() a "fr" .\mydomain_it.mo .\mydomain_es.mo

Podemos cambiar el $domain para usar un archivo MO diferente para otro proyecto (myotherdomain_fr.mo) y podemos cambiar el $language para usar el archivo MO para otro idioma (mydomain_es.mo).

Traducir automáticamente los archivos PHP con xgettext y msgfmt

Este paso es opcional, es útil sólo si necesitamos compilar y actualizar automáticamente los ficheros PO, para las nuevas cadenas de texto que hayamos añadido en nuestro sitio web o aplicación PHP.

Incluso en Windows podemos realizar las mismas acciones que en Unix usando xgettext y msgfmt.

Todas las cadenas usadas con la función php gettext() o con su alias _() serán configuradas en un fichero PO para ser traducidas, esto se hará automáticamente, ¡lo que nos ahorrará mucho tiempo!

Ejemplo de cadenas traducibles:

# index.php _('Título de la página') _('Mi texto') gettext('Algún otro texto')

Veamos cómo hacer esto:

  1. Descarguemos gettext para Windows del repositorio Github de Michele Locali, a quien agradecemos por esto. Si la descarga no funciona, he subido la última versión de gettext aquí: gettext 0.21 Windows
  2. Alternativamente, podemos instalar Poedit, un software de código abierto que ofrece una interfaz gráfica para editar archivos PO y generar archivos MO
  3. En la carpeta de instalación de Poedit encontraremos GettextTools\bin donde tenemos los archivos ejecutables para Windows (.exe.) para xgettext y msgfmt
  4. Llegados a este punto, vamos a configurar la ruta en las variables de sistema de Windows, a continuación se muestran las capturas de pantalla con los pasos a seguir:
    1. Buscamos en Windows Edición de variables de entorno
    2. Luego hacemos clic en Variables de entorno
    3. En las variables de sistema, en la parte inferior, modificamos la Ruta, o la añadimos si no la tenemos
    4. Añadimos una nueva variable con la ruta que lleva a Poedit\GettextTools\bin

En la práctica, las variables Path sirven para hacer ejecutables desde cualquier punto del terminal ciertos comandos como xgettext y msgfmt , o por ejemplo cwebp si usamos las librerías WebP, o ffmpeg si queremos convertir un vídeo.

Modificación de las variables de entorno del sistema en Windows

Usando la barra de búsqueda en Windows podemos abrir la herramienta para editar las variables de sistema:

How to find the tool for modying System environment variables on Windows
Modificar las variables de entorno en Windows
System properties: Environment variables
Propiedades del sistema: Variables de entorno

Modificando la Variable de Sistema PATH

Vamos a modificar la variable Path para nuestro Sistema, o eventualmente sólo para nuestro usuario si lo preferimos:

System variables: Path
Variables de sistema: Ruta

Añadiendo una nueva variable con la ruta de los ejecutables de gettext

Ahora añadimos una nueva variable para el Path, escribiendo la ruta a la carpeta gettext con los ejecutables:

Adding a new environment variable for gettext
Añadiendo una nueva variable de entorno para gettext

Tengo que dar crédito al usuario Desintegr de StackOverflow, gracias a él descubrí que los ejecutables de Windows estaban en la carpeta GettextTools de Poedit.

Ejecutando los comandos para compilar y actualizar los ficheros PO automáticamente

Después de hacer estas operaciones para configurar globalmente xgettext y msgfmt, pasemos al siguiente paso. En realidad también podríamos lanzar los ejecutables directamente desde la carpeta Poedit, pero es mucho más cómodo establecer las variables de entorno.

Si hemos optado por PoEdit, el ejecutable msgattrib no está presente en el GettextTools. Para usarlo, necesitamos descargar gettext para Windows del repositorio Github de Michele Locali como ya hemos mencionado. En este caso, la variable de entorno para Path debe apuntar a la ruta donde ponemos gettext, por ejemplo C:\Programs\gettext

REM Traduciendo un único fichero, uno PHP en este caso xgettext -d it -p \path\to\project\locale\it -L PHP --from-code=UTF-8 --no-location -j \path\to\project\index.php REM Compilando ficheros MO msgfmt -D \path\to\project\locale\it it.po -o \path\to\project\locale\it\LC_MESSAGES\mydomain_it.mo

Nota: Para ser detectadas por gettext, las cadenas deben usarse con la función gettext() o su alias _(). Consulte la documentación de gettext.

Si obtiene un error "No such file or directory" para el fichero PO, es probablemente porque necesitamos crear el fichero PO primero, no es creado por gettext, debe existir ya. Podemos crearlo como un simple fichero de texto o usar PoEdit.

Traduciendo todos los ficheros PHP a la vez

Podemos modificar este código con nuestras necesidades y guardarlo como un archivo .bat :

@echo off REM aquí podemos omitir el recuento y la pausa si no es necesario SET count=1 REM preguntando el idioma al usuario, como es o fr SET /p language=Introduzca el idioma: 
FOR /f "tokens=*" %%G IN ('dir *.php /b /s') DO (call :subrutina "%%G") pause GOTO :eof :subrutina REM aquí también podemos omitir los comandos echo y set echo %count%:%1 REM añadimos --no-location si queremos eliminar del fichero PO la ruta donde se ha encontrado la traducción xgettext.exe -j %1 -d %language% -p \path\to\locale\it -L PHP --from-code=UTF-8 set /a count+=1 GOTO :eof

Este archivo BAT guardará dentro de \path\to\locale\it el archivo it. po con todas las cadenas precompiladas para cada PHP encontrado. Podemos evitar usar "pause" si queremos que la terminal se cierre tan pronto como haya completado la tarea.

Nota: este script busca todos los archivos PHP dentro de la misma carpeta y las subcarpetas, comenzando desde donde se encuentra el archivo BAT. Podemos ampliar o reducir el rango de carpetas simplemente moviendo los archivos BAT en el árbol de carpetas.

Tengo que agradecer por esto la documentación sobre el bucle FOR en CMD de SS64 porque me estaba costando hacer funcionar el bucle wel,, entonces encontré esta página y entendí que tenía que usar la subrutina.

La cuenta es solo algo util para tener una mejor salida en el prompt de comandos.

Añadimos --no-location si queremos eliminar del fichero PO la ruta donde se ha encontrado la traducción.

El siguiente error puede ignorarse, probablemente esté causado por el uso del atributo -j dentro del bucle for.

Charset "CHARSET" no es un nombre de codificación portable. La conversión del mensaje al conjunto de caracteres del usuario podría no funcionar.

Compilación automática de los ficheros MO

Si no desea utilizar PoEdit para crear el fichero MO y quiere hacerlo automáticamente, puede guardar este código en un fichero BAT, después de ajustarlo a sus necesidades:

@echo off REM preguntando el idioma al usuario, como es o fr, y el dominio, por ejemplo "mi_proyecto" SET /p idioma=Introduzca idioma: 
SET /p dominio=Introduzca el dominio: msgfmt -D \path\to\locale\%language% %language%.po -o \path\to\locale\%language%\LC_MESSAGES\mydomain_%language%.mo

Si lo prefiere, puede eliminar las entradas de idioma y dominio. Gracias a Instantsoup de StackOverflow por la idea.

Eliminar cadenas obsoletas que ya no son necesarias

REM Para actualizar los ficheros PO, eliminando las cadenas no usadas que se encuentran en los ficheros PHP: REM 1) Cree el PO SET /p sourcelanguage=Introduzca el idioma al que hacer referencia: 
SET /p destinationlanguage=Introduzca el idioma de destino: 
xgettext -d %sourcelanguage% -p \path\to\project\locale\%sourcelanguage% -L PHP --from-code=UTF-8 --no-location -j file.php REM 2) Haz un diff entre it.po y en.po, luego marca como obsoletas las cadenas extra que no esten dentro de it.po msgattrib --set-obsolete --ignore-file=\path\to\project\locale\%sourcelanguage%.po -o \path\to\project\locale\%destinationlanguage%.po \path\to\project\locale\%destinationlanguage%.po REM 3) Eliminar cadenas obsoletas msgattrib --no-obsolete -o \path\to\project\locale\%destinationlanguage%.po \path\to\project\locale\%destinationlanguage%.po

Guardamos el código anterior como un archivo .bat, después de ajustarlo a nuestras necesidades.

Básicamente, después de crear el fichero PO, ejecutamos msgattrib que encontrará las cadenas que aún están dentro de en.po pero que ya no están dentro de it.po, marcará las sobrantes como obsoletas, y las eliminará.

Por favor, tenga en cuenta que si msgattrib encuentra cadenas no traducidas, donde msgstr está vacío, eliminará esas cadenas.

Cómo traducir manualmente con PoEdit

Está claro que tarde o temprano también tendremos que traducir los textos, o entregarlos a traductores.

Los ficherosPO (Portable Object) son "legibles por humanos", podemos abrirlos con cualquier editor de texto y editarlos. En cambio, los archivos MO (Machine Object) sólo los puede leer el ordenador, son archivos binarios. Además, los ficheros Portable Object Template (POT ) son una plantilla de partida para crear ficheros PO, pero son opcionales.

Aqui hay un ejemplo de fichero PO :

# it.po msgid "Esto es un titulo" msgstr "Questo è un titolo" msgid "Algún otro texto" msgstr "Dell'altro testo"

Así que tenemos el msgid que indica la cadena a traducir, y el msgstr que indica la traducción real en el idioma de interés.

Podemos editar estos POs a través del editor o con PoEdit: ten en cuenta que en PoEdit sólo podemos editar las traducciones finales, no las cadenas originales:

Poedit interface
Interfaz de Poedit

En la parte superior tenemos las cadenas presentes en el PO, en la parte inferior tenemos el texto fuente (msgid) y la traducción (msgstr).

A la derecha tenemos también las traducciones sugeridas, que sin embargo son una característica de pago.

Guardamos el fichero PO y automáticamente se generará el fichero MO en la misma carpeta. También podemos generar sólo el fichero MO yendo a Archivo > Compilar MO

Modificar ficheros PO con un editor de texto

Como vimos, podemos modificar los archivos PO con cualquier editor. Yo personalmente uso VSCodium y tengo instalada la extensión gettext para resaltar la sintaxis. También podemos usar Notepad++ o el viejo y viejo Notepad de Windows.

Cuando hayamos terminado, podemos guardar el fichero PO sobreescribiendo el anterior. Para convertirlo en MO necesitaremos usar PoEdit o ejecutar el comando msgfmt.

Conclusiones

Lo que realmente importa es que todos los ficheros MO estén dentro de la misma carpeta, ¡basándonos en nuestro Formato Regional de Windows!

Si este artículo te ha sido útil, ¡sígueme en Facebook y Youtube! ¡ Deja un comentario a continuación para hacerme saber lo que piensas, o si has tenido alguna dificultad para configurar gettext!

Lee también

Si puedes, ¡apoya mi trabajo! Créditos de la imagen de previsualización: Unsplash

Deja un comentario

Todos los comentarios estarán sujetos a aprobación después de ser enviados. Es posible que se publiquen después de varias horas.

Puedes usar un apodo al azar, es útil para que al menos pueda responder a tus comentarios. Y si eliges enviar tu correo electrónico, puedes recibir una notificación cada vez que responda a tu comentario.

Aún no se han escrito comentarios sobre este artículo. ¡Sé el primero en compartir tu opinión!

*