Internationalizing and Localizing Applications in Oracle Solaris

Exit Print View

Updated: July 2014
 
 

gettext APIs

Text messages to be localized must be separated from the source code of the application and stored in a separate file. These files are referred to as message bundles, message catalogs, or portable message files. Every programming language provides a set of tools to work with these files. For example, in programming languages, such as the C, Python, and Perl programming languages provide gettext functions for translating messages.

You create portable message files with the gettext utility. These files are in plain text, and have .po as the file extension. You send the portable message file to translators for translation, and the translators update the file with the translated text. Post translation, the .po file contains the message ID with the corresponding translated text. For example:

$ cat cs.po   
.
.
#: code.c:37
#,c-format
msgid "My hovercraft is full of eels.\n"
msgstr "Moje vznášedlo je plné úhořů.\n"

However, you can improve the performace of the system by converting a portable object file to a message object file. A message object file has .mo as the file extension. To convert a portable object file to a message object file, use the msgfmt utility.


Note -  If the messages are wrapped in the gettext functions, translation is done depending on the current locale. This way the original text messages are used as keys to the message catalog.

How to Generate Localized Message Objects for a Shell Script

  1. Prepend /usr/gnu/bin to your PATH environment variable in order to use the GNU versions of the gettext tools.
  2. Extract messages from the shell script into a message file template using the xgettext command.
  3. Create a portable message (.po) file specific to the translation language by using the msginit command.
  4. Translate the created messages in the .po file.
  5. Create the LC_MESSAGES directory in the directory specified by the TEXTDOMAINDIR environment variable.
  6. Either create symbolic links or set the LANGUAGE variable.
  7. Create the message object (.mo) file.
Example 2-7  Generating Localized Message Objects for a Shell Script

This example shows how to generate localized message objects for a shell script. It assumes that you have the following shell script that has calls to the gettext function.

#!/usr/bin/bash
#

# set TEXTDOMAIN and TEXTDOMAINDIR as per the gettext(1) manual page
TEXTDOMAIN=test_gettext_sh
export TEXTDOMAIN
TEXTDOMAINDIR=/home/xxxxx/lib/locale
export TEXTDOMAINDIR
PATH=/usr/gnu/bin:/usr/bin
export PATH

# source gettext.sh for using eval_gettext and eval_ngettext
. gettext.sh

f="filename.dat"
# Use eval_gettext or eval_ngettext if it refers to shell variables

# TRANSLATORS: $f is replaced with a file name
eval_gettext "\$f not found"; echo
gettext "file not found"; echo

echo "`eval_gettext "\\\$f not found"`"
echo "`gettext "file not found"`"

For this shell script, you can create localized message objects by using the following steps:

  1. Prepend usr/gnu/bin to your PATH environment variable in order to use the GNU versions of gettext tools.

    $ PATH=/usr/gnu/bin:$PATH
  2. Extract messages from the shell script into a message file template using the xgettext command.

    $ xgettext -c"TRANSLATORS:" -L"Shell" test_gettext.sh

    A file called messages.po is created, which contains the header information and the message strings from the shell script. It also includes the explanatory comments for the translators. The following example shows an excerpt of the messages.po file:

    #. TRANSLATORS: $f is replaced with a file name
    #: test_gettext.sh:18 test_gettext.sh:21
    #, sh-format
    msgid "$f not found"
    msgstr ""
    
    #: test_gettext.sh:19 test_gettext.sh:22
    msgid "file not found"
    msgstr ""
  3. Create a portable message (.po) file which is specific to the translation language by using the msginit command. For example, use the following command to create a .po file for the Japanese ja_JP.UTF-8 locale:

    $ msginit --no-translator --locale=ja_JP.UTF-8 --input=messages.po

    A file called ja.po is created.

  4. Translate the messages in the ja.po file.

  5. Create the LC_MESSAGES directory in the directory specified by the TEXTDOMAINDIR environment variable.

    $ mkdir -p lib/locale/ja/LC_MESSAGES
  6. Either create symbolic links or set the LANGUAGE variable.

    • Create symbolic links

      $ ln -s ja lib/locale/ja_JP.UTF-8
    • Set the LANGUAGE variable.

      $ LANGUAGE=ja_JP.UTF-8:ja
      $ export LANGUAGE
  7. Create the message object (.mo) file.

    $ msgfmt -o lib/locale/ja/LC_MESSAGES/test_gettext_sh.mo ja.po

