From 6778b65f1b617527d2402c8b78b3419fb2a7c502 Mon Sep 17 00:00:00 2001 From: ericm Date: Tue, 11 Apr 2000 21:16:17 +0000 Subject: * msgcat.n: Added docs for new behavior from patch in [Bug: 4158]. * msgcat.test: Added tests for new behavior from patch in [Bug: 4158]. * msgcat.tcl: Applied patch from [Bug: 4158], which enables msgcat::mc to search the entire namespace ancestry chain for message translations (ie, first it checks the current namespace, then the parent, then the parent's parent, etc). Also allows the specification of additional args for msgcat::mc; if extra args are given, the [format] command is used to substitute the additional args in the translated message. --- doc/msgcat.n | 42 ++++++++++++++++-- library/msgcat/msgcat.tcl | 32 +++++++++++--- library/msgcat1.0/msgcat.tcl | 32 +++++++++++--- 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 -- cgit v0.12