diff options
-rw-r--r-- | doc/msgcat.n | 42 | ||||
-rw-r--r-- | library/msgcat/msgcat.tcl | 32 | ||||
-rw-r--r-- | library/msgcat1.0/msgcat.tcl | 32 | ||||
-rw-r--r-- | tests/msgcat.test | 102 |
4 files changed, 187 insertions, 21 deletions
diff --git a/doc/msgcat.n b/doc/msgcat.n index 748d023..37c2a76 100644 --- a/doc/msgcat.n +++ b/doc/msgcat.n @@ -43,10 +43,17 @@ wishes to be enabled for multi-lingual applications. .SH COMMANDS .TP -\fB::msgcat::mc \fIsrc-string\fR +\fB::msgcat::mc \fIsrc-string\fR ?\fIarg arg ...\fR? Returns a translation of \fIsrc-string\fR according to the -user's current locale. If no translation string -exists, \fB::msgcat::mcunknown\fR is called and the string +user's current locale. If additional arguments past \fIsrc-string\fR +are given, the \fBformat\fR command is used to substitute the +additional arguments in the translation of \fIsrc-string\fR. + +\fB::msgcat::mc\fR will search the messages defined +in the current namespace for a translation of \fIsrc-string\fR; if +none is found, it will search in the parent of the current namespace, +and so on until it reaches the global namespace. If no translation +string exists, \fB::msgcat::mcunknown\fR is called and the string returned from \fB::msgcat::mcunknown\fR is returned. .PP \fB::msgcat::mc\fR is the main function used to localize an @@ -137,6 +144,35 @@ will print hello from :: hello from ::foo .CE +.PP +When searching for a translation of a message, the +message catalog will search first the current namespace, +then the parent of the current namespace, and so on until +the global namespace is reached. This allows child namespaces +to "inherit" messages from their parent namespace. +.PP +For example, executing the code +.CS +mcset en m1 ":: message1" +mcset en m2 ":: message2" +mcset en m3 ":: message3" +namespace eval ::foo { + mcset en m2 "::foo message2" + mcset en m3 "::foo message3" +} +namespace eval ::foo::bar { + mcset en m3 "::foo::bar message3" +} +puts "[mc m1]; [mc m2]; [mc m3]" +namespace eval ::foo {puts "[mc m1]; [mc m2]; [mc m3]"} +namespace eval ::foo::bar {puts "[mc m1]; [mc m2]; [mc m3]"} +.CE +will print +.CS +:: message1; :: message2; :: message3 +:: message1; ::foo message2; ::foo message3 +:: message1; ::foo message2; ::foo::bar message3 +.CE .SH "LOCATION AND FORMAT OF MESSAGE FILES" .PP diff --git a/library/msgcat/msgcat.tcl b/library/msgcat/msgcat.tcl index 7eb1f90..4619ae1 100644 --- a/library/msgcat/msgcat.tcl +++ b/library/msgcat/msgcat.tcl @@ -10,7 +10,7 @@ # 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 1999/08/19 02:59:49 hobbs Exp $ +# RCS: @(#) $Id: msgcat.tcl,v 1.4 2000/04/11 21:16:18 ericm Exp $ package provide msgcat 1.0 @@ -32,20 +32,38 @@ namespace eval msgcat { # msgcat::mc -- # # Find the translation for the given string based on the current -# locale setting. +# 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. +# 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. -proc msgcat::mc {src} { set ns [uplevel {namespace current}] - foreach loc $::msgcat::loclist { - if {[info exists ::msgcat::msgs($loc,$ns,$src)]} { - return $::msgcat::msgs($loc,$ns,$src) + + while {$ns != ""} { + foreach loc $::msgcat::loclist { + if {[info exists ::msgcat::msgs($loc,$ns,$src)]} { + if {[llength $args] == 0} { + return $::msgcat::msgs($loc,$ns,$src) + } else { + return [eval \ + [list format $::msgcat::msgs($loc,$ns,$src)] \ + $args] + } + } } + set ns [namespace parent $ns] } # we have not found the translation return [uplevel 1 [list [namespace origin mcunknown] \ diff --git a/library/msgcat1.0/msgcat.tcl b/library/msgcat1.0/msgcat.tcl index 7eb1f90..4619ae1 100644 --- a/library/msgcat1.0/msgcat.tcl +++ b/library/msgcat1.0/msgcat.tcl @@ -10,7 +10,7 @@ # 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 1999/08/19 02:59:49 hobbs Exp $ +# RCS: @(#) $Id: msgcat.tcl,v 1.4 2000/04/11 21:16:18 ericm Exp $ package provide msgcat 1.0 @@ -32,20 +32,38 @@ namespace eval msgcat { # msgcat::mc -- # # Find the translation for the given string based on the current -# locale setting. +# 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. +# 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. -proc msgcat::mc {src} { set ns [uplevel {namespace current}] - foreach loc $::msgcat::loclist { - if {[info exists ::msgcat::msgs($loc,$ns,$src)]} { - return $::msgcat::msgs($loc,$ns,$src) + + while {$ns != ""} { + foreach loc $::msgcat::loclist { + if {[info exists ::msgcat::msgs($loc,$ns,$src)]} { + if {[llength $args] == 0} { + return $::msgcat::msgs($loc,$ns,$src) + } else { + return [eval \ + [list format $::msgcat::msgs($loc,$ns,$src)] \ + $args] + } + } } + set ns [namespace parent $ns] } # we have not found the translation return [uplevel 1 [list [namespace origin mcunknown] \ diff --git a/tests/msgcat.test b/tests/msgcat.test index c9c3eca..04e570b 100644 --- a/tests/msgcat.test +++ b/tests/msgcat.test @@ -12,7 +12,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: msgcat.test,v 1.7 2000/04/10 17:19:02 ericm Exp $ +# RCS: @(#) $Id: msgcat.test,v 1.8 2000/04/11 21:16:18 ericm Exp $ if {[lsearch [namespace children] ::tcltest] == -1} { package require tcltest @@ -302,14 +302,108 @@ test msgcat-5.9 {::msgcat::mcload} { set result } {unknown:no_fi_notexist:abc} -# cleanup +# cleanup temp files foreach l $locales { file delete [string tolower [file join msgdir $l.msg]] } - # Clean out the msg catalogs -::msgcat::mclocale $oldlocale file delete msgdir +# +# Test mcset and mc, ensuring that resolution for messages +# proceeds from the current ns to its parent and so on to the +# global ns. +# +# Do this for the 12 permutations of +# locales: foo +# namespaces: ::foo ::foo::bar ::foo::bar::baz +# strings: {ov1 ov2 ov3 ov4} +# namespace ::foo defines ov1, ov2, ov3 +# namespace ::foo::bar defines ov2, ov3 +# namespace ::foo::bar::baz defines ov3 +# +# ov4 is not defined in any namespace. +# +# So, +# ov3 should be resolved in ::foo::bar::baz, ::foo::bar, ::foo; +# ov2 should be resolved in ::foo, ::foo::bar +# ov1 should be resolved in ::foo +# ov4 should be resolved in none, and call mcunknown +# + +namespace eval ::foo { + ::msgcat::mcset foo ov1 "ov1_foo" + ::msgcat::mcset foo ov2 "ov2_foo" + ::msgcat::mcset foo ov3 "ov3_foo" +} +namespace eval ::foo::bar { + ::msgcat::mcset foo ov2 "ov2_foo_bar" + ::msgcat::mcset foo ov3 "ov3_foo_bar" +} +namespace eval ::foo::bar::baz { + ::msgcat::mcset foo ov3 "ov3_foo_bar_baz" +} +::msgcat::mclocale foo + +# namespace ::foo +test msgcat-6.1 {::msgcat::mc, namespace resolution} { + namespace eval ::foo {::msgcat::mc ov1} +} {ov1_foo} +test msgcat-6.2 {::msgcat::mc, namespace resolution} { + namespace eval ::foo {::msgcat::mc ov2} +} {ov2_foo} +test msgcat-6.3 {::msgcat::mc, namespace resolution} { + namespace eval ::foo {::msgcat::mc ov3} +} {ov3_foo} +test msgcat-6.4 {::msgcat::mc, namespace resolution} { + namespace eval ::foo {::msgcat::mc ov4} +} {ov4} +# namespace ::foo::bar +test msgcat-6.5 {::msgcat::mc, namespace resolution} { + namespace eval ::foo::bar {::msgcat::mc ov1} +} {ov1_foo} +test msgcat-6.6 {::msgcat::mc, namespace resolution} { + namespace eval ::foo::bar {::msgcat::mc ov2} +} {ov2_foo_bar} +test msgcat-6.7 {::msgcat::mc, namespace resolution} { + namespace eval ::foo::bar {::msgcat::mc ov3} +} {ov3_foo_bar} +test msgcat-6.8 {::msgcat::mc, namespace resolution} { + namespace eval ::foo::bar {::msgcat::mc ov4} +} {ov4} +# namespace ::foo +test msgcat-6.9 {::msgcat::mc, namespace resolution} { + namespace eval ::foo::bar::baz {::msgcat::mc ov1} +} {ov1_foo} +test msgcat-6.10 {::msgcat::mc, namespace resolution} { + namespace eval ::foo::bar::baz {::msgcat::mc ov2} +} {ov2_foo_bar} +test msgcat-6.11 {::msgcat::mc, namespace resolution} { + namespace eval ::foo::bar::baz {::msgcat::mc ov3} +} {ov3_foo_bar_baz} +test msgcat-6.12 {::msgcat::mc, namespace resolution} { + namespace eval ::foo::bar::baz {::msgcat::mc ov4} +} {ov4} + +namespace delete ::foo::bar::baz ::foo::bar ::foo + +::msgcat::mclocale foo +::msgcat::mcset foo format1 "this is a test" +::msgcat::mcset foo format2 "this is a %s" +::msgcat::mcset foo format3 "this is a %s %s" + +test msgcat-7.1 {::msgcat::mc, extra args go through to format} { + ::msgcat::mc format1 "good test" +} "this is a test" +test msgcat-7.2 {::msgcat::mc, extra args go through to format} { + ::msgcat::mc format2 "good test" +} "this is a good test" +test msgcat-7.3 {::msgcat::mc, errors from format are propagated} { + catch {::msgcat::mc format3 "good test"} +} 1 + +# Reset the locale +::msgcat::mclocale $oldlocale + ::tcltest::cleanupTests return |