Update tcl to version 8.5.13
[msysgit.git] / mingw / lib / tcl8 / 8.5 / msgcat-1.5.0.tm
blob112507a6399252ebb9c9c325118edba685b34a3d
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.
13 package require Tcl 8.5
14 # When the version number changes, be sure to update the pkgIndex.tcl file,
15 # and the installation directory in the Makefiles.
16 package provide msgcat 1.5.0
18 namespace eval msgcat {
19     namespace export mc mcload mclocale mcmax mcmset mcpreferences mcset \
20             mcunknown mcflset mcflmset
22     # Records the current locale as passed to mclocale
23     variable Locale ""
25     # Records the list of locales to search
26     variable Loclist {}
28     # Records the locale of the currently sourced message catalogue file
29     variable FileLocale
31     # Records the mapping between source strings and translated strings.  The
32     # dict key is of the form "<locale> <namespace> <src>", where locale and
33     # namespace should be themselves dict values and the value is
34     # the translated string.
35     variable Msgs [dict create]
37     # Map of language codes used in Windows registry to those of ISO-639
38     if {[info sharedlibextension] eq ".dll"} {
39         variable WinRegToISO639 [dict create  {*}{
40             01 ar 0401 ar_SA 0801 ar_IQ 0c01 ar_EG 1001 ar_LY 1401 ar_DZ
41                   1801 ar_MA 1c01 ar_TN 2001 ar_OM 2401 ar_YE 2801 ar_SY
42                   2c01 ar_JO 3001 ar_LB 3401 ar_KW 3801 ar_AE 3c01 ar_BH
43                   4001 ar_QA
44             02 bg 0402 bg_BG
45             03 ca 0403 ca_ES
46             04 zh 0404 zh_TW 0804 zh_CN 0c04 zh_HK 1004 zh_SG 1404 zh_MO
47             05 cs 0405 cs_CZ
48             06 da 0406 da_DK
49             07 de 0407 de_DE 0807 de_CH 0c07 de_AT 1007 de_LU 1407 de_LI
50             08 el 0408 el_GR
51             09 en 0409 en_US 0809 en_GB 0c09 en_AU 1009 en_CA 1409 en_NZ
52                   1809 en_IE 1c09 en_ZA 2009 en_JM 2409 en_GD 2809 en_BZ
53                   2c09 en_TT 3009 en_ZW 3409 en_PH
54             0a es 040a es_ES 080a es_MX 0c0a es_ES@modern 100a es_GT 140a es_CR
55                   180a es_PA 1c0a es_DO 200a es_VE 240a es_CO 280a es_PE
56                   2c0a es_AR 300a es_EC 340a es_CL 380a es_UY 3c0a es_PY
57                   400a es_BO 440a es_SV 480a es_HN 4c0a es_NI 500a es_PR
58             0b fi 040b fi_FI
59             0c fr 040c fr_FR 080c fr_BE 0c0c fr_CA 100c fr_CH 140c fr_LU
60                   180c fr_MC
61             0d he 040d he_IL
62             0e hu 040e hu_HU
63             0f is 040f is_IS
64             10 it 0410 it_IT 0810 it_CH
65             11 ja 0411 ja_JP
66             12 ko 0412 ko_KR
67             13 nl 0413 nl_NL 0813 nl_BE
68             14 no 0414 no_NO 0814 nn_NO
69             15 pl 0415 pl_PL
70             16 pt 0416 pt_BR 0816 pt_PT
71             17 rm 0417 rm_CH
72             18 ro 0418 ro_RO 0818 ro_MO
73             19 ru 0819 ru_MO
74             1a hr 041a hr_HR 081a sr_YU 0c1a sr_YU@cyrillic
75             1b sk 041b sk_SK
76             1c sq 041c sq_AL
77             1d sv 041d sv_SE 081d sv_FI
78             1e th 041e th_TH
79             1f tr 041f tr_TR
80             20 ur 0420 ur_PK 0820 ur_IN
81             21 id 0421 id_ID
82             22 uk 0422 uk_UA
83             23 be 0423 be_BY
84             24 sl 0424 sl_SI
85             25 et 0425 et_EE
86             26 lv 0426 lv_LV
87             27 lt 0427 lt_LT
88             28 tg 0428 tg_TJ
89             29 fa 0429 fa_IR
90             2a vi 042a vi_VN
91             2b hy 042b hy_AM
92             2c az 042c az_AZ@latin 082c az_AZ@cyrillic
93             2d eu
94             2e wen 042e wen_DE
95             2f mk 042f mk_MK
96             30 bnt 0430 bnt_TZ
97             31 ts 0431 ts_ZA
98             32 tn
99             33 ven 0433 ven_ZA
100             34 xh 0434 xh_ZA
101             35 zu 0435 zu_ZA
102             36 af 0436 af_ZA
103             37 ka 0437 ka_GE
104             38 fo 0438 fo_FO
105             39 hi 0439 hi_IN
106             3a mt 043a mt_MT
107             3b se 043b se_NO
108             043c gd_UK 083c ga_IE
109             3d yi 043d yi_IL
110             3e ms 043e ms_MY 083e ms_BN
111             3f kk 043f kk_KZ
112             40 ky 0440 ky_KG
113             41 sw 0441 sw_KE
114             42 tk 0442 tk_TM
115             43 uz 0443 uz_UZ@latin 0843 uz_UZ@cyrillic
116             44 tt 0444 tt_RU
117             45 bn 0445 bn_IN
118             46 pa 0446 pa_IN
119             47 gu 0447 gu_IN
120             48 or 0448 or_IN
121             49 ta
122             4a te 044a te_IN
123             4b kn 044b kn_IN
124             4c ml 044c ml_IN
125             4d as 044d as_IN
126             4e mr 044e mr_IN
127             4f sa 044f sa_IN
128             50 mn
129             51 bo 0451 bo_CN
130             52 cy 0452 cy_GB
131             53 km 0453 km_KH
132             54 lo 0454 lo_LA
133             55 my 0455 my_MM
134             56 gl 0456 gl_ES
135             57 kok 0457 kok_IN
136             58 mni 0458 mni_IN
137             59 sd
138             5a syr 045a syr_TR
139             5b si 045b si_LK
140             5c chr 045c chr_US
141             5d iu 045d iu_CA
142             5e am 045e am_ET
143             5f ber 045f ber_MA
144             60 ks 0460 ks_PK 0860 ks_IN
145             61 ne 0461 ne_NP 0861 ne_IN
146             62 fy 0462 fy_NL
147             63 ps
148             64 tl 0464 tl_PH
149             65 div 0465 div_MV
150             66 bin 0466 bin_NG
151             67 ful 0467 ful_NG
152             68 ha 0468 ha_NG
153             69 nic 0469 nic_NG
154             6a yo 046a yo_NG
155             70 ibo 0470 ibo_NG
156             71 kau 0471 kau_NG
157             72 om 0472 om_ET
158             73 ti 0473 ti_ET
159             74 gn 0474 gn_PY
160             75 cpe 0475 cpe_US
161             76 la 0476 la_VA
162             77 so 0477 so_SO
163             78 sit 0478 sit_CN
164             79 pap 0479 pap_AN
165         }]
166     }
169 # msgcat::mc --
171 #       Find the translation for the given string based on the current
172 #       locale setting. Check the local namespace first, then look in each
173 #       parent namespace until the source is found.  If additional args are
174 #       specified, use the format command to work them into the traslated
175 #       string.
177 # Arguments:
178 #       src     The string to translate.
179 #       args    Args to pass to the format command
181 # Results:
182 #       Returns the translated string.  Propagates errors thrown by the
183 #       format command.
185 proc msgcat::mc {src args} {
186     # Check for the src in each namespace starting from the local and
187     # ending in the global.
189     variable Msgs
190     variable Loclist
191     variable Locale
193     set ns [uplevel 1 [list ::namespace current]]
195     while {$ns != ""} {
196         foreach loc $Loclist {
197             if {[dict exists $Msgs $loc $ns $src]} {
198                 if {[llength $args] == 0} {
199                     return [dict get $Msgs $loc $ns $src]
200                 } else {
201                     return [format [dict get $Msgs $loc $ns $src] {*}$args]
202                 }
203             }
204         }
205         set ns [namespace parent $ns]
206     }
207     # we have not found the translation
208     return [uplevel 1 [list [namespace origin mcunknown] \
209             $Locale $src {*}$args]]
212 # msgcat::mclocale --
214 #       Query or set the current locale.
216 # Arguments:
217 #       newLocale       (Optional) The new locale string. Locale strings
218 #                       should be composed of one or more sublocale parts
219 #                       separated by underscores (e.g. en_US).
221 # Results:
222 #       Returns the current locale.
224 proc msgcat::mclocale {args} {
225     variable Loclist
226     variable Locale
227     set len [llength $args]
229     if {$len > 1} {
230         return -code error "wrong # args: should be\
231                 \"[lindex [info level 0] 0] ?newLocale?\""
232     }
234     if {$len == 1} {
235         set newLocale [lindex $args 0]
236         if {$newLocale ne [file tail $newLocale]} {
237             return -code error "invalid newLocale value \"$newLocale\":\
238                     could be path to unsafe code."
239         }
240         set Locale [string tolower $newLocale]
241         set Loclist {}
242         set word ""
243         foreach part [split $Locale _] {
244             set word [string trim "${word}_${part}" _]
245             if {$word ne [lindex $Loclist 0]} {
246                 set Loclist [linsert $Loclist 0 $word]
247             }
248         }
249         lappend Loclist {}
250         set Locale [lindex $Loclist 0]
251     }
252     return $Locale
255 # msgcat::mcpreferences --
257 #       Fetch the list of locales used to look up strings, ordered from
258 #       most preferred to least preferred.
260 # Arguments:
261 #       None.
263 # Results:
264 #       Returns an ordered list of the locales preferred by the user.
266 proc msgcat::mcpreferences {} {
267     variable Loclist
268     return $Loclist
271 # msgcat::mcload --
273 #       Attempt to load message catalogs for each locale in the
274 #       preference list from the specified directory.
276 # Arguments:
277 #       langdir         The directory to search.
279 # Results:
280 #       Returns the number of message catalogs that were loaded.
282 proc msgcat::mcload {langdir} {
283     variable FileLocale
284     # Save the file locale if we are recursively called
285     if {[info exists FileLocale]} {
286         set nestedFileLocale $FileLocale
287     }
288     set x 0
289     foreach p [mcpreferences] {
290         if { $p eq {} } {
291             set p ROOT
292         }
293         set langfile [file join $langdir $p.msg]
294         if {[file exists $langfile]} {
295             incr x
296             set FileLocale [string tolower [file tail [file rootname $langfile]]]
297             if {"root" eq $FileLocale} {
298                 set FileLocale ""
299             }
300             uplevel 1 [list ::source -encoding utf-8 $langfile]
301             unset FileLocale
302         }
303     }
304     if {[info exists nestedFileLocale]} {
305         set FileLocale $nestedFileLocale
306     }
307     return $x
310 # msgcat::mcset --
312 #       Set the translation for a given string in a specified locale.
314 # Arguments:
315 #       locale          The locale to use.
316 #       src             The source string.
317 #       dest            (Optional) The translated string.  If omitted,
318 #                       the source string is used.
320 # Results:
321 #       Returns the new locale.
323 proc msgcat::mcset {locale src {dest ""}} {
324     variable Msgs
325     if {[llength [info level 0]] == 3} { ;# dest not specified
326         set dest $src
327     }
329     set ns [uplevel 1 [list ::namespace current]]
331     set locale [string tolower $locale]
333     dict set Msgs $locale $ns $src $dest
334     return $dest
337 # msgcat::mcflset --
339 #       Set the translation for a given string in the current file locale.
341 # Arguments:
342 #       src             The source string.
343 #       dest            (Optional) The translated string.  If omitted,
344 #                       the source string is used.
346 # Results:
347 #       Returns the new locale.
349 proc msgcat::mcflset {src {dest ""}} {
350     variable FileLocale
351     variable Msgs
353     if {![info exists FileLocale]} {
354         return -code error \
355             "must only be used inside a message catalog loaded with ::msgcat::mcload"
356     }
357     if {[llength [info level 0]] == 2} { ;# dest not specified
358         set dest $src
359     }
361     set ns [uplevel 1 [list ::namespace current]]
362     dict set Msgs $FileLocale $ns $src $dest
363     return $dest
366 # msgcat::mcmset --
368 #       Set the translation for multiple strings in a specified locale.
370 # Arguments:
371 #       locale          The locale to use.
372 #       pairs           One or more src/dest pairs (must be even length)
374 # Results:
375 #       Returns the number of pairs processed
377 proc msgcat::mcmset {locale pairs } {
378     variable Msgs
380     set length [llength $pairs]
381     if {$length % 2} {
382         return -code error "bad translation list:\
383                  should be \"[lindex [info level 0] 0] locale {src dest ...}\""
384     }
386     set locale [string tolower $locale]
387     set ns [uplevel 1 [list ::namespace current]]
389     foreach {src dest} $pairs {
390         dict set Msgs $locale $ns $src $dest
391     }
393     return [expr {$length / 2}]
396 # msgcat::mcflmset --
398 #       Set the translation for multiple strings in the mc file locale.
400 # Arguments:
401 #       pairs           One or more src/dest pairs (must be even length)
403 # Results:
404 #       Returns the number of pairs processed
406 proc msgcat::mcflmset {pairs} {
407     variable FileLocale
408     variable Msgs
410     if {![info exists FileLocale]} {
411         return -code error \
412             "must only be used inside a message catalog loaded with ::msgcat::mcload"
413     }
414     set length [llength $pairs]
415     if {$length % 2} {
416         return -code error "bad translation list:\
417                 should be \"[lindex [info level 0] 0] locale {src dest ...}\""
418     }
420     set ns [uplevel 1 [list ::namespace current]]
421     foreach {src dest} $pairs {
422         dict set Msgs $FileLocale $ns $src $dest
423     }
424     return [expr {$length / 2}]
427 # msgcat::mcunknown --
429 #       This routine is called by msgcat::mc if a translation cannot
430 #       be found for a string.  This routine is intended to be replaced
431 #       by an application specific routine for error reporting
432 #       purposes.  The default behavior is to return the source string.
433 #       If additional args are specified, the format command will be used
434 #       to work them into the traslated string.
436 # Arguments:
437 #       locale          The current locale.
438 #       src             The string to be translated.
439 #       args            Args to pass to the format command
441 # Results:
442 #       Returns the translated value.
444 proc msgcat::mcunknown {locale src args} {
445     if {[llength $args]} {
446         return [format $src {*}$args]
447     } else {
448         return $src
449     }
452 # msgcat::mcmax --
454 #       Calculates the maximum length of the translated strings of the given
455 #       list.
457 # Arguments:
458 #       args    strings to translate.
460 # Results:
461 #       Returns the length of the longest translated string.
463 proc msgcat::mcmax {args} {
464     set max 0
465     foreach string $args {
466         set translated [uplevel 1 [list [namespace origin mc] $string]]
467         set len [string length $translated]
468         if {$len>$max} {
469             set max $len
470         }
471     }
472     return $max
475 # Convert the locale values stored in environment variables to a form
476 # suitable for passing to [mclocale]
477 proc msgcat::ConvertLocale {value} {
478     # Assume $value is of form: $language[_$territory][.$codeset][@modifier]
479     # Convert to form: $language[_$territory][_$modifier]
480     #
481     # Comment out expanded RE version -- bugs alleged
482     # regexp -expanded {
483     #   ^               # Match all the way to the beginning
484     #   ([^_.@]*)       # Match "lanugage"; ends with _, ., or @
485     #   (_([^.@]*))?    # Match (optional) "territory"; starts with _
486     #   ([.]([^@]*))?   # Match (optional) "codeset"; starts with .
487     #   (@(.*))?        # Match (optional) "modifier"; starts with @
488     #   $               # Match all the way to the end
489     # } $value -> language _ territory _ codeset _ modifier
490     if {![regexp {^([^_.@]+)(_([^.@]*))?([.]([^@]*))?(@(.*))?$} $value \
491             -> language _ territory _ codeset _ modifier]} {
492         return -code error "invalid locale '$value': empty language part"
493     }
494     set ret $language
495     if {[string length $territory]} {
496         append ret _$territory
497     }
498     if {[string length $modifier]} {
499         append ret _$modifier
500     }
501     return $ret
504 # Initialize the default locale
505 proc msgcat::Init {} {
506     global env
508     #
509     # set default locale, try to get from environment
510     #
511     foreach varName {LC_ALL LC_MESSAGES LANG} {
512         if {[info exists env($varName)] && ("" ne $env($varName))} {
513             if {![catch {
514                 mclocale [ConvertLocale $env($varName)]
515             }]} {
516                 return
517             }
518         }
519     }
520     #
521     # On Darwin, fallback to current CFLocale identifier if available.
522     #
523     if {[info exists ::tcl::mac::locale] && $::tcl::mac::locale ne ""} {
524         if {![catch {
525             mclocale [ConvertLocale $::tcl::mac::locale]
526         }]} {
527             return
528         }
529     }
530     #
531     # The rest of this routine is special processing for Windows or
532     # Cygwin. All other platforms, get out now.
533     #
534     if {([info sharedlibextension] ne ".dll")
535             || [catch {package require registry}]} {
536         mclocale C
537         return
538     }
539     #
540     # On Windows or Cygwin, try to set locale depending on registry
541     # settings, or fall back on locale of "C".
542     #
544     # First check registry value LocalName present from Windows Vista
545     # which contains the local string as RFC5646, composed of:
546     # [a-z]{2,3} : language
547     # -[a-z]{4}  : script (optional, translated by table Latn->latin)
548     # -[a-z]{2}|[0-9]{3} : territory (optional, numerical region codes not used)
549     # (-.*)* : variant, extension, private use (optional, not used)
550     # Those are translated to local strings.
551     # Examples: de-CH -> de_ch, sr-Latn-CS -> sr_cs@latin, es-419 -> es
552     #
553     set key {HKEY_CURRENT_USER\Control Panel\International}
554     if {([registry values $key "LocaleName"] ne "")
555             && [regexp {^([a-z]{2,3})(?:-([a-z]{4}))?(?:-([a-z]{2}))?(?:-.+)?$}\
556             [string tolower [registry get $key "LocaleName"]] match locale\
557             script territory]} {
558         if {"" ne $territory} {
559             append locale _ $territory
560         }
561         set modifierDict [dict create latn latin cyrl cyrillic]
562         if {[dict exists $modifierDict $script]} {
563             append locale @ [dict get $modifierDict $script]
564         }
565         if {![catch {
566             mclocale [ConvertLocale $locale]
567         }]} {
568             return
569         }
570     }
572     # then check key locale which contains a numerical language ID
573     if {[catch {
574         set locale [registry get $key "locale"]
575     }]} {
576         mclocale C
577         return
578     }
579     #
580     # Keep trying to match against smaller and smaller suffixes
581     # of the registry value, since the latter hexadigits appear
582     # to determine general language and earlier hexadigits determine
583     # more precise information, such as territory.  For example,
584     #     0409 - English - United States
585     #     0809 - English - United Kingdom
586     # Add more translations to the WinRegToISO639 array above.
587     #
588     variable WinRegToISO639
589     set locale [string tolower $locale]
590     while {[string length $locale]} {
591         if {![catch {
592             mclocale [ConvertLocale [dict get $WinRegToISO639 $locale]]
593         }]} {
594             return
595         }
596         set locale [string range $locale 1 end]
597     }
598     #
599     # No translation known.  Fall back on "C" locale
600     #
601     mclocale C
603 msgcat::Init