gettext

2013-10-03
gettext for Common Lisp
=======================

This is a port of the GNU gettext runtime to Common Lisp. For more
information about GNU gettext see http://www.gnu.org/software/gettext/.


Highlights
----------

* Implemented in Common Lisp. No need for any C libraries.
* Use GNU gettext tools during development.
* Supports multithreaded applications with different langauge in each thread.
* Translations can be embedded into compiled applications.


Download and installation
-------------------------

The libarary has some dependencies, so the easiest way is to use
Quicklisp, see http://www.quicklisp.org/

Get the gettext sources from GitHub:

    cd quicklisp/local-projects
    git clone git://github.com/copyleft/gettext.git
    ln -s gettext/gettext-example .

Then load it:

    (ql:quickload "gettext")

Eventually gettext will become part of quicklisp, and only the last
step will be neccary (unless you need the latest version).


How to use
----------

This library reimplements only the runtime part of GNU gettext in
Common Lisp. In order to successfully use this library you need to
install the GNU gettext tools. No GNU gettext tools or library are
needed at runtime.

The easiest way to get started is to look at the sample application
located in the subdirectory gettext-example.

Step 1: Add gettext as a dependency to your system definition
(gettext-example.asd):

    :depends-on (:gettext)

Step 2: Add the GETTEXT package to the use list of you applications
package. Setup gettext in your applications package (package.lisp):

    (gettext:setup-gettext #:example "gettext-example")

The first parameter is the name of the package. The second parameter
is the textdomain. Textdomains are namespaces for the translated
text. For most application a single textdomain (with the same name as
the asdf system) will suffice.

Step 3: Load the translated messages (example.lisp):

    (preload-catalogs #.(asdf:system-relative-pathname :gettext-example "locale/"))

This macro takes a single parameter, the pathname of a directory tree
containing translation catalogs in MO format. The texts are loaded at
compile time and become part of the compiled file, thus the the MO
files are not need at runtime. An alternative approch is to load the
MO files at runtime, see the section "Loading catalogs at runtime".

Step 4: Make sure the current locale is set by binding the special
variable GETTEXT:*CURRENT-LOCALE*:

    (setf *current-locale* "nn")

Here the locale is hardcoded to "nn". In a real world application it
would be set to the current users preferred language. If the
application is a multithreaded multiuser application (like most web
applications), dynamically bind GETTEXT:*CURRENT-LOCALE* to the logged
in users preferred language.

Step 5: Mark texts for translation, e.g.:

    (write-line (_ "This is an example gettext program."))

Extract the texts for translations using the xgettext program from GNU
gettext. This step will have to be repeated whenver the texts are
updated in the source code. See update-translations.sh for a script
that automates this job.

Step 6: Translate the texts. First create a PO file for the langauge,
using the msginit tool. Then you edit this file using e.g. Emacs with
po-mode. The PO file can easly be updated with new texts with the help
of the msgmerge tool. See update-translations.sh.

Step 7: Finally convert the PO files into MO files using msgfmt. See
update-translations.sh for details.


Loading catalogs at runtime
---------------------------

Replace GETTEXT:PRELOAD-CATALOGS with a SETF of the place
GETTEXT:TEXTDOMAINDIR:

    (setf (textdomaindir "gettext-example")
          (asdf:system-relative-pathname :gettext-example "locale/"))

The catalogs will be loaded as needed and cached until the
appliciatons quits.


Reference
---------

Special variable
GETTEXT:*CURRENT-LOCALE*

This variable must be bound to a string denoting the current
locale. The library does not try to decode this string any way, it
simply uses it to look for message catalogs in the file system. It's
recommended to use the two letter language codes defined in ISO 639-1.
A list can be found here:
http://www.gnu.org/software/gettext/manual/gettext.html#Language-Codes


Macro
GETTEXT:SETUP-GETTEXT package default-domain

Defines gettext lookup functions in the provided package. _ and
GETTEXT is as shortcut for GETTEXT:GETTEXT* and NGETTEXT is a shortcut
for GETTEXT:NGETTEXT*, but with default-domain as the default
domain. N_ is simply a shortcut for GETTEXT:GETTEXT-NOOP.


Place
GETTEXT:TEXTDOMAINDIR domain
(SETF (GETTEXT:TEXTDOMAINDIR domain) directory)

Specifies where to find translation catalogs for the given domain. The
directory is the base directory used for lookup. The actual name of
message catalog will be TEXTDOMAINDIR/l/LC_MESSAGES/domain.mo, where
TEXTDOMAINDIR is the directory given setf to GETTEXT:TEXTDOMAINDIR, l
is the current locale, LC_MESSAGES is the category and domain is the
text domain.

Place
GETTEXT:TEXTDOMAIN
(SETF (GETTEXT:TEXTDOMAIN) domain)

Returns or sets the default text domain used by GETTEXT:GETTEXT* and
GETTEXT:NGETTEXT*. Shortcut lookup functions defined by
GETTEXT:SETUP-GETTEXT ignores this place.


Function
GETTEXT:GETTEXT* msgid &optional domain category locale

Lookup a translated using the provided msgid. Domain defaults to
(GETTEXT:TEXTDOMAIN). Category defaults to :LC_MESSAGES, and locale
defaults to GETTEXT:*CURRENT-LOCALE*.


Function
GETTEXT:NGETTEXT* msgid1 msgid2 n &optional domain category locale

Lookup a plural translated text. msgid1 is the singular form, msgid2
is the plural form and n is the number used to decide which plural
form to use. See GETTEXT:GETTEXT* for description of the optional
parameters.


Function
GETTEXT:GETTEXT-NOOP msgid

This function simply returns its msgid parameter untranslated. It's
only used to mark texts that shall be translated, but where the text
occurs in an expression that will be executed at compile time. Example:

    (defparameter *greeting* (gettext-noop "Hello world"))
    ;; In some function:
       (write-line (gettext* *greeting*))


Macro
GETTEXT:PRELOAD-CATALOGS textdomaindir

Loads all message catalogs (in every category, in every locale and
every domain) in the textdomaindir directory tree. This loading is
done at macro expantion time. The effects is as if the translated text
where part of the source code. Thus, the message catalogs are not needed
at runtime.


Type
GETTEXT:LC-CATEGORY

The allowed type of the locale parameter. One of the keyword symbols
L:LC_ADDRESS, :LC_ALL, :LC_COLLATE, :LC_CTYPE, :LC_IDENTIFICATION,
:LC_MEASUREMENT, :LC_MESSAGES, :LC_MONETARY, :LC_NAME, :LC_NUMERIC,
:LC_PAPER, :LC_TELEPHONE and :LC_TIME.


TODO
----

The pgettext function of GNU gettext is not implemented. Implementing
this should be fairly stright forward, but apparently the xgettext
tool doesn't support this function when extracting texts form Lisp
code.

Encoding in MO files is hardcoded to UTF-8. Is this really a problem?
Is there really a good reason for using anything else these days?
Author
Thomas Bakketun <thomas.bakketun@copyleft.no>
License
GNU Lesser General Public Licence 3.0