gettext
Roman Klochkov <kalimehtar@mail.ru>
(require gettext) | package: gettext |
1 Introduction
This package is an implementation of GNU gettext. It is implemented from scratch purely in Scheme and thus available with a BSD license. It 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.
(require gettext) (textdomain "myapp") (print (gettext "Hello, world!"))
The above is the most basic usage and is equivalent to the GNU gettext module.
procedure
(textdomain name [ #:locale locale #:dirs dirs #:cdir cdir #:cached cached? #:lookup-cached? lookup-cached?]) → procedure? name : (or/c string? (listof string?)) locale : (or/c #f string? (listof string?)) = #f dirs : (or/c #f path-string? (listof path-string?)) = #f cdir : (or/c #f string?) = #f cached? : boolean? = #t lookup-cached? : boolean? = #t (textdomain) → string?
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!")
2 Plural forms
There is also a procedure ngettext for working with plural forms:
3 Multiple Domains
procedure
domain : (or/c string? (listof string?)) msg-singular : string? msg-plural : string? n : number?
procedure
(dcngettext domain msg-singular msg-plural n category) → string? domain : (or/c string? (listof string?)) msg-singular : string? msg-plural : string? n : number? category : string?
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) → void?
domain : string? dirname : string?
4 Cascaded Domains
A major inconvenience of GNU 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.
This package, 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.
5 Context support
procedure
(dpgettext domain msgctxt msgid) → string?
domain : string? msgctxt : string? msgid : string?
procedure
(dcpgettext domain msgctxt msgid category) → string?
domain : string? msgctxt : string? msgid : string? category : string?
6 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 locale #:dirs dirs #:cdir cdir #:cached cached? #:lookup-cached? lookup-cached?]) → procedure? domain : (or/c string? (listof string?)) locale : (or/c #f string? (listof string?)) = #f dirs : (or/c #f path-string? (listof path-string?)) = #f cdir : (or/c #f string?) = #f cached? : boolean? = #t lookup-cached? : boolean? = #t
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:
6.1 Procedures
(<self> ’getter) - returns a gettext-style procedure
(<self> ’ngetter) - returns an ngettext-style procedure
(<self> ’setter) - returns a procedure for manually setting message translations
6.2 Actions
(<self> ’get <message>) => ((<self> ’getter) <message>)
(<self> ’nget <msg-singular> <msg-plural> <n>) => ((<self> ’ngetter) <msg-singular> <msg-plural> <n>)
(<self> ’set! <message> <value>) => ((<self> ’set!) <message> <value>)
6.3 Accessors
(<self> ’locale)
(<self> ’domain)
(<self> ’dirs)
(<self> ’files)
6.4 Cache management
(<self> ’use-cache <true-or-false>) - enable or disable message caching
(<self> ’clear) - clears the cache
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.
7 Quick Start Tutorial
(require gettext)
Replace any messages with (gettext <message>) or a shortcut.
- Generate the .po file with the command “xgettext myapp.rkt”.
If you use a shortcut its “xgettext –keyword=_ myapp.rkt”.
The -a parameter will extract _all_ strings.
The default output is messages.po, but the -d<name> parameter overrides this.
Make a “locale” subdirectory tree, and move the file to “./locale/en/LC_MESSAGES/myapp.po”.
When testing, "export GETTEXT_PATH=./locale/" so it can access the uninstalled files.
Optionally, compile the .po with the "msgfmt" command.
Write your info.rkt file to install the locale files in /usr/share/locale/.
8 Why Gettext?
Because you need some file format for your messages, and gettext is widely recognized and has extensive tool support.