How to Generate Localized Text Messages for a C Program

  1. Prepend /usr/gnu/bin to your PATH environment variable.
  2. Extract messages from the source code into the message file template using the xgettext command.
  3. Create another .po file for the LC_TIME locale category.
  4. Create portable message (.po) files specific to the translation language by using the msginit command.
  5. Translate the created .po files.
  6. Create the LC_MESSAGES and LC_TIME directories in the directory specified by the LOCALEDIR variable.
  7. Either create symbolic links or set the LANGUAGE variable.
  8. Create the message object (.mo) files.
Example 2-8  Generating Localized Text Messages for a C Program

This example shows how to generate localized message objects for a C program. It assumes that you have the following C program that has calls to the gettext function.

#include <stdio.h>
#include <sys/types.h>
#include <libintl.h>
#include <locale.h>
#include <time.h>



/*
 * _()   is used for the strings to extract messages.
 * N_()  is used for the string array message to extract messages.
 * T_()  is used for the strings to extract messages with working on LC_TIME
 */
#define _(String)      gettext (String)
#define gettext_noop(String)   String
#define N_(String)      gettext_noop (String)
#define T_(String)      gettext_noop (String)

#define LOCALEDIR      "/home/xxxxx/lib/locale"
#define PACKAGE         "test_gettext"

static const char *msg[] = {
   N_("The first message"),
   N_("The second message"),
};

int main(int ac, char **av)
{
   char *file = "test.dat";
   int line = 40;
   int column = 10;
   time_t tloc;
   char time_buf[BUFSIZ];

   setlocale(LC_ALL, "");
   bindtextdomain(PACKAGE, LOCALEDIR);
   textdomain(PACKAGE);
   /*
    * By default, the characters are converted to current locale's encoding.
    * If this is not desired, call bind_textdomain_codeset(). For example,
    * if you want "UTF-8" encoding, specify "UTF-8" in the second argument.
    *
    *   bind_textdomain_codeset("test_gettext", "UTF-8");
    */
   printf(_("This is a test\n"));

   printf("%s\n", _(msg[0]));
   printf("%s\n", _(msg[1]));
   /* TRANSLATORS:
      First %d is replaced by a line number.
      Second %d is replaced by a column number.
      %s is replaced by a file name. */
   printf(_("ERROR: invalid input at line %1$d, %2$d in %3$s\n"),
       line, column, file);

   /*
    * strftime() works with LC_TIME not LC_MESSAGES so to get properly
    * formatted time messages we have to call dcgettext() with LC_TIME category.
    */
   (void) time(&tloc);
   (void) strftime(time_buf, sizeof (time_buf),
       /* TRANSLATORS:
        This is time format used with strftime().
        Please modify time format to fit your locale by using
        date '+%a %b %e %H:%M:%S' */
       dcgettext(NULL, T_("%a %b %e %H:%M:%S"), LC_TIME),
       localtime(&tloc));
   printf("%s\n", time_buf);

   return(0);
}

For this C program, you can create localized message objects using the following steps:

  1. Prepend /usr/gnu/bin to your PATH environment variable:

    $ PATH=/usr/gnu/bin:$PATH
  2. Extract messages from the source code into the message file template using the xgettext command:

    $ xgettext -c"TRANSLATORS:" -k -k"_" -k"N_" -L"C" test_gettext.c

    The messages.po file is created for the LC_MESSAGES locale category. It contains the header information and the message strings including the explanatory comments for the translators. The following example shows an excerpt of the messages.po file:

    #: test_gettext.c:21
    msgid "The first message"
    msgstr ""
    
    #: test_gettext.c:22
    msgid "The second message"
    msgstr ""
    
    #: test_gettext.c:43
    #, c-format
    msgid "This is a test\n"
    msgstr ""
    
    #. TRANSLATORS:
    #. First %d is replaced by a line number.
    #. Second %d is replaced by a column number.
    #. %s is replaced by a file name.
    #: test_gettext.c:51
    #, c-format
    msgid "ERROR: invalid input at line %1$d, %2$d in %3$s\n"
    msgstr ""
  3. Create another .po file for the LC_TIME locale category:

    $ xgettext -c"TRANSLATORS:" -k -k"T_" -L"C" -o messages_t.po test_gettext.c

    The messages_t.po file is created for the LC_TIME locale category.

  4. Create portable message (.po) files which are specific to the translation language by using the msginit command.

    For example, use the following command to create a portable file messages for the Japanese ja_JP.UTF-8 locale:

    $ msginit --no-translator --locale=ja_JP.UTF-8
     --input=messages.po
    $ msginit --no-translator --locale=ja_JP.UTF-8 --input=messages_t.po
     --output-file=ja_t.po
    

    The ja.po and ja_t.po files are created.

  5. Translate the created ja.po and ja_t.po files.

  6. Create the LC_MESSAGES and LC_TIME directories in the directory specified by the LOCALEDIR variable.

    $ mkdir -p lib/locale/ja/LC_MESSAGES lib/locale/ja/LC_TIME
  7. Either create symbolic links or set the LANGUAGE variable:

    • Create symbolic links.

      $ ln -s ja lib/locale/ja_JP.UTF-8
    • Set the LANGUAGE variable.

      $ LANGUAGE=ja_JP.UTF-8:ja
      $ export LANGUAGE
  8. Create the message objects (.mo files).

    $ msgfmt -o lib/locale/ja/LC_MESSAGES/test_gettext.mo ja.po
    $ msgfmt -o lib/locale/ja/LC_TIME/test_gettext.mo ja_t.po

