Update tcl to version 8.5.5
[git/jnareb-git.git] / mingw / lib / tcl8 / 8.5 / msgcat-1.4.2.tm
blobccf4054dc84b893af060f4763d22b8275fbaff01
1 # msgcat.tcl --
3 #       This file defines various procedures which implement a
4 #       message catalog facility for Tcl programs.  It should be
5 #       loaded with the command "package require msgcat".
7 # Copyright (c) 1998-2000 by Ajuba Solutions.
8 # Copyright (c) 1998 by Mark Harrison.
10 # See the file "license.terms" for information on usage and redistribution
11 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12
13 # RCS: @(#) $Id: msgcat.tcl,v 1.26 2006/11/03 00:34:52 hobbs Exp $
15 package require Tcl 8.5
16 # When the version number changes, be sure to update the pkgIndex.tcl file,
17 # and the installation directory in the Makefiles.
18 package provide msgcat 1.4.2
20 namespace eval msgcat {
21     namespace export mc mcload mclocale mcmax mcmset mcpreferences mcset \
22             mcunknown
24     # Records the current locale as passed to mclocale
25     variable Locale ""
27     # Records the list of locales to search
28     variable Loclist {}
30     # Records the mapping between source strings and translated strings.  The
31     # dict key is of the form "<locale> <namespace> <src>", where locale and
32     # namespace should be themselves dict values and the value is
33     # the translated string.
34     variable Msgs [dict create]
36     # Map of language codes used in Windows registry to those of ISO-639
37     if { $::tcl_platform(platform) eq "windows" } {
38         variable WinRegToISO639 [dict create  {*}{
39             01 ar 0401 ar_SA 0801 ar_IQ 0c01 ar_EG 1001 ar_LY 1401 ar_DZ
40                   1801 ar_MA 1c01 ar_TN 2001 ar_OM 2401 ar_YE 2801 ar_SY
41                   2c01 ar_JO 3001 ar_LB 3401 ar_KW 3801 ar_AE 3c01 ar_BH
42                   4001 ar_QA
43             02 bg 0402 bg_BG
44             03 ca 0403 ca_ES
45             04 zh 0404 zh_TW 0804 zh_CN 0c04 zh_HK 1004 zh_SG 1404 zh_MO
46             05 cs 0405 cs_CZ
47             06 da 0406 da_DK
48             07 de 0407 de_DE 0807 de_CH 0c07 de_AT 1007 de_LU 1407 de_LI
49             08 el 0408 el_GR
50             09 en 0409 en_US 0809 en_GB 0c09 en_AU 1009 en_CA 1409 en_NZ
51                   1809 en_IE 1c09 en_ZA 2009 en_JM 2409 en_GD 2809 en_BZ
52                   2c09 en_TT 3009 en_ZW 3409 en_PH
53             0a es 040a es_ES 080a es_MX 0c0a es_ES@modern 100a es_GT 140a es_CR
54                   180a es_PA 1c0a es_DO 200a es_VE 240a es_CO 280a es_PE
55                   2c0a es_AR 300a es_EC 340a es_CL 380a es_UY 3c0a es_PY
56                   400a es_BO 440a es_SV 480a es_HN 4c0a es_NI 500a es_PR
57             0b fi 040b fi_FI
58             0c fr 040c fr_FR 080c fr_BE 0c0c fr_CA 100c fr_CH 140c fr_LU
59                   180c fr_MC
60             0d he 040d he_IL
61             0e hu 040e hu_HU
62             0f is 040f is_IS
63             10 it 0410 it_IT 0810 it_CH
64             11 ja 0411 ja_JP
65             12 ko 0412 ko_KR
66             13 nl 0413 nl_NL 0813 nl_BE
67             14 no 0414 no_NO 0814 nn_NO
68             15 pl 0415 pl_PL
69             16 pt 0416 pt_BR 0816 pt_PT
70             17 rm 0417 rm_CH
71             18 ro 0418 ro_RO
72             19 ru
73             1a hr 041a hr_HR 081a sr_YU 0c1a sr_YU@cyrillic
74             1b sk 041b sk_SK
75             1c sq 041c sq_AL
76             1d sv 041d sv_SE 081d sv_FI
77             1e th 041e th_TH
78             1f tr 041f tr_TR
79             20 ur 0420 ur_PK 0820 ur_IN
80             21 id 0421 id_ID
81             22 uk 0422 uk_UA
82             23 be 0423 be_BY
83             24 sl 0424 sl_SI
84             25 et 0425 et_EE
85             26 lv 0426 lv_LV
86             27 lt 0427 lt_LT
87             28 tg 0428 tg_TJ
88             29 fa 0429 fa_IR
89             2a vi 042a vi_VN
90             2b hy 042b hy_AM
91             2c az 042c az_AZ@latin 082c az_AZ@cyrillic
92             2d eu
93             2e wen 042e wen_DE
94             2f mk 042f mk_MK
95             30 bnt 0430 bnt_TZ
96             31 ts 0431 ts_ZA
97             33 ven 0433 ven_ZA
98             34 xh 0434 xh_ZA
99             35 zu 0435 zu_ZA
100             36 af 0436 af_ZA
101             37 ka 0437 ka_GE
102             38 fo 0438 fo_FO
103             39 hi 0439 hi_IN
104             3a mt 043a mt_MT
105             3b se 043b se_NO
106             043c gd_UK 083c ga_IE
107             3d yi 043d yi_IL
108             3e ms 043e ms_MY 083e ms_BN
109             3f kk 043f kk_KZ
110             40 ky 0440 ky_KG
111             41 sw 0441 sw_KE
112             42 tk 0442 tk_TM
113             43 uz 0443 uz_UZ@latin 0843 uz_UZ@cyrillic
114             44 tt 0444 tt_RU
115             45 bn 0445 bn_IN
116             46 pa 0446 pa_IN
117             47 gu 0447 gu_IN
118             48 or 0448 or_IN
119             49 ta
120             4a te 044a te_IN
121             4b kn 044b kn_IN
122             4c ml 044c ml_IN
123             4d as 044d as_IN
124             4e mr 044e mr_IN
125             4f sa 044f sa_IN
126             50 mn
127             51 bo 0451 bo_CN
128             52 cy 0452 cy_GB
129             53 km 0453 km_KH
130             54 lo 0454 lo_LA
131             55 my 0455 my_MM
132             56 gl 0456 gl_ES
133             57 kok 0457 kok_IN
134             58 mni 0458 mni_IN
135             59 sd
136             5a syr 045a syr_TR
137             5b si 045b si_LK
138             5c chr 045c chr_US
139             5d iu 045d iu_CA
140             5e am 045e am_ET
141             5f ber 045f ber_MA
142             60 ks 0460 ks_PK 0860 ks_IN
143             61 ne 0461 ne_NP 0861 ne_IN
144             62 fy 0462 fy_NL
145             63 ps
146             64 tl 0464 tl_PH
147             65 div 0465 div_MV
148             66 bin 0466 bin_NG
149             67 ful 0467 ful_NG
150             68 ha 0468 ha_NG
151             69 nic 0469 nic_NG
152             6a yo 046a yo_NG
153             70 ibo 0470 ibo_NG
154             71 kau 0471 kau_NG
155             72 om 0472 om_ET
156             73 ti 0473 ti_ET
157             74 gn 0474 gn_PY
158             75 cpe 0475 cpe_US
159             76 la 0476 la_VA
160             77 so 0477 so_SO
161             78 sit 0478 sit_CN
162             79 pap 0479 pap_AN
163         }]
164     }
167 # msgcat::mc --
169 #       Find the translation for the given string based on the current
170 #       locale setting. Check the local namespace first, then look in each
171 #       parent namespace until the source is found.  If additional args are
172 #       specified, use the format command to work them into the traslated
173 #       string.
175 # Arguments:
176 #       src     The string to translate.
177 #       args    Args to pass to the format command
179 # Results:
180 #       Returns the translated string.  Propagates errors thrown by the 
181 #       format command.
183 proc msgcat::mc {src args} {
184     # Check for the src in each namespace starting from the local and
185     # ending in the global.
187     variable Msgs
188     variable Loclist
189     variable Locale
191     set ns [uplevel 1 [list ::namespace current]]
192     
193     while {$ns != ""} {
194         foreach loc $Loclist {
195             if {[dict exists $Msgs $loc $ns $src]} {
196                 if {[llength $args] == 0} {
197                     return [dict get $Msgs $loc $ns $src]
198                 } else {
199                     return [format [dict get $Msgs $loc $ns $src] {*}$args]
200                 }
201             }
202         }
203         set ns [namespace parent $ns]
204     }
205     # we have not found the translation
206     return [uplevel 1 [list [namespace origin mcunknown] \
207             $Locale $src {*}$args]]
210 # msgcat::mclocale --
212 #       Query or set the current locale.
214 # Arguments:
215 #       newLocale       (Optional) The new locale string. Locale strings
216 #                       should be composed of one or more sublocale parts
217 #                       separated by underscores (e.g. en_US).
219 # Results:
220 #       Returns the current locale.
222 proc msgcat::mclocale {args} {
223     variable Loclist
224     variable Locale
225     set len [llength $args]
227     if {$len > 1} {
228         return -code error "wrong # args: should be\
229                 \"[lindex [info level 0] 0] ?newLocale?\""
230     }
232     if {$len == 1} {
233         set newLocale [lindex $args 0]
234         if {$newLocale ne [file tail $newLocale]} {
235             return -code error "invalid newLocale value \"$newLocale\":\
236                     could be path to unsafe code."
237         }
238         set Locale [string tolower $newLocale]
239         set Loclist {}
240         set word ""
241         foreach part [split $Locale _] {
242             set word [string trim "${word}_${part}" _]
243             if {$word ne [lindex $Loclist 0]} {
244                 set Loclist [linsert $Loclist 0 $word]
245             }
246         }
247         lappend Loclist {}
248         set Locale [lindex $Loclist 0]
249     }
250     return $Locale
253 # msgcat::mcpreferences --
255 #       Fetch the list of locales used to look up strings, ordered from
256 #       most preferred to least preferred.
258 # Arguments:
259 #       None.
261 # Results:
262 #       Returns an ordered list of the locales preferred by the user.
264 proc msgcat::mcpreferences {} {
265     variable Loclist
266     return $Loclist
269 # msgcat::mcload --
271 #       Attempt to load message catalogs for each locale in the
272 #       preference list from the specified directory.
274 # Arguments:
275 #       langdir         The directory to search.
277 # Results:
278 #       Returns the number of message catalogs that were loaded.
280 proc msgcat::mcload {langdir} {
281     set x 0
282     foreach p [mcpreferences] {
283         if { $p eq {} } {
284             set p ROOT
285         }
286         set langfile [file join $langdir $p.msg]
287         if {[file exists $langfile]} {
288             incr x
289             uplevel 1 [list ::source -encoding utf-8 $langfile]
290         }
291     }
292     return $x
295 # msgcat::mcset --
297 #       Set the translation for a given string in a specified locale.
299 # Arguments:
300 #       locale          The locale to use.
301 #       src             The source string.
302 #       dest            (Optional) The translated string.  If omitted,
303 #                       the source string is used.
305 # Results:
306 #       Returns the new locale.
308 proc msgcat::mcset {locale src {dest ""}} {
309     variable Msgs
310     if {[llength [info level 0]] == 3} { ;# dest not specified
311         set dest $src
312     }
314     set ns [uplevel 1 [list ::namespace current]]
315     
316     set locale [string tolower $locale]
317     
318     # create nested dictionaries if they do not exist
319     if {![dict exists $Msgs $locale]} {
320         dict set Msgs $locale  [dict create] 
321     }
322     if {![dict exists $Msgs $locale $ns]} {
323         dict set Msgs $locale $ns [dict create]
324     }
325     dict set Msgs $locale $ns $src $dest
326     return $dest
329 # msgcat::mcmset --
331 #       Set the translation for multiple strings in a specified locale.
333 # Arguments:
334 #       locale          The locale to use.
335 #       pairs           One or more src/dest pairs (must be even length)
337 # Results:
338 #       Returns the number of pairs processed
340 proc msgcat::mcmset {locale pairs } {
341     variable Msgs
343     set length [llength $pairs]
344     if {$length % 2} {
345         return -code error "bad translation list:\
346                  should be \"[lindex [info level 0] 0] locale {src dest ...}\""
347     }
348     
349     set locale [string tolower $locale]
350     set ns [uplevel 1 [list ::namespace current]]
352     # create nested dictionaries if they do not exist
353     if {![dict exists $Msgs $locale]} {
354         dict set Msgs $locale  [dict create] 
355     }
356     if {![dict exists $Msgs $locale $ns]} {
357         dict set Msgs $locale $ns [dict create]
358     }    
359     foreach {src dest} $pairs {
360         dict set Msgs $locale $ns $src $dest
361     }
363     return $length
366 # msgcat::mcunknown --
368 #       This routine is called by msgcat::mc if a translation cannot
369 #       be found for a string.  This routine is intended to be replaced
370 #       by an application specific routine for error reporting
371 #       purposes.  The default behavior is to return the source string.  
372 #       If additional args are specified, the format command will be used
373 #       to work them into the traslated string.
375 # Arguments:
376 #       locale          The current locale.
377 #       src             The string to be translated.
378 #       args            Args to pass to the format command
380 # Results:
381 #       Returns the translated value.
383 proc msgcat::mcunknown {locale src args} {
384     if {[llength $args]} {
385         return [format $src {*}$args]
386     } else {
387         return $src
388     }
391 # msgcat::mcmax --
393 #       Calculates the maximum length of the translated strings of the given 
394 #       list.
396 # Arguments:
397 #       args    strings to translate.
399 # Results:
400 #       Returns the length of the longest translated string.
402 proc msgcat::mcmax {args} {
403     set max 0
404     foreach string $args {
405         set translated [uplevel 1 [list [namespace origin mc] $string]]
406         set len [string length $translated]
407         if {$len>$max} {
408             set max $len
409         }
410     }
411     return $max
414 # Convert the locale values stored in environment variables to a form
415 # suitable for passing to [mclocale]
416 proc msgcat::ConvertLocale {value} {
417     # Assume $value is of form: $language[_$territory][.$codeset][@modifier]
418     # Convert to form: $language[_$territory][_$modifier]
419     #
420     # Comment out expanded RE version -- bugs alleged
421     # regexp -expanded {
422     #   ^               # Match all the way to the beginning
423     #   ([^_.@]*)       # Match "lanugage"; ends with _, ., or @
424     #   (_([^.@]*))?    # Match (optional) "territory"; starts with _
425     #   ([.]([^@]*))?   # Match (optional) "codeset"; starts with .
426     #   (@(.*))?        # Match (optional) "modifier"; starts with @
427     #   $               # Match all the way to the end
428     # } $value -> language _ territory _ codeset _ modifier
429     if {![regexp {^([^_.@]+)(_([^.@]*))?([.]([^@]*))?(@(.*))?$} $value \
430             -> language _ territory _ codeset _ modifier]} {
431         return -code error "invalid locale '$value': empty language part"
432     }
433     set ret $language
434     if {[string length $territory]} {
435         append ret _$territory
436     }
437     if {[string length $modifier]} {
438         append ret _$modifier
439     }
440     return $ret
443 # Initialize the default locale
444 proc msgcat::Init {} {
445     #
446     # set default locale, try to get from environment
447     #
448     foreach varName {LC_ALL LC_MESSAGES LANG} {
449         if {[info exists ::env($varName)] && ("" ne $::env($varName))} {
450             if {![catch {
451                 mclocale [ConvertLocale $::env($varName)]
452             }]} {
453                 return
454             }
455         }
456     }
457     #
458     # On Darwin, fallback to current CFLocale identifier if available.
459     #
460     if {$::tcl_platform(os) eq "Darwin" && $::tcl_platform(platform) eq "unix"
461             && [info exists ::tcl::mac::locale] && $::tcl::mac::locale ne ""} {
462         if {![catch {
463             mclocale [ConvertLocale $::tcl::mac::locale]
464         }]} {
465             return
466         }
467     }
468     #
469     # The rest of this routine is special processing for Windows;
470     # all other platforms, get out now.
471     #
472     if { $::tcl_platform(platform) ne "windows" } {
473         mclocale C
474         return
475     }
476     #
477     # On Windows, try to set locale depending on registry settings,
478     # or fall back on locale of "C".  
479     #
480     set key {HKEY_CURRENT_USER\Control Panel\International}
481     if {[catch {package require registry}] \
482             || [catch {registry get $key "locale"} locale]} {
483         mclocale C
484         return
485     }
486     #
487     # Keep trying to match against smaller and smaller suffixes
488     # of the registry value, since the latter hexadigits appear
489     # to determine general language and earlier hexadigits determine
490     # more precise information, such as territory.  For example,
491     #     0409 - English - United States
492     #     0809 - English - United Kingdom
493     # Add more translations to the WinRegToISO639 array above.
494     #
495     variable WinRegToISO639
496     set locale [string tolower $locale]
497     while {[string length $locale]} {
498         if {![catch {
499                 mclocale [ConvertLocale [dict get $WinRegToISO639 $locale]]
500         }]} {
501             return
502         }
503         set locale [string range $locale 1 end]
504     }
505     #
506     # No translation known.  Fall back on "C" locale
507     #
508     mclocale C
510 msgcat::Init