summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/msgcat.n42
-rw-r--r--library/msgcat/msgcat.tcl32
-rw-r--r--library/msgcat1.0/msgcat.tcl32
-rw-r--r--tests/msgcat.test102
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