Message Object File Format

The message object files are created in the following format:

/usr/lib/locale/locale/category/textdomain.mo

The path has several components:

/usr/lib/locale

The default path predicate for the message object files. For example, in case of text domain collisions, the path is specified by a call to the bindtextdomain() function. In the case of third party software, the message object files will be available in /usr/share/locale directory.

locale

The locale directory.

category

The locale category.

textdomain.mo

The text domain specified by a textdomain() function call. It is a unique identifier and the file name for the message catalog.

Consider the following example:

/usr/lib/locale/it_IT.UTF-8/LC_MESSAGES/mymessages.mo

where:

it_IT.UTF-8

The locale directory. This message object contains translations for Italian language and will be used for this locale and any other which are symbolic links to this directory.

LC_MESSAGES

The locale category.


Note -  Messages are usually in the LC_MESSAGES and LC_TIME categories.
mymessages

The message catalog name.

Oracle Solaris and GNU-compatible gettext Interfaces

The Oracle Solaris gettext APIs provide support for both Oracle Solaris and GNU-compatible message catalog files. However, some gettext APIs are specific to the GNU-compatible message catalog files. The Solaris and GNU-compatible gettext interfaces are as follows:

gettext()

Retrieve a text string from the message catalog

dgettext()

Retrieve a message from a message catalog for a specific domain

textdomain()

Set and query the current domain

bindtextdomain()

Bind the path for a message domain

dcgettext()

Retrieve a message from a message catalog for a specific domain and category

GNU gettext Interfaces

The gettext APIs that work only with GNU-compatible message catalog files are as follows:

ngettext()

Retrieve a text string from the message catalog and choose a plural form

dngettext()

Retrieve a text string from the message catalog for a specific domain and choose a plural form

bind_textdomain_codeset()

Specify the output codeset for message catalogs for a domain

dcngettext()

Retrieve a text string from the message catalog for a specific domain and category and choose a plural form

For more information about GNU text message handling, see the GNU gettext reference.

For more information about gettext functions, see the msgfmt(1), xgettext(1), and gettext(1) man pages.

Message Handling Tools

The gettext provides functions and command-line tools to create and handle message object files. Oracle Solaris message objects have a different format from GNU gettext message objects. The Oracle Solaris variants of command-line tools to handle messages are as follows:

/usr/bin/gettext

Retrieve a text string from the message catalog

/usr/bin/msgfmt

Create a message object from a portable message file

/usr/bin/xgettext

Retrieve calls to gettext strings from C programs

The GNU variants of the command-line tools to handle messages are as follows:

/usr/bin/ggettext

Retrieve a text string from the message catalog

/usr/bin/gmsgfmt

Create a message object from a message file

/usr/bin/gxgettext

Retrieve gettext call strings

To distinguish from Oracle Solaris tools, the GNU variant tools are prefixed with the letter g, and are symbolic links to the /usr/gnu/bin directory. For example, /usr/bin/ggettext is a symbolic link to /usr/gnu/bin/gettext.

The GNU gettext tools are part of the text/gnu-gettext package, which also includes other utilities for processing message catalogs.


Note -  The Python gettext implementation supports only the GNU gettext message object format. Therefore, for Python programs, you must create GNU compatible message objects.

For more information, see the msgcat(1), msgcmp(1), and msgmerge(1) man pages.