Free-gettext

free-gettext is a drop-in compatible replacement for the gettext egg implemented from scratch purely in Scheme and thus available with a BSD license. free-gettext is also easier to use, allowing you to optionally load .po files directly without compiling them to .mo files first, and has many advanced features such as multi-language locales and cascaded message domains.

  1. Free-gettext
    1. Introduction
      1. Plural forms
      2. Multiple Domains
    2. Cascaded Domains
    3. First-class Lookup Interface
      1. Procedures
      2. Actions
      3. Accessors
      4. Cache management
    4. Quick Start Tutorial
    5. Why Gettext?
    6. Requirements
    7. License
    8. History

Introduction

[procedure] (textdomain [<name>])
[procedure] (gettext <message>)
(use free-gettext)
(textdomain "myapp")
(print (gettext "Hello, world!"))

The above is the most basic usage and is equivalent to the GNU gettext module. TEXTDOMAIN sets the name of the current "domain", which is the name of the message catalog (typically the name of the application). With no arguments it returns the name of that domain. GETTEXT then fetches the translation of the message in that domain in the current locale (from the LANG environment variable), or returns the original string if the message can't be found.

Apart from actually creating the message files (which you can put off indefinitely), that's all you need to internationalize your message strings. Since every natural language string needs to be translated, a common idiom is to give a short name to GETTEXT:

(define _ gettext)
(print (_"Hello, world!"))

Alternately, you could make , a prefix for all message strings:

(define-syntax unquote
  (syntax-rules ()
    ((_ str) (gettext str))))

(print ,"Hello, world!")

Note that free-gettext converts all strings to utf8 internally, to work best with the utf8 egg and other eggs which assume Chicken strings are utf8. This is a feature - all strings are consistently the same encoding, and you don't need to create separate message files for every different encoding a user might want. Many GUI toolkits like Gtk also assume utf8. On the other hand, if you want to output text to a non-utf8 terminal you'll need to perform the translation manually.

Plural forms

There is also a procedure NGETTEXT for working with plural forms:

[procedure] (ngettext <msg-singular> <msg-plural> <n>)
(format #t (ngettext "~D file removed" "~D files removed" n) n)

In the case of English (or if no translation is found), this applies the familiar logic of using the first string (singular) if N is 1, and the second string (plural) otherwise. Not all languages have two plural forms, however, and the NGETTEXT interface allows you to write message files with the proper inflections for any language and any value of N. The exact details of how to write a message file is beyond the scope of this document - see the GNU gettext documentation.

Multiple Domains

As with GNU gettext, the following are provided:

[procedure] (dgettext <domain> <message>)
[procedure] (dcgettext <domain> <message> <locale>)
[procedure] (dngettext <domain> <msg-singular> <msg-plural> <n>)
[procedure] (dcngettext <domain> <msg-singular> <msg-plural> <n> <locale>)

These let you lookup messages in domains other than that specified by TEXTDOMAIN. This is just a clumsy way to make up for inadequacies in the traditional gettext design - if you want to work with multiple domains, you should use the cascaded domains described below.

[procedure] (bindtextdomain <domain> <dirname>)

Override the directory to look for the given domain in.

Cascaded Domains

A major inconvenience of gettext is that it's essentially a one message file per application design. Related applications, applications with the same menu entries, or same error messages, all of these need to have their own duplicated translations of all the same messages.

free-gettext, however, lets you specify a list of strings as the text domain, and for any message lookup these domains are searched in order.

(textdomain '("myapp" "gimp"))  ; search 1st myapp, then gimp
(gettext "/File/Close")         ; "Close" from gimp unless overridden

You can thus share messages freely between applications, and effectively have collections of message dictionaries.

First-class Lookup Interface

One of the most common types of application to write these days is a web application, for which gettext is poorly suited. Gettext assumes a single locale, but for any kind of server the clients may each have their own locale. free-gettext therefore provides a way to generate separate first class gettext procedures.

[procedure] (make-gettext domain [locale] [dirs] [cdir] [cached?] [lookup-cached?])

DOMAIN is the same as the first argument to TEXTDOMAIN, and may be similarly cascaded.

LOCALE is the locale, as a string or list of strings of the form LANG[_REGION][.ENCODING], and defaults to the LANG or LC_ALL environment variable, or C if neither is set. Multiple locales are also searched in order, which can be useful when you have incomplete translations in similar languages.

DIRS (again a string or list of strings) is the search path of directories which should hold the LOCALE/CDIR/ directories which contain the actual message catalogs. This is always appended with the system default, e.g. "/usr/share/locale", and may also inherit from the GETTEXT_PATH colon-delimited environment variable.

CDIR is the category directory, defaulting to either the LC_CATEGORY environment variable or the appropriate system default (e.g. LC_MESSAGES). You generally won't need to specify this.

CACHED? means to cache individual messages, and defaults to #t. This is a natural default (GNU gettext similarly caches messages), but during development it can be handy to disable caching if you intend to edit messages while coding.

LOOKUP-CACHED? means to cache the lookup dispatch generated by these parameters, and defaults to #t. Thus by default multiple calls to MAKE-GETTEXT with the same parameters return the same object, and they in turn share the same message cache.

MAKE-GETTEXT returns a dispatch closure with the following parameters:

Procedures

Actions

Accessors

Cache management

Note that TEXTDOMAIN actually accepts all of the same parameters as MAKE-GETTEXT, so that you can specify the locale and other settings manually if you want.

Quick Start Tutorial

Why Gettext?

Because you need some file format for your messages, and gettext is widely recognized and has extensive tool support.

Requirements

charconv

mime

License

BSD

History

1.0
initial release