diff options
Diffstat (limited to 'library/msgcat/msgcat.tcl')
-rw-r--r-- | library/msgcat/msgcat.tcl | 485 |
1 files changed, 309 insertions, 176 deletions
diff --git a/library/msgcat/msgcat.tcl b/library/msgcat/msgcat.tcl index 217a618..cf3b9d7 100644 --- a/library/msgcat/msgcat.tcl +++ b/library/msgcat/msgcat.tcl @@ -9,17 +9,15 @@ # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. -# -# RCS: @(#) $Id: msgcat.tcl,v 1.17.2.4 2004/08/13 21:45:16 dgp Exp $ -package require Tcl 8.2 +package require Tcl 8.5 # When the version number changes, be sure to update the pkgIndex.tcl file, # and the installation directory in the Makefiles. -package provide msgcat 1.3.3 +package provide msgcat 1.5.2 namespace eval msgcat { namespace export mc mcload mclocale mcmax mcmset mcpreferences mcset \ - mcunknown + mcunknown mcflset mcflmset # Records the current locale as passed to mclocale variable Locale "" @@ -27,137 +25,144 @@ namespace eval msgcat { # Records the list of locales to search variable Loclist {} + # Records the locale of the currently sourced message catalogue file + variable FileLocale + # Records the mapping between source strings and translated strings. The - # array key is of the form "<locale>,<namespace>,<src>" and the value is + # dict key is of the form "<locale> <namespace> <src>", where locale and + # namespace should be themselves dict values and the value is # the translated string. - array set Msgs {} + variable Msgs [dict create] # Map of language codes used in Windows registry to those of ISO-639 - array set WinRegToISO639 { - 01 ar 0401 ar_SA 0801 ar_IQ 0c01 ar_EG 1001 ar_LY 1401 ar_DZ - 1801 ar_MA 1c01 ar_TN 2001 ar_OM 2401 ar_YE 2801 ar_SY - 2c01 ar_JO 3001 ar_LB 3401 ar_KW 3801 ar_AE 3c01 ar_BH - 4001 ar_QA - 02 bg 0402 bg_BG - 03 ca 0403 ca_ES - 04 zh 0404 zh_TW 0804 zh_CN 0c04 zh_HK 1004 zh_SG 1404 zh_MO - 05 cs 0405 cs_CZ - 06 da 0406 da_DK - 07 de 0407 de_DE 0807 de_CH 0c07 de_AT 1007 de_LU 1407 de_LI - 08 el 0408 el_GR - 09 en 0409 en_US 0809 en_GB 0c09 en_AU 1009 en_CA 1409 en_NZ - 1809 en_IE 1c09 en_ZA 2009 en_JM 2409 en_GD 2809 en_BZ - 2c09 en_TT 3009 en_ZW 3409 en_PH - 0a es 040a es_ES 080a es_MX 0c0a es_ES@modern 100a es_GT 140a es_CR - 180a es_PA 1c0a es_DO 200a es_VE 240a es_CO 280a es_PE - 2c0a es_AR 300a es_EC 340a es_CL 380a es_UY 3c0a es_PY - 400a es_BO 440a es_SV 480a es_HN 4c0a es_NI 500a es_PR - 0b fi 040b fi_FI - 0c fr 040c fr_FR 080c fr_BE 0c0c fr_CA 100c fr_CH 140c fr_LU - 180c fr_MC - 0d he 040d he_IL - 0e hu 040e hu_HU - 0f is 040f is_IS - 10 it 0410 it_IT 0810 it_CH - 11 ja 0411 ja_JP - 12 ko 0412 ko_KR - 13 nl 0413 nl_NL 0813 nl_BE - 14 no 0414 no_NO 0814 nn_NO - 15 pl 0415 pl_PL - 16 pt 0416 pt_BR 0816 pt_PT - 17 rm 0417 rm_CH - 18 ro 0418 ro_RO - 19 ru - 1a hr 041a hr_HR 081a sr_YU 0c1a sr_YU@cyrillic - 1b sk 041b sk_SK - 1c sq 041c sq_AL - 1d sv 041d sv_SE 081d sv_FI - 1e th 041e th_TH - 1f tr 041f tr_TR - 20 ur 0420 ur_PK 0820 ur_IN - 21 id 0421 id_ID - 22 uk 0422 uk_UA - 23 be 0423 be_BY - 24 sl 0424 sl_SI - 25 et 0425 et_EE - 26 lv 0426 lv_LV - 27 lt 0427 lt_LT - 28 tg 0428 tg_TJ - 29 fa 0429 fa_IR - 2a vi 042a vi_VN - 2b hy 042b hy_AM - 2c az 042c az_AZ@latin 082c az_AZ@cyrillic - 2d eu - 2e wen 042e wen_DE - 2f mk 042f mk_MK - 30 bnt 0430 bnt_TZ - 31 ts 0431 ts_ZA - 33 ven 0433 ven_ZA - 34 xh 0434 xh_ZA - 35 zu 0435 zu_ZA - 36 af 0436 af_ZA - 37 ka 0437 ka_GE - 38 fo 0438 fo_FO - 39 hi 0439 hi_IN - 3a mt 043a mt_MT - 3b se 043b se_NO - 043c gd_UK 083c ga_IE - 3d yi 043d yi_IL - 3e ms 043e ms_MY 083e ms_BN - 3f kk 043f kk_KZ - 40 ky 0440 ky_KG - 41 sw 0441 sw_KE - 42 tk 0442 tk_TM - 43 uz 0443 uz_UZ@latin 0843 uz_UZ@cyrillic - 44 tt 0444 tt_RU - 45 bn 0445 bn_IN - 46 pa 0446 pa_IN - 47 gu 0447 gu_IN - 48 or 0448 or_IN - 49 ta - 4a te 044a te_IN - 4b kn 044b kn_IN - 4c ml 044c ml_IN - 4d as 044d as_IN - 4e mr 044e mr_IN - 4f sa 044f sa_IN - 50 mn - 51 bo 0451 bo_CN - 52 cy 0452 cy_GB - 53 km 0453 km_KH - 54 lo 0454 lo_LA - 55 my 0455 my_MM - 56 gl 0456 gl_ES - 57 kok 0457 kok_IN - 58 mni 0458 mni_IN - 59 sd - 5a syr 045a syr_TR - 5b si 045b si_LK - 5c chr 045c chr_US - 5d iu 045d iu_CA - 5e am 045e am_ET - 5f ber 045f ber_MA - 60 ks 0460 ks_PK 0860 ks_IN - 61 ne 0461 ne_NP 0861 ne_IN - 62 fy 0462 fy_NL - 63 ps - 64 tl 0464 tl_PH - 65 div 0465 div_MV - 66 bin 0466 bin_NG - 67 ful 0467 ful_NG - 68 ha 0468 ha_NG - 69 nic 0469 nic_NG - 6a yo 046a yo_NG - 70 ibo 0470 ibo_NG - 71 kau 0471 kau_NG - 72 om 0472 om_ET - 73 ti 0473 ti_ET - 74 gn 0474 gn_PY - 75 cpe 0475 cpe_US - 76 la 0476 la_VA - 77 so 0477 so_SO - 78 sit 0478 sit_CN - 79 pap 0479 pap_AN + if {[info sharedlibextension] eq ".dll"} { + variable WinRegToISO639 [dict create {*}{ + 01 ar 0401 ar_SA 0801 ar_IQ 0c01 ar_EG 1001 ar_LY 1401 ar_DZ + 1801 ar_MA 1c01 ar_TN 2001 ar_OM 2401 ar_YE 2801 ar_SY + 2c01 ar_JO 3001 ar_LB 3401 ar_KW 3801 ar_AE 3c01 ar_BH + 4001 ar_QA + 02 bg 0402 bg_BG + 03 ca 0403 ca_ES + 04 zh 0404 zh_TW 0804 zh_CN 0c04 zh_HK 1004 zh_SG 1404 zh_MO + 05 cs 0405 cs_CZ + 06 da 0406 da_DK + 07 de 0407 de_DE 0807 de_CH 0c07 de_AT 1007 de_LU 1407 de_LI + 08 el 0408 el_GR + 09 en 0409 en_US 0809 en_GB 0c09 en_AU 1009 en_CA 1409 en_NZ + 1809 en_IE 1c09 en_ZA 2009 en_JM 2409 en_GD 2809 en_BZ + 2c09 en_TT 3009 en_ZW 3409 en_PH + 0a es 040a es_ES 080a es_MX 0c0a es_ES@modern 100a es_GT 140a es_CR + 180a es_PA 1c0a es_DO 200a es_VE 240a es_CO 280a es_PE + 2c0a es_AR 300a es_EC 340a es_CL 380a es_UY 3c0a es_PY + 400a es_BO 440a es_SV 480a es_HN 4c0a es_NI 500a es_PR + 0b fi 040b fi_FI + 0c fr 040c fr_FR 080c fr_BE 0c0c fr_CA 100c fr_CH 140c fr_LU + 180c fr_MC + 0d he 040d he_IL + 0e hu 040e hu_HU + 0f is 040f is_IS + 10 it 0410 it_IT 0810 it_CH + 11 ja 0411 ja_JP + 12 ko 0412 ko_KR + 13 nl 0413 nl_NL 0813 nl_BE + 14 no 0414 no_NO 0814 nn_NO + 15 pl 0415 pl_PL + 16 pt 0416 pt_BR 0816 pt_PT + 17 rm 0417 rm_CH + 18 ro 0418 ro_RO 0818 ro_MO + 19 ru 0819 ru_MO + 1a hr 041a hr_HR 081a sr_YU 0c1a sr_YU@cyrillic + 1b sk 041b sk_SK + 1c sq 041c sq_AL + 1d sv 041d sv_SE 081d sv_FI + 1e th 041e th_TH + 1f tr 041f tr_TR + 20 ur 0420 ur_PK 0820 ur_IN + 21 id 0421 id_ID + 22 uk 0422 uk_UA + 23 be 0423 be_BY + 24 sl 0424 sl_SI + 25 et 0425 et_EE + 26 lv 0426 lv_LV + 27 lt 0427 lt_LT + 28 tg 0428 tg_TJ + 29 fa 0429 fa_IR + 2a vi 042a vi_VN + 2b hy 042b hy_AM + 2c az 042c az_AZ@latin 082c az_AZ@cyrillic + 2d eu + 2e wen 042e wen_DE + 2f mk 042f mk_MK + 30 bnt 0430 bnt_TZ + 31 ts 0431 ts_ZA + 32 tn + 33 ven 0433 ven_ZA + 34 xh 0434 xh_ZA + 35 zu 0435 zu_ZA + 36 af 0436 af_ZA + 37 ka 0437 ka_GE + 38 fo 0438 fo_FO + 39 hi 0439 hi_IN + 3a mt 043a mt_MT + 3b se 043b se_NO + 043c gd_UK 083c ga_IE + 3d yi 043d yi_IL + 3e ms 043e ms_MY 083e ms_BN + 3f kk 043f kk_KZ + 40 ky 0440 ky_KG + 41 sw 0441 sw_KE + 42 tk 0442 tk_TM + 43 uz 0443 uz_UZ@latin 0843 uz_UZ@cyrillic + 44 tt 0444 tt_RU + 45 bn 0445 bn_IN + 46 pa 0446 pa_IN + 47 gu 0447 gu_IN + 48 or 0448 or_IN + 49 ta + 4a te 044a te_IN + 4b kn 044b kn_IN + 4c ml 044c ml_IN + 4d as 044d as_IN + 4e mr 044e mr_IN + 4f sa 044f sa_IN + 50 mn + 51 bo 0451 bo_CN + 52 cy 0452 cy_GB + 53 km 0453 km_KH + 54 lo 0454 lo_LA + 55 my 0455 my_MM + 56 gl 0456 gl_ES + 57 kok 0457 kok_IN + 58 mni 0458 mni_IN + 59 sd + 5a syr 045a syr_TR + 5b si 045b si_LK + 5c chr 045c chr_US + 5d iu 045d iu_CA + 5e am 045e am_ET + 5f ber 045f ber_MA + 60 ks 0460 ks_PK 0860 ks_IN + 61 ne 0461 ne_NP 0861 ne_IN + 62 fy 0462 fy_NL + 63 ps + 64 tl 0464 tl_PH + 65 div 0465 div_MV + 66 bin 0466 bin_NG + 67 ful 0467 ful_NG + 68 ha 0468 ha_NG + 69 nic 0469 nic_NG + 6a yo 046a yo_NG + 70 ibo 0470 ibo_NG + 71 kau 0471 kau_NG + 72 om 0472 om_ET + 73 ti 0473 ti_ET + 74 gn 0474 gn_PY + 75 cpe 0475 cpe_US + 76 la 0476 la_VA + 77 so 0477 so_SO + 78 sit 0478 sit_CN + 79 pap 0479 pap_AN + }] } } @@ -174,7 +179,7 @@ namespace eval msgcat { # args Args to pass to the format command # # Results: -# Returns the translatd string. Propagates errors thrown by the +# Returns the translated string. Propagates errors thrown by the # format command. proc msgcat::mc {src args} { @@ -186,23 +191,22 @@ proc msgcat::mc {src args} { variable Locale set ns [uplevel 1 [list ::namespace current]] - + while {$ns != ""} { foreach loc $Loclist { - if {[info exists Msgs($loc,$ns,$src)]} { + if {[dict exists $Msgs $loc $ns $src]} { if {[llength $args] == 0} { - return $Msgs($loc,$ns,$src) + return [dict get $Msgs $loc $ns $src] } else { - return [uplevel 1 \ - [linsert $args 0 ::format $Msgs($loc,$ns,$src)]] + return [format [dict get $Msgs $loc $ns $src] {*}$args] } } } set ns [namespace parent $ns] } # we have not found the translation - return [uplevel 1 \ - [linsert $args 0 [::namespace origin mcunknown] $Locale $src]] + return [uplevel 1 [list [namespace origin mcunknown] \ + $Locale $src {*}$args]] } # msgcat::mclocale -- @@ -223,7 +227,8 @@ proc msgcat::mclocale {args} { set len [llength $args] if {$len > 1} { - error {wrong # args: should be "mclocale ?newLocale?"} + return -code error "wrong # args: should be\ + \"[lindex [info level 0] 0] ?newLocale?\"" } if {$len == 1} { @@ -236,9 +241,13 @@ proc msgcat::mclocale {args} { set Loclist {} set word "" foreach part [split $Locale _] { - set word [string trimleft "${word}_${part}" _] - set Loclist [linsert $Loclist 0 $word] + set word [string trim "${word}_${part}" _] + if {$word ne [lindex $Loclist 0]} { + set Loclist [linsert $Loclist 0 $word] + } } + lappend Loclist {} + set Locale [lindex $Loclist 0] } return $Locale } @@ -271,17 +280,30 @@ proc msgcat::mcpreferences {} { # Returns the number of message catalogs that were loaded. proc msgcat::mcload {langdir} { + variable FileLocale + # Save the file locale if we are recursively called + if {[info exists FileLocale]} { + set nestedFileLocale $FileLocale + } set x 0 foreach p [mcpreferences] { + if {$p eq {}} { + set p ROOT + } set langfile [file join $langdir $p.msg] if {[file exists $langfile]} { incr x - set fid [open $langfile "r"] - fconfigure $fid -encoding utf-8 - uplevel 1 [read $fid] - close $fid + set FileLocale [string tolower [file tail [file rootname $langfile]]] + if {"root" eq $FileLocale} { + set FileLocale "" + } + uplevel 1 [list ::source -encoding utf-8 $langfile] + unset FileLocale } } + if {[info exists nestedFileLocale]} { + set FileLocale $nestedFileLocale + } return $x } @@ -301,12 +323,43 @@ proc msgcat::mcload {langdir} { proc msgcat::mcset {locale src {dest ""}} { variable Msgs if {[llength [info level 0]] == 3} { ;# dest not specified - set dest $src + set dest $src } set ns [uplevel 1 [list ::namespace current]] - set Msgs([string tolower $locale],$ns,$src) $dest + set locale [string tolower $locale] + + dict set Msgs $locale $ns $src $dest + return $dest +} + +# msgcat::mcflset -- +# +# Set the translation for a given string in the current file locale. +# +# Arguments: +# src The source string. +# dest (Optional) The translated string. If omitted, +# the source string is used. +# +# Results: +# Returns the new locale. + +proc msgcat::mcflset {src {dest ""}} { + variable FileLocale + variable Msgs + + if {![info exists FileLocale]} { + return -code error \ + "must only be used inside a message catalog loaded with ::msgcat::mcload" + } + if {[llength [info level 0]] == 2} { ;# dest not specified + set dest $src + } + + set ns [uplevel 1 [list ::namespace current]] + dict set Msgs $FileLocale $ns $src $dest return $dest } @@ -321,22 +374,54 @@ proc msgcat::mcset {locale src {dest ""}} { # Results: # Returns the number of pairs processed -proc msgcat::mcmset {locale pairs } { +proc msgcat::mcmset {locale pairs} { variable Msgs set length [llength $pairs] if {$length % 2} { - error {bad translation list: should be "mcmset locale {src dest ...}"} + return -code error "bad translation list:\ + should be \"[lindex [info level 0] 0] locale {src dest ...}\"" } - + set locale [string tolower $locale] set ns [uplevel 1 [list ::namespace current]] - + foreach {src dest} $pairs { - set Msgs($locale,$ns,$src) $dest + dict set Msgs $locale $ns $src $dest } - - return $length + + return [expr {$length / 2}] +} + +# msgcat::mcflmset -- +# +# Set the translation for multiple strings in the mc file locale. +# +# Arguments: +# pairs One or more src/dest pairs (must be even length) +# +# Results: +# Returns the number of pairs processed + +proc msgcat::mcflmset {pairs} { + variable FileLocale + variable Msgs + + if {![info exists FileLocale]} { + return -code error \ + "must only be used inside a message catalog loaded with ::msgcat::mcload" + } + set length [llength $pairs] + if {$length % 2} { + return -code error "bad translation list:\ + should be \"[lindex [info level 0] 0] locale {src dest ...}\"" + } + + set ns [uplevel 1 [list ::namespace current]] + foreach {src dest} $pairs { + dict set Msgs $FileLocale $ns $src $dest + } + return [expr {$length / 2}] } # msgcat::mcunknown -- @@ -344,7 +429,7 @@ proc msgcat::mcmset {locale pairs } { # This routine is called by msgcat::mc if a translation cannot # be found for a string. This routine is intended to be replaced # by an application specific routine for error reporting -# purposes. The default behavior is to return the source string. +# purposes. The default behavior is to return the source string. # If additional args are specified, the format command will be used # to work them into the traslated string. # @@ -358,7 +443,7 @@ proc msgcat::mcmset {locale pairs } { proc msgcat::mcunknown {locale src args} { if {[llength $args]} { - return [uplevel 1 [linsert $args 0 ::format $src]] + return [format $src {*}$args] } else { return $src } @@ -366,7 +451,7 @@ proc msgcat::mcunknown {locale src args} { # msgcat::mcmax -- # -# Calculates the maximun length of the translated strings of the given +# Calculates the maximum length of the translated strings of the given # list. # # Arguments: @@ -379,10 +464,10 @@ proc msgcat::mcmax {args} { set max 0 foreach string $args { set translated [uplevel 1 [list [namespace origin mc] $string]] - set len [string length $translated] - if {$len>$max} { - set max $len - } + set len [string length $translated] + if {$len>$max} { + set max $len + } } return $max } @@ -418,33 +503,79 @@ proc msgcat::ConvertLocale {value} { # Initialize the default locale proc msgcat::Init {} { + global env + # # set default locale, try to get from environment # foreach varName {LC_ALL LC_MESSAGES LANG} { - if {[info exists ::env($varName)] - && ![string equal "" $::env($varName)]} { - if {![catch {mclocale [ConvertLocale $::env($varName)]}]} { + if {[info exists env($varName)] && ("" ne $env($varName))} { + if {![catch { + mclocale [ConvertLocale $env($varName)] + }]} { return } } } # - # The rest of this routine is special processing for Windows; - # all other platforms, get out now. + # On Darwin, fallback to current CFLocale identifier if available. + # + if {[info exists ::tcl::mac::locale] && $::tcl::mac::locale ne ""} { + if {![catch { + mclocale [ConvertLocale $::tcl::mac::locale] + }]} { + return + } + } # - if { ![string equal $::tcl_platform(platform) windows] } { + # The rest of this routine is special processing for Windows or + # Cygwin. All other platforms, get out now. + # + if {([info sharedlibextension] ne ".dll") + || [catch {package require registry}]} { mclocale C return } # - # On Windows, try to set locale depending on registry settings, - # or fall back on locale of "C". + # On Windows or Cygwin, try to set locale depending on registry + # settings, or fall back on locale of "C". + # + + # On Vista and later: + # HCU/Control Panel/Desktop : PreferredUILanguages is for language packs, + # HCU/Control Pannel/International : localName is the default locale. + # + # They contain the local string as RFC5646, composed of: + # [a-z]{2,3} : language + # -[a-z]{4} : script (optional, translated by table Latn->latin) + # -[a-z]{2}|[0-9]{3} : territory (optional, numerical region codes not used) + # (-.*)* : variant, extension, private use (optional, not used) + # Those are translated to local strings. + # Examples: de-CH -> de_ch, sr-Latn-CS -> sr_cs@latin, es-419 -> es # - set key {HKEY_CURRENT_USER\Control Panel\International} - if {[catch {package require registry}] \ - || [catch {registry get $key "locale"} locale]} { - mclocale C + foreach key {{HKEY_CURRENT_USER\Control Panel\Desktop} {HKEY_CURRENT_USER\Control Panel\International}}\ + value {PreferredUILanguages localeName} { + if {![catch {registry get $key $value} localeName] + && [regexp {^([a-z]{2,3})(?:-([a-z]{4}))?(?:-([a-z]{2}))?(?:-.+)?$}\ + [string tolower $localeName] match locale script territory]} { + if {"" ne $territory} { + append locale _ $territory + } + set modifierDict [dict create latn latin cyrl cyrillic] + if {[dict exists $modifierDict $script]} { + append locale @ [dict get $modifierDict $script] + } + if {![catch {mclocale [ConvertLocale $locale]}]} { + return + } + } + } + + # then check value locale which contains a numerical language ID + if {[catch { + set locale [registry get $key "locale"] + }]} { + mclocale C return } # @@ -459,7 +590,9 @@ proc msgcat::Init {} { variable WinRegToISO639 set locale [string tolower $locale] while {[string length $locale]} { - if {![catch {mclocale [ConvertLocale $WinRegToISO639($locale)]}]} { + if {![catch { + mclocale [ConvertLocale [dict get $WinRegToISO639 $locale]] + }]} { return } set locale [string range $locale 1 end] |