Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: data/trunk/Media/tcl8.4/msgcat/msgcat.tcl @ 5180

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