Gettext izar en “C”
Gettex_izar aplicativos en “C”
Este COMO en castellano
Un agradecemento especial a Martin Vazquez (GLUG) e a Cesar Mauri (eViacam) que case ao mesmo tempo detectaron unha trapallada miña que levaba retrasando o resultado durante máis dunha semana
Xeral
Este proceso é o que se define como “i18n” ou “internacionalización” (segundo a Wikipedia):
A internacionalización é o proceso de deseñar software de xeito tal que poida adaptarse a diferentes idiomas e rexións sen a necesidade de realizar cambios de enxeñería nin no código.
Preparación/adaptación dos ficheiros
Esta preparación ou adaptación deberemos realizala en todos os ficheiros que conteñan cadeas de texto traducibles, considerando como tales aquelas que o aplicativo utiliza como elemento de comunicación ou información co/ao usuario. Non se consideran cadeas traducibles aquelas que pertencen a notas do desenvolvedor, comentarios internos, etc…
Utilizando gettext(“cadea_de_texto_orixinal”)
/* includes para localizar con gettext */ #include <libintl.h> #include <locale.h> // Definición de ficheiros e rutas para gettext trans_text(void) { setlocale( LC_ALL, "" ); bindtextdomain( "nome_executable", "/usr/share/locale" ); bind_textdomain_codeset ( "nome_executable", "UTF-8" ); textdomain( "nome_executable" ); }
Orixinal:
void create_file_selection(void)
{
// file_selection_box = gtk_file_selection_new("Select MIXER application");
file_selection_box = gtk_file_chooser_dialog_new("Select MIXER application", NULL,
...
Modificado:
void create_file_selection(void)
{
// file_selection_box = gtk_file_selection_new("Select MIXER application");
file_selection_box = gtk_file_chooser_dialog_new(gettext("Select MIXER application"), NULL,
...
Utilizando _(“cadea_de_texto_orixinal”)
/* includes e define para localizar con gettext */ #include <libintl.h> #include <locale.h> #define _(String) gettext(String) // cadeas traducibles #define N_(String) (String) // cadeas non traducibles // Definición de ficheiros e rutas para gettext trans_text(void) { setlocale( LC_ALL, "" ); bindtextdomain( "nome_executable", "/usr/share/locale" ); bind_textdomain_codeset ("nome_executable", "UTF-8"); textdomain( "nome_executable" ); }
Orixinal:
void create_file_selection(void)
{
// file_selection_box = gtk_file_selection_new("Select MIXER application");
file_selection_box = gtk_file_chooser_dialog_new("Select MIXER application", NULL,
...
Modificado:
void create_file_selection(void)
{
// file_selection_box = gtk_file_selection_new("Select MIXER application");
file_selection_box = gtk_file_chooser_dialog_new(_("Select MIXER application"), NULL,
...
NOTAS IMPORTANTES:
Os “include” de bibliotecas que se indican son os específicos para utilizar gettext, deben engadirse aos que teña o aplicativo. Así mesmo necesita doutras bibliotecas que adoitan formar parte dos aplicativos, en todo caso non está de máis comprobar que estean xa “incluídas” e se non o están incluílas.
#include <stdio.h> #include <stdlib.h>
Usar LC_ALL en “setlocale” podería non ser apropiado. LC_ALL inclúe todas as categorias de localización (locale), especialmente LC_CTYPE, responsable de determinar as clases de carácteres nunha serie de funcións de “ctype.h” o cal podería producir unha saída incorrecta. Ademais, algúns sistemas teñen problemas procesando números usando as funcións scanf cando se utiliza LC_ALL. É por iso que normalmente é necesario substituír a liña que fai referencia a LC_ALL por unha secuencia de liñas setlocale:
setlocale(LC_TIME, ""); setlocale(LC_MESSAGES, "");
No mesmo ficheiro, debemos buscar a chamada a “main()”
main (int argc, char *argv[])
e ao principio facer unha chamada a “trans_text()”
main (int argc, char *argv[]) { trans_text(); ...
Tamén deberemos ter en conta que algúns aplicativoss, ao seren compilados, instálansenos en “/usr/locale/bin”. Neste caso deberemos ter en conta que a ruta que indiquemos en “bindtextdomain”debe ser a “usr/local/share/locale” xa que será aquí onde busque de forma predeterminada.
bindtextdomain( "absvolume", "/usr/local/share/locale" );
Creación dos ficheiros de tradución
Extracción de textos a unha plantilla .pot “nome_executable.pot”
- Utilizando gettext(“cadea_de_texto_orixinal”)
xgettext -d nome_executable -s -o po/nome_executable.pot src/main.c
- Utilizando _(“cadea_de_texto_orixinal”) debe engadirse a opción -k_
xgettext -k_ -kN_ -d nome_ejecutable -s -o po/nome_executable.pot src/main.c
Se hai que extraer as cadeas de máis dun ficheiro, o primeiro será crear un ficheiro nome_executable.pot
touch po/nome_executable.pot
agora extraemos as cadeas coa opción “-j” (–join-existing)
xgettext -k_ -kN_ -d grandr -s -o po/nome_executable.pot -j src/*.c
Desta forma extraemos as cadeas de todos os ficheiros “.c” en “./src”, existen máis opcións, como excluír ficheiros etc…
man xgettext ... Operation mode: -j, --join-existing join messages with existing file
Xeración de ficheiro de tradución “nome_executable.po”
Desprazámonos ao cartafol “po” ( $ cd po )
No mesmo “locale” (idioma) do noso sistema
msginit -o po/es.po -i po/nome_executable.pot
Devólvenos a seguinte mensaxe:
O novo catálogo de mensaxes debería conter a súa dirección de correo electrónico, de tal forma que os usuarios poidan retroalimentalo sobre as traducións, e o persoal de mantemento poida contactalo en caso de ter problemas técnicos inesperados. Is the following your email address? manolito@maquina Please confirm by pressing Return, or enter your email address.
Introducimos o noso correo-e e pódenos devolver algo como isto:
If you want to create a new translation team for gl, please visit http://www.iro.umontreal.ca/contrib/po/HTML/teams.html http://www.iro.umontreal.ca/contrib/po/HTML/leaders.html http://www.iro.umontreal.ca/contrib/po/HTML/index.html
Noutro “locale” (idioma) distinto ao do noso sistema
msginit -l gl -o gl.po -i nome_executable.pot
Se nos fixamos, a diferenza está na opción “-l” e no parámetro do idioma, ter en conta que é costume que o ficheiro “.po” resultante leve por nome o código do idioma ao que corresponde xa que desta forma, calquera que o vexa saberá que é.
Creando (compilando) o ficheiro binario “.mo” que lerá o executable
msgfmt -c -v -o po/nome_executable.mo po/es.po
Normalmente o ficheiro “.mo” leva o mesmo nome que o aplicativo, sempre deberá ser o mesmo que definamos nas liñas
bindtextdomain( "nome_executable", "/usr/share/locale" ); textdomain( "nome_executable" );
senon o executable non saberá como buscalo.
Casos especiais
Engadir comentarios para os tradutores
Colocar comentarios antes das cadeas para dar instrucións ou axudas aos tradutores
/// TRANSLATORS: Please leave %s as it is, because it is needed by the program. /// Thank you for contributing to this project. printf(_("My name is %s.\n"), my_name);
Neste caso iníciase o comentario con /// e debe terse en conta á hora de construír a plantilla .pot a fin de que poida extraer estes comentarios.
xgettext --add-comments=///
O ficheiro .pot terá este aspecto
#. TRANSLATORS: Please leave %s as it is, because it is needed by the program. #. Thank you for contributing to this project. #: src/name.c:36 msgid "My name is %s.\n" msgstr ""
Modificar a orde das variables
Este é un erro moi frecuente nas traducións
Frase exemplo
printf (gettext ("Written on `%s' at %s\n"), s, strlen (s));
Na plantilla verémolo así:
msgid "Written on `%s' at %s\n"
Traducímolo literalmente por msgstr
"Escrito o «%s» ás %s\n"
Como nos gusta máis dicir “Escrito ás %s do día «%s»” traducímolo así e o resultado será algo como:
Escrito ás 25/7/2009 do dia 7:30
Para solucionalo deberemos ter en conta a orde das variables e modificalo engadindolle un valor “ordinal” 1, 2, etc… antes do tipo de dato
msgstr "Escrito ás %$2s do día «%$1s»\n"
Xeración/modificación do procedemento xeral de compilación
para que compile todo o aplicativo e sitúe correctamente os ficheiros “.mo”
Dependencias
Loxicamente necesitamos ter instalado “gettext” e para poder compilar necesitaremos ter instaldos automake gcc e as cabeceiras (header) -En Ubuntu é “build-essentials”-
apt-get install gettext automake build-essentials
E segundo o tipo de aplicativo deberemos ter instaladas as bibliotecas das que dependa.
Exemplos de avisos ao traballar con GRandR
Cando non existe “automake”
**Error**: You must have `autoconf' installed. Download the appropriate package for your distribution, or get the source tarball at ftp://ftp.gnu.org/pub/gnu/ **Error**: You must have `automake' installed.
Cando non existe gcc, etc…
checking for gcc... gcc checking for C compiler default output file name... configure: erro: C compiler cannot create executables See `config.log' for more details.
Cando nos faltan librerias particulares do aplicativo
Non package 'gtk+-2.0' found Non package 'gconf-2.0' found Non package 'xrandr' found
sudo apt-get install libgtk2.0-dev libgconf2-dev libxrandr-dev
Procedemento
Procedemento “gettextize”
Sempre dentro do directorio/cartafol do aplicativo
cd /Ruta/a/o/cartafol/do/proxecto
Aplicamos a orde
gettextize
Invocando esta orde non se copia o directorio ./intl polo que deberemos invocalo no ficheiro “configure.ac” (ou en configure.in) coa seguinte liña
AM_GNU_GETTEXT([external])
Recomendo que se inclúa o directorio ./intl a fin de que nas fontes xa vaian incluídos os “cabeceiras” para a compilación, ademais para previr calquera fallo bobo, é preferible aplicar a orde completa
gettextize --copy --force --intl
Devólvenos a seguinte mensaxe:
Please use AM_GNU_GETTEXT([external]) in order to cause autoconfiguration to look for an external libintl. Please create po/Makevars from the template in po/Makevars.template. You can then remove po/Makevars.template. Please fill po/POTFILES.in as described in the documentation. Please run 'aclocal -I m4' to regenerate the aclocal.m4 file. You need aclocal from GNU automake 1.9 (or newer) to do this. Then run 'autoconf' to regenerate the configure file. You will also need config.guess and config.sub, which you can get from the CVS of the 'config' project at http://savannah.gnu.org/. The commands to fetch them are $ wget 'http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess' $ wget 'http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub' You might also want to copy the convenience header file gettext.h from the /usr/share/gettext directory into your package. It is a wrapper around <libintl.h> that implements the configure --disable-nls option. Press Return to acknowledge the previous 6 paragraphs.
Abrimos outra terminal e imos facendo cada un dos pasos que se nos indican
cp po/Makevars.template po/Makevars
nano po/POTFILES.in
engadimos o que sexa preciso en función do aplicativo:
# List of source files containing translable strings. src/main.c src/interface.c src/callbacks.c src/outro_ficheiro src/ ...
Situar só os ficheiros que teñen cadeas traducibles por gettext
Aínda que non nolo di na mensaxe de saída de “gettextize”, agora deberemos crear o Ficheiro “LINGUAS” que é unha listaxe dos idiomas que vai procesar durante a compilación
nano po/LINGUAS
e nel engadimos os idiomas separados por un espazo ou, se nos gusta máis, un en cada liña
es gl
ou
es gl
Seguimos co que nos indica gettextize na mensaxe e executamos:
aclocal -I m4
autoconf
wget http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess wget http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
cp /usr/share/gettext/gettext.h .
Opcionalmente
cp -r /usr/share/gettext/intl/ .
- Árbore de ficheiros
./ ./ |----aplicativon |----aplicativo | | |----src/ |----src/ | | | | | |---- ... | |---- ... | |---- ... | |---- ... | | |----po/ |----po/ | | | | | |----aplicativo.pot | |----aplicativo.pot | |----es.po | |----es.po | |----gl.po | |----gl.po | |---- ... | |---- ... | | |---- ... |----intl/ |---- ... | | | |---- ... | |---- ...
Compilación final
./configure make make install
Construír un “.deb”
Como isto é tema doutro manual e hai bastantes e bos na rede, deixo as ligazóns:
- http://women.debian.org/wiki/Spanish/PackagingTutorial
- http://women.debian.org/wiki/Spanish/BuildingTutorial
- http://soleup.eup.uva.es/mario/post/1/446
- http://www.musix.org.ar/wiki/index.php/Como_hacer_un_paquete_db
Un metodo sinxelo que permite controlar algunhas variables é “checkinstall”
E aínda que con poucas posibilidades de control é moi sinxelo utilizar o script “autodeb”
Como crear paquetes “.deb” con “checkinstall”
- http://www.glug.es/node/2447
- http://www.linuxfocus.org/Castellano/December2004/article360.shtml
Como crear paquetes “.deb” con AutoDeb
Descargamos o script
- http://apt.byethost14.com/autodeb.sh
e dámoslle os permisos necesarios:
chmod +x autodeb.sh
executamos o script:
# (ou sudo) ./autodeb.sh
Se executamos
# (ou sudo) ./autodeb.sh --gnome
Autodeb execútase en modo gráfico e traballa sobre o ficheiro comprimido -tarball- (tgz, tar.gz, etc…)
- http://www.tuxi.com.ar/2007/12/05/autodeb-compilar-programas-y-generar-archivos-deb-automaticamente/
Corrección final
Nos aplicativos que de forma predeterminada instálansenos en “/usr/local/bin/XXXX” e os ficheiros de idioma “.mo” en “/usr/local/share/locale” pero queremos forzar a instalación en “/usr/bin/XXXX” e os ficheiros de idioma en “/usr/share/local”, teremos que facer a configuración coa instrución:
./configure --prefix=/usr
Asegurandonos de antemán que a ruta en “bindtextdomain” é:
bindtextdomain( "absvolume", "/usr/share/locale" );
Referencias
http://www.gnu.org/software/gettext/manual/gettext.html
http://www.alu.ua.es/p/psp4/Documentacion/Febrero_2002/gettext2.html
http://www.escomposlinux.org/lfs-es/lfs-es-6.3/chapter06/gettext.html
http://www.alu.ua.es/p/psp4/Documentacion/Febrero_2002/gettext.html