Internationalisation et adaptation locale
Les applications gtkmm prennent facilement en charge plusieurs langues, y compris les langues non-européennes telles que le chinois et les langues s'écrivant de droite à gauche comme l'arabe. Une application gtkmm écrite et traduite de manière appropriée, utilise lors de son exécution la langue définie par l'environnement utilisateur.
Vous ne devez pas anticiper le besoin de prendre en charge telles ou telles autres langues, mais vous ne devez jamais en exclure l'utilisation. De plus, il est plus facile de développer une application correctement dès le début que de l'améliorer après coup.
Le processus consistant à écrire un code source autorisant la traduction s'appelle l'internationalisation, souvent abrégé en i18n. Le processus d'adaptation locale (localisation) quelquefois abrégé en l10n, met à disposition du texte, fondé sur ce code source, traduit dans d'autres langues.
Le gros du travail dans le processus d'internationalisation est de rechercher les chaînes visibles par l'utilisateur et de les marquer comme traduisibles. Ce travail n'a pas besoin d'être réalisé en une seule fois - si vous réglez l'infrastructure du projet correctement, votre application fonctionnera normalement quel que soit le nombre de chaînes traîtées.
String literals should be typed in the source code in English, but surrounded by a macro. The gettext (or intltool) utility can then extract the marked strings for translation, and substitute the translated text at runtime.

Préparation du projet
In the instructions below we will assume that you will not be using gettext directly, but intltool, which was written specifically for GNOME. intltool uses gettext(), which extracts strings from source code, but intltool can also combine strings from other files, for example from desktop menu details, and GUI resource files such as Glade files, into standard gettext .pot/.po files.
We also assume that you are using autotools (e.g. automake and autoconf) to build your project, and that you are using ./autogen.sh from gnome-common, which, among other things, takes care of some intltool initialization.
Créez un sous-répertoire nommé po dans le répertoire racine de votre projet. Ce répertoire contiendra en phase finale toutes les traductions. Dans celui-ci, créez un fichier nommé LINGUAS et un fichier nommé POTFILES.in. Il est aussi de pratique courante de créer un fichier ChangeLog dans le répertoire po pour que les traducteurs puissent garder trace des modifications de traduction.
LINGUAS contient une liste triée alphabétiquement des codes identifiant toutes les langues dans lesquelles votre programme est traduit (les lignes de commentaires débutant par # sont ignorées). Chaque code de langue listé dans LINGUAS doit avoir un fichier .po correspondant. Ainsi, si votre programme a des traductions en allemand et en japonais, le fichier LINGUAS ressemblera à :

# keep this file sorted alphabetically, one language code per line
de
ja

(en outre, vous devez avoir dans le dossier po des fichiers ja.po et de.po contenant respectivement la traduction japonaise et allemande).
POTFILES.in est une liste des chemins vers tous les fichiers contenant des chaînes marquées pour la traduction, à partir du répertoire racine du projet. Ainsi, par exemple, si les sources du projet sont situées dans un sous-répertoire nommé src et si vous avez deux fichiers contenant des chaînes à traduire, votre fichier POTFILES.in ressemble à quelque chose comme :

src/main.cc
src/other.cc

Si vous utilisez gettext directement, vous ne pouvez marquer pour traduction que les chaînes situées dans les fichiers de code source. Mais, si vous vous servez de intltool, vous pouvez marquer des chaînes pour traduction dans divers autres formats de fichier, y compris les fichiers d'interface utilisateur Glade, xml, .desktop et de nombreux autres. Donc si vous avez conçu une partie des interfaces utilisateur du projet à l'aide de Glade, ajoutez également vos fichiers .glade dans la liste du fichier POTFILES.in.
Maintenant que vous disposez d'un endroit où mettre vos traductions, il est nécessaire d'initialiser intltool et gettext. Ajoutez le code ci-après dans votre fichier configure.ac, en mettant le nom de votre programme à la place de « nom_programme » :

IT_PROG_INTLTOOL([0.35.0])

GETTEXT_PACKAGE=nom_programme
AC_SUBST(GETTEXT_PACKAGE)
AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"],
                   [Domaine de portée de gettext])
AM_GLIB_GNU_GETTEXT

PROGRAMNAME_LOCALEDIR=[${datadir}/locale]
AC_SUBST(PROGRAMNAME_LOCALEDIR)

La variable PROGRAMNAME_LOCALEDIR sera utilisée plus tard dans le fichier Makefile.am pour définir une macro utilisée quand vous initialisez gettext dans votre code source.
In the top-level Makefile.am:
Add po to the SUBDIRS variable. Without this, your translations won't get built and installed when you build the program
Define INTLTOOL_FILES as:

INTLTOOL_FILES = intltool-extract.in \
                 intltool-merge.in \
                 intltool-update.in

Add INTLTOOL_FILES to the EXTRA_DIST list of files. This ensures that when you do a make dist, these commands will be included in the source tarball.
Update your DISTCLEANFILES:

