| ~kiko | Resumé | Software | Diary | Papers | Outdoors | Travel | Book shelf | Developer |
August 2000 (Updated March 2003)
Gettext has been built into glibc2/libc6, so there is no need for linking with libintl (-lintl) any more if you're on a Linux glibc system such as Slackware 7. If you're a platform other than linux you will need to link with libintl, which is included with gettext; look for the latest version at here.
setlocale(),
bindtextdomain() and
textdomain(). These lines require some commenting:
setlocale(LC_ALL,"");
LC_ALL is a catch-all Locale Category (LC); setting it
will alter all LC categories. There are other,
specific, categories for translations; for example
LC_MESSAGES is the LC (LC) for message translation;
LC_CTYPE is the category that indicates the character
set supported.
By setting the locale to "", you are implicitly
assigning the locale to the user's defined locale (grabbed from the
user's LC or LANG environment variables).
If there is no user-defined locale, the default locale "C" is used.
bindtextdomain("foo","/usr/local/share/locale/");
This command binds the name "foo" to the directory root of the
message files. This is used to specify where you want your locale
files stored; using the standard
/usr/local/share/locale or
/usr/share/locale is a good idea. "foo" should
correspond to the application name; you will use it when setting the
gettext domain through textdomain(), and it corresponds
to the name of the file to be looked up in the appropriate locale
directory.
The bindtextdomain() call is not mandatory; if you
choose to install your file in the system's default locale directory
it can be omitted. Since the default can change from system to
system, however, it is recommended.
textdomain("foo");
This sets the application name as "foo", as cited above. This makes
gettext calls look for the file foo.po in the appropriate
directory. By binding various domains and setting the textdomain (or
using dcgettext(), explained elsewhere) at runtime, you can switch
between different domains as desired.
Substitute string references such as
printf("foo");
for code using gettext():
printf(gettext("foo"));
To make things simple, _() (the underscore function) is
often defined as shorthand for gettext():
#define _(str) gettext(str)
printf(_("foo"));
[This has impact on the way you call xgettext; check for this on next section]
xgettext -k_ foo.c -o foo.po
This will create the file foo.po file with the messages marked in
your sourcefile. Based on the source file
#include <libintl.h>
#define _(str) gettext(str)
int main() {
setlocale(LC_MESSAGES,"");
setlocale(LC_CTYPE,"");
bindtextdomain("foo","/usr/local/share/locale");
textdomain("foo");
printf(_("foo_in_english"));
printf(_("bar_in_english"));
}
the generated .po file will look like
# [ommiting comments and meta-definitions]
#: foo.c:10
msgid "foo_in_english\n"
msgstr ""
#: foo.c:12
msgid "bar_in_english\n"
msgstr ""
This file should be used as a template for all translations you will
perform. The .po file is a simple key-value database: each msgid
field contains the initial (default) string for the C (default)
locale, and msgstr contains the translated string. Gettext is smart
in using msgid as a key to access the message translation; this
reduces enormously the work you'd have in modifying the source code
and indexing the translations.
# [Omitted target language headings, etc]
#: foo.c:10
msgid "foo_in_english\n"
msgstr "foo_in_target_language\n"
#: foo.c:12
msgid "bar_in_english\n"
msgstr "bar_in_target_language\n"
Note here that you should *not* have a msgid of "". gettext("")
returns the header information in the po-file and that isn't what
you want.
msgfmt foo.po -o foo.mo
If you have trouble running msgfmt, you might want to use the
-v option to it, which increases verbosity and shows
which errors might have happened in more detail.
<LOCALE_ROOT>/<LL_CODE>/LC_MESSAGES/
<LOCALE_ROOT> is the directory you set to your domain in your
bindtextdomain() call. LL_CODE is the ISO
639 code for the language you're providing a translated catalog. The
/usr/local/share/locale; a German (ISO 639 code "de")
message catalog should be copied to
/usr/local/share/locale/de/LC_MESSAGES/foo.mo
As a sidenote - if you're developing a standalone test and don't
want to install the message file, simple use bindtextdomain to point
elsewhere. If you use a non-absolute directory it will base it on
your current path:
bindtextdomain("foo","intl");
will have gettext() search for the message catalogs in the hierarchy
rooted inside the directory intl on your current working directory.
LC_MESSAGES to an ISO
code for a language you've translated to, recompiling and running your
program. It should work, and if it doesn't, 'strace -eopen' is your
friend. The most common mistake I've encontered is placing the file in
the wrong place, and stracing will certainly help you see where libc is
looking.
There shouldn't be much difference towards C gettext other than that.
glade and libglade now support i18n as well, and even include a libglade-xgettext for extracting strings from the XML; building an internationalized GTK interface is now a trivial task.
PHP (3.0.7 onwards) also includes a full set of calls to gettext (including a _() gettext() alias), making internationalizing Websites and applications straightforward and portable.
Python has full gettext support. Version 2.0 onwards includes full Unicode and intl support inbuilt; if you are using 1.5.2 or earlier you can use Martin von Loewis' standalone intl module.. For PyGTK issues on internationalization see the PyGTK FAQ