COVERAGE SUMMARY
FILE SUMMARY
NameExecutedRoutines%ExecutedLines%Unexecuted
/home/matt/eu/rds/include/std/locale.e121392.31%16618689.25%20
ROUTINE SUMMARY
RoutineExecutedLinesUnexecuted
lang_load()384584.44%7
datetime()111478.57%3
get_def_lang()020.00%2
number()343694.44%2
get()7887.50%1
money()101190.91%1
set()111291.67%1
set_def_lang()5683.33%1
translate()111291.67%1
trsprintf()101190.91%1
get_lang_path()22100.00%0
mk_tm_struct()1212100.00%0
set_lang_path()33100.00%0
LINE COVERAGE DETAIL
#Executed
1
-- (c) Copyright - See License.txt
2
3
--****
4
-- == Locale Routines
5
--
6
-- <>
7
8
namespace locale
9
10
include std/dll.e
11
include std/machine.e
12
include std/error.e
13
include std/datetime.e as dt
14
include std/text.e
15
include std/io.e
16
include std/map.e
17
include std/localeconv.e as lcc
18
include std/lcid.e as lcid
19
include std/convert.e
20
include std/filesys.e
21
include std/mathcons.e
22
include std/search.e
23
24
25
------------------------------------------------------------------------------------------
26
--
27
-- Local Constants
28
--
29
------------------------------------------------------------------------------------------
30
311
constant P = C_POINTER, I = C_INT
32
33
------------------------------------------------------------------------------------------
34
--
35
-- Local Variables
36
--
37
------------------------------------------------------------------------------------------
38
391
object def_lang = 0
40
411
object lang_path = 0
42
43
--****
44
-- === Message translation functions
45
--
46
47
--**
48
-- Set the language path.
49
--
50
-- Parameters:
51
-- # ##pp## : an object, either an actual path or an atom.
52
--
53
-- Comments:
54
-- When the language path is not set, and it is unset by default, [[:set]]() does not load any language file.
55
--
56
-- See Also:
57
-- [[:set]]
58
591
601
lang_path = pp
611
end procedure
62
63
--**
64
-- Get the language path.
65
--
66
-- Returns:
67
-- An **object**, the current language path.
68
--
69
-- See Also:
70
-- [[:get_lang_path]]
71
721
731
return lang_path
74
end function
75
76
--**
77
-- Load a language file.
78
--
79
-- Parameters:
80
-- # ##filename## : a sequence, the name of the file to load. If no file
81
-- extension is supplied, then ".lng" is used.
82
--
83
-- Returns:
84
-- A language **map**, if successful. This is to be used when calling [[:translate]]().
85
--
86
-- If the load fails it returns a zero.
87
--
88
-- Comments:
89
-- The language file must be made of lines which are either comments, empty lines
90
-- or translations. Note that leading whitespace is ignored on all lines except
91
-- continuation lines.
92
--
93
-- * **Comments** are lines that begin with a ~# character and extend to the end of the line.
94
-- * **Empty Lines** are ignored.
95
-- * **Translations** have two forms ...
96
--
97
-- {{{
98
-- keyword translation_text
99
-- }}}
100
-- In which the 'keyword' is a word that must not have any spaces in it.
101
-- {{{
102
-- keyphrase = translation_text
103
-- }}}
104
-- In which the 'keyphrase' is anything up to the first '=' symbol.
105
--
106
-- It is possible to have the translation text span multiple lines. You do this by
107
-- having '&' as the last character of the line. These are placed by newline characters
108
-- when loading.
109
--
110
-- Example:
111
--{{{
112
--# Example translation file
113
--#
114
--
115
--
116
--hello Hola
117
--world Mundo
118
--greeting %s, %s!
119
--
120
--help text = &
121
--This is an example of some &
122
--translation text that spans &
123
--multiple lines.
124
--
125
-- # End of example PO #2
126
--}}}
127
--
128
-- See Also:
129
-- [[:translate]]
130
1312
132
object lines
133
sequence line, key, msg
134
integer delim
135
integer cont -- continuation
136
map:map keylang
137
map:map altlang
138
1392
keylang = map:new()
1402
altlang = map:new()
1412
cont = 0
1422
filename = defaultext(filename, "lng")
143
1442
if sequence(lang_path) and length(lang_path) > 0 then
1450
sequence tempname
1460
tempname = locate_file(filename, {lang_path})
1470
if equal(tempname, filename) then
1480
filename = locate_file(filename)
149
else
1500
filename = tempname
151
end if
152
else
1532
filename = locate_file(filename)
154
end if
155
1562
lines = read_lines(filename)
1572
if atom(lines) then
1580
return 0 -- Language maps unchanged.
159
end if
160
1612
for i = 1 to length(lines) do
162
16325
if cont then
164
1654
line = trim_tail(lines[i])
1664
if line[$] = '&' then
1672
msg &= line[1..$-1] & '\n'
168
else
1692
msg &= line
1702
map:put(keylang, key, msg)
1712
map:put(altlang, msg, key)
1722
cont = 0
173
end if
174
else
17521
line = trim(lines[i])
17621
if length(line) = 0 then
1777
continue
178
end if
17914
if line[1] = '#' then
1806
continue
181
end if
182
1838
delim = find('=', line)
1848
if delim = 0 then
1857
delim = find(' ', line)
186
end if
187
1888
if delim = 0 then
189
-- Ignore lines with missing translation text
1900
continue
191
end if
192
1938
key = trim(line[1..delim-1])
1948
if line[$] = '&' then
1952
cont = 1
1962
msg = trim(line[delim+1..$-1])
1972
if length(msg) > 0 then
1981
msg &= '\n'
199
end if
200
else
2016
msg = trim(line[delim+1..$])
2026
map:put(keylang, key, msg)
2036
map:put(altlang, msg, key)
204
end if
205
end if
20612
end for
207
2082
return {keylang, altlang}
209
end function
210
211
--**
212
-- Sets the default language (translation) map
213
--
214
-- Parameters:
215
-- # ##langmap## : A value returned by [[:lang_load]](), or zero to remove any default map.
216
--
217
-- Example:
218
--
219
-- set_def_lang( lang_load("appmsgs") )
220
--
221
2222
2232
if atom(langmap) and langmap = 0 then
2240
def_lang = langmap
2252
elsif length(langmap) = 2 then
2262
def_lang = langmap
227
end if
2282
end procedure
229
230
--**
231
-- Gets the default language (translation) map
232
--
233
-- Parameters:
234
-- none.
235
--
236
-- Returns:
237
-- An **object**, a language map, or zero if there is no default language map yet.
238
--
239
-- Example:
240
--
241
-- object langmap = get_def_lang()
242
--
243
2440
2450
return def_lang
246
end function
247
248
--**
249
-- Translates a word, using the current language file.
250
--
251
-- Parameters:
252
-- # ##word## : a sequence, the word to translate.
253
-- # ##langmap## : Either a value returned by [[:lang_load]]() or zero to use the default language map
254
-- # ##defval## : a object. The value to return if the word cannot be translated.
255
-- Default is "". If ##defval## is ##PINF## then the ##word## is returned
256
-- if it can't be translated.
257
-- # ##mode## : an integer. If zero (the default) it uses ##word## as the keyword and returns
258
-- the translation text. If not zero it uses ##word##
259
-- as the translation and returns the keyword.
260
--
261
-- Returns:
262
-- A **sequence**, the value associated with ##word##, or ##defval## if there
263
-- is no association.
264
--
265
-- Example 1:
266
--
267
-- sequence newword
268
-- newword = translate(msgtext)
269
-- if length(msgtext) = 0 then
270
-- error_message(msgtext)
271
-- else
272
-- error_message(newword)
273
-- end if
274
--
275
--
276
-- Example 2:
277
--
278
-- error_message(translate(msgtext, , PINF))
279
--
280
--
281
-- See Also:
282
-- [[:set]], [[:lang_load]]
283
28425
28525
if equal(defval, PINF) then
2867
defval = word
287
end if
288
28925
if equal(langmap, 0) then
29024
if equal(def_lang, 0) then
291
-- No default language map loaded yet.
2921
return defval
293
else
29423
langmap = def_lang
295
end if
296
end if
29724
if atom(langmap) or length(langmap) != 2 then
298
-- Not a valid language map passed.
2990
return defval
300
end if
301
30224
if mode = 0 then
30323
return map:get(langmap[1], word, defval)
304
else
3051
return map:get(langmap[2], word, defval)
306
end if
307
308
end function
309
310
--**
311
-- Returns a formatted string with automatic translation performed on the parameters.
312
--
313
-- Parameters:
314
-- # ##fmt## : A sequence. Contains the formatting string. see [[:printf]]() for details.
315
-- # ##data## : A sequence. Contains the data that goes into the formatted result. see [[:printf]] for details.
316
-- # ##langmap## : An object. Either 0 (the default) to use the default language maps, or
317
-- the result returned from [[:lang_load]]() to specify a particular
318
-- language map.
319
--
320
-- Returns:
321
-- A **sequence**, the formatted result.
322
--
323
-- Comments:
324
-- This works very much like the [[:sprintf]]() function. The difference is that the ##fmt## sequence
325
-- and sequences contained in the ##data## parameter are [[:translate | translated ]] before
326
-- passing them to [[:sprintf]]. If an item has no translation, it remains unchanged.
327
--
328
-- Further more, after the translation pass, if the result text begins with {{{"__"}}},
329
-- the {{{"__"}}} is removed.
330
-- This method can be used when you do not want an item to be translated.
331
--
332
-- Examples:
333
--
334
-- -- Assuming a language has been loaded and
335
-- -- "greeting" translates as '%s %s, %s'
336
-- -- "hello" translates as "G'day"
337
-- -- "how are you today" translates as "How's the family?"
338
-- sequence UserName = "Bob"
339
-- sequence result = trsprintf( "greeting", {"hello", "__" & UserName, "how are you today"})
340
-- --> "G'day Bob, How's the family?"
341
--
342
--
3432
3442
for i = 1 to length(data) do
3454
if sequence(data[i]) then
3464
data[i] = translate(data[i], langmap, PINF)
3474
if begins("__", data[i]) then
3481
data[i] = data[i][3 .. $]
349
end if
350
end if
3514
end for
3522
fmt = translate(fmt, langmap, PINF)
3532
if begins("__", fmt) then
3540
fmt = fmt[3 .. $]
355
end if
356
3572
return sprintf(fmt, data)
358
end function
359
360
361
------------------------------------------------------------------------------------------
362
--
363
-- Library Open/Checking
364
--
365
------------------------------------------------------------------------------------------
366
3671
ifdef WIN32 then
368
constant
369
lib = open_dll("MSVCRT.DLL"),
370
lib2 = open_dll("KERNEL32.DLL"),
371
f_strfmon = define_c_func(lib2, "GetCurrencyFormatA", {I, I, P, P, P, I}, I),
372
f_strfnum = define_c_func(lib2, "GetNumberFormatA", {I, I, P, P, P, I}, I),
373
f_setlocale = define_c_func(lib, "setlocale", {I, P}, P),
374
f_strftime = define_c_func(lib, "strftime", {P, I, P, P}, I),
375
LC_ALL = 0,
376
-- LC_COLLATE = 1,
377
-- LC_CTYPE = 2,
378
LC_MONETARY = 3,
379
LC_NUMERIC = 4,
380
-- LC_TIME = 5,
381
-- LC_MESSAGES = 6,
382
$
383
384
/* constant
385
FORMAT_SIZE = 6 * 4,
386
NUM_DIGITS = 0,
387
LEADING_ZERO = 4,
388
GROUPING = 8,
389
DECIMAL_SEP = 12,
390
THOUSANDS_SEP = 16,
391
NEGATIVE_ORDER = 20
392
*/
393
sequence current_locale = ""
394
395
elsifdef LINUX then
396
constant
3971
lib = open_dll(""),
3981
f_strfmon = define_c_func(lib, "strfmon", {P, I, P, C_DOUBLE}, I),
3991
f_strfnum = -1,
4001
f_setlocale = define_c_func(lib, "setlocale", {I, P}, P),
4011
f_strftime = define_c_func(lib, "strftime", {P, I, P, P}, I),
4021
LC_ALL = 6,
403
-- LC_CTYPE = 0,
4041
LC_NUMERIC = 1,
405
-- LC_TIME = 2,
406
-- LC_COLLATE = 3,
4071
LC_MONETARY = 4,
408
-- LC_MESSAGES = 5,
409
$
410
411
elsifdef FREEBSD or SUNOS then
412
constant
413
lib = open_dll("libc.so"),
414
f_strfmon = define_c_func(lib, "strfmon", {P, I, P, C_DOUBLE}, I),
415
f_strfnum = -1,
416
f_setlocale = define_c_func(lib, "setlocale", {I, P}, P),
417
f_strftime = define_c_func(lib, "strftime", {P, I, P, P}, I),
418
LC_ALL = 0,
419
-- LC_COLLATE = 1,
420
-- LC_CTYPE = 2,
421
LC_MONETARY = 3,
422
LC_NUMERIC = 4,
423
-- LC_TIME = 5,
424
-- LC_MESSAGES = 6,
425
$
426
427
elsifdef OSX then
428
constant
429
lib = open_dll("libc.dylib"),
430
f_strfmon = define_c_func(lib, "strfmon", {P, I, P, C_DOUBLE}, I),
431
f_strfnum = -1,
432
f_setlocale = define_c_func(lib, "setlocale", {I, P}, P),
433
f_strftime = define_c_func(lib, "strftime", {P, I, P, P}, I),
434
LC_ALL = 0,
435
-- LC_COLLATE = 1,
436
-- LC_CTYPE = 2,
437
LC_MONETARY = 3,
438
LC_NUMERIC = 4,
439
-- LC_TIME = 5,
440
-- LC_MESSAGES = 6,
441
$
442
443
elsedef
444
445
constant
446
lib = -1
447
lib2 = -1
448
f_strfmon = -1
449
f_strfnum = -1
450
f_setlocale = -1
451
f_strftime = -1
452
LC_ALL = -1,
453
-- LC_COLLATE = -1,
454
-- LC_CTYPE = -1,
455
LC_MONETARY = -1,
456
LC_NUMERIC = -1,
457
-- LC_TIME = -1,
458
-- LC_MESSAGES = -1,
459
$
460
461
end ifdef
462
463
--****
464
-- === Time/Number Translation
465
466
--**
467
-- Set the computer locale, and possibly load appropriate translation file.
468
--
469
-- Parameters:
470
-- # ##new_locale## : a sequence representing a new locale.
471
--
472
-- Returns:
473
-- An **integer**, either 0 on failure or 1 on success.
474
--
475
-- Comments:
476
-- Locale strings have the following format: xx_YY or xx_YY.xyz .
477
-- The xx part refers to a culture, or main language/script. For instance, "en" refers to
478
-- English, "de" refers to German, and so on. For some language, a script may be specified,
479
-- like in "mn_Cyrl_MN" (mongolian in cyrillic transcription).
480
--
481
-- The YY part refers to a subculture, or variant, of the main language. For instance, "fr_FR"
482
-- refers to metropolitan France, while "fr_BE" refers to the variant spoken in Wallonie, the
483
-- French speaking region of Belgium.
484
--
485
-- The optional .xyz part specifies an encoding, like .utf8 or .1252 . This is required in some cases.
486
4873
488
atom lAddr_localename
489
atom ign
490
sequence nlocale
491
4923
nlocale = lcc:decanonical(new_locale)
4933
lAddr_localename = allocate_string(nlocale)
4943
ign = c_func(f_setlocale, {LC_MONETARY, lAddr_localename})
4953
ign = c_func(f_setlocale, {LC_NUMERIC, lAddr_localename})
4963
ign = c_func(f_setlocale, {LC_ALL, lAddr_localename})
4973
free(lAddr_localename)
498
4993
if sequence(lang_path) then
500
-- Note: Failure to update language map does not fail this function.
5010
def_lang = lang_load(nlocale)
502
end if
503
5043
ign = (ign != NULL)
5053
ifdef WIN32 then
506
if ign then
507
current_locale = new_locale
508
end if
509
end ifdef
5103
return ign
511
end function
512
513
--**
514
-- Get current locale string
515
--
516
-- Returns:
517
-- A **sequence**, a locale string.
518
--
519
-- See Also:
520
-- [[:set]]
521
5223
523
sequence r
524
atom p
525
5263
p = c_func(f_setlocale, {LC_ALL, NULL})
5273
if p = NULL then
5280
return ""
529
end if
530
5313
r = peek_string(p)
5323
ifdef WIN32 then
533
if equal(lcc:decanonical(r), lcc:decanonical(current_locale)) then
534
return current_locale
535
end if
536
end ifdef
5373
r = lcc:canonical(r)
538
5393
return r
540
end function
541
542
--**
543
-- Converts an amount of currency into a string representing that amount.
544
--
545
-- Parameters:
546
-- # ##amount## : an atom, the value to write out.
547
--
548
-- Returns:
549
-- A **sequence**, a string that writes out ##amount## of current currency.
550
--
551
-- Example 1:
552
--
553
-- -- Assuming an en_US locale
554
-- ? money(1020.5) -- returns"$1,020.50"
555
--
556
--
557
-- See Also:
558
-- [[:set]], [[:number]]
559
5601
561
sequence result
562
integer size
563
atom pResult, pTmp
564
5651
if f_strfmon != -1 then
5661
ifdef UNIX then
5671
pResult = allocate(4 * 160)
5681
pTmp = allocate_string("%n")
5691
size = c_func(f_strfmon, {pResult, 4 * 160, pTmp, amount})
570
elsifdef WIN32 then
571
pResult = allocate(4 * 160)
572
pTmp = allocate_string(sprintf("%.8f", {amount}))
573
size = c_func(f_strfmon, {lcid:get_lcid(get()), 0, pTmp, NULL, pResult, 4 * 160})
574
end ifdef
575
5761
result = peek_string(pResult)
5771
free(pResult)
5781
free(pTmp)
579
5801
return result
581
else
5820
return text:format("$[,,.2]", amount)
583
end if
584
end function
585
586
--**
587
-- Converts a number into a string representing that number.
588
--
589
-- Parameters:
590
-- # ##num## : an atom, the value to write out.
591
--
592
-- Returns:
593
-- A **sequence**, a string that writes out ##num##.
594
--
595
-- Example 1:
596
--
597
-- -- Assuming an en_US locale
598
-- ? number(1020.5) -- returns "1,020.50"
599
--
600
--
601
-- See Also:
602
-- [[:set]], [[:money]]
603
6044
605
sequence result
606
integer size
607
atom pResult, pTmp
608
6094
ifdef UNIX then
6104
if f_strfmon != -1 then
6114
pResult = allocate(4 * 160)
6124
if integer(num) then
6131
pTmp = allocate_string("%!.0n")
614
else
6153
pTmp = allocate_string("%!n")
616
end if
6174
size = c_func(f_strfmon, {pResult, 4 * 160, pTmp, num})
618
else
6190
return text:format("[,,]", num)
620
end if
621
622
elsifdef WIN32 then
623
atom lpFormat
624
if f_strfnum != -1 then
625
pResult = allocate(4 * 160)
626
if integer(num) then
627
pTmp = allocate_string(sprintf("%d", {num}))
628
-- lpFormat must not only be allocated but completely populated
629
-- with values from the current locale.
630
lpFormat = NULL
631
else
632
lpFormat = NULL
633
pTmp = allocate_string(sprintf("%.15f", {num}))
634
end if
635
size = c_func(f_strfnum, {lcid:get_lcid(get()), 0, pTmp, lpFormat, pResult, 4 * 160})
636
else
637
return text:format("[,,]", num)
638
end if
639
640
elsedef
641
return text:format("[,,]", num)
642
end ifdef
643
6444
result = peek_string(pResult)
6454
free(pResult)
6464
free(pTmp)
647
648
-- Assumption: All locales remove trailing zeros and decimal point from integers
6494
integer is_int = integer(num)
6504
if is_int = 0 then
6513
sequence float = sprintf("%.15f", num)
6523
is_int = find('.', float)
6533
if is_int then
6543
for i = length(float) to is_int+1 by -1 do
65531
if float[i] != '0' then
6562
is_int = 0
6572
exit
658
end if
65929
end for
660
end if
661
end if
6624
if is_int != 0 then -- The input was an integer
663
-- remove all trailing zeros and decimal point, but not any trailing symbols --
664
-- Step 1. Locate rightmost digit.
6652
is_int = 0
6662
for i = length(result) to 1 by -1 do
6672
if find(result[i], "1234567890") then
6682
is_int = i + 1
6692
exit
670
end if
6710
end for
672
-- Step 2. From rightmost digit, stop when we get to a non-zero
6732
for i = is_int - 1 to 1 by -1 do
6745
if result[i] != '0' then
6752
if not find(result[i], "1234567890") then
676
-- Step 3. Remove decimal point and all zeros, preserving any trailing symbols.
6771
result = result[1..i-1] & result[is_int .. $]
678
end if
6792
exit
680
end if
6813
end for
682
end if
683
6844
return result
685
end function
686
6871
688
atom pDtm
689
6901
pDtm = allocate(36)
6911
poke4(pDtm, dtm[SECOND]) -- int tm_sec
6921
poke4(pDtm+4, dtm[MINUTE]) -- int tm_min
6931
poke4(pDtm+8, dtm[HOUR]) -- int tm_hour
6941
poke4(pDtm+12, dtm[DAY]) -- int tm_mday
6951
poke4(pDtm+16, dtm[MONTH] - 1) -- int tm_mon
6961
poke4(pDtm+20, dtm[YEAR] - 1900) -- int tm_year
6971
poke4(pDtm+24, dt:weeks_day(dtm) - 1) -- int tm_wday
6981
poke4(pDtm+28, dt:years_day(dtm)) -- int tm_yday
6991
poke4(pDtm+32, 0) -- int tm_isdst
700
7011
return pDtm
702
end function
703
704
--**
705
-- Formats a date according to current locale.
706
--
707
-- Parameters:
708
-- # ##fmt## : A format string, as described in datetime:[[:format]]
709
-- # ##dtm## : the datetime to write out.
710
--
711
-- Returns:
712
-- A **sequence**, representing the formatted date.
713
--
714
-- Example 1:
715
--
716
-- ? datetime("Today is a %A",dt:now())
717
--
718
--
719
-- See Also:
720
-- datetime:[[:format]]
721
7221
723
atom pFmt, pRes, pDtm
724
integer size
725
sequence res
726
7271
if f_strftime != -1 then
7281
pDtm = mk_tm_struct(dtm)
7291
pFmt = allocate_string(fmt)
7301
pRes = allocate(1024)
7311
size = c_func(f_strftime, {pRes, 256, pFmt, pDtm})
7321
res = peek_string(pRes)
7331
free(pRes)
7341
free(pFmt)
7351
free(pDtm)
736
else
7370
res = date()
7380
res[1] += 1900
7390
res = dt:format(res[1..6], fmt)
740
end if
741
7421
return res
743
end function