DISTCLEANFILES = ... intltool-extract \
                 intltool-merge \
                 intltool-update \
                 po/.intltool-merge-cache

Dans votre src/Makefile.am, mettez à jour votre variable AM_CPPFLAGS en ajoutant la définition de macro préprocesseur suivante :

AM_CPPFLAGS = ... -DPROGRAMNAME_LOCALEDIR=\"${PROGRAMNAME_LOCALEDIR}\"

Cette macro sert quand vous initialisez gettext dans votre code source.

Marquage des chaînes à traduire
Les chaînes littérales doivent être saisies dans le code source en anglais, mais elles doivent être incorporées dans un appel à la fonction gettext(). Ces chaînes seront extraites pour traduction et les traductions utilisées lors de l'exécution au lieu des chaînes originelles en anglais.
Le paquetage GNU gettext vous permet de marquer des chaînes dans le code source, de les extraire pour traduction et d'utiliser les chaînes traduites dans votre application.
However, Glib defines gettext() support macros which are shorter wrappers in an easy-to-use form. To use these macros, include <glibmm/i18n.h>, and then, for example, substitute:

display_message("Getting ready for i18n.");

with:

display_message(_("Getting ready for i18n."));

For reference, it is possible to generate a file which contains all strings which appear in your code, even if they are not marked for translation, together with file name and line number references. To generate such a file named my-strings, execute the following command, within the source code directory:

xgettext -a -o my-strings --omit-header *.cc *.h

Finally, to let your program use the translation for the current locale, add this code to the beginning of your main.cc file, to initialize gettext.

bindtextdomain(GETTEXT_PACKAGE, PROGRAMNAME_LOCALEDIR);
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
textdomain(GETTEXT_PACKAGE);

