<?xml version='1.0'?>

<!--
SPDX-FileCopyrightText: Copyright The SCons Foundation (https://scons.org)
SPDX-License-Identifier: MIT
SPDX-FileType: DOCUMENTATION

This file is processed by the bin/SConsDoc.py module.
-->

<!DOCTYPE sconsdoc [
    <!ENTITY % scons SYSTEM "../scons.mod">
    %scons;

    <!ENTITY % builders-mod SYSTEM "../generated/builders.mod">
    %builders-mod;
    <!ENTITY % functions-mod SYSTEM "../generated/functions.mod">
    %functions-mod;
    <!ENTITY % tools-mod SYSTEM "../generated/tools.mod">
    %tools-mod;
    <!ENTITY % variables-mod SYSTEM "../generated/variables.mod">
    %variables-mod;

]>

<chapter id="chap-gettext"
         xmlns="http://www.scons.org/dbxsd/v1.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd">
<title>Internationalization and localization with gettext</title>

  <para>
  The &t-link-gettext; toolset supports internationalization and localization
  of SCons-based projects. Builders provided by &t-link-gettext; automatize
  generation and updates of translation files. You can manage translations and
  translation templates similarly to how it's done with autotools.
  </para>

  <section id="sect-gettext-prereq">
  <title>Prerequisites</title>
  <para>
    To follow examples provided in this chapter set up your operating system to
    support two or more languages. In following examples we use locales
    <literal>en_US</literal>, <literal>de_DE</literal>, and
    <literal>pl_PL</literal>.
  </para>

  <para>
    Ensure, that you have <ulink
    url="http://www.gnu.org/software/gettext/manual/gettext.html">GNU gettext
    utilities</ulink> installed on your system.
  </para>

  <para>
    To edit translation files you may wish to install <ulink
    url="http://www.poedit.net/">poedit</ulink> editor.
  </para>
  </section>

  <section id="sect-gettext-simple">
  <title>Simple project</title>
    <para>
    Let's start with a very simple project, the "Hello world" program
    for example
    <scons_example name="gettext_ex1">
    <file name="hello.c" printme="1">
/* hello.c */
#include &lt;stdio.h&gt;
int main(int argc, char* argv[])
{
  printf("Hello world\n");
  return 0;
}
    </file>
    </scons_example>

    Prepare a <filename>SConstruct</filename> to compile the program
    as usual.
    <scons_example name="gettext_ex2">
    <file name="SConstruct" printme="1">
# SConstruct
env = Environment()
hello = Program(["hello.c"])
    </file>
    </scons_example>
    </para>

    <para>
    Now we'll convert the project to a multilingual one. If you don't
    already have <ulink
    url="http://www.gnu.org/software/gettext/manual/gettext.html">GNU gettext
    utilities</ulink> installed, install them from your preferred
    package repository, or download from <ulink
    url="http://ftp.gnu.org/gnu/gettext/">
    http://ftp.gnu.org/gnu/gettext/</ulink>. For the purpose of this example,
    you should have following three locales installed on your system:
    <literal>en_US</literal>, <literal>de_DE</literal> and
    <literal>pl_PL</literal>. On Debian, for example, you may enable certain
    locales through <command>dpkg-reconfigure locales</command>.
    </para>

    <para>
    First prepare the <filename>hello.c</filename> program for
    internationalization. Change the previous code so it reads as follows:
    <scons_example name="gettext_ex3">
    <file name="hello.c" printme="1">
/* hello.c */
#include &lt;stdio.h&gt;
#include &lt;libintl.h&gt;
#include &lt;locale.h&gt;
int main(int argc, char* argv[])
{
  bindtextdomain("hello", "locale");
  setlocale(LC_ALL, "");
  textdomain("hello");
  printf(gettext("Hello world\n"));
  return 0;
}
    </file>
    </scons_example>
    Detailed recipes for such conversion can
    be found at <ulink
    url="http://www.gnu.org/software/gettext/manual/gettext.html#Sources">
    http://www.gnu.org/software/gettext/manual/gettext.html#Sources</ulink>.
    The <function>gettext("...")</function> has two purposes.
    First, it marks messages for the <command>xgettext(1)</command> program, which
    we will use to extract from the sources the messages for localization.
    Second, it calls the <literal>gettext</literal> library internals to
    translate the message at runtime.
    </para>

    <para>
    Now we shall instruct SCons how to generate and maintain translation files.
    For that, use the &b-link-Translate; builder and &b-link-MOFiles; builder.
    The first one takes source files, extracts internationalized
    messages from them, creates so-called <literal>POT</literal> file
    (translation template), and then creates <literal>PO</literal> translation
    files, one for each requested language. Later, during the development
    lifecycle, the builder keeps all these files up-to date. The
    &b-link-MOFiles; builder compiles the <literal>PO</literal> files to binary
    form. Then install the <literal>MO</literal> files under directory
    called <filename>locale</filename>.
    </para>

    <para>  The completed
    <filename>SConstruct</filename> is as follows:
    <scons_example name="gettext_ex4">
    <file name="SConstruct" printme="1">
# SConstruct
env = Environment( tools = ['default', 'gettext'] )
hello = env.Program(["hello.c"])
env['XGETTEXTFLAGS'] = [
  '--package-name=%s' % 'hello',
  '--package-version=%s' % '1.0',
]
po = env.Translate(["pl","en", "de"], ["hello.c"], POAUTOINIT = 1)
mo = env.MOFiles(po)
InstallAs(["locale/en/LC_MESSAGES/hello.mo"], ["en.mo"])
InstallAs(["locale/pl/LC_MESSAGES/hello.mo"], ["pl.mo"])
InstallAs(["locale/de/LC_MESSAGES/hello.mo"], ["de.mo"])
    </file>
    </scons_example>
    </para>
    <para>
    Generate the translation files with <command>scons po-update</command>.
    You should see the output from SCons similar to this:
    <screen>
user@host:$ scons po-update
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
Entering '/home/ptomulik/projects/tmp'
xgettext --package-name=hello --package-version=1.0 -o - hello.c
Leaving '/home/ptomulik/projects/tmp'
Writting 'messages.pot' (new file)
msginit --no-translator -l pl -i messages.pot -o pl.po
Created pl.po.
msginit --no-translator -l en -i messages.pot -o en.po
Created en.po.
msginit --no-translator -l de -i messages.pot -o de.po
Created de.po.
scons: done building targets.
    </screen>
    </para>

    <para>
    If everything is right, you should see following new files.
    <screen>
user@host:$ ls *.po*
de.po  en.po  messages.pot  pl.po
    </screen>
    </para>

    <para>
    Open <filename>en.po</filename> in <command>poedit</command> and provide
    the English translation to message <literal>"Hello world\n"</literal>. Do the
    same for <filename>de.po</filename> (deutsch) and
    <filename>pl.po</filename> (polish). Let the translations be, for example:
    <itemizedlist>
      <listitem><para>
        <literal>en: "Welcome to beautiful world!\n"</literal>
      </para></listitem>
      <listitem><para>
        <literal>de: "Hallo Welt!\n"</literal>
      </para></listitem>
      <listitem><para>
        <literal>pl: "Witaj swiecie!\n"</literal>
      </para></listitem>
    </itemizedlist>
    </para>
    <para>
    Now compile the project by executing <command>scons</command>. The
    output should be similar to this:
    <screen>
user@host:$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
msgfmt -c -o de.mo de.po
msgfmt -c -o en.mo en.po
gcc -o hello.o -c hello.c
gcc -o hello hello.o
Install file: "de.mo" as "locale/de/LC_MESSAGES/hello.mo"
Install file: "en.mo" as "locale/en/LC_MESSAGES/hello.mo"
msgfmt -c -o pl.mo pl.po
Install file: "pl.mo" as "locale/pl/LC_MESSAGES/hello.mo"
scons: done building targets.
    </screen>
    SCons automatically compiled the <literal>PO</literal> files to binary format
    <literal>MO</literal>, and the <literal>InstallAs</literal> lines installed
    these files under <filename>locale</filename> folder.
    </para>
    <para>
    Your program should be now ready. You may try it as follows (Linux):
    <screen>
user@host:$ LANG=en_US.UTF-8 ./hello
Welcome to beautiful world
    </screen>
    <screen>
user@host:$ LANG=de_DE.UTF-8 ./hello
Hallo Welt
    </screen>
    <screen>
user@host:$ LANG=pl_PL.UTF-8 ./hello
Witaj swiecie
    </screen>
    </para>
    <para>
    To demonstrate the further life of translation files, let's change Polish
    translation (<command>poedit pl.po</command>) to <literal>"Witaj drogi
    swiecie\n"</literal>. Run <command>scons</command> to see how scons
    reacts to this
    <screen>
user@host:$scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
msgfmt -c -o pl.mo pl.po
Install file: "pl.mo" as "locale/pl/LC_MESSAGES/hello.mo"
scons: done building targets.
    </screen>
    </para>
    <para>
    Now, open <filename>hello.c</filename> and add another one
    <literal>printf</literal> line with new message.
    <scons_example name="gettext_ex5">
    <file name="hello.c" printme="1">
/* hello.c */
#include &lt;stdio.h&gt;
#include &lt;libintl.h&gt;
#include &lt;locale.h&gt;
int main(int argc, char* argv[])
{
  bindtextdomain("hello", "locale");
  setlocale(LC_ALL, "");
  textdomain("hello");
  printf(gettext("Hello world\n"));
  printf(gettext("and good bye\n"));
  return 0;
}
    </file>
    </scons_example>
    </para>
    <para>
    Compile project with <command>scons</command>. This time, the
    <command>msgmerge(1)</command> program is used by SCons to update
    <literal>PO</literal> file. The output from compilation is like:
    <screen>
user@host:$scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
Entering '/home/ptomulik/projects/tmp'
xgettext --package-name=hello --package-version=1.0 -o - hello.c
Leaving '/home/ptomulik/projects/tmp'
Writting 'messages.pot' (messages in file were outdated)
msgmerge --update de.po messages.pot
... done.
msgfmt -c -o de.mo de.po
msgmerge --update en.po messages.pot
... done.
msgfmt -c -o en.mo en.po
gcc -o hello.o -c hello.c
gcc -o hello hello.o
Install file: "de.mo" as "locale/de/LC_MESSAGES/hello.mo"
Install file: "en.mo" as "locale/en/LC_MESSAGES/hello.mo"
msgmerge --update pl.po messages.pot
... done.
msgfmt -c -o pl.mo pl.po
Install file: "pl.mo" as "locale/pl/LC_MESSAGES/hello.mo"
scons: done building targets.
    </screen>
    </para>
    <para>
    The next example demonstrates what happens if we change the source code
    in such way that the internationalized messages do not change. The answer
    is that none of translation files (<literal>POT</literal>,
    <literal>PO</literal>) are touched (i.e. no content changes, no
    creation/modification time changed and so on). Let's append another
    line to the program (after the last printf), so its code becomes:
    <scons_example name="gettext_ex6">
    <file name="hello.c" printme="1">
/* hello.c */
#include &lt;stdio.h&gt;
#include &lt;libintl.h&gt;
#include &lt;locale.h&gt;
int main(int argc, char* argv[])
{
  bindtextdomain("hello", "locale");
  setlocale(LC_ALL, "");
  textdomain("hello");
  printf(gettext("Hello world\n"));
  printf(gettext("and good bye\n"));
  printf("----------------\n");
  return a;
}
    </file>
    </scons_example>
    Compile the project. You'll see on your screen
    <screen>
user@host:$scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
Entering '/home/ptomulik/projects/tmp'
xgettext --package-name=hello --package-version=1.0 -o - hello.c
Leaving '/home/ptomulik/projects/tmp'
Not writting 'messages.pot' (messages in file found to be up-to-date)
gcc -o hello.o -c hello.c
gcc -o hello hello.o
scons: done building targets.
    </screen>
    As you see, the internationalized messages didn't change, so the
    <literal>POT</literal> and the rest of translation files have not
    even been touched.
    </para>
  </section>

</chapter>
