gettext for Common Lisp

This is a port of the GNU gettext runtime to Common Lisp. For more
information about GNU gettext see


* 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

Get the gettext sources from GitHub:

    cd quicklisp/local-projects
    git clone 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

    :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

    (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 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

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

Loading catalogs at runtime

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

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

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


Special variable

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:

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.


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/, 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.


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.

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

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


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*))


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.


The allowed type of the locale parameter. One of the keyword symbols

GETTEXT:CATALOG-META* &optional domain category locale

Returns meta data about catalog as an assoc list. See


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

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?
Thomas Bakketun <>
GNU Lesser General Public Licence 3.0