An pure Common Lisp implementation of gettext runtime. gettext is an internationalization and localization (i18n) system commonly used for writing multilingual programs on Unix-like computer operating systems.
Thomas Bakketun <email@example.com>
GNU Lesser General Public Licence 3.0
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. Function GETTEXT:CATALOG-META* &optional domain category locale Returns meta data about catalog as an assoc list. See https://www.gnu.org/software/gettext/manual/gettext.html#Header-Entry 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?