msgcat.tcl
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:13k
源码类别:

通讯编程

开发平台:

Visual C++

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