GNUGettext is ridiculously easy to use and works wonders; however,there is little documentation on how to use it beyond the rather verboseGNU gettext manual. I outline here a simple tasklist forinternationalizing your applications so you don't have to suffer toomuch to learn how to use it. The tasklist is for C (and thus, C++), butif your language has support for gettext it is bound to function verysimilarly.
Gettext has been built into glibc2/libc6, so there is no need forlinking with libintl (-lintl) any more if you're on a Linux glibc system suchas Slackware 7. If you're aplatform other than linux you will need to link with libintl, which isincluded with gettext; look for the latest version at here.
- Alter your source to include
setlocale()
,bindtextdomain()
andtextdomain()
. These lines require some commenting:setlocale(LC_ALL,"");
LC_ALL
is a catch-all Locale Category (LC); setting itwill alter allLC
categories. There are other,specific, categories for translations; for exampleLC_MESSAGES
is the LC (LC) for message translation;LC_CTYPE
is the category that indicates the characterset supported.By setting the locale to
""
, you are implicitly assigning the locale to the user's defined locale (grabbed from the user'sLC
orLANG
environment variables). If there is no user-defined locale, the default locale "C" is used.bindtextdomain("foo","/usr/local/share/locale/");
/usr/local/share/locale
or/usr/share/locale
is a good idea. "foo" shouldcorrespond to the application name; you will use it when setting thegettext domain throughtextdomain()
, and it correspondsto the name of the file to be looked up in the appropriate localedirectory.The
bindtextdomain()
call is not mandatory; if youchoose to install your file in the system's default locale directoryit can be omitted. Since the default can change from system tosystem, however, it is recommended.textdomain("foo");
foo.po
in the appropriatedirectory. By binding various domains and setting the textdomain (orusing dcgettext(), explained elsewhere) at runtime, you can switchbetween different domains as desired. - Mark strings for extraction in your C source:
Substitute string references such as
printf("foo");
gettext()
:printf(gettext("foo"));
_()
(the underscore function) is often defined as shorthand forgettext()
:#define _(str) gettext(str)
printf(_("foo"));
[This has impact on the way you call xgettext; check for this onnext section]
- Extract these strings using xgettext:xgettext scans your source code and creates .po files that contain the messages to be translated based on the strings in your source code. It does this by checking which strings are wrapped in gettext() in your source; if you use a macro for gettext() such as _(), you must invoke xgettext specifying another keyword with the argument -k as in "xgettext -k_".
xgettext -k_ foo.c -o foo.po
#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 willperform. The .po file is a simple key-value database: each msgidfield contains the initial (default) string for the C (default)locale, and msgstr contains the translated string. Gettext is smartin using msgid as a key to access the message translation; thisreduces enormously the work you'd have in modifying the source codeand indexing the translations. - Make a copy of this .po file for each language you want, andtranslate the strings into the target languages. Your translatedstrings should look like this:
# [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. - Generate .mo files with msgfmt; this does the generation of themachine-dependant message catalogs.
msgfmt foo.po -o foo.mo
-v
option to it, which increases verbosity and shows which errors might have happened in more detail. - Place the message file in the proper place. The directory hierarchyis as follows:
<LOCALE_ROOT>/<LL_CODE>/
<LOCALE_ROOT> is the directory you set to your domain in yourLC_MESSAGES
/bindtextdomain()
call.LL_CODE
is the ISO639 code for the language you're providing a translated catalog. The part is the domain name you've set using textdomain(). Inour example, the text domain is "foo" and the locale root is/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
bindtextdomain("foo","intl");
- Test your set up by setting
LC_MESSAGES
to an ISOcode for a language you've translated to, recompiling and running yourprogram. It should work, and if it doesn't, 'strace -eopen' is yourfriend. The most common mistake I've encontered is placing the file inthe wrong place, and stracing will certainly help you see where libc islooking. - For other languages, basic items to check are:
- Is there gettext support in the API?
- Is there an easy way to extract strings from the source code(In other words, is there the equivalent of xgettext for thelanguage?
转载地址:http://www.async.com.br/~kiko/gettext.html