An Internationalized Software Project With Auto Tools
Prev Adding gettext Support Next

Adding gettext Support

The first step is to run gettextize, which does most of the job.
# gettextize
Creating po/ subdirectory
Symlinking file ABOUT-NLS
Symlinking file config.rpath
Symlinking file mkinstalldirs
Not copying intl/ directory.
Symlinking file po/Makefile.in.in

...

Symlinking file po/remove-potcdate.sin
Creating initial po/POTFILES.in
Creating po/ChangeLog
Creating directory m4
Symlinking file m4/codeset.m4

...

Symlinking file m4/xsize.m4
Creating m4/Makefile.am
Creating m4/ChangeLog
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.

Please run 'automake m4/Makefile' to create m4/Makefile.in

You might also want to copy the convenience header file gettext.h
from the /usr/local/share/gettext directory into your package.
It is a wrapper around  that implements the configure --disable-nls
option.

Press Return to acknowledge the previous 6 paragraphs.

Lots of files (and even directories) have been created and some files have been modified. A list of todos are given, which will now be worked on. Most important are the "Symlinking file mkinstalldirs" and "Creating m4/Makefile.am" lines. If they appear, either the used gettext or aclocal version is quite old (as currently on FreeBSD). In this case, they have been handled in Makefile.am and configure.ac as described below. If these lines are missing on all systems used to develop the project, all references to mkinstalldirs and m4/Makefile.am can be deleted from the following files. If there are developers using an outdated gettext (as in this example), these files are just created and left empty on the more modern systems (as a workaround).

First the changed configuration files need some fine tunig.

Makefile.am

SUBDIRS = src po

ACLOCAL_AMFLAGS = -I m4

EXTRA_DIST = config.rpath mkinstalldirs m4/Makefile.in src/gettext.h

check-gettext:
	@if test x$(USE_NLS) != "xyes" ; then echo "Missing gettext. Rerun configure and check for" \
	"'checking whether to use NLS... yes'!" ; exit 1 ; fi

update-po: check-gettext
	@find $(srcdir)/src/ -name "*.cpp" -print | sort > $(srcdir)/po/POTFILES.in.2 ; \
	if diff $(srcdir)/po/POTFILES.in $(srcdir)/po/POTFILES.in.2 >/dev/null 2>&1 ; then \
		rm -f $(srcdir)/po/POTFILES.in.2 ; \
	else \
		mv $(srcdir)/po/POTFILES.in.2 $(srcdir)/po/POTFILES.in ; \
	fi
	cd po && $(MAKE) $(AM_MAKEFLAGS) update-po

update-gmo: check-gettext
	cd po && $(MAKE) $(AM_MAKEFLAGS) update-gmo

