An Internationalized Software Project With Auto Tools
Prev Internationalization of the Documentation Next

Internationalization of the Documentation

Overview

As mentioned before, po4a does the internationalization. Unfortunately there is no "po4alize", so that the autotool integration is done manually here. The main idea is, that the "make" target work similar to the gettext targets, except that "make all" builds the english documentation from the .xml sources. This is no problem here, as the documentation sources are changed by the documentation authors but not by the developers, so that repeatedly calling "make" after code changes during the devlopment does not cause any extra actions in the doc directory.

Another difference to the gettext workflow is, that the translation files are called doc-<language>.po here, e.g. doc-de.po. This should avoid confusion with the <language>.po files from the po directory. So if a translator sends back just one of these two files, it is immediately clear, where it belongs to.

The "make" actions to be done are: The "make force-update-doc" target will delete the english html files and the .pot file and therefore trigger a full rebuild of the english documentation. "make force-update-docs" will force the creation of the recreation of the localized documentation. "make force-update-docs-XX" forces the recreation of a single language.

Integration Into The Build System

First, configure has to check for po4a and for the sgml processor, required by po4a:

configure.ac

...

AC_CHECK_FILE([doc/docbook/htmlhelp/htmlhelp.xsl], , [AC_MSG_WARN([htmlhelp sytle sheet was not found. \
If you want to change and compile the documentation, please install docbook-xsl \
(http://docbook.sourceforge.net/) and create a symlink in doc]) ; can_build_documentation=no])

AC_PATH_PROG([po4a_gettextize],[po4a-gettextize])
AC_PATH_PROG([po4a_updatepo],[po4a-updatepo])
AC_PATH_PROG([po4a_translate],[po4a-translate])
if test -z "$po4a_gettextize" -o -z "$po4a_updatepo" -o -z "$po4a_translate" ; then
	AC_MSG_WARN([po4a was not found. If you want to change and compile the documentation, \
please install po4a (packages.debian.org/unstable/text/po4a)])
	can_build_documentation=no
fi

AC_PATH_PROG([nsgmls],[nsgmls])
if test -z "$nsgmls" ; then
	AC_MSG_WARN([nsgmls was not found. If you want to change and compile the documentation, \
please install sp (www.jclark.com/sp/ or jade (www.jclark.com/jade/))])
	can_build_documentation=no
fi

AC_MSG_CHECKING([whether documentation can be changed and compiled])

...
Next, the new "make" have to be added, as discused above:

Makefile.am

...

force-update-doc:
	cd doc && $(MAKE) $(AM_MAKEFLAGS) force-update-doc

force-update-docs:
	cd doc && $(MAKE) $(AM_MAKEFLAGS) force-update-docs

force-update-docs-%: dummy
	cd doc && $(MAKE) $(AM_MAKEFLAGS) $@

dummy:

.PHONY: check-gettext update-po update-gmo force-update-gmo force-update-doc
.PHONY: force-update-docs dummy
All new targets mainly call the corresponding target in doc's Makefile. "dummy" is added, to make force-update-docs-% a phony target, i.e. if someone accidentially created a file force-update-docs-de, this target will nevertheless call force-update-docs-de in doc.

Finally, the doc's Makefile needs this new targets and a rule to build the .pot file, which has to be called on "make all". The .pot file has the same name as the project (here: testproj.pot) and is shipped with the source tarball. This ensures, that end users will not rebuild the html help files, if they are just running "make".

doc/Makefile.am

SUBDIRS =

@AMDEP_TRUE@@am__include@ @am__quote@generated_files.mk@am__quote@

pictures = apicture.png
include $(srcdir)/Makefile.pictures.am
source_tarball_files = $(PACKAGE).xsl $(PACKAGE).xml test_chapter.xml 

htmlhelpdir = $(helpdir)
htmlhelp_created_files = $(PACKAGE).hhc $(PACKAGE).hhp $(PACKAGE).hhk $(TESTPROJ_DOCFILES)
htmlhelp_DATA = $(pictures) $(TESTPROJ_DOCFILES)

EXTRA_DIST = $(source_tarball_files) $(pictures) generated_files.mk $(htmlhelp_created_files) $(PACKAGE).pot

DISTCLEANFILES = Makefile

check_can_build_documentation:
	@if test x$(can_build_documentation) != "xyes" ; then echo "Missing libxslt, po4a, docbook-xsl or sp." \
	"Rerun configure and check for 'checking whether documentation can be changed and compiled... yes'!" ; \
	exit 1 ; fi

...

all: $(htmlhelp_created_files) $(PACKAGE).pot

force-update-doc: doc-clean $(htmlhelp_created_files)
	rm -f $(srcdir)/$(PACKAGE).pot
	$(MAKE) $(PACKAGE).pot

force-update-docs:
	@list='$(SUBDIRS)'; for subdir in $$list; do \
		cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) force-update-docs ; \
	done

force-update-docs-%: dummy
	@language=`echo $@ | sed s/force-update-docs-//` ; \
	if test ! -d $(srcdir)/$$language ; then echo "directory $$language does not exist" ; exit 1 ; fi ; \
	cd $(srcdir)/$$language && $(MAKE) $(AM_MAKEFLAGS) force-update-docs

$(htmlhelp_created_files): $(source_tarball_files)
	@ $(MAKE) check_can_build_documentation
	$(MAKE) doc-clean
	$(xsltproc) $(srcdir)/$(PACKAGE).xsl $(srcdir)/$(PACKAGE).xml
	@echo "creating generated_files.mk"
	$(MAKE) generated_files_include

$(PACKAGE).pot: $(source_tarball_files)
	@ $(MAKE) check_can_build_documentation
	$(po4a_gettextize) -f sgml -m $(srcdir)/$(PACKAGE).xml -p $(srcdir)/$(PACKAGE).pot

dummy:

maintainer-clean: doc-clean maintainer-clean-recursive

doc-clean:
	-rm -f *.hhp
	-rm -f *.html
	-rm -f *.hhk
	-rm -f *.hhc

.PHONY: check_can_build_documentation generated_files_include force-update-doc doc-clean
.PHONY: force-update-docs dummy
Another change in this Makefile.am is, that the "pictures" variable was moved into the file Makefile.pictures.am. This makes maintaining the pictures easier, as this variable is required by all language dependent Makefiles as well.

Additionally, a Makefile included by the Makfiles of each language is created. Each language will reside in a sub directory of doc and contain (besides the .po file) a simple Makefile.am, which just defines the variable "language" and includes doc/Makefile.lang.am.
# touch doc/Makefile.lang.am
# touch doc/Makefile.pictures.am
First the "pictures" definition:

doc/Makefile.pictures.am

pictures = apicture.png
Then the common Makefile for all languages, which is again split into parts:

doc/Makefile.lang.am

SUBDIRS =

@AMDEP_TRUE@@am__include@ @am__quote@../generated_files.mk@am__quote@

include $(srcdir)/../Makefile.pictures.am
source_tarball_files = doc-$(language).po $(PACKAGE).xml

htmlhelpdir = $(helpdir)/$(language)
htmlhelp_created_files = $(PACKAGE).hhc $(PACKAGE).hhk $(PACKAGE).hhp $(TESTPROJ_DOCFILES)
htmlhelp_DATA = $(pictures) $(TESTPROJ_DOCFILES)

EXTRA_DIST = $(source_tarball_files) $(pictures) $(htmlhelp_created_files)
GMSGFMT = @GMSGFMT@

DISTCLEANFILES = Makefile
MAINTAINERCLEANFILES = $(PACKAGE).xml

It includes the same generated_files.mk as doc's Makefile, as it will create the same html files. Also Makefile.pictures.am is includes, which defines the pictures variable. The translated .xml file will be deleted on "make maintainer-clean", as it is aded to the variable MAINTAINERCLEANFILES.

doc/Makefile.lang.am

...

all: $(htmlhelp_created_files)

force-update-docs:
	@if test -f $(srcdir)/doc-$(language).po ; then \
		touch $(srcdir)/doc-$(language).po ; \
	fi
	$(MAKE) all

$(htmlhelp_created_files): $(PACKAGE).xml
	@cd .. ; $(MAKE) check_can_build_documentation
	$(MAKE) doc-clean
	$(xsltproc) $(srcdir)/../$(PACKAGE).xsl $(srcdir)/$(PACKAGE).xml

$(PACKAGE).xml: doc-$(language).po
	@cd .. ; $(MAKE) check_can_build_documentation
	$(GMSGFMT) -c --statistics doc-$(language).po || exit 0
	@rm -f messages.mo
	$(po4a_translate) -f sgml -m $(srcdir)/../$(PACKAGE).xml -p doc-$(language).po -l $(srcdir)/$(PACKAGE).xml -k 0

"make" or "make all" will build the created files from a single xml file (testproj.xml) by, which is the localized version, by calling xsltproc. This localized .xml file is created by the target $(PACKAGE).xml by calling po4a_translate. It requires the language dependent .po file. It also calls msgfmt, to print the translation statistics. (see below).

force-update-doc triggers a full rebuild, by touching the langauge dependent po file, if it already exists. If it does not exist, a full build is done anyway.

doc/Makefile.lang.am

...

doc-$(language).po: ../$(PACKAGE).pot
	@cd .. ; $(MAKE) check_can_build_documentation
	@if test -f "$@" ; then \
		echo "$(po4a_updatepo) -f sgml -m $(srcdir)/../$(PACKAGE).xml -p $@" ; \
		$(po4a_updatepo) -f sgml -m $(srcdir)/../$(PACKAGE).xml -p $@ ; \
	else \
		echo "Creating $@" ; \
		cp $(srcdir)/../$(PACKAGE).pot $@ ; \
	fi

../$(PACKAGE).pot: dummy
	cd .. ; $(MAKE) $(AM_MAKEFLAGS) $(PACKAGE).pot

dummy:

maintainer-clean: doc-clean maintainer-clean-recursive

doc-clean:
	-rm -f *.hhp
	-rm -f *.html
	-rm -f *.hhk
	-rm -f *.hhc

.PHONY: force-update-docs dummy doc-clean
The language dependent .po file is updated by the target doc-$(language).po, if the .pot file has been changed. If so, the .po file is either created (by copying the .pot file) or updated via po4a_updatepo. The doc-$(language).po target depends on ../$(PACKAGE).pot. This checks, if the .pot file has been modified. If so, it calls the same target of doc's Makefile.

What happens, if a documentation source file has been changed is the following: "make all" calls the targets $(htmlhelp_created_files), $(PACKAGE).xml, doc-$(language).po, ../$(PACKAGE).pot and "dummy" in a recursion in this order. "dummy" is a pseudo target, which does nothing. Within ../$(PACKAGE).pot, "dummy" is treated as a file, with the current time as timestamp (this is a "make" feature). As this is newer as ../$(PACKAGE).pot, "make $(PACKAGE).pot" is called in the doc directory. This checks if any source file is newer than $(PACKAGE).pot (which is true in this example) and will eventally rebuild ../$(PACKAGE).pot.

Returned to the language dependent Makefile, doc-$(language).po is older than the newly created../$(PACKAGE).pot, so that the .po file is updated. The same logic applies for the translated .xml file (which is recreated) and the translated .html files, which are also recreated. Afterwards the .html files of the current language are up to date.

When the localized .xml file is recreated ($(PACKAGE).xml target), "msgfmt -c --statistics" is called on the .po file. This checks the .po file for errors and prints out a translation statistics.

This is repeated for all languages. For all these languages, "make $(PACKAGE).pot" of the doc directory will do nothing, as the .pot file is now up to date. But as the .pot files time stamp is still very new, the .po, .xml and .html files are updated (for each language).

Adding A New Language

To make this working, at least one language has to be added. This requires the creation of a sub directory and a Makefile.am. The sub-directory should also contain all additional files, i.e. all files mentioned in the Makefile.pictures.am. These files usually contain screen shots and have to be replaced by screen shots of the localized version!
# mkdir doc/de
# touch doc/de/Makefile.am
# cp doc/*.png doc/de
Furthermore the new Makefile has to be added to configure.ac:

configure.ac

...

# Checks for library functions.
AC_CONFIG_FILES([Makefile src/Makefile src/testmodule/Makefile po/Makefile.in m4/Makefile \
	doc/Makefile doc/de/Makefile])
AC_OUTPUT
and to doc's Makefile.am:

doc/Makefile.am

SUBDIRS = de

...
The language dependent Makefile.am is quit simple. It just declares a variable, specifying the language code (which is also the directory name):

doc/de/Makefile.am

language = de

include $(srcdir)/../Makefile.lang.am

Optional Source Code Changes

A real integration of the help files into a software package requires a way to check, where the help files are installed. The english help files go into the directory $(HELPDIR). The localized version are in sub-directories of $(HELPDIR). So there are two problems to solve: find the currently choosen language and check, if there are help files for that language.

i18n.h defines two functions for that:

src/i18n.h

...

int strcntchar(const char * s, char c);
const char * get_gettext_language();
const char * get_help_dir();

Which are implemented in i18n.cpp:

src/i18n.cpp

#include "i18n.h"
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <limits.h>

...

const char * get_gettext_language()
{
        char * c;
        if (c = getenv("LANGUAGE"))
                return c;
        if (c = getenv("LC_ALL"))
                return c;
        if (c = getenv("LC_MESSAGES"))
                return c;
        return getenv("LC_LANG");
}

char get_help_dir_result[NAME_MAX+1];

const char * get_help_dir()
{
        char lang[NAME_MAX+1];
        const char * helpdir = HELPDIR;
        const char * l = get_gettext_language();
        if (!l)
                return helpdir;
        strcpy(lang, l);
        DIR * dir = NULL;
        for (int i = 0; i < 3; ++i)
        {
                strcpy(get_help_dir_result, helpdir);
                strcat(get_help_dir_result, "/");
                strcat(get_help_dir_result, lang);
                dir = opendir(get_help_dir_result);
                if (dir)
                {
                        struct dirent * dirEntry;
                        while ((dirEntry = readdir(dir)) != NULL)
                        {
                                if (!strcmp(dirEntry->d_name, ".") || !strcmp(dirEntry->d_name, ".."))
                                        continue;
                                closedir(dir);
                                return get_help_dir_result;
                        }
                }
                char * c = strchr(lang, '.');
                if (c)
                        *c = 0;
                else
                {
                        c = strchr(lang, '_');
                        if (c)
                                *c = 0;
                        else
                                break;
                }
        }
        if (dir)
                closedir(dir);
        return helpdir;
}

Unfortunately, gettext does not have a public funtion, which returns the language used by gettext. The documentation just specifies the way, gettext calculates this language: by checking some LC_ environment variables. This is re-implemented in get_gettext_language.

To check for the help directory, it is assumed, that the string returned by get_gettext_language is either XX or XX_YY or XX_YY.ZZZZ. All these directories are checked (below $(HELPDIR)). If the language has a complex form, the .ZZZZ and afterwards the _YY is removed and the result checked as well. In other words: if the choosen language is de_DE.ISO8859-1, the directories $(HELPDIR)/de_DE.ISO8859-1, $(HELPDIR)/de_DE and $(HELPDIR)/de are checked in this order. The directory check seeks for any files in a directory (so empty directories do not count). If no directory was found, $(HELPDIR) is returned (english as fallback documentation).

To test this, main.cpp returns the location of the localized documentation, if possible:

src/main.cpp

...

        // reference to english html help
        printf(i18n("\nHelp: %s/index.html\n"), get_help_dir());
        printf(i18n("Test chapter: %s/test_chapter.html\n"), get_help_dir());

Usage

"make" compiles all sources. Furthermore it rebuilds the english documentation (not necessary here) and the .pot file:
# gmake

...

Making all in doc
gmake[2]: Entering directory `/usr/home/he/develop/testproj/doc'
Making all in de

...

gmake[3]: Entering directory `/usr/home/he/develop/testproj/doc'
/usr/local/bin/po4a-gettextize -f sgml -m ./testproj.xml -p ./testproj.pot
Use of uninitialized value in hash element at /usr/local/lib/perl5/site_perl/5.8.8/Locale/Po4a/Sgml.pm line 668.
po4a::sgml: Warning: nsgmls is missing or non-functional.  Please make sure that
            nsgmls is present and does not produce any error (with the
            -wno-valid option), and report a bug otherwise.  Continuing...

...

gmake[2]: Entering directory `/usr/home/he/develop/testproj/doc/de'
cd .. ; gmake  testproj.pot
gmake[3]: Entering directory `/usr/home/he/develop/testproj/doc'
gmake[3]: `testproj.pot' is up to date.
gmake[3]: Leaving directory `/usr/home/he/develop/testproj/doc'
gmake[3]: Entering directory `/usr/home/he/develop/testproj/doc'
gmake[3]: Leaving directory `/usr/home/he/develop/testproj/doc'
Creating doc-de.po
gmake[3]: Entering directory `/usr/home/he/develop/testproj/doc'
gmake[3]: Leaving directory `/usr/home/he/develop/testproj/doc'
/usr/local/bin/msgfmt -c --statistics doc-de.po || exit 0
/usr/local/bin/msgfmt: doc-de.po: warning: Charset "CHARSET" is not a portable encoding name.
                                       Message conversion to user's charset might not work.
/usr/local/bin/msgfmt: doc-de.po: some header fields still have the initial default value
/usr/local/bin/msgfmt: doc-de.po: warning: PO file header fuzzy
                              warning: older versions of msgfmt will give an error on this
/usr/local/bin/msgfmt: found 1 fatal error
/usr/local/bin/po4a-translate -f sgml -m ./../testproj.xml -p doc-de.po -l ./testproj.xml -k 0
Use of uninitialized value in hash element at 
	/usr/local/lib/perl5/site_perl/5.8.8/Locale/Po4a/Sgml.pm line 652.
po4a::sgml: Warning: nsgmls is missing or non-functional.  Please make sure that
            nsgmls is present and does not produce any error (with the
            -wno-valid option), and report a bug otherwise.  Continuing...
gmake[3]: Entering directory `/usr/home/he/develop/testproj/doc'
gmake[3]: Leaving directory `/usr/home/he/develop/testproj/doc'
gmake doc-clean
gmake[3]: Entering directory `/usr/home/he/develop/testproj/doc/de'
rm -f *.hhp
rm -f *.html
rm -f *.hhk
rm -f *.hhc
gmake[3]: Leaving directory `/usr/home/he/develop/testproj/doc/de'
/usr/local/bin/xsltproc ./../testproj.xsl ./testproj.xml
./testproj.xml:74: parser error : Opening and ending tag mismatch: imagedata line 73 
	and imageobject
    <>/imageobject>
                  ^
./testproj.xml:75: parser error : Opening and ending tag mismatch: imageobject line 72 
	and mediaobject

...

unable to parse ./testproj.xml
gmake[2]: *** [testproj.hhc] Error 6
gmake[2]: Leaving directory `/usr/home/he/develop/testproj/doc/de'
gmake[1]: *** [update-docs] Error 2
gmake[1]: Leaving directory `/usr/home/he/develop/testproj/doc'
gmake: *** [update-docs] Error 2
The workflow is as described above. "make all" recursively drops into doc/de. Here it calls "make testproj.pot" in doc. As this is up to date, nothing is done here. Back in doc/de, doc-de.po is created by just copying testproj.pot into it. Next msgfmt is called, which checks the .po file. It prints out some warningsm which show, that the .po was not really handled by a translator.

Afterwards po4a-translate is called to create a localized testproj.xml from doc-de.po and the documentation source files in doc. The final step in doc/de would be the creation of the localized .html files by calling xsltproc. Unfortunately this step ends in an error. The created doc-de.po file shows the reason:

doc/de/doc-de.po

...

# type: <imageobject></imageobject>
#: ./testproj.xml:64
msgid "<imagedata format=\"png\" fileref=\"apicture.png\">>"
msgstr ""
msgstr "<imagedata format=\"png\" fileref=\"apicture.png\"/>"

...
For some reason, po4a extratced the imagedata tag wrongly: it ends with a double larger than sign. This must be corrected, before the localized documentation builds:
# gmake

...

gmake[4]: Leaving directory `/usr/home/he/develop/testproj/doc/de'
/usr/local/bin/xsltproc ./../testproj.xsl ./testproj.xml
Writing overview.html for preface(overview)
Writing test_chapter.html for chapter(test_chapter)
Writing index.html for book
Writing testproj.hhp
Writing testproj.hhc
Writing testproj.hhk

...
This time "make all" takes the same path as before. po4a-translate is called again, as doc-de.po was changed. testproj.xml is recreated, which this time is syntactically correct. Therefore xsltproc does not end with an error. The .html files are created instead.

To get a useful result, the .po file has to be translated:

doc/de/doc-de.po

# SOME DESCRIPTIVE TITLE
# Copyright (C) YEAR Free Software Foundation, Inc.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# 
msgid ""
msgstr ""
"Project-Id-Version: de\n"
"Report-Msgid-Bugs-To: j.t.kirk@ncc-1701.ufp\n"
"POT-Creation-Date: 2006-10-11  2:46+0000\n"
"PO-Revision-Date: 2006-10-15  2:46+0000\n"
"Last-Translator: p.chekov@ncc-1701.ufp\n"
"Language-Team: german\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms:  nplurals=2; plural=(n != 1);\n"

# type: definition of entity &directions_right;
#, no-wrap
msgid "right"
msgstr "rechts"

# type: definition of entity &results_right;
#, no-wrap
msgid "right "
msgstr "richtig"

# type: attribute <book>lang
#: ./testproj.xml:8
#, no-wrap
msgid "en"
msgstr "de"

# type: <para></para>
#: ./testproj.xml:12
msgid "Legal notice ..."
msgstr "Rechtlicher Hinweis"

# type: <author></author>
#: ./testproj.xml:16
msgid "<firstname>J.T.</firstname><surname>Kirk</surname>"
msgstr "<firstname>J.T.</firstname><surname>Kirk</surname>"

# type: <copyright><holder>
#: ./testproj.xml:20
msgid "<year>2005</year><year>2006</year>"
msgstr "<year>2005</year><year>2006</year>"

# type: <holder></holder>
#: ./testproj.xml:20
#, no-wrap
msgid "J.T. Kirk"
msgstr "J.T. Kirk"

# type: <primary></primary>
#: ./testproj.xml:20 ./testproj.xml:28
#, no-wrap
msgid "testproj"
msgstr "testproj"

# type: <title></title>
#: ./testproj.xml:20
#, no-wrap
msgid "Preface to testproj"
msgstr "Vorwort zu testproj"

# type: <primary></primary>
#: ./testproj.xml:28
msgid "Preface"
msgstr "Vorwort"

# type: </indexterm></para>
#: ./testproj.xml:28
msgid ""
"This is the preface. It contains of a paragrapgh, a list and a link. This "
"paragraph contains useless words only. Its mere purpose is to have some text "
"in order to see the paragraphs end."
msgstr ""
"Dies ist das Vorwort. Es besteht aus einem Absatz, einer Liste und einem "
"Link. Dieser Absatz enthält nur Unsinniges. Der einzige Zweck ist, einigen "
"Text zusammenzubekommen, um das Absatzende erkennen zu können."

# type: <para><variablelist>
#: ./testproj.xml:35
msgid "A small list:"
msgstr "Eine kleine Liste"

# type: <listitem></listitem>
#: ./testproj.xml:37
msgid "list item 1"
msgstr "Listen Eintrag 1"

# type: <listitem></listitem>
#: ./testproj.xml:37
msgid "list item 2"
msgstr "Listen Eintrag 2"

# type: <para><informaltable>
#: ./testproj.xml:42
msgid "Demonstration of amigous translations:"
msgstr "Demonstration von mehrdeutigen Übersetzungen"

# type: <entry></entry>
#: ./testproj.xml:47
msgid "Possible directions"
msgstr "Mögliche Richtungen"

# type: <entry></entry>
#: ./testproj.xml:47
msgid "Possible results"
msgstr "Mögliche Ergebnisse"

# type: <entry></entry>
#: ./testproj.xml:55
msgid "left"
msgstr "links"

# type: <entry></entry>
#: ./testproj.xml:55
msgid "wrong"
msgstr "falsch"

# type: <para></para>
#: ./testproj.xml:64
msgid "Link: <link linkend=\"test_chapter\"> Jump to test chapter. </link>"
msgstr "Link: <link linkend=\"test_chapter\"> Springe zum Test Kapitel. </link>"

# type: <title></title>
#: ./testproj.xml:64
#, no-wrap
msgid "Test Chapter"
msgstr "Test Kapitel"

# type: <primary></primary>
#: ./testproj.xml:64
msgid "Test chapter"
msgstr "Test Kapitel"

# type: </indexterm></para>
#: ./testproj.xml:64
msgid ""
"This is the first and only chapter. It contains a note, a picture and a "
"table."
msgstr ""
"Dies ist das erste und einzige Kapitel. Es enthält einen Hinweis, ein Bild "
"und eine Tabelle."

# type: <para><mediaobject>
#: ./testproj.xml:64
msgid "This paragraph contains a picture"
msgstr "Dieser Absatz enthält ein Bild"

# type: <imageobject></imageobject>
#: ./testproj.xml:64
msgid "<imagedata fileref=\"apicture.png\" format=\"png\">>"
msgstr "<imagedata fileref=\"apicture.png\" format=\"png\"/>"

# type: </mediaobject><note>
#: ./testproj.xml:64
msgid "<emphasis>within</emphasis> the paragraph, which is surrounded by the text."
msgstr "<emphasis>innerhalb</emphasis> des Absatzes, welches von Text umgeben ist."

# type: <note></note>
#: ./testproj.xml:64
msgid "This is a small note"
msgstr "Ein kleiner Hinweis"

# type: <para><informaltable>
#: ./testproj.xml:68 ./test_chapter.xml:11
msgid "Finally a table with some xml tags:"
msgstr "Zum Schluß eine Tabelle mit XML Tags:"

# type: <entry></entry>
#: ./testproj.xml:68 ./test_chapter.xml:16
msgid "xml tag"
msgstr "XML Tag"

# type: <entry></entry>
#: ./testproj.xml:68 ./test_chapter.xml:16
msgid "result"
msgstr "Ergebnis"

# type: <para></para>
#: ./testproj.xml:68 ./test_chapter.xml:20
msgid "command"
msgstr "command"

# type: <para></para>
#: ./testproj.xml:68 ./test_chapter.xml:20
msgid "<command>command</command>"
msgstr "<command>command</command>"

# type: <para></para>
#: ./testproj.xml:68 ./test_chapter.xml:25
msgid "replaceable"
msgstr "replaceable"

# type: <para></para>
#: ./testproj.xml:68 ./test_chapter.xml:25
msgid "<replaceable>replaceable</replaceable>"
msgstr "<replaceable>replaceable</replaceable>"

# type: <para></para>
#: ./testproj.xml:68 ./test_chapter.xml:30
msgid "prompt"
msgstr "prompt"

# type: <para></para>
#: ./testproj.xml:68 ./test_chapter.xml:30
msgid "<prompt>prompt</prompt>"
msgstr "<prompt>prompt</prompt>"
It is very important to translate the "en" at the beginning to "de". This replaces the language in the starting tag "<book lang="en">", which translates strings introduced by xsltproc (like "chapter").

Another thing to mention are the two strings "right" at the beginning. Both appear here because one has an trailing space in testproj.xml, which the other does not have. It does not harm here, as it will be followed by an xml tag in the translated (as well as in the original) xml.

Note, that some strings contain xml tags. These must be copied into the translation as well! If done, the translated help files can be created:
# gmake

...

/usr/local/bin/msgfmt -c --statistics doc-de.po || exit 0
36 translated messages.
/usr/local/bin/po4a-translate -f sgml -m ./../testproj.xml -p doc-de.po -l ./testproj.xml -k 0
Use of uninitialized value in hash element at /usr/local/lib/perl5/site_perl/5.8.8/Locale/Po4a/Sgml.pm line 652.
po4a::sgml: Warning: nsgmls is missing or non-functional.  Please make sure that
            nsgmls is present and does not produce any error (with the
            -wno-valid option), and report a bug otherwise.  Continuing...

...
The generated help files can be viewed here.

The translated documentation will be installed into a language dependent sub directory:
# gmake install

...

gmake[4]: Entering directory `/usr/home/he/develop/testproj/doc/de'
gmake[4]: Nothing to be done for `install-exec-am'.
test -z "/usr/local/share/doc/testproj/de" || /usr/local/bin/bash ../../mkinstalldirs "/usr/local/share/doc/testproj/de"
 /usr/bin/install -c -m 644 'apicture.png' '/usr/local/share/doc/testproj/de/apicture.png'
 /usr/bin/install -c -m 644 './index.html' '/usr/local/share/doc/testproj/de/index.html'
 /usr/bin/install -c -m 644 './overview.html' '/usr/local/share/doc/testproj/de/overview.html'
 /usr/bin/install -c -m 644 './test_chapter.html' '/usr/local/share/doc/testproj/de/test_chapter.html'
gmake[4]: Leaving directory `/usr/home/he/develop/testproj/doc/de'

...
If started, the correct documentation path is printed out:
# testproj

...

Help: /usr/local/share/doc/testproj/index.html
Test chapter: /usr/local/share/doc/testproj/test_chapter.html
# setenv LC_ALL de_DE.ISO8859-1 ; testproj ; unsetenv LC_ALL

...

Hilfe: /usr/local/share/doc/testproj/de/index.html
Test Kapitel: /usr/local/share/doc/testproj/de/test_chapter.html
The result source code can be downloaded here.

Next an internationalized gui project is discussed.
Prev Home Next
Adding HTML Help Documentation wxWidgets Overview