Retrouvez cet article dans : Linux Magazine 84
Implémentez le support de l’internationalisation dans votre application et intégrez-la avec les auto-tools.
Introduction
Le sujet de l’internationalisation de projets a déjà été abordé dans les colonnes de Linux Magazine. Dans le numéro 10, vous participiez à la réalisation d’une application GNOME multilingue.
Mais c’est la dernière partie de l’introduction aux auto-tools du numéro 24 que nous allons reprendre ici, en nous focalisant sur l’internationalisation d’un projet avec les auto-tools, ce qui est le but de l’article.
L’internationalisation d’un programme sans utilisation des auto-tools est possible. Vous pouvez utiliser gettext, comme nous le verrons plus loin.
Cependant, ce serait à vous de coder certains mécanismes que nous ne décrirons pas ici. Pour cela, veuillez vous reporter à l’ouvrage C en action (O’Reilly) qui y consacre un chapitre entier. Nous préférerons ici ne pas nous priver des avantages que procurent les auto-tools.
Bonjour le monde
L’exemple que nous avons l’habitude de prendre, à savoir afficher « Bonjour le monde », convient presque. En effet, nous pourrions l’utiliser tel quel et proposer une traduction dans d’autres langues dont l’anglais, langue dominante de notre civilisation.
Cependant, à cause de cette domination linguistique précisément, nous allons préférer un affichage natif en anglais et une traduction en français.
Cela a peu d’importance pour la traduction français/anglais, mais cela en revêt tout de suite plus lorsque vous devez traduire de la langue native de votre programme en une langue exotique.
Vous trouverez probablement plus de gens aptes à traduire de l’anglais vers le portugais ou le polonais que du français vers l’une de ces langues.
Hello World
Notre exemple parlera donc anglais contrairement aux articles précédents. Rassurez-vous, l’article a pour objet de le traduire ! Mais voici le code :
src/main.c
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char*argv[]) {
printf(“Hello World\n”);
exit(EXIT_SUCCESS);
}
configure.ac
AC_PREREQ(2.59) AC_INIT(bonjour_le_monde, 0.1, ymettier@libertysurf.fr) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADER([config.h]) # Checks for programs. AC_PROG_CC AC_PROG_MAKE_SET # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdlib.h]) # Checks for library functions. AC_CONFIG_FILES([ Makefile src/Makefile ]) AC_OUTPUT
Makefile.am
SUBDIRS=src
src/Makefile.am
bin_PROGRAMS=bonjour bonjour_SOURCES=main.c
Étape 1 : internationalisation des auto-fichiers
Ajout du support de gettext
La première chose à faire est d’exécuter gettextize -c. Nous passons sur les répertoires créés et les nouveaux fichiers installés.
Vous noterez néanmoins que deux nouveaux répertoires apparaissent : m4/ pour contenir des macros pour l’internationalisation avec les auto-tools et po/ le répertoire dédié aux traductions.
À la fin de l’opération, vous pouvez lire ce genre de choses :
Updating Makefile.am (backup is in Makefile.am~) Updating configure.ac (backup is in configure.ac~) Adding an entry to ChangeLog (backup is in ChangeLog~) 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.5 (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.
Nous allons revenir sur tout ce qui précède. D’abord, il nous est indiqué que les fichiers Makefile.am et configure.ac ont été modifiés. Vous pouvez les éditer et vous vous rendrez compte que gettextize a ajouté le répertoire po/ et tout ce qui s’y rapporte partout où cela était nécessaire.
Comme indiqué ensuite, vous pouvez ajouter AM_GNU_GETTEXT([external]) dans votre fichier configure.ac afin que votre script configure effectue les tests nécessaires sur gettext. La mention external signifie que la bibliothèque libintl n’est pas incluse et doit avoir été installée au préalable sur le système. Si vous tenez à inclure cette bibliothèque dans votre projet, vous avez la possibilité d’exécuter gettextize -c --intl qui fera le nécessaire.
Vous devez ensuite créer le fichier po/Makevars. Renommez le fichier po/Makevars.template en po/Makevars et le tour est joué. Il n’y a a priori rien à modifier dans ce fichier dans le cadre d’un projet simple.
Toujours dans le répertoire po/, vous devez éditer le fichier POTFILES.in afin d’y ajouter la liste des fichiers contenant des chaînes de caractères à traduire. Voici le contenu de ce fichier pour nous :
# List of source files which contain translatable strings. src/main.c
Dans le désordre, il est conseillé de copier le fichier gettext.h de votre système avec vos sources. Vous pouvez l’installer dans le répertoire src/ et modifier Makefile.am en conséquence.
Vous en profiterez pour ajouter une ligne #include <gettext.h> dans chaque fichier de sources contenant des chaînes à traduire. Mais nous reviendrons à cela plus loin, dans l’étape 2.
Maintenant, exécutez la commande aclocal -I m4 comme indiqué plus haut. Lancez ensuite la suite classique autoconf; autoheader; automake -a -c.
Vous remarquez qu’automake se charge lui-même d’installer les fichiers config.sub et config.guess. Vous n’avez donc pas besoin d’aller chercher ces fichiers à l’endroit indiqué.
$ automake -a -c configure.ac:11: installing `./config.guess’ configure.ac:11: installing `./config.sub’
Lancez encore ceci pour tester que tout fonctionne : ./configure; make distcheck et la première étape sera achevée. Nous ne touchons maintenant plus aux fichiers de définitions pour les auto-tools.
Définition de LOCALEDIR
Dans les sources, nous allons avoir besoin du chemin contenant les locales. Celui-ci est généralement défini dans une macro du joyeux nom de LOCALEDIR.
Partout où vous avez une fonction main() (le répertoire src/ dans notre cas), ajoutez ceci au fichier Makefile.am. Cette macro n’est a priori pas utilisée ailleurs.
datadir = @datadir@ localedir = $(datadir)/locale DEFS = -DLOCALEDIR=\»$(localedir)\» @DEFS@
Il pourrait exister une autre façon de faire, à savoir définir la macro dans le fichier config.h. Pour cela, nous utiliserions AC_DEFINE_UNQUOTED. Cependant, un simple echo ${datadir} dans configure.ac motre le problème :
${prefix}/share
En poussant un peu plus loin, avec par exemple eval bonjour_localedir=${datadir}/locale, nous voyons qu’il vaut mieux abandonner la piste :
NONE/share/locale
En fait, le script configure ne définit la variable ${prefix} que très tard. En outre, il est plutôt déconseillé de forcer cette valeur plus tôt ou d’en choisir une soi-même. Mieux vaut se contenter de ce que propose autoconf et définir la macro dans le fichier Makefile.am, comme nous l’avons vu ci-dessus.
Étape 2 : internationalisation du code
Les fichiers sources
Dans cette étape, nous allons effectuer des modifications dans le code source. Dans chaque fichier de code, vous allez vous assurer que le fichier d’en-tête config.h est inclus, et vous allez le faire suivre d’une inclusion de gettext.h que nous avons recopié dans l’étape 1. Vous ajouterez de plus quelques définitions de macros qui permettront de rendre plus aisé le marquage des chaînes à traduire. Voici ce que cela peut donner, au début d’un fichier source :
#include <config.h> #include <gettext.h> #define _(String) gettext (String) #define N_(String) String
L’inclusion du fichier config.h est importante car, grâce aux auto-tools, il contient des lignes de code nécessaires à gettext.h. Par exemple, sans lui, vous pouvez avoir par la suite une erreur à cause d’un LC_ALL manquant.
Ces trois macros servent, comme nous l’avons vu juste avant, au marquage des chaînes à traduire. En fait, le mécanisme de traduction passe par l’appel à la fonction gettext() à laquelle vous indiquez la chaîne à traduire. Mais le nom de cette fonction est trop long à écrire si vous devez le faire pour chaque chaîne. Aussi, il est plus simple d’utiliser la macro _() à la place.
Parfois, il n’est pas possible d’appeler gettext() dans le code à l’endroit où est définie la chaîne. Voici un exemple :
char chaine[] = «Plouf !»;
Ici, la chaîne est statique. Vous devez donc appeler gettext() ailleurs. Par exemple, si par la suite vous avez printf(«%s\n», chaine);, vous mettrez à la place ceci : printf(«%s\n», gettext(chaine));. Cela devrait fonctionner tout aussi bien, avec l’inconvénient cependant de ne pas marquer la chaîne de caractères à traduire. C’est là que vous utilisez N_() qui ne fait rien si ce n’est la marquer. Le programme xgettext, qui réalise l’extraction des chaînes à traduire, saura la trouver grâce à cela.
main()
Le fichier source contenant main() reçoit un traitement particulier supplémentaire. En effet, au début de la fonction principale, vous devez ajouter les trois lignes suivantes, telles quelles :
setlocale (LC_ALL, «»); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE);
Ces trois lignes de code initialisent tout le nécessaire pour l’internationalisation de votre code, aussi bien la traduction que la localisation. Les macros utilisées ici ne tombent pas du ciel. La première, LC_ALL, est définie dans les en-têtes système. PACKAGE n’est rien d’autre que le nom de notre paquet, défini dans config.h. Quant à LOCALEDIR, nous avons sué plus haut pour le définir dans le fichier Makefile.am. C’est ici qu’il est utilisé.
Recompilation
Si vous n’avez pas fait d’erreur, l’exécution de make s’effectue sans difficulté. Sinon, peut-être avez-vous oublié de définir une macro ?
Étape 3 : traduction des chaînes
La traduction des chaînes de caractères est, au niveau informatique, l’étape la plus simple probablement.
Nouvelle traduction
Démarrer une nouvelle traduction s’effectue en trois parties :
- Ajoutez ou complétez la macro
LINGUASdans le fichierconfigure.ac. Indiquez-y la langue de la nouvelle traduction, par exemplefrpour notre bonne vieille langue française. - Dans le répertoire
po/, trouvez le fichier portant le nom du projet et d’extension.pot. Faites-en une copie que vous appellerezxx.pooùxxindique la langue. Une traduction française ira donc dans le fichierfr.po. - Éditez le début du fichier de traduction pour y indiquer votre nom, la date de traduction, et d’autres choses comme le jeu de caractères choisi (par exemple ISO-8859-15 ou UTF-8).
Il ne vous reste ensuite plus qu’à traduire. Après les en-têtes, le fichier de traduction contient un certain nombre de séquences avec une chaîne issue des sources indiquée par msgid suivie d’une traduction qui n’est initialement pas faite, précédée de msgstr. Vous devez laisser les msgid tels quels et traduire les msgstr.
Compléter une traduction
Avant de compléter une traduction, vous devez vous assurer que le fichier d’extension .po que vous voulez traduire est à jour. Pour cela, allez dans le répertoire po/ et exécutez make update-po. Vous pouvez maintenant vous mettre à traduire.
Vous remarquerez probablement, pour un projet un peu plus gros que le nôtre, des chaînes à traduire précédées d’un commentaire # fuzzy. Cela signifie que la traduction est une traduction automatique. Il est très important de chercher tous ces commentaires afin de vérifier les traductions. Il est tout aussi important de supprimer ce commentaire lorsque vous avez vérifié (et parfois corrigé) la traduction. En effet, ce n’est pas la peine de vous donner du travail supplémentaire de vérification pour la prochaine fois que vous traduisez.
Quelques remarques sur la traduction
Lorsque vous traduisez, et que la chaîne à traduire est longue, vous pouvez couper la chaîne en fermant les guillemets et en les ouvrant à nouveau à la ligne suivante. Le résultat sera concaténé, exactement comme cela se fait en C. Ainsi,
msgstr «deux lignes»
et
msgstr «deux « «lignes»
sont équivalents. N’hésitez pas à en profiter pour plus de lisibilité.
Lors de la mise à jour des chaînes de caractères (réalisée avec make update-po dans le répertoire po/ ou avec make distcheck), certains points de vos traductions sont vérifiés. En particulier, si la chaîne originale contient un retour à la ligne à sa fin, il faudra que votre traduction le contienne aussi. Avis aux programmeurs : le pluriel des mots est un sujet complexe.
Si le français respecte le même genre de règles qu’en anglais, avec un s au pluriel, ce n’est pas le cas dans toutes les langues, et cela commence à côté de chez nous. Les Italiens aiment remplacer la fin des mots par un i et non un s lorsqu’ils parlent au pluriel. De plus, la notion de pluriel n’est pas la même partout.
Encore plus près de chez, disons tout simplement chez nous en France, le pluriel se définit par un nombre strictement supérieur à un. De l’autre côté de la Manche, en Angleterre, le pluriel correspond à un nombre différent de un.
Le zéro est donc pluriel chez les anglais et singulier chez nous. Cette problématique est décrite dans le manuel de gettext.
Le résultat
À titre d’illustration, voici tous les fichiers ayant subi des modifications par rapport aux fichiers initiaux, suite à l’internationalisation de notre code.
configure.ac
AC_PREREQ(2.59) AC_INIT(bonjour_le_monde, 0.1, ymettier@libertysurf.fr) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADER([config.h]) # Checks for programs. AC_PROG_CC AC_PROG_MAKE_SET AM_GNU_GETTEXT([external]) ALL_LINGUAS=fr # Checks for libraries. # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdlib.h]) # Checks for typedefs, structures, and compiler characteristics.
configure.ac AC_PREREQ(2.59) AC_INIT(bonjour_le_monde, 0.1, ymettier@libertysurf.fr) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADER([config.h]) # Checks for programs. AC_PROG_CC AC_PROG_MAKE_SET AM_GNU_GETTEXT([external]) ALL_LINGUAS=fr # Checks for libraries. # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdlib.h]) # Checks for typedefs, structures, and compiler characteristics. # Checks for library functions. AC_CONFIG_FILES([ po/Makefile.in Makefile src/Makefile ]) AC_OUTPUT
Makefile.am
SUBDIRS= po src ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = config.rpath mkinstalldirs m4/ChangeLog
src/Makefile.am
datadir = @datadir@ localedir = $(datadir)/locale DEFS = -DLOCALEDIR=\”$(localedir)\” @DEFS@ bin_PROGRAMS=bonjour bonjour_SOURCES=main.c gettext.h
src/main.c
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <gettext.h>
#define _(String) gettext (String)
#define N_(String) String
int
main (int argc, char *argv[])
{
setlocale (LC_ALL, “”);
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
printf (_(“Hello World\n”));
exit (EXIT_SUCCESS);
}
po/POTFILES.in
# List of source files which contain translatable strings. src/main.c
po/fr.po
# Translation for the bonjour project # Copyright (C) YEAR Free Software Foundation, Inc. # This file is distributed under the same license as the PACKAGE package. # Yves Mettier <ymettier@libertysurf.fr>, 2006. # msgid “” msgstr “” “Project-Id-Version: bonjour_le_monde 0.1\n” «Report-Msgid-Bugs-To: ymettier@libertysurf.fr\n» «POT-Creation-Date: 2006-01-13 00:03+0100\n» «PO-Revision-Date: 2006-01-09 17:29+GMT+1\n» «Last-Translator: Yves Mettier <ymettier@libertysurf.fr>\n» “Language-Team: Français <fr@li.org>\n» «MIME-Version: 1.0\n» «Content-Type: text/plain; charset=ISO-8859-15\n» «Content-Transfer-Encoding: 8bit\n» #: src/main.c:14 #, c-format msgid «Hello World\n» msgstr “Bonjour le monde\n”
Conclusion
Un programme ne parle pas encore français ? Votre projet se limite toujours à la langue française ? Vous avez maintenant de quoi faire pour le traduire. Pour un projet encore non internationalisé, écrivez vos chaînes en anglais pour toucher un public de traducteurs plus grand et, surtout, traduisez d’emblée en français. Cela vous permettra de vous rendre compte d’erreurs ou de difficultés de traduction que vous corrigerez immédiatement. Cela évitera à un traducteur contributeur dans une autre langue de se trouver face à des difficultés de base, qui peuvent le mener à l’abandon de sa contribution spontanée. Cela serait dommage. Alors, à vos dictionnaires bilingues !
Références
- Le manuel d’autoconf :
http://www.gnu.org/software/autoconf/manual/ - Le manuel d’automake :
http://www.gnu.org/software/automake/manual/ - Le manuel de gettext :
http://www.gnu.org/software/gettext/manual/ - C en action, O’Reilly, chapitres 17 et 20.
Retrouvez cet article dans : Linux Magazine 84

