-
Notifications
You must be signed in to change notification settings - Fork 1
/
srfi-29.html
518 lines (443 loc) · 21.2 KB
/
srfi-29.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<meta name="generator" content="HTML Tidy, see www.w3.org">
<title>SRFI 29: Localization</title>
<meta name="author" content="Scott G. Miller">
<meta name="description" content="Localization">
</head>
<body>
<H1>Title</H1>
Localization
<H1>Author</H1>
Scott G. Miller
<H1>Status</H1>
This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see <a href="http://srfi.schemers.org/srfi-process.html">here</a>.
You can access the discussion on this SRFI via <a href="http://srfi.schemers.org/srfi-29/mail-archive/maillist.html">the archive of the mailing list</a>.
<ul>
<li>Draft: 2002/03/26-2002/06/19</li>
<li>Final: 2002/06/19
</ul>
<H1>Abstract</H1>
This document specifies an interface to retrieving and
displaying locale sensitive messages. A Scheme program can
register one or more translations of templated messages, and
then write Scheme code that can transparently retrieve the
appropriate message for the locale under which the Scheme
system is running. <br>
<H1>Rationale</H1>
<p>As any programmer that has ever had to deal with making his
or her code readable in more than one locale, the process of
sufficiently abstracting program messages from their
presentation to the user is non-trivial without help from the
programming language. Most modern programming language
libraries do provide some mechanism for performing this
separation.</p>
<p>A portable API that allows a piece of code to run without
modification in different countries and under different
languages is a must for any non-trivial software project.
The interface should separate the logic of a program from
the myriad of translations that may be necessary.</p>
<p>The interface described in this document provides such
functionality. The underlying implementation is also allowed to
use whatever datastructures it likes to provide access to the
translations in the most efficient manner possible. In
addition, the implementation is provided with standardized
functions that programs will use for accessing an external,
unspecified repository of translations.</p>
<p>This interface <i>does not</i> cover all aspects of
localization, including support for non-latin characters,
number and date formatting, etc. Such functionality is the
scope of a future SRFI that may extend this one.</p>
<H1>Dependencies</H1>
An SRFI-29 conformant implementation must also implement
SRFI-28, Basic Format Strings. Message templates are strings
that must be processed by the <tt>format</tt> function
specified in that SRFI.
<H1>Specification</H1>
<h3>Message Bundles</h3>
<p>A Message Bundle is a set of message templates and their
identifying keys. Each bundle contains one or more such
key/value pairs. The bundle itself is associated with a
<i>bundle specifier</i> which uniquely identifies the
bundle.</p>
<h3>Bundle Specifiers</h3>
<p>A Bundle Specifier is a Scheme list that describes, in order
of importance, the package and locale of a message bundle.
In most cases, a locale specifier will have between one
and three elements. The first element is a symbol denoting the
package for which this bundle applies. The second and third
elements denote a <i>locale</i>. The second element (first
element of the locale) if present, is the two letter, ISO 639-1
language code for the bundle. The third element, if present, is
a two letter ISO 3166-1 country code. In some cases, a
fourth element may be present, specifying the encoding used for
the bundle. All bundle specifier elements are Scheme
symbols.</p>
<p>If only one translation is provided, it should be designated
only by a package name, for example <tt>(mathlib)</tt>. This
translation is called the <i>default</i> translation.</p>
<h3>Bundle Searching</h3>
<p>When a message template is retrieved from a bundle, the
Scheme implementation will provide the locale under which the
system is currently running. When the template is retrieved,
the package name will be specified. The Scheme system should
construct a Bundle Specifier from the provided package name and
the active locale. For example, when retrieving a message
template for French Canadian, in the <tt>mathlib</tt> package,
the bundle specifier '<tt>(mathlib fr ca)</tt>' is used. A
program may also retrieve the elements of the current locale
using the no-argument procedures:</p>
<p><b><tt>current-language</tt></b> <tt>->
<i>symbol</i></tt><br>
<tt><b>current-language</b> <i>symbol</i> ->
undefined</tt><br>
</p>
<blockquote>
When given no arguments, returns the current ISO 639-1
language code as a symbol. If provided with an
argument, the current language is set to that named by the
symbol for the currently executing Scheme thread (or for the
entire Scheme system if such a distinction is not possible).
</blockquote>
<p><b><tt>current-country</tt></b> <tt>->
<i>symbol</i></tt><br>
<tt><b>current-country</b> <i>symbol</i> ->
undefined</tt><br>
</p>
<blockquote>
returns the current ISO 3166-1 country code as a symbol.
If provided with an argument, the current country is
set to that named by the symbol for the currently executing
Scheme thread (or for the entire Scheme system if such a
distinction is not possible).
</blockquote>
<p><b><tt>current-locale-details</tt></b> <tt>-> <i>list of
symbol</i></tt>s<br>
<tt><b>current-locale-details</b> <i>list-of-symbols</i> ->
undefined</tt><br>
</p>
<blockquote>
Returns a list of additional locale details as a list of
symbols. This list may contain information about
encodings or other more specific information. If
provided with an argument, the current locale details are set
to those given in the currently executing Scheme thread (or
for the entire Scheme system if such a distinction is not
possible).
</blockquote>
<p>The Scheme System should first check for a bundle with the
exact name provided. If no such bundle is found, the last
element from the list is removed and a search is tried for a
bundle with that name. If no bundle is then found, the list is
shortened by removing the last element again. If no message is
found and the bundle specifier is now the empty list, an error
should be raised.</p>
<p>The reason for this search order is to provide the most
locale sensitive template possible, but to fall back on more
general templates if a translation has not yet been provided
for the given locale.</p>
<h3>Message Templates</h3>
<p>A message template is a localized message that may or may
not contain one of a number of formatting codes. A message
template is a Scheme string. The string is of a form that can
be processed by the <tt>format</tt> procedure found in many
Scheme systems and formally specified in SRFI-28 (Basic Format
Strings).</p>
<p>This SRFI also extends SRFI-28 to provide an additional
<tt>format</tt> escape code:</p>
<blockquote>
<tt>~[n]@*</tt> - Causes a value-requiring escape code that
follows this code immediately to reference the [N]'th
optional value absolutely, rather than the next unconsumed
value. The referenced value is <i>not</i> consumed.
</blockquote>
This extension allows optional values to be positionally
referenced, so that message templates can be constructed that
can produce the proper word ordering for a language.
<h3>Preparing Bundles</h3>
Before a bundle may be used by the Scheme system to retrieve
localized template messages, they must be made available to the
Scheme system. This SRFI specifies a way to portably
define the bundles, as well as store them in and retrieve them
from an unspecified system which may be provided by resources
outside the Scheme system.<br>
<p><b><tt>declare-bundle!</tt></b> <tt><i>bundle-specifier
association-list</i> -> undefined<br>
</tt></p>
<blockquote>
Declares a new bundle named by the given bundle-specifier.
The contents of the bundle are defined by the provided
association list. The list contains associations
between Scheme symbols and the message templates (Scheme
strings) they name. If a bundle already exists with the
given name, it is overwritten with the newly declared
bundle.<br>
</blockquote>
<tt><b>store-bundle</b> <i>bundle-specifier</i> ->
boolean</tt><br>
<blockquote>
Attempts to store a bundle named by the given bundle
specifier, and previously made available using
<tt>declare-bundle!</tt> or <tt>load-bundle!</tt>, in an
unspecified mechanism that may be persistent across Scheme
system restarts. If successful, a non-false value is
returned. If unsuccessful, <tt>#f</tt> is returned.<br>
</blockquote>
<tt><b>load-bundle!</b> <i>bundle-specifier</i> ->
boolean</tt><br>
<blockquote>
Attempts to retrieve a bundle from an unspecified mechanism
which stores bundles outside the Scheme system. If the
bundle was retrieved successfully, the function returns a
non-false value, and the bundle is immediately available to
the Scheme system. If the bundle could not be found or loaded
successfully, the function returns <tt>#f</tt>, and the
Scheme system's bundle registry remains unaffected.<br>
</blockquote>
A compliant Scheme system may choose not to provide any
external mechanism to store localized bundles. If it does
not, it must still provide implementations for
<tt>store-bundle</tt> and <tt>load-bundle!</tt>. In such a
case, both functions must return <tt>#f</tt> regardless of the
arguments given. Users of this SRFI should recognize that the
inability to load or store a localized bundle in an external
repository is <i>not</i> a fatal error.<br>
<h3>Retrieving Localized Message Templates</h3>
<p><b><tt>localized-template</tt></b> <i><tt>package-name
message-template-name</tt></i> <tt>-> <i>string or #f<br>
</i></tt></p>
<blockquote>
Retrieves a localized message template for the given package
name and the given message template name (both symbols).
If no such message could be found, false (#f) is
returned.<br>
<br>
</blockquote>
After retrieving a template, the calling program can use
<tt>format</tt> to produce a string that can be displayed to
the user.<br>
<h2>Examples</h2>
The below example makes use of SRFI-29 to display simple,
localized messages. It also defines its bundles in such a
way that the Scheme system may store and retrieve the bundles
from a more efficient system catalog, if available.<br>
<pre>
(let ((translations
'(((en) . ((time . "Its ~a, ~a.")
(goodbye . "Goodbye, ~a.")))
((fr) . ((time . "~1@*~a, c'est ~a.")
(goodbye . "Au revoir, ~a."))))))
(for-each (lambda (translation)
(let ((bundle-name (cons 'hello-program (car translation))))
(if (not (load-bundle! bundle-name))
(begin
(declare-bundle! bundle-name (cdr translation))
(store-bundle! bundle-name)))))
translations))
(define localized-message
(lambda (message-name . args)
(apply format (cons (localized-template 'hello-program
message-name)
args))))
(let ((myname "Fred"))
(display (localized-message 'time "12:00" myname))
(display #\newline)
(display (localized-message 'goodbye myname))
(display #\newline))
;; Displays (English):
;; Its 12:00, Fred.
;; Goodbye, Fred.
;;
;; French:
;; Fred, c'est 12:00.
;; Au revoir, Fred.
</pre>
<H1>Implementation</H1>
<p>The implementation requires that the Scheme system provide a
definition for <tt>current-language</tt> and
<tt>current-country</tt> capable of distinguishing the correct
locale present during a Scheme session. The definitions of
those functions in the reference implementation are not capable
of that distinction. Their implementation is provided only so
that the following code can run in any R4RS scheme system.
<br>
</p>
<p>In addition, the below implementation of a compliant
<tt>format</tt> requires SRFI-6 (Basic String Ports) and
SRFI-23 (Error reporting)</p>
<pre>
;; The association list in which bundles will be stored
(define *localization-bundles* '())
;; The current-language and current-country functions provided
;; here must be rewritten for each Scheme system to default to the
;; actual locale of the session
(define current-language
(let ((current-language-value 'en))
(lambda args
(if (null? args)
current-language-value
(set! current-language-value (car args))))))
(define current-country
(let ((current-country-value 'us))
(lambda args
(if (null? args)
current-country-value
(set! current-country-value (car args))))))
;; The load-bundle! and store-bundle! both return #f in this
;; reference implementation. A compliant implementation need
;; not rewrite these procedures.
(define load-bundle!
(lambda (bundle-specifier)
#f))
(define store-bundle!
(lambda (bundle-specifier)
#f))
;; Declare a bundle of templates with a given bundle specifier
(define declare-bundle!
(letrec ((remove-old-bundle
(lambda (specifier bundle)
(cond ((null? bundle) '())
((equal? (caar bundle) specifier)
(cdr bundle))
(else (cons (car bundle)
(remove-old-bundle specifier
(cdr bundle))))))))
(lambda (bundle-specifier bundle-assoc-list)
(set! *localization-bundles*
(cons (cons bundle-specifier bundle-assoc-list)
(remove-old-bundle bundle-specifier
*localization-bundles*))))))
;;Retrieve a localized template given its package name and a template name
(define localized-template
(letrec ((rdc
(lambda (ls)
(if (null? (cdr ls))
'()
(cons (car ls) (rdc (cdr ls))))))
(find-bundle
(lambda (specifier template-name)
(cond ((assoc specifier *localization-bundles*) =>
(lambda (bundle) bundle))
((null? specifier) #f)
(else (find-bundle (rdc specifier)
template-name))))))
(lambda (package-name template-name)
(let loop ((specifier (cons package-name
(list (current-language)
(current-country)))))
(and (not (null? specifier))
(let ((bundle (find-bundle specifier template-name)))
(and bundle
(cond ((assq template-name bundle) => cdr)
((null? (cdr specifier)) #f)
(else (loop (rdc specifier)))))))))))
;;An SRFI-28 and SRFI-29 compliant version of format. It requires
;;SRFI-23 for error reporting.
(define format
(lambda (format-string . objects)
(let ((buffer (open-output-string)))
(let loop ((format-list (string->list format-string))
(objects objects)
(object-override #f))
(cond ((null? format-list) (get-output-string buffer))
((char=? (car format-list) #\~)
(cond ((null? (cdr format-list))
(error 'format "Incomplete escape sequence"))
((char-numeric? (cadr format-list))
(let posloop ((fl (cddr format-list))
(pos (string->number
(string (cadr format-list)))))
(cond ((null? fl)
(error 'format "Incomplete escape sequence"))
((and (eq? (car fl) '#\@)
(null? (cdr fl)))
(error 'format "Incomplete escape sequence"))
((and (eq? (car fl) '#\@)
(eq? (cadr fl) '#\*))
(loop (cddr fl) objects (list-ref objects pos)))
(else
(posloop (cdr fl)
(+ (* 10 pos)
(string->number
(string (car fl)))))))))
(else
(case (cadr format-list)
((#\a)
(cond (object-override
(begin
(display object-override buffer)
(loop (cddr format-list) objects #f)))
((null? objects)
(error 'format "No value for escape sequence"))
(else
(begin
(display (car objects) buffer)
(loop (cddr format-list)
(cdr objects) #f)))))
((#\s)
(cond (object-override
(begin
(display object-override buffer)
(loop (cddr format-list) objects #f)))
((null? objects)
(error 'format "No value for escape sequence"))
(else
(begin
(write (car objects) buffer)
(loop (cddr format-list)
(cdr objects) #f)))))
((#\%)
(if object-override
(error 'format "Escape sequence following positional override does not require a value"))
(display #\newline buffer)
(loop (cddr format-list) objects #f))
((#\~)
(if object-override
(error 'format "Escape sequence following positional override does not require a value"))
(display #\~ buffer)
(loop (cddr format-list) objects #f))
(else
(error 'format "Unrecognized escape sequence"))))))
(else (display (car format-list) buffer)
(loop (cdr format-list) objects #f)))))))
</pre>
<H1>Copyright</H1>
Copyright (C) Scott G. Miller (2002). All Rights Reserved.
<p>This document and translations of it may be copied and
furnished to others, and derivative works that comment on or
otherwise explain it or assist in its implementation may be
prepared, copied, published and distributed, in whole or in
part, without restriction of any kind, provided that the above
copyright notice and this paragraph are included on all such
copies and derivative works. However, this document itself may
not be modified in any way, such as by removing the copyright
notice or references to the Scheme Request For Implementation
process or editors, except as needed for the purpose of
developing SRFIs in which case the procedures for copyrights
defined in the SRFI process must be followed, or as required to
translate it into languages other than English.</p>
<p>The limited permissions granted above are perpetual and will
not be revoked by the authors or their successors or
assigns.</p>
<p>This document and the information contained herein is
provided on an "AS IS" basis and THE AUTHOR AND THE SRFI
EDITORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES
OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.</p>
<hr>
<address>
Editor: <a href="mailto:srfi minus editors at srfi dot schemers dot org">David
Rush</a>
</address>
<address>
Author: <a href="mailto:scgmille@freenetproject.org">Scott G.
Miller</a>
</address>
<!-- Created: Tue Sep 29 19:20:08 EDT 1998 -->
<!-- hhmts start -->Last modified: Mon Jun 17 12:00:08 Pacific
Daylight Time 2002 <!-- hhmts end --> <br>
</body>
</html>