force-update-gmo: check-gettext
	touch po/*.po
	cd po && $(MAKE) $(AM_MAKEFLAGS) update-gmo

force-update-gmo-%: check-gettext
	@language=`echo $@ | sed s/force-update-gmo-//` ; \
	if test ! -f po/$$language.po ; then echo "file po/$$language.po does not exist" ; exit 1 ; fi ; \
	touch po/$$language.po ; \
	cd po && $(MAKE) $(AM_MAKEFLAGS) update-gmo

.PHONY: check-gettext update-po update-gmo force-update-gmo
Note: The indention must be a tab, not a series of spaces!

'po' is added to the SUBDIRS so that all 'make' commands will go into the po directory as well.

EXTRA_DIST lists files, which have to be added to the source tarballs. m4/Makefile.in is such a file, which is missing by default. gettext.h will be created below and is required as well. Instead of mentioning it here it could also be added to noinst_HEADERS of src/Makefile.am.

The four 'update' targets have been added for convenience. They mainly call corresponding targets in the po directory. 'update-po' first maintains the po/POTFILES.in which was demanded by gettextize. But instead of letting the developer enter each newly added source code file here manually (which is very error-prone) just all *.cpp files are added automatically. If this is not sufficient or bad in other projects, this line should be changed or removed. Afterwards 'make update-po' is called in the po directory, which creates the .pot file from the source files and merges this .pot file into all .po files.

'update-gmo' builds the binary .gmo files from the .po files. Only changed .po files will be built.

'force update-gmo' does the same. The touch enforces this, so that all .po files will be converted. This make target therefore also displays the degree of translation (see next chapter).

'force update-gmo-%' finally enforces this for just one language, which will replace the '%'.

All four targets depend on check-gettext which checks, if 'configure' found libtool. If not 'configure' automatically enables a fallback: libtool is disabled and strings are not translated. As this might be usefull for or even wanted by the end user, it is definitely unwanted for the translators and internationalization maintainers, who are using these 4 targets. In the fallback, the program works flawless - just the translations will not appear and the reason is not easy to find. Therfore all 4 targets call check-gettext, which just stops 'make' with an error to remaind the user.

configure.ac

AC_PREREQ(2.59)
AC_INIT(testproj, 0.1, j.t.kirk@ncc-1701.ufp)
AC_CONFIG_SRCDIR([src/main.cpp])
AC_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE
AC_LIBTOOL_DLOPEN
AC_PROG_LIBTOOL
AM_GNU_GETTEXT([external])

# Checks for programs.
AC_PROG_CXX
AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.
AC_CONFIG_FILES([Makefile src/Makefile src/testmodule/Makefile po/Makefile.in m4/Makefile])
AC_OUTPUT
AM_GNU_GETTEXT([external]) has been added as suggested by gettextize. Note that gettextize could also have been called with the switch --intl. This would have additionally created a subdirectory intl, with all necessary gettext sources. They can be compiled into the system eliminating the need to link against gettext on runtime. In this case, AM_GNU_GETTEXT should be used, without an argument. Here --intl is omitted, so that the result executable dynamically links against the gettext libraries.

As suggested, po/Makevars is created and modified:
# cp po/Makevars.template po/Makevars

LOCALEDIR

po/Makevars

...

# These options get passed to xgettext.
XGETTEXT_OPTIONS = --keyword=i18n --keyword=i18nM --keyword=i18nP:1,2

...

COPYRIGHT_HOLDER = James T.

...

MSGID_BUGS_ADDRESS = j.t.kirk@ncc-1701.ufp

...
Most changes here are adminstrational. Important is the XGETTEXT_OPTIONS line, where the function names are defined, which will lateron mark translatable strings in the source code. Here i18n(), i18nM() and i18nP() are used for normal, marked and plural strings. Marked strings and the plural topic is discussed lateron. This chapter will use i18n() only.

The next steps are as suggested by gettextize:
# ${ACLOCAL} -I m4
... lots of warnings
# ${AUTOMAKE} m4/Makefile
# ln -s /usr/local/share/gettext/gettext.h src
The file gettext.h might be located in another directory on other systems. Finally, the compiler needs to know, how to link against the gettext libraries:

src/Makefile.am

SUBDIRS = testmodule

bin_PROGRAMS = testproj
testproj_SOURCES = main.cpp
noinst_HEADERS = testproj.h i18n.h
testproj_LDADD = $(top_srcdir)/src/testmodule/libtestmodule.a
testproj_LDFLAGS = $(LTLIBINTL)

localedir = $(datadir)/locale
DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
LTLIBINTL is the short form of "libtool libintl" and should be used, if libtool support is added to the auto tools (as in this test project). Otherwise LIBINTL should be used. The macro LOCALEDIR contains the directory, where the .mo files will be installed lateron. This is required to initialize gettext (see below). It is passed to all source code files by a compiler flag.

Now gettext is integrated into the auto tools and the source code can be prepared, to use it.

Adding gettext Support into the Source Code

First, the internationalization function i18n has to be declared:
# touch src/i18n.h

src/i18n.h

#include "../config.h"
#include "gettext.h"
#define i18n(x) gettext(x)

First, the internationalization function i18n has to be declared: Each source code file should include i18n.h. config.h has to be included first, as it contains the 'configure' results, which are used in some #ifdefs of gettext.h. gettext.h defines gettext(), which does the translation. Finally i18n() is defined as a short form to mark all translatable strings. Note that i18n() may take only string literals as arguments. Macros or variables are forbidden! The reason is, that xgettext, the tool which extracts the translatable strings, just takes what is in the parenthesis of i18n, without looking deeper into it.

An example of how to use it is here:

src/testmodule/testfunc.cpp

#include <stdio.h>
#include "testfunc.h"
#include "../i18n.h"

void printMessage()
{
	printf(i18n("hello world!\n"));
	printf(i18n("Press a key\n"));
	getchar();
}
Finally the gettext library has to be initialized on runtime:

src/main.cpp

#include <locale.h>
#include "testmodule/testfunc.h"
#include "i18n.h"

int main(int)
{
        // initialize gettext
        setlocale (LC_ALL, "");
        bindtextdomain (PACKAGE, LOCALEDIR);
        textdomain (PACKAGE);

        printMessage();
}
The macros 'PACKAGE' and 'LOCALEDIR' are defined in the Makefile or in config.h and contain the package name (testproj) and the path where 'make install' copies the .mo files to. Finally all changes should be checked:
# gmake

...

checking whether to use NLS... yes

...
It is important to seek the NLS line above. If it shows 'no', gettext support is disabled. One reason for this is, that the gettext header and library files are not found. They are searched in the pathes supplied, when 'configure' was called. If nothing is set, they are looked up in $(prefix)/include and $(prefix)/lib, where they are usually reside, if the prefix is not touched and therefore set to /usr/local automatically. This is important to remember, as therefore gettext support usually works - until someone chooses another installation prefix without adding it '-I /usr/local/include' to CPPFLAGS and '-L /usr/local/lib' LDFLAGS before running 'configure'!

In order to create the project from cvs, createFromCvs needs some changes:

createFromCvs.freebsd

#!/bin/sh
libtoolize
ln -s /usr/local/share/libtool/libltdl/acinclude.m4 acinclude.m4
cp configure.ac configure.ac~
cp Makefile.am Makefile.am~
gettextize
mv configure.ac~ configure.ac
mv Makefile.am~ Makefile.am
${ACLOCAL} -I m4
${AUTOHEADER}
${AUTOCONF}
${AUTOMAKE} --add-missing
${AUTOMAKE} m4/Makefile
ln -s /usr/local/share/gettext/gettext.h src
The gentoo linux version has a newer gettext. Therefore mkinstalldirs and m4/Makefile.am have to created as a workaround as described above:

createFromCvs.gentoo

#!/bin/sh
libtoolize
ln -s /usr/share/libtool/libltdl/acinclude.m4 acinclude.m4
cp configure.ac configure.ac~
cp Makefile.am Makefile.am~
gettextize
touch m4/Makefile.am
touch mkinstalldirs
mv configure.ac~ configure.ac
mv Makefile.am~ Makefile.am
aclocal -I m4
autoheader
autoconf
automake --add-missing
automake m4/Makefile
ln -s /usr/share/gettext/gettext.h src
The copy and move commands just revert the automatic changes, gettextize does on Makefile.am and configure.ac.

An important question is: which files from the po directory should be added to a revision control system? For sure LINGUAS, Makevars and ChangeLog, as they contain developer changes. Also the .po files, which will contain the translations. POTFILES.in is a problem. It does not contain anything, which can not be rebuild (see 'make update-po'). So there is no need to put it under revision control. On the other hand, if it is not under revision control, a 'make dist' will fail from a clean check out. So there are good arguments for and against putting it under revision control.

Here, POTFILES.in are not saved in the sample projects. Therefore a 'make update-po' must be run before any 'make dist' or any other of the gettext related makes.

Now gettext is integrated into the gnu auto tools. The result source code can be downloaded here.

The next chapter describes the localization workflow during the project development.
Prev Home Next
gettext Overview Using gettext