You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
458 lines
12 KiB
Tcl
458 lines
12 KiB
Tcl
15 years ago
|
# msgcat.tcl --
|
||
|
#
|
||
|
# This file defines various procedures which implement a
|
||
|
# message catalog facility for Tcl programs. It should be
|
||
|
# loaded with the command "package require msgcat".
|
||
|
#
|
||
|
# Copyright (c) 1998-2000 by Ajuba Solutions.
|
||
|
# Copyright (c) 1998 by Mark Harrison.
|
||
|
#
|
||
|
# 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.3 2003/01/21 19:40:09 hunt Exp $
|
||
|
|
||
|
package require Tcl 8.2
|
||
|
# 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
|
||
|
|
||
|
namespace eval msgcat {
|
||
|
namespace export mc mcload mclocale mcmax mcmset mcpreferences mcset \
|
||
|
mcunknown
|
||
|
|
||
|
# Records the current locale as passed to mclocale
|
||
|
variable Locale ""
|
||
|
|
||
|
# Records the list of locales to search
|
||
|
variable Loclist {}
|
||
|
|
||
|
# Records the mapping between source strings and translated strings. The
|
||
|
# array key is of the form "<locale>,<namespace>,<src>" and the value is
|
||
|
# the translated string.
|
||
|
array set Msgs {}
|
||
|
|
||
|
# 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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# msgcat::mc --
|
||
|
#
|
||
|
# Find the translation for the given string based on the current
|
||
|
# locale setting. Check the local namespace first, then look in each
|
||
|
# parent namespace until the source is found. If additional args are
|
||
|
# specified, use the format command to work them into the traslated
|
||
|
# string.
|
||
|
#
|
||
|
# Arguments:
|
||
|
# src The string to translate.
|
||
|
# args Args to pass to the format command
|
||
|
#
|
||
|
# Results:
|
||
|
# Returns the translatd string. Propagates errors thrown by the
|
||
|
# format command.
|
||
|
|
||
|
proc msgcat::mc {src args} {
|
||
|
# Check for the src in each namespace starting from the local and
|
||
|
# ending in the global.
|
||
|
|
||
|
variable Msgs
|
||
|
variable Loclist
|
||
|
variable Locale
|
||
|
|
||
|
set ns [uplevel 1 [list ::namespace current]]
|
||
|
|
||
|
while {$ns != ""} {
|
||
|
foreach loc $Loclist {
|
||
|
if {[info exists Msgs($loc,$ns,$src)]} {
|
||
|
if {[llength $args] == 0} {
|
||
|
return $Msgs($loc,$ns,$src)
|
||
|
} else {
|
||
|
return [uplevel 1 \
|
||
|
[linsert $args 0 ::format $Msgs($loc,$ns,$src)]]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
set ns [namespace parent $ns]
|
||
|
}
|
||
|
# we have not found the translation
|
||
|
return [uplevel 1 \
|
||
|
[linsert $args 0 [::namespace origin mcunknown] $Locale $src]]
|
||
|
}
|
||
|
|
||
|
# msgcat::mclocale --
|
||
|
#
|
||
|
# Query or set the current locale.
|
||
|
#
|
||
|
# Arguments:
|
||
|
# newLocale (Optional) The new locale string. Locale strings
|
||
|
# should be composed of one or more sublocale parts
|
||
|
# separated by underscores (e.g. en_US).
|
||
|
#
|
||
|
# Results:
|
||
|
# Returns the current locale.
|
||
|
|
||
|
proc msgcat::mclocale {args} {
|
||
|
variable Loclist
|
||
|
variable Locale
|
||
|
set len [llength $args]
|
||
|
|
||
|
if {$len > 1} {
|
||
|
error {wrong # args: should be "mclocale ?newLocale?"}
|
||
|
}
|
||
|
|
||
|
if {$len == 1} {
|
||
|
set Locale [string tolower [lindex $args 0]]
|
||
|
set Loclist {}
|
||
|
set word ""
|
||
|
foreach part [split $Locale _] {
|
||
|
set word [string trimleft "${word}_${part}" _]
|
||
|
set Loclist [linsert $Loclist 0 $word]
|
||
|
}
|
||
|
}
|
||
|
return $Locale
|
||
|
}
|
||
|
|
||
|
# msgcat::mcpreferences --
|
||
|
#
|
||
|
# Fetch the list of locales used to look up strings, ordered from
|
||
|
# most preferred to least preferred.
|
||
|
#
|
||
|
# Arguments:
|
||
|
# None.
|
||
|
#
|
||
|
# Results:
|
||
|
# Returns an ordered list of the locales preferred by the user.
|
||
|
|
||
|
proc msgcat::mcpreferences {} {
|
||
|
variable Loclist
|
||
|
return $Loclist
|
||
|
}
|
||
|
|
||
|
# msgcat::mcload --
|
||
|
#
|
||
|
# Attempt to load message catalogs for each locale in the
|
||
|
# preference list from the specified directory.
|
||
|
#
|
||
|
# Arguments:
|
||
|
# langdir The directory to search.
|
||
|
#
|
||
|
# Results:
|
||
|
# Returns the number of message catalogs that were loaded.
|
||
|
|
||
|
proc msgcat::mcload {langdir} {
|
||
|
set x 0
|
||
|
foreach p [mcpreferences] {
|
||
|
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
|
||
|
}
|
||
|
}
|
||
|
return $x
|
||
|
}
|
||
|
|
||
|
# msgcat::mcset --
|
||
|
#
|
||
|
# Set the translation for a given string in a specified locale.
|
||
|
#
|
||
|
# Arguments:
|
||
|
# locale The locale to use.
|
||
|
# src The source string.
|
||
|
# dest (Optional) The translated string. If omitted,
|
||
|
# the source string is used.
|
||
|
#
|
||
|
# Results:
|
||
|
# Returns the new locale.
|
||
|
|
||
|
proc msgcat::mcset {locale src {dest ""}} {
|
||
|
variable Msgs
|
||
|
if {[string equal $dest ""]} {
|
||
|
set dest $src
|
||
|
}
|
||
|
|
||
|
set ns [uplevel 1 [list ::namespace current]]
|
||
|
|
||
|
set Msgs([string tolower $locale],$ns,$src) $dest
|
||
|
return $dest
|
||
|
}
|
||
|
|
||
|
# msgcat::mcmset --
|
||
|
#
|
||
|
# Set the translation for multiple strings in a specified locale.
|
||
|
#
|
||
|
# Arguments:
|
||
|
# locale The locale to use.
|
||
|
# pairs One or more src/dest pairs (must be even length)
|
||
|
#
|
||
|
# Results:
|
||
|
# Returns the number of pairs processed
|
||
|
|
||
|
proc msgcat::mcmset {locale pairs } {
|
||
|
variable Msgs
|
||
|
|
||
|
set length [llength $pairs]
|
||
|
if {$length % 2} {
|
||
|
error {bad translation list: should be "mcmset 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
|
||
|
}
|
||
|
|
||
|
return $length
|
||
|
}
|
||
|
|
||
|
# msgcat::mcunknown --
|
||
|
#
|
||
|
# 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.
|
||
|
# If additional args are specified, the format command will be used
|
||
|
# to work them into the traslated string.
|
||
|
#
|
||
|
# Arguments:
|
||
|
# locale The current locale.
|
||
|
# src The string to be translated.
|
||
|
# args Args to pass to the format command
|
||
|
#
|
||
|
# Results:
|
||
|
# Returns the translated value.
|
||
|
|
||
|
proc msgcat::mcunknown {locale src args} {
|
||
|
if {[llength $args]} {
|
||
|
return [uplevel 1 [linsert $args 0 ::format $src]]
|
||
|
} else {
|
||
|
return $src
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# msgcat::mcmax --
|
||
|
#
|
||
|
# Calculates the maximun length of the translated strings of the given
|
||
|
# list.
|
||
|
#
|
||
|
# Arguments:
|
||
|
# args strings to translate.
|
||
|
#
|
||
|
# Results:
|
||
|
# Returns the length of the longest translated string.
|
||
|
|
||
|
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
|
||
|
}
|
||
|
}
|
||
|
return $max
|
||
|
}
|
||
|
|
||
|
# Convert the locale values stored in environment variables to a form
|
||
|
# suitable for passing to [mclocale]
|
||
|
proc msgcat::ConvertLocale {value} {
|
||
|
# Assume $value is of form: $language[_$territory][.$codeset][@modifier]
|
||
|
# Convert to form: $language[_$territory][_$modifier]
|
||
|
#
|
||
|
# Comment out expanded RE version -- bugs alleged
|
||
|
# regexp -expanded {
|
||
|
# ^ # Match all the way to the beginning
|
||
|
# ([^_.@]*) # Match "lanugage"; ends with _, ., or @
|
||
|
# (_([^.@]*))? # Match (optional) "territory"; starts with _
|
||
|
# ([.]([^@]*))? # Match (optional) "codeset"; starts with .
|
||
|
# (@(.*))? # Match (optional) "modifier"; starts with @
|
||
|
# $ # Match all the way to the end
|
||
|
# } $value -> language _ territory _ codeset _ modifier
|
||
|
regexp {^([^_.@]*)(_([^.@]*))?([.]([^@]*))?(@(.*))?$} $value \
|
||
|
-> language _ territory _ codeset _ modifier
|
||
|
set ret $language
|
||
|
if {[string length $territory]} {
|
||
|
append ret _$territory
|
||
|
}
|
||
|
if {[string length $modifier]} {
|
||
|
append ret _$modifier
|
||
|
}
|
||
|
return $ret
|
||
|
}
|
||
|
|
||
|
# Initialize the default locale
|
||
|
proc msgcat::Init {} {
|
||
|
#
|
||
|
# set default locale, try to get from environment
|
||
|
#
|
||
|
foreach varName {LC_ALL LC_MESSAGES LANG} {
|
||
|
if {[info exists ::env($varName)]
|
||
|
&& ![string equal "" $::env($varName)]} {
|
||
|
mclocale [ConvertLocale $::env($varName)]
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
#
|
||
|
# On Windows, try to set locale depending on registry settings,
|
||
|
# or fall back on locale of "C". Other platforms will return
|
||
|
# when they fail to load the registry package.
|
||
|
#
|
||
|
set key {HKEY_CURRENT_USER\Control Panel\International}
|
||
|
if {[catch {package require registry}] \
|
||
|
|| [catch {registry get $key "locale"} locale]} {
|
||
|
mclocale C
|
||
|
return
|
||
|
}
|
||
|
#
|
||
|
# Keep trying to match against smaller and smaller suffixes
|
||
|
# of the registry value, since the latter hexadigits appear
|
||
|
# to determine general language and earlier hexadigits determine
|
||
|
# more precise information, such as territory. For example,
|
||
|
# 0409 - English - United States
|
||
|
# 0809 - English - United Kingdom
|
||
|
# Add more translations to the WinRegToISO639 array above.
|
||
|
#
|
||
|
variable WinRegToISO639
|
||
|
set locale [string tolower $locale]
|
||
|
while {[string length $locale]} {
|
||
|
if {![catch {mclocale [ConvertLocale $WinRegToISO639($locale)]}]} {
|
||
|
return
|
||
|
}
|
||
|
set locale [string range $locale 1 end]
|
||
|
}
|
||
|
#
|
||
|
# No translation known. Fall back on "C" locale
|
||
|
#
|
||
|
mclocale C
|
||
|
}
|
||
|
msgcat::Init
|