- Add a section about using POSIX NLS to internationalize programs

Reviewed by:	rene, rwatson
This commit is contained in:
Gabor Kovesdan 2009-03-21 02:38:53 +00:00
parent 3352ee7db1
commit ac9abd3ad2
Notes: svn2git 2020-12-08 03:00:23 +00:00
svn path=/head/; revision=33987

View file

@ -58,4 +58,215 @@
libraries. Please use them for I18N compliance.</para>
</sect2>
</sect1>
</chapter>
<sect1 id="posix-nls">
<sect1info>
<authorgroup>
<author>
<firstname>G&aacute;bor</firstname>
<surname>K&ouml;vesd&aacute;n</surname>
<contrib>Contributed by </contrib>
</author>
</authorgroup>
</sect1info>
<title>Localized Messages with POSIX.1 Native Language Support (NLS)</title>
<para>Beyond the basic I18N functions, like supporting various input
encodings or supporting national conventions, such as the different
decimal separators, at a higher level of I18N, it is possible to localize the
messages written to the output by the various programs. A common way of doing
this is using the POSIX.1 NLS functions, which are provided as a part
of the &os; base system.</para>
<sect2 id="nls-catalogs">
<title>Organizing Localized Messages into Catalog Files</title>
<para>POSIX.1 NLS is based on catalog files, which contain the
localized messages in the desired encoding. The messages are
organized into sets and each message is identified by an integer
number in the containing set. The catalog files are conventionally
named after the locale they contain localized messages for, followed
by the <literal>.msg</literal> extension. For instance, the
Hungarian messages for ISO8859-2 encoding should be stored in a file
called <filename>hu_HU.ISO8859-2</filename>.</para>
<para>These catalog files are common text files that contain the
numbered messages. It is possible to write comments by starting
the line with a <literal>$</literal> sign. Set boundaries are also separated by
special comments, where the keyword <literal>set</literal> must
directly follow the <literal>$</literal> sign. The <literal>set</literal> keyword
is then followed by the set number. For example:</para>
<programlisting>$set 1</programlisting>
<para>The actual message entries start with the message number and
followed by the localized message. The well-known
modifiers from &man.printf.3; are accepted:</para>
<programlisting>15 "File not found: %s\n"</programlisting>
<para>The language catalog files have to be compiled into a binary
form before they can be opened from the program. This conversion
is done with the &man.gencat.1; utility. Its first argument is the
filename of the compiled catalog and its further arguments are the
input catalogs. The localized messages can also be organized into
more catalog files and then all of them can be processed with
&man.gencat.1;.</para>
</sect2>
<sect2 id="nls-using">
<title>Using the Catalog Files from the Source Code</title>
<para>Using the catalog files is simple. To use
the related functions, <filename
class="headerfile">nl_types.h</filename> must be included. Before
using a catalog, it has to be opened with &man.catopen.3;.
The function takes two arguments. The first parameter is the name of the
installed and compiled catalog. Usually, the name of the
program is used, such as <application>grep</application>.
This name will be used when looking for the compiled
catalog file. The &man.catopen.3; call looks for this file
in <filename
class="directory">/usr/share/nls/<replaceable>locale</replaceable>/<replaceable>catname</replaceable></filename>
and in <filename
class="directory">/usr/local/share/nls/<replaceable>locale</replaceable>/<replaceable>catname</replaceable></filename>,
where <literal>locale</literal> is the locale set and
<literal>catname</literal> is the catalog name being
discussed. The second parameter is a constant, which can have
two values:</para>
<itemizedlist>
<listitem>
<para><literal>NL_CAT_LOCALE</literal>, which means that
the used catalog file will be based on
<envar>LC_MESSAGES</envar>.</para>
</listitem>
<listitem>
<para><literal>0</literal>, which means that
<envar>LANG</envar> has to be used to open
the proper catalog.</para>
</itemizedlist>
<para>The &man.catopen.3; call returns a catalog identifier of
type <literal>nl_catd</literal>. Please refer to the manual page for a list of possible returned error
codes.</para>
<para>After opening a catalog &man.catgets.3; can be used to retrieve
a message. The first parameter is the catalog identifier returned
by &man.catopen.3;, the second one is the number of the set, the
third one is the number of the messages, and the fourth one is a
fallback message, which will be returned if the requested message
cannot be retrieved from the catalog file.</para>
<para>After using the catalog file, it must be closed by calling
&man.catclose.3;, which has one argument, the catalog id.</para>
</sect2>
<sect2 id="nls-example">
<title>A Practical Example</title>
<para>The following example will demonstrate an easy solution on how to
use NLS catalogs in a flexible way.</para>
<para>The below lines need to be put into a common header file of
the program, which is included into all source files where
localized messages are necessary:</para>
<programlisting>
#ifdef WITHOUT_NLS
#define getstr(n) nlsstr[n]
#else
#include &lt;nl_types.h&gt;
extern nl_catd catalog;
#define getstr(n) catgets(catalog, 1, n, nlsstr[n])
#endif
extern char *nlsstr[];</programlisting>
<para>Next, put these lines into the global declaration part of the
main source file:</para>
<programlisting>
#ifndef WITHOUT_NLS
#include &lt;nl_types.h&gt;
nl_catd catalog;
#endif
/*
* Default messages to use when NLS is disabled or no catalog
* is found.
*/
char *nlsstr[] = {
"",
/* 1*/ "some random message",
/* 2*/ "some other message"
};</programlisting>
<para>Next come the real code snippets, which open, read, and
close the catalog:</para>
<programlisting>
#ifndef WITHOUT_NLS
catalog = catopen("myapp", NL_CAT_LOCALE);
#endif
...
printf(getstr(1));
...
#ifndef WITHOUT_NLS
catclose(catalog);
#endif</programlisting>
</sect2>
<sect2 id="nls-mk">
<title>Making use of <filename>bsd.nls.mk</filename></title>
<para>Using the catalog files requires few repeatable steps,
such as compiling the catalogs and installing them to the
proper location. In order to simplify this process even
more, <filename>bsd.nls.mk</filename> introduces some macros.
It is not necessary to include <filename>bsd.nls.mk</filename>
explicitly, it is pulled in from the common Makefiles,
such as <filename>bsd.prog.mk</filename> or
<filename>bsd.lib.mk</filename>.</para>
<para>Usually it is enough to define <makevar>NLSNAME</makevar>,
which should have the catalog name mentioned as the first
argument of &man.catopen.3; and list the catalog files in
<makevar>NLS</makevar> without their <literal>.msg</literal>
extension. Here is an example, which makes it possible to
to disable NLS when used with the code examples before.
The <makevar>WITHOUT_NLS</makevar> &man.make.1; variable has
to be defined in order to build the program without NLS
support.</para>
<programlisting>
.if !defined(WITHOUT_NLS)
NLS= es_ES.ISO8859-1
NLS+= hu_HU.ISO8859-2
NLS+= pt_BR.ISO8859-1
.else
CFLAGS+= -DWITHOUT_NLS
.endif</programlisting>
<para>Conventionally, the catalog files are placed under the
<filename class="directory">nls</filename> subdirectory and
this is the default behaviour of <filename>bsd.nls.mk</filename>.
It is possible, though to override the location of the
catalogs with the <makevar>NLSSRCDIR</makevar> &man.make.1;
variable. The default name of the precompiled catalog files
also follow the naming convention mentioned before. It can be
overriden by setting the <makevar>NLSNAME</makevar> variable.
There are other options to fine tune the processing of the catalog
files but usually it is not needed, thus they are not described
here. For further information on <filename>bsd.nls.mk</filename>,
please refer to the file itself, it is short and easy to
understand.</para>
</sect2>
</sect1>
</chapter>