Aplicación multiidioma con GNU Gettext
He probado algunos sistema de traducción para programas Delphi, entre ellos he de destacar los componentes de TsiLang Component Suite , que me parecen muy buenos y completos.
Estos días necesitaba un paquete gratuíto y revisando páginas y foros he llegado a GNU Gettext for Delphi and C++ Builder. Me parece un paquete muy sencillo de utilizar y por lo que he probado hasta ahora, cómodo y eficaz.
En esta entrada voy a explicar desde cero los pasos que he seguido para conseguir una aplicación multiidioma.
PREPARATIVOS
(1) GNU Gettext
Descargar desde la página «GNU Gettext for Delphi and C++ Builder» el programa de instalación. También se puede encontrar la última versión en SourceForge.
Una vez descargado el ficchero e instalado tendremos lo necesario para generar nuestros ficheros de traducción e integrarlos en el programa.
NOTA: Hay una versión especial compatible con Delphi 2009 que podemos descargar desde aquí, si estamos utilizando esta versión de Delphi..
Si se ha instalado correctamente, nos pedirá reiniciar el sistema.
Una vez instalado, si desde el explorador de Windows pulsamos Click derecho con el ratón sobre una carpeta, nos debería aparecer una nueva opción «Extract Translations to Template«.
(2) Poedit for Windows
Necesitaremos una herramienta para poder traducir (ya seamos nosotros o una tercera persona) los ficheros de traducción a los diferentes idiomas. Esto está pensado para que esa posible tercera persona (que puede no tener Dephi instalado) pueda traducir un fichero sólo con esta herramienta (y el fichero .po como veremos después).
Descargamos el programa desde la página (http://www.poedit.net/) y lo nstalaremos.
Una vez realizados estos dos pasos previos, crearemos un pequeño proyecto de ejemplo donde realizar las pruebas.
Para ello, desde Delphi comenzamos una nueva aplicación y en el formulario generaremos algo similar a esto:
TRADUCCIÓN
(a) Preparar el proyecto.
Una vez tenemos nuestra aplicación funcionando debemos preparnos para comentar lo necesario para traducción.
(1) Lo primero es añadir la unit gnugettext.pas a nuestro proyecto. Las units que usesn traducciones deberán tenerla en el uses.
NOTA: Si habéis realizado la instalación estandard debería estar en:
«c:\Archivos de programa\dxgettext\gnugettext.pas»
(2) Añadir al OnCreate de nuestros formularios el siguiente código:
// Traducir el formulario TranslateComponent(Self); |
(3) Preparar la estructura de directorios para organizar las traducciones. Dentro del directorio de la aplicación crearemos una carpeta llamada locale y dentro de ella un árbol de directorios similar al que se ve en la imagen.
En este ejemplos, yo voy a utilizar 4 idiomas (es, en, fr, ca); Contando que el original va a ser el Español(es) debemos crear la estructura para los otros 3 (inglés, francés y catalán). Nos quedará un arbol de directorios como este:
(b) Generar fichero de traducciones (.po)
(1) Para generar el fichero de traducciones del proyecto se debe extraer todas las cadena «traducibles» y almacenarlas en un fichero; Para ello basta con que, desde el explorador de Windows, pulsemos Click derecho sobre la carpeta del proyecto y seleccionemos la opción «Extract Translations to template».
Esto generará un fichero default.po en el directorio de la aplicación.
(2) Copiaremos este fichero dentro de los directorios LC_MESSAGES; Tantas copias como directorios tengamos. Después de eso tendremos varios ficheros .po; Uno en el directorio de la aplicación y uno dentro de cada carpeta de traducción:
appdir/appTest.exe
appdir/default.po
appdir/locale/en/LC_MESSAGES/default.po (Traducciones al inglés)
appdir/locale/fr/LC_MESSAGES/default.po (Traducciones al francés)
appdir/locale/ca/LC_MESSAGES/default.po (Traducciones al catalán)
…
(c) Traducir los ficheros.
En este punto ya se pueden abrir cada uno de los ficheros .po y traducirlo utilizando poedit. Una vez abierto el fichero con poedit, veremos una pantalla como esta:
Una vez finalizada la traducción de palabras y cerrado el programa veremos que en cada directorio aparecen 2 ficheros; El default.po (original) y el de traducción llamado default.mo.
Repetimos esto con todos los ficheros de traducción.
(d) Cómo utilizar las traducciones en nuestro programa
Una vez los ficheros de traducción estén generados, para utilizarlos desde la aplicación añadirenmos el siguiente código a los botones de activación de los idiomas.
// para el catalán UseLanguage('ca'); RetranslateComponent(Self); // para el inglés UseLanguage('en'); RetranslateComponent(Self); ... |
Si ejecutamos la aplicación veremos que al pulsar los botones cambia es aspecto del formulario.
Para finalizar, aun nos queda un detalle, que es, que la cadena que aparece al pulsar el botón «Hola Mundo!» no aparece traducida.
Para ello utilizaremos la función de traducción _().
La llamada que actualmente está así:
MessageDlg('Hola', mtInformation, [mbOK], 0); |
Pasará a colocarse así:
MessageDlg(_('Hola'), mtInformation, [mbOK], 0); |
PASOS FINALES
Con eso nuestro programa debería quedar traducido completamente. Si movemos el EXE y la carpeta locale a cualquier otro sitio veremos que el programa funciona correctamente. Si sólo movemos el EXE, el efecto es que no aparace nada traducido.
Queda un último detalle, que es integrar las traducciones en el EXE, para que todo quede en el ejecutable y no debamos preocuparnos del directorio locale.
Basta con pulsar click derecho sobre el EXE de nuestra aplicación desde el explorador de Windows, Seleccionar la opción «Embeb Translations» y seleccionar los ficheros de traducción que queremos integrar.
Con esto debería ser todo.
UPDATE: (20/04/2011)
Pues haciendo pruebas he llegado a obtener el mismo proble que comenta Sil en sus comentarios (en mi caso utilizando Delphi XE). Se trata de que al intentar añadir las traducciones al exe se obtiene un error con la referencia: «6637DB2E-62E1-4A60-AC19-C23867046A89».
Revisando por Internet, parece que se solventa sustituyendo unas constantes que hay en gnugettext.pas.
(1) Buscar esta sección en la unit gnugettext.pas:
// DetectionSignature: used solely to detect gnugettext usage by assemble DetectionSignature: array[0..35] of AnsiChar='2E23E563-31FA-4C24-B7B3-90BE720C6B1A'; // Embedded Header Begin Signature (without dynamic prefix written by assemble) BeginHeaderSignature: array[0..35] of AnsiChar='BD7F1BE4-9FCF-4E3A-ABA7-3443D11AB362'; // Embedded Header End Signature (without dynamic prefix written by assemble) EndHeaderSignature: array[0..35] of AnsiChar='1C58841C-D8A0-4457-BF54-D8315D4CF49D'; // Assemble Prefix (do not put before the Header Signatures!) SignaturePrefix: array[0..2] of AnsiChar='DXG'; // written from assemble |
(2) Y sustituirlas por estas:
// DetectionSignature: used solely to detect gnugettext usage by assemble DetectionSignature: array[0..35] of AnsiChar='6637DB2E-62E1-4A60-AC19-C23867046A89'; // Embedded Header Begin Signature (without dynamic prefix written by assemble) BeginHeaderSignature: array[0..35] of AnsiChar=''; // Embedded Header End Signature (without dynamic prefix written by assemble) EndHeaderSignature: array[0..35] of AnsiChar=''; // Assemble Prefix (do not put before the Header Signatures!) SignaturePrefix: array[0..16] of AnsiChar='#0#0#0#0#0#0#0#0';//'DXG'; // written from assemble |
Con este ambio ha desaparecido el error y se integran perfectamente las traducciones.
Espero que haya sido útil.
Espero comentarios, sugerencias, errores,… y demás.
Embarcadero MVP.
Analista y Programador de Sistemas Informáticos.
Estudios de Informática (Ingeniería Técnica Superior) en la UPC (Universidad Politécnica de Barcelona).
Llevo utilizando Delphi desde su versión 3. Especialista en diseño de componentes, Bases de Datos, Frameworks de Persistencia, Integración Continua, Desarrollo móvil,…