Comment fonctionne gettext
Le script intltool / xgettext extrait les chaînes et les place dans un fichier mon_paquet.pot. Les traducteurs de votre application créent leur traduction en faisant d'abord une copie du fichier .pot dans un fichier nom_de_locale.po. Une « locale » identifie une langue et un codage pour cette langue, y compris les formats numériques et de date. Ultérieurement, si le texte de votre code source a été modifié, le script msmerge sera utilisé pour mettre à jour les fichiers nom_de_locale.po à partir d'une nouvelle génération du fichier .pot.
Au moment de l'installation, les fichiers .po sont convertis dans un format binaire (avec l'extension .mo) et placés dans un répertoire système pour les fichiers d'environnement linguistique, par exemple /usr/share/locale/.
Quand l'application est lancée, la bibliothèque gettext vérifie dans le répertoire système s'il y a un fichier .mo pour l'environnement linguistique de l'utilisateur (environnement que vous pouvez définir avec, par exemple, « export LANG=de_DE.UTF-8 » à partir d'un terminal). Puis, quand le programme atteint un appel gettext, il examine s'il existe une traduction pour la chaîne donnée. Si aucune n'est trouvée, la chaîne d'origine est utilisée.

Vérification et ajout de traductions
To convince yourself that you've done well, you may wish to add a translation for a new locale. In order to do that, go to the po subdirectory of your project and execute the following command:

intltool-update --pot

Cette commande crée un fichier nommé nom_du_programme.pot. Copiez ensuite ce fichier en lui donnant le nom code_langue.po, comme de.po ou hu.po. Ajoutez également ce code langue dans le fichier LINGUAS. Le fichier .po comporte un en-tête et une liste des chaînes en anglais, avec des emplacements pour les chaînes traduites à saisir. Assurez-vous que vous avez défini et utilisé UTF-8 comme codage du fichier .po (comme défini dans l'en-tête, mais utilisé dans le contenu).
Il est possible que certaines chaînes soient marquées fuzzy (approximatives) dans le fichier .po. Les traductions ainsi marquées ne se substituent pas à la chaîne originelle. Pour qu'elles apparaissent, enlevez simplement la marque fuzzy.

Ressources
More information about what lies behind the internationalization and localization process is presented and demonstrated in:
L10N Guidelines for Developers
Intltool README
How to use Git for GNOME translators
gettext manual
gtkmm_hello example package
gnomemm_hello example package

En attendant UTF8
Une application correctement internationalisée ne doit pas faire de suppositions sur le nombre d'octets par caractère. Cela signifie que vous ne devez pas faire d'arithmétique de pointeurs pour cheminer parmi les caractères d'une chaîne ; cela signifie aussi que vous ne devez pas vous servir d'objets std::string ou de fonctions C standard telles que strlen() car elle se fondent sur de telles suppositions pour effectuer leurs calculs.
Même si vous avez certainement déjà délaissé les tableaux de char* vides et l'arithmétique des pointeurs en vous servant de std::string, vous devez maintenant utiliser à la place des objets Glib::ustring. Voyez le chapitre concernant les Glib::ustring dans les Fondamentaux.

Glib::ustring et std::iostreams
Unfortunately, the integration with the standard iostreams is not completely foolproof. gtkmm converts Glib::ustrings to a locale-specific encoding (which usually is not UTF-8) if you output them to an ostream with operator<<. Likewise, retrieving Glib::ustrings from istream with operator>> causes a conversion in the opposite direction. But this scheme breaks down if you go through a std::string, e.g. by inputting text from a stream to a std::string and then implicitly converting it to a Glib::ustring. If the string contained non-ASCII characters and the current locale is not UTF-8 encoded, the result is a corrupted Glib::ustring. You can work around this with a manual conversion. For instance, to retrieve the std::string from a ostringstream:

std::ostringstream output;
output.imbue(std::locale("")); // use the user's locale for this stream
output << percentage << " % done";
label->set_text(Glib::locale_to_utf8(output.str()));

Embûches
Voici quelques erreurs courantes que vous découvririez peut-être par vous-même. Mais il se peut que ce paragraphe vous aide à les éviter.

Mêmes chaînes, sémantiques différentes
Quelquefois deux chaînes en anglais sont identiques, mais ont des significations différentes selon le contexte ; elles ne mériteraient donc probablement pas d'être traduites de la même façon. Comme les chaînes en anglais sont utilisées comme clés de recherche, cela pose problème.
In these cases, you should add extra characters to the strings. For instance, use "jumps[noun]" and "jumps[verb]" instead of just "jumps" and strip them again outside the gettext call. If you add extra characters you should also add a comment for the translators before the gettext call. Such comments will be shown in the .po files. For instance:

// note to translators: don't translate the "[noun]" part - it is
// just here to distinguish the string from another "jumps" string
text = strip(gettext("jumps[noun]"), "[noun]");

Composition de chaînes
Les programmeurs C se servent de la fonction sprintf() pour composer et concaténer des chaînes. Le C++ favorise les flux, mais malheureusement, cette approche rend la traduction difficile, parce que chaque fragment de texte est traduit séparément sans permettre aux traducteurs de les ré-agencer selon la grammaire de la langue.
Par exemple, ce code risque d'être sujet à problèmes :

std::cout << _("Current amount: ") << amount
          << _(" Future: ") << future << std::endl;

label.set_text(_("Really delete ") + filename + _(" now?"));

So you should either avoid this situation or use Glib::ustring::compose() which supports syntax such as:

std::cout << Glib::ustring::compose(
             _("Current amount: %1 Future: %2"), amount, future) << std::endl;

label.set_text(Glib::ustring::compose(_("Really delete %1 now?"), filename));

Hypothèse concernant la taille d'affichage des chaînes
Vous ne savez jamais quel sera l'espace pris à l'écran par une chaîne après traduction. Il est tout à fait possible qu'elle soit deux fois plus longue que la chaîne originelle en anglais. Par chance, la plupart des éléments graphiques gtkmm se redimensionnent à la taille voulue lors de l'exécution.

Mots inhabituels
Vous devez éviter les abréviations mystérieuses, l'argot ou du jargon de spécialiste. Ces termes sont généralement difficiles à traduire et souvent même à comprendre pour les autochtones. Par exemple, préférez « application » à « app ».

Utilisation de caractères non-ASCII dans les chaînes
Actuellement, gettext ne prend pas en charge les caractères non-ASCII (c'est-à-dire tout caractère de code supérieur à 127) dans le code source. Par exemple, vous ne pouvez pas utiliser le signe de copyright (©).
To work around this, you could write a comment in the source code just before the string, telling the translators to use the special character if it is available in their languages. For english, you could then make an American English en_US.po translation which used that special character.

Obtenir de l'aide avec les traductions
If your program is free software, there is a whole GNOME subproject devoted to helping you make translations, the GNOME Translation Project.
The way it works is that you upload your source code to a git repository where translators can access it, then contact the gnome-i18n mailing list and ask to have your program added to the list of modules to translate.
Assurez-vous que le fichier POTFILES.in dans le sous-répertoire po/ est à jour (la commande intltool‑update -M peut vous aider pour cela) de façon à ce que les traducteurs aient toujours accès à des fichiers mon_programme.pot actualisés et gelez tout simplement les chaînes au moins quelques jours avant de téléverser une nouvelle version en l'annonçant sur gnome-i18n. Selon le nombre de chaînes que votre programme comporte et selon sa popularité, les traductions vont alors commencer à apparaître dans les fichiers nom_de_langue.po.
Notez que la plupart des équipes de traducteurs ne sont constituées que de 1 à 3 personnes ; donc, si votre programme comporte beaucoup de chaînes, cela peut prendre beaucoup de temps avant que quelqu'un ait le temps d'y jeter un coup d'œil. De plus, pas mal de traducteurs ne souhaitent pas dilapider leur temps (la traduction est une tâche très consommatrice de temps) donc, il ne prendront en charge votre projet que s'il est vraiment sérieux (dans le sens, travaillé et maintenu) ; si ce n'est pas le cas il peuvent décider de s'occuper sur d'autres projets.