1<?xml version="1.0" encoding="UTF-8"?> 2 3<section id="i18n-mod"><title>Internationalization of User Programs</title> 4 5<section id="ggettext"><title>The &ggettext;</title> 6 7<para>&ggettext; is a set of functions, included in &clisp; or the 8&c-lang; library, which permit looking up translations of strings 9through message catalogs. It is also a set of tools which makes the 10translation maintenance easy for the translator and the program 11maintainer.</para> 12 13<para>The &ggettext; functions are available in &clisp; in the 14&i18n-pac; package, which is &re-export;ed from the &ext-pac; 15package.</para> 16&base-module; 17<simpara>When this module is present, &features-my; 18 contains the symbol <constant>:I18N</constant>.</simpara> 19 20<variablelist> 21 <varlistentry id="gettext"><term><code>(&gettext; 22 &msgid; &optional-amp; &domain; &category;)</code></term> 23 <listitem><simpara>returns the translation of the message &msgid;, 24 in the given &domain;, depending on the given &category;. 25 &msgid; should be an &ascii; string, and is normally the English message. 26 </simpara></listitem></varlistentry> 27 <varlistentry id="ngettext"><term><code>(&ngettext; 28 &msgid; <replaceable>msgid_plural</replaceable> 29 &n-r; &optional-amp; &domain; &category;)</code></term> 30 <listitem><simpara>returns the plural form of the translation for of 31 &msgid; and &n-r; in the given &domain;, depending on the given 32 &category;. &msgid; and <replaceable>msgid_plural</replaceable> 33 should be &ascii; strings, and are normally the English singular and 34 English plural variant of the message, respectively. 35 </simpara></listitem></varlistentry> 36</variablelist> 37 38<section id="domain"><title>Domain</title> 39 <para>The &domain; is a string identifier denoting the program that 40is requesting the translation. The pathname of the message catalog 41depends on the &domain;: usually it is located at 42<filename>TEXTDOMAINDIR/l/LC_MESSAGES/domain.mo</filename>, where 43&l-r; is the <ulink url="http://www.loc.gov/standards/iso639-2/">ISO 44 639-2</ulink> code of the language. 45The notion of &domain; allows several Lisp programs running in the same 46image to request translations independently of each other.</para> 47 48<formalpara id="textdomain"><title>Function &textdomain;</title> 49<para><code>(&textdomain;)</code> is a &place; that returns the default 50 &domain;, used when no &domain; argument is passed to the &gettext; and 51 &ngettext; functions. It is &setf;able. 52<code>(&setf; &textdomain;)</code> is usually used 53during the startup phase of a program. 54Note that the default &domain; is not saved in a &mem-image;. 55The use of <code>(&setf; &textdomain;)</code> is 56recommended only for programs that are so simple that they will never 57need more than one &domain;.</para></formalpara> 58 59<formalpara id="textdomaindir"><title>Function &textdomaindir;</title> 60<para><code>(&textdomaindir; &domain;)</code> 61is a &place; that returns the base directory, called 62<filename>TEXTDOMAINDIR</filename> above, where the message 63catalogs for the given &domain; are assumed to be installed. 64It is &setf;able. 65<code>(&setf; &textdomaindir;)</code> is usually used 66during the startup phase of a program, and should be used because only 67the program knows where its message catalogs are installed. 68Note that the <filename>TEXTDOMAINDIR</filename>s 69are not saved in a &mem-image;.</para></formalpara> 70 71</section> 72 73<section id="category"><title>Category</title> 74<para>The &category; argument of the &gettext; and &ngettext; 75functions denotes which &locale; facet the result should depend on. 76The possible values are a platform-dependent subset of 77<simplelist type="inline"> 78<member><constant>:LC_ADDRESS</constant></member> 79<member><constant>:LC_ALL</constant></member> 80<member><constant>:LC_COLLATE</constant></member> 81<member><constant>:LC_CTYPE</constant></member> 82<member><constant>:LC_IDENTIFICATION</constant></member> 83<member><constant>:LC_MEASUREMENT</constant></member> 84<member><constant>:LC_MESSAGES</constant></member> 85<member><constant>:LC_MONETARY</constant></member> 86<member><constant>:LC_NAME</constant></member> 87<member><constant>:LC_NUMERIC</constant></member> 88<member><constant>:LC_PAPER</constant></member> 89<member><constant>:LC_TELEPHONE</constant></member> 90<member><constant>:LC_TIME</constant></member></simplelist> 91The use of these values is useful for users who have a 92character/time/collation/money handling set differently from the usual 93message handling. 94Note that when a &category; argument is used, the message catalog 95location depends on the &category;: it will be expected at 96<filename>TEXTDOMAINDIR/ll/category/domain.mo</filename>.</para> 97</section> 98 99<section id="i18n-example"><title>Internationalization Example</title> 100 101<para>A non-internationalized program simulating a restaurant dialogue 102might look as follows.</para> 103 104<formalpara id="i18n-non-i"><title>prog.lisp</title> 105<para><programlisting language="lisp"> 106(setq n (parse-integer (first &args;))) 107 108(format t "~A~%" "'Your command, please?', asked the waiter.") 109 110(format t "~@?~%" 111 (if (= n 1) "a piece of cake" "~D pieces of cake") 112 n) 113</programlisting></para></formalpara> 114 115<para>After being internationalized, all strings are wrapped in 116&gettext; calls, and &ngettext; is used for plurals. 117Also, &textdomaindir; is assigned a value; in our case, for simplicity, 118the current directory.</para> 119 120<formalpara id="i18n-i"><title>prog.lisp</title> 121<para><programlisting language="lisp"> 122(setf (textdomain) "prog") 123(setf (textdomaindir "prog") "./") 124 125(setq n (parse-integer (first &args;))) 126 127(format t "~A~%" 128 (gettext "'Your command, please?', asked the waiter.")) 129 130(format t "~@?~%" 131 (ngettext "a piece of cake" "~D pieces of cake" n) 132 n) 133</programlisting></para></formalpara> 134 135<para>For ease of reading, it is customary to define an abbreviation 136for the &gettext; function. An underscore is customary.</para> 137 138<formalpara id="i18n-i-abbrev"><title>prog.lisp</title> 139<para><programlisting language="lisp"> 140(setf (textdomaindir "prog") "./") 141(defun _ (msgid) (gettext msgid "prog")) 142 143(setq n (parse-integer (first &args;))) 144 145(format t "~A~%" 146 (_"'Your command, please?', asked the waiter.")) 147 148(format t "~@?~%" 149 (ngettext "a piece of cake" "~D pieces of cake" n "prog") 150 n) 151</programlisting></para></formalpara> 152 153<para>Now the program's maintainer creates a message catalog template 154through the command 155<screen> 156&sh-prompt; xgettext -o prog.pot prog.lisp 157</screen> 158<note><title>Note</title> 159 <para>xgettext version 0.11 or higher is required here.</para></note></para> 160 161<para>The message catalog template looks roughly like this.</para> 162 163<formalpara id="i18n-pot"><title>prog.pot</title> 164<para><programlisting language="pot"> 165msgid "'Your command, please?', asked the waiter." 166msgstr "" 167 168msgid "a piece of cake" 169msgid_plural "%d pieces of cake" 170msgstr[0] "" 171msgstr[1] "" 172</programlisting></para></formalpara> 173 174<para>Then a French translator creates a French message catalog</para> 175 176<formalpara id="i18n-fr-po"><title>prog.fr.po</title> 177<para><programlisting language="po"> 178msgid "" 179msgstr "" 180"Content-Type: text/plain; charset=ISO-8859-1\n" 181"Plural-Forms: nplurals=2; plural=(n > 1);\n" 182 183msgid "'Your command, please?', asked the waiter." 184msgstr "«Votre commande, s'il vous plait», dit le garçon." 185 186# Les gateaux allemands sont les meilleurs du monde. 187msgid "a piece of cake" 188msgid_plural "%d pieces of cake" 189msgstr[0] "un morceau de gateau" 190msgstr[1] "%d morceaux de gateau" 191</programlisting></para></formalpara> 192 193<para>and sends it to the program's maintainer.</para> 194 195<para>The program's maintainer compiles the catalog as follows: 196<screen> 197&sh-prompt; mkdir -p ./fr/LC_MESSAGES 198&sh-prompt; msgfmt -o ./fr/LC_MESSAGES/prog.mo prog.fr.po 199</screen></para> 200 201<para>When a user in a french &locale; then runs the program 202<screen> 203&sh-prompt; clisp prog.lisp 2 204</screen> 205she will get the output 206<screen> 207 «Votre commande, s'il vous plait», dit le garçon. 208 2 morceaux de gateau 209</screen> 210</para> 211</section> 212 213</section> 214 215<section id="i18n-locale"><title>Locale</title> 216 217<variablelist> 218<varlistentry id="set-locale"><term><code>(&set-locale; 219 &optional-amp; &category; &locale;)</code></term> 220 <listitem><simpara>This is an interface to 221 <function role="unix">setlocale</function>.</simpara> 222 <simpara>When &locale; is missing or &nil;, return the current one.</simpara> 223 <simpara>When &category; is missing or &nil;, return all categories 224 as a &list-t;.</simpara></listitem></varlistentry> 225 226<varlistentry id="locale-conv"><term><code>(&locale-conv;)</code></term> 227 <listitem><simpara>This is an interface to 228 <function role="unix">localeconv</function>.</simpara> 229 <simpara>Returns a <type>I18N:LOCALE-CONV</type> 230 structure.</simpara></listitem></varlistentry> 231 232<varlistentry id="lang-info"><term><code>(&lang-info; 233 &optional-amp; &item-r;)</code></term> 234 <listitem><simpara>This is an interface to 235 <function role="unix">nl_langinfo</function> (&unix;) 236 and <function role="win32">GetLocaleInfo</function> (&win32;).</simpara> 237 <simpara>When &item-r; is missing or &nil;, 238 return all available information as a &list-t;.</simpara> 239</listitem></varlistentry></variablelist> 240 241</section> 242</section> 243