33.2. Internationalization of User Programs
Prev | Chapter 33. Extensions Implemented as Modules | Next

## 33.2. Internationalization of User Programs

### 33.2.1. The GNU gettext

GNU gettext is a set of functions, included in CLISP or the C library, which permit looking up translations of strings through message catalogs. It is also a set of tools which makes the translation maintenance easy for the translator and the program maintainer.

The GNU gettext functions are available in CLISP in the “I18N” package, which is `EXT:RE-EXPORT`ed from the “EXT” package.

This module is present in the base linking set by default.

When this module is present, `*FEATURES*` contains the symbol `:I18N`.

`(`I18N:GETTEXT` `MSGID` `&OPTIONAL` `DOMAIN` `CATEGORY`)`
  returns the translation of the message `MSGID`, in the given `DOMAIN`, depending on the given `CATEGORY`. `MSGID` should be an ASCII string, and is normally the English message.
`(`I18N:NGETTEXT` `MSGID` `msgid_plural` `n` `&OPTIONAL` `DOMAIN` `CATEGORY`)`
  returns the plural form of the translation for of `MSGID` and `n` in the given `DOMAIN`, depending on the given `CATEGORY`. `MSGID` and `msgid_plural` should be ASCII strings, and are normally the English singular and English plural variant of the message, respectively.

#### 33.2.1.1. Domain

The `DOMAIN` is a string identifier denoting the program that is requesting the translation. The pathname of the message catalog depends on the `DOMAIN`: usually it is located at `TEXTDOMAINDIR/l/LC_MESSAGES/domain.mo`, where `l` is the ISO 639-2 code of the language. The notion of `DOMAIN` allows several Lisp programs running in the same image to request translations independently of each other.

Function `I18N:TEXTDOMAIN`. `(`I18N:TEXTDOMAIN`)` is a place that returns the default `DOMAIN`, used when no `DOMAIN` argument is passed to the `I18N:GETTEXT` and `I18N:NGETTEXT` functions. It is `SETF`able. `(`SETF` `I18N:TEXTDOMAIN`)` is usually used during the startup phase of a program. Note that the default `DOMAIN` is not saved in a memory image. The use of `(`SETF` `I18N:TEXTDOMAIN`)` is recommended only for programs that are so simple that they will never need more than one `DOMAIN`.

Function `I18N:TEXTDOMAINDIR`. `(`I18N:TEXTDOMAINDIR` `DOMAIN`)` is a place that returns the base directory, called `TEXTDOMAINDIR` above, where the message catalogs for the given `DOMAIN` are assumed to be installed. It is `SETF`able. `(`SETF` `I18N:TEXTDOMAINDIR`)` is usually used during the startup phase of a program, and should be used because only the program knows where its message catalogs are installed. Note that the `TEXTDOMAINDIR`s are not saved in a memory image.

#### 33.2.1.2. Category

The `CATEGORY` argument of the `I18N:GETTEXT` and `I18N:NGETTEXT` functions denotes which `LOCALE` facet the result should depend on. The possible values are a platform-dependent subset of `:LC_ADDRESS`, `:LC_ALL`, `:LC_COLLATE`, `:LC_CTYPE`, `:LC_IDENTIFICATION`, `:LC_MEASUREMENT`, `:LC_MESSAGES`, `:LC_MONETARY`, `:LC_NAME`, `:LC_NUMERIC`, `:LC_PAPER`, `:LC_TELEPHONE`, `:LC_TIME` The use of these values is useful for users who have a character/time/collation/money handling set differently from the usual message handling. Note that when a `CATEGORY` argument is used, the message catalog location depends on the `CATEGORY`: it will be expected at `TEXTDOMAINDIR/ll/category/domain.mo`.

#### 33.2.1.3. Internationalization Example

A non-internationalized program simulating a restaurant dialogue might look as follows.

prog.lisp.

    (setq n (parse-integer (first EXT:*ARGS*)))

    (format t "~A~%" "'Your command, please?', asked the waiter.")

    (format t "~@?~%"
              (if (= n 1) "a piece of cake" "~D pieces of cake")
              n)

After being internationalized, all strings are wrapped in `I18N:GETTEXT` calls, and `I18N:NGETTEXT` is used for plurals. Also, `I18N:TEXTDOMAINDIR` is assigned a value; in our case, for simplicity, the current directory.

prog.lisp.

    (setf (textdomain) "prog")
    (setf (textdomaindir "prog") "./")

    (setq n (parse-integer (first EXT:*ARGS*)))

    (format t "~A~%"
              (gettext "'Your command, please?', asked the waiter."))

    (format t "~@?~%"
              (ngettext "a piece of cake" "~D pieces of cake" n)
              n)

For ease of reading, it is customary to define an abbreviation for the `I18N:GETTEXT` function. An underscore is customary.

prog.lisp.

    (setf (textdomaindir "prog") "./")
    (defun _ (msgid) (gettext msgid "prog"))

    (setq n (parse-integer (first EXT:*ARGS*)))

    (format t "~A~%"
              (_"'Your command, please?', asked the waiter."))

    (format t "~@?~%"
              (ngettext "a piece of cake" "~D pieces of cake" n "prog")
              n)

Now the program's maintainer creates a message catalog template through the command

    $ xgettext -o prog.pot prog.lisp

### Note

xgettext version 0.11 or higher is required here.

The message catalog template looks roughly like this.

prog.pot.

    msgid "'Your command, please?', asked the waiter."
    msgstr ""

    msgid "a piece of cake"
    msgid_plural "%d pieces of cake"
    msgstr[0] ""
    msgstr[1] ""

Then a French translator creates a French message catalog

prog.fr.po.

    msgid ""
    msgstr ""
    "Content-Type: text/plain; charset=ISO-8859-1\n"
    "Plural-Forms: nplurals=2; plural=(n > 1);\n"

    msgid "'Your command, please?', asked the waiter."
    msgstr "«Votre commande, s'il vous plait», dit le garçon."

    # Les gateaux allemands sont les meilleurs du monde.
    msgid "a piece of cake"
    msgid_plural "%d pieces of cake"
    msgstr[0] "un morceau de gateau"
    msgstr[1] "%d morceaux de gateau"

and sends it to the program's maintainer.

The program's maintainer compiles the catalog as follows:

    $ mkdir -p ./fr/LC_MESSAGES
    $ msgfmt -o ./fr/LC_MESSAGES/prog.mo prog.fr.po

When a user in a french `LOCALE` then runs the program

    $ clisp prog.lisp 2

she will get the output

        «Votre commande, s'il vous plait», dit le garçon.
        2 morceaux de gateau

### 33.2.2. Locale

`(`I18N:SET-LOCALE` `&OPTIONAL` `CATEGORY` `LOCALE`)`

  This is an interface to `setlocale`.

  When `LOCALE` is missing or `NIL`, return the current one.

  When `CATEGORY` is missing or `NIL`, return all categories as a `LIST`.

`(`I18N:LOCALE-CONV`)`

  This is an interface to `localeconv`.

  Returns a I18N:LOCALE-CONV structure.

`(`I18N:LANGUAGE-INFORMATION` `&OPTIONAL` `item`)`

  This is an interface to `nl_langinfo` (UNIX) and `GetLocaleInfo` (Win32).

  When `item` is missing or `NIL`, return all available information as a `LIST`.

| These notes document CLISP version 2.45 | Last modified: 2008-05-14

Enter your search termsSubmit search form
Webclisp.cons.orgclisp.podval.orgwww.lisp.org

Prev | Up | Next
33.1. System Calls | Home | 33.3. POSIX Regular Expressions