Tcl Source Code

Artifact [effe8bc5f8]
Login

Artifact effe8bc5f85d18b0b023e732b28fb1eaabd859de:

Attachment "msgcat.patch" to ticket [525525ffff] added by dgp 2002-06-17 12:26:58.
Index: doc/msgcat.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/msgcat.n,v
retrieving revision 1.12
diff -u -r1.12 msgcat.n
--- doc/msgcat.n	19 Apr 2002 23:09:37 -0000	1.12
+++ doc/msgcat.n	17 Jun 2002 05:22:13 -0000
@@ -15,7 +15,7 @@
 .SH SYNOPSIS
 \fBpackage require Tcl 8.2\fR
 .sp
-\fBpackage require msgcat 1.2\fR
+\fBpackage require msgcat 1.3\fR
 .sp
 \fB::msgcat::mc \fIsrc-string\fR ?\fIarg arg ...\fR?
 .sp
@@ -80,37 +80,46 @@
 \fB::msgcat::mclocale \fR?\fInewLocale\fR?  
 This function sets the locale to \fInewLocale\fR.  If \fInewLocale\fR
 is omitted, the current locale is returned, otherwise the current locale
-is set to \fInewLocale\fR.
-The initial locale defaults to the locale specified in
-the user's environment.  See \fBLOCALE AND SUBLOCALE SPECIFICATION\fR
+is set to \fInewLocale\fR.  msgcat stores and compares the locale in a
+case-insensitive manner, and returns locales in lowercase.
+The initial locale is determined by the locale specified in
+the user's environment.  See \fBLOCALE SPECIFICATION\fR
 below for a description of the locale string format.
 .TP
 \fB::msgcat::mcpreferences\fR
 Returns an ordered list of the locales preferred by
 the user, based on the user's language specification.
 The list is ordered from most specific to least
-preference.  If the user has specified LANG=en_US_funky,
-this procedure would return {en_US_funky en_US en}.
+preference.  The list is derived from the current
+locale set in msgcat by \fBmsgcat::mclocale\fR, and
+cannot be set independently.  For example, if the
+current locale is en_US_funky, then \fBmsgcat::mcpreferences\fR
+returns {en_US_funky en_US en}.
 .TP
 \fB::msgcat::mcload \fIdirname\fR
 Searches the specified directory for files that match
-the language specifications returned by \fB::msgcat::mcpreferences\fR.
-Each file located is sourced.  The file extension is ``.msg''.
-The number of message files which matched the specification
+the language specifications returned by \fB::msgcat::mcpreferences\fR,
+extended by the file extension ``.msg''.  Each matching file is 
+read in order, assuming a UTF-8 encoding.  The file contents are
+then evaluated as a Tcl script.  This means that non-Latin characters
+may be present in the message file either directly in their UTF-8
+encoded form, or by use of the backslash-u quoting recognized by Tcl
+evaluation.  The number of message files which matched the specification
 and were loaded is returned.
 .TP
 \fB::msgcat::mcset \fIlocale src-string \fR?\fItranslate-string\fR?
 Sets the translation for \fIsrc-string\fR to \fItranslate-string\fR
-in the specified \fIlocale\fR.  If \fItranslate-string\fR is not
-specified, \fIsrc-string\fR is used for both.  The function
-returns \fItranslate-string\fR.
+in the specified \fIlocale\fR and the current namespace.  If
+\fItranslate-string\fR is not specified, \fIsrc-string\fR is used
+for both.  The function returns \fItranslate-string\fR.
 .TP
 \fB::msgcat::mcmset \fIlocale src-trans-list\fR
 Sets the translation for multiple source strings in
-\fIsrc-trans-list\fR in the specified \fIlocale\fR.
+\fIsrc-trans-list\fR in the specified \fIlocale\fR and the current
+namespace.
 \fIsrc-trans-list\fR must have an even number of elements and is in
 the form {\fIsrc-string translate-string\fR ?\fIsrc-string
-translate-string ...\fR?} \fBmcsetcat::mcmset\fR can be significantly
+translate-string ...\fR?} \fBmsgcat::mcmset\fR can be significantly
 faster than multiple invocations of \fBmsgcat::mcset\fR. The function
 returns the number of translations set.
 .TP
@@ -125,22 +134,35 @@
 of \fB::msgcat::mcunknown\fR is used as the return vaue for the call
 to \fB::msgcat::mc\fR.  
 
-.SH "LOCALE AND SUBLOCALE SPECIFICATION"
+.SH "LOCALE SPECIFICATION"
 .PP
-The locale is specified by a locale string.
+The locale is specified to \fBmsgcat\fR by a locale string
+passed to \fB::msgcat::mclocale\fR.
 The locale string consists of
 a language code, an optional country code, and an optional
 system-specific code, each separated by ``_''.  The country and language
 codes are specified in standards ISO-639 and ISO-3166.
-For example, the locale ``en'' specifies English and
- ``en_US'' specifes  U.S. English.
+For example, the locale ``en'' specifies English and ``en_US'' specifies
+U.S. English.
 .PP
-The locale defaults to the value in \fBenv(LANG)\fR at the time the
-\fBmsgcat\fR package is loaded.  On Windows, if \fBenv(LANG)\fR is not
-set, the package will attempt to extract locale information from the
-registry.  If it cannot find this information in the registry, or on
-non-Windows platforms when \fBenv(LANG)\fR is not defined, the
-locale defaults to ``C''.
+When the msgcat package is first loaded, the locale is initialized
+according to the user's environment.  The variables \fBenv(LC_ALL)\fR,
+\fBenv(LC_MESSAGES)\fR, and \fBenv(LANG)\fR are examined in order.
+The first of them to have a non-empty value is used to determine the
+initial locale.  The value is parsed according to the XPG4 pattern
+.CS
+language[_country][.codeset][@modifier]
+.CE
+to extract its parts.  The initial locale is then set by calling
+\fBmsgcat::mclocale\fR with the argument 
+.CS
+language[_country][_modifier]
+.CE
+On Windows, if none of those environment variables is set, msgcat will
+attempt to extract locale information from the
+registry.  If all these attempts to discover an initial locale
+from the user's environment fail, msgcat defaults to an initial
+locale of ``C''.
 .PP
 When a locale is specified by the user, a ``best match'' search is
 performed during string translation.  For example, if a user specifies
@@ -177,7 +199,7 @@
 the global namespace is reached.  This allows child namespaces
 to "inherit" messages from their parent namespace.
 .PP
-For example, executing the code
+For example, executing (in the ``en'' locale) the code 
 .CS
 mcset en m1 ":: message1"
 mcset en m2 ":: message2"
@@ -214,10 +236,15 @@
 en_UK.msg -- UK English
 .CE
 .IP [3]
-The file contains a series of calls to mcset, setting the
-necessary translation strings for the language. For example:
+The file contains a series of calls to \fBmcset\fR and
+\fBmcmset\fR, setting the necessary translation strings
+for the language, likely enclosed in a \fBnamespace eval\fR
+so that all source strings are tied to the namespace of
+the package. For example, a short \fBes.msg\fR might contain:
 .CS
-::msgcat::mcset es "Free Beer!" "Cerveza Gracias!"
+namespace eval ::mypackage {
+    ::msgcat::mcset es "Free Beer!" "Cerveza Gracias!"
+}
 .CE
 
 .SH "RECOMMENDED MESSAGE SETUP FOR PACKAGES"
Index: library/msgcat/msgcat.tcl
===================================================================
RCS file: /cvsroot/tcl/tcl/library/msgcat/msgcat.tcl,v
retrieving revision 1.13
diff -u -r1.13 msgcat.tcl
--- library/msgcat/msgcat.tcl	20 Apr 2002 00:35:19 -0000	1.13
+++ library/msgcat/msgcat.tcl	17 Jun 2002 05:22:23 -0000
@@ -13,22 +13,38 @@
 # RCS: @(#) $Id: msgcat.tcl,v 1.13 2002/04/20 00:35:19 dgp Exp $
 
 package require Tcl 8.2
-package provide msgcat 1.2.3
+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 ""
+    variable Locale ""
 
     # Records the list of locales to search
-    variable loclist {}
+    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 {}
+    array set Msgs {}
+
+    # Map of language codes used in Windows registry to those of ISO-639
+    array set WinRegToISO639 {
+	0409 en_US 0809 en_UK 43c gd 83c ga 01 ar 02 bg 03 ca 04 zh 05
+	cs 06 da 07 de 08 el 0a es 0b fi 0c fr 0d he 0e hu 0f is 10 it
+	11 ja 12 ko 13 da 14 no 15 pl 16 pt 17 rm 18 ro 19 ru 1a hr
+	1b sk 1c sq 1d sv 1e th 1f tr 20 ur 21 id 22 uk 23 be 24 sl
+	25 et 26 lv 27 lt 28 tg 29 fa 2a vi 2b hy 2c az 2d eu 2e wen
+	2f mk 30 bnt 31 ts 33 ven 34 xh 35 zu 36 af 37 ka 38 fo 39 hi
+	3a mt 3b se 3d yi 3e ms 3f kk 40 ky 41 sw 42 tk 43 uz 44 tt
+	45 bn 46 pa 47 gu 48 or 49 ta 4a te 4b kn 4c ml 4d as 4e mr
+	4f sa 50 mn 51 bo 52 cy 53 km 54 lo 55 my 56 gl 57 kok 58 mni
+	59 sd 5a syr 5b si 5c chr 5d iu 5e am 5f ber 60 ks 61 ne 62 fy
+	63 ps 64 tl 65 div 66 bin 67 ful 68 ha 69 nic 6a yo 70 ibo
+	71 kau 72 om 73 ti 74 gn 75 cpe 76 la 77 so 78 sit 79 pap
+    }
 }
 
 # msgcat::mc --
@@ -51,20 +67,20 @@
     # Check for the src in each namespace starting from the local and
     # ending in the global.
 
-    variable msgs
-    variable loclist
-    variable locale
+    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)]} {
+	foreach loc $Loclist {
+	    if {[info exists Msgs($loc,$ns,$src)]} {
 		if {[llength $args] == 0} {
-		    return $msgs($loc,$ns,$src)
+		    return $Msgs($loc,$ns,$src)
 		} else {
 		    return [uplevel 1 \
-			    [linsert $args 0 ::format $msgs($loc,$ns,$src)]]
+			    [linsert $args 0 ::format $Msgs($loc,$ns,$src)]]
 		}
 	    }
 	}
@@ -72,7 +88,7 @@
     }
     # we have not found the translation
     return [uplevel 1 \
-	    [linsert $args 0 [::namespace origin mcunknown] $locale $src]]
+	    [linsert $args 0 [::namespace origin mcunknown] $Locale $src]]
 }
 
 # msgcat::mclocale --
@@ -88,8 +104,8 @@
 #	Returns the current locale.
 
 proc msgcat::mclocale {args} {
-    variable loclist
-    variable locale
+    variable Loclist
+    variable Locale
     set len [llength $args]
 
     if {$len > 1} {
@@ -97,15 +113,15 @@
     }
 
     if {$len == 1} {
-	set locale [string tolower [lindex $args 0]]
-	set loclist {}
+	set Locale [string tolower [lindex $args 0]]
+	set Loclist {}
 	set word ""
-	foreach part [split $locale _] {
+	foreach part [split $Locale _] {
 	    set word [string trimleft "${word}_${part}" _]
-	    set loclist [linsert $loclist 0 $word]
+	    set Loclist [linsert $Loclist 0 $word]
 	}
     }
-    return $locale
+    return $Locale
 }
 
 # msgcat::mcpreferences --
@@ -120,8 +136,8 @@
 #	Returns an ordered list of the locales preferred by the user.
 
 proc msgcat::mcpreferences {} {
-    variable loclist
-    return $loclist
+    variable Loclist
+    return $Loclist
 }
 
 # msgcat::mcload --
@@ -164,14 +180,14 @@
 #	Returns the new locale.
 
 proc msgcat::mcset {locale src {dest ""}} {
-    variable msgs
+    variable Msgs
     if {[string equal $dest ""]} {
 	set dest $src
     }
 
     set ns [uplevel 1 [list ::namespace current]]
 
-    set msgs([string tolower $locale],$ns,$src) $dest
+    set Msgs([string tolower $locale],$ns,$src) $dest
     return $dest
 }
 
@@ -187,7 +203,7 @@
 #	Returns the number of pairs processed
 
 proc msgcat::mcmset {locale pairs } {
-    variable msgs
+    variable Msgs
 
     set length [llength $pairs]
     if {$length % 2} {
@@ -198,7 +214,7 @@
     set ns [uplevel 1 [list ::namespace current]]
     
     foreach {src dest} $pairs {
-        set msgs($locale,$ns,$src) $dest
+        set Msgs($locale,$ns,$src) $dest
     }
     
     return $length
@@ -252,61 +268,76 @@
     return $max
 }
 
-# Initialize the default locale
+# 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
+}
 
-namespace eval msgcat {
+# Initialize the default locale
+proc msgcat::Init {} {
+    #
     # set default locale, try to get from environment
-    if {[info exists ::env(LANG)]} {
-        mclocale $::env(LANG)
-    } else {
-        if { $tcl_platform(platform) == "windows" } {
-            # try to set locale depending on registry settings
-            #
-            set key {HKEY_CURRENT_USER\Control Panel\International}
-            if {[catch {package require registry}] || \
-		    [catch {registry get $key "locale"} locale]} {
-                mclocale "C"
-            } else {
-		
-                #
-                # Clean up registry value for translating LCID value
-                # by using only the last 2 digits, since first
-                # 2 digits appear to be the country...  For example
-                #     0409 - English - United States
-                #     0809 - English - United Kingdom
-                #
-                set locale [string trimleft $locale "0"]
-                set locale [string range $locale end-1 end]
-                set locale [string tolower $locale]
-                switch -- $locale {
-		    01      { mclocale "ar" }
-		    02      { mclocale "bg" }
-		    03      { mclocale "ca" }
-		    04      { mclocale "zh" }
-		    05      { mclocale "cs" }
-		    06      { mclocale "da" }
-		    07      { mclocale "de" }
-		    08      { mclocale "el" }
-		    09      { mclocale "en" }
-		    0a      { mclocale "es" }
-		    0b      { mclocale "fi" }
-		    0c      { mclocale "fr" }
-		    0d      { mclocale "he" }
-		    0e      { mclocale "hu" }
-		    0f      { mclocale "is" }
-		    10      { mclocale "it" }
-		    11      { mclocale "ja" }
-		    12      { mclocale "ko" }
-		    13      { mclocale "da" }
-		    14      { mclocale "no" }
-		    15      { mclocale "pl" }
-		    16      { mclocale "pt" }
-		    
-		    default  { mclocale "C" }
-		}
-            }
-        } else {
-            mclocale "C"
-        }
+    #
+    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 $WinRegToISO639($locale)}]} {
+	    return
+	}
+	set locale [string range $locale 1 end]
     }
+    #
+    # No translation known.  Fall back on "C" locale
+    #
+    mclocale C
 }
+msgcat::Init
Index: library/msgcat/pkgIndex.tcl
===================================================================
RCS file: /cvsroot/tcl/tcl/library/msgcat/pkgIndex.tcl,v
retrieving revision 1.6
diff -u -r1.6 pkgIndex.tcl
--- library/msgcat/pkgIndex.tcl	19 Apr 2002 23:09:37 -0000	1.6
+++ library/msgcat/pkgIndex.tcl	17 Jun 2002 05:22:24 -0000
@@ -1,2 +1,2 @@
 if {![package vsatisfies [package provide Tcl] 8.2]} {return}
-package ifneeded msgcat 1.2.3 [list source [file join $dir msgcat.tcl]]
+package ifneeded msgcat 1.3 [list source [file join $dir msgcat.tcl]]
Index: tests/msgcat.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/msgcat.test,v
retrieving revision 1.10
diff -u -r1.10 msgcat.test
--- tests/msgcat.test	17 Jul 2000 22:25:26 -0000	1.10
+++ tests/msgcat.test	17 Jun 2002 05:22:30 -0000
@@ -1,330 +1,457 @@
-# Commands covered: ::msgcat::mc ::msgcat::mclocale
-#                   ::msgcat::mcpreferences ::msgcat::mcload
-#                   ::msgcat::mcset ::msgcat::mcmset ::msgcat::mcunknown
-#
-# This file contains a collection of tests for the msgcat script library.
+# This file contains a collection of tests for the msgcat package.
 # Sourcing this file into Tcl runs the tests and
 # generates output for errors.  No output means no errors were found.
 #
 # Copyright (c) 1998 Mark Harrison.
 # Copyright (c) 1998-1999 by Scriptics Corporation.
+# Contributions from Don Porter, NIST, 2002.  (not subject to US copyright)
 #
 # See the file "license.terms" for information on usage and redistribution
 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 #
+# Note that after running these tests, entries will be left behind in the
+# message catalogs for locales foo, foo_BAR, and foo_BAR_baz.
+#
 # RCS: @(#) $Id: msgcat.test,v 1.10 2000/07/17 22:25:26 ericm Exp $
 
-if {[lsearch [namespace children] ::tcltest] == -1} {
-    package require tcltest
-    namespace import -force ::tcltest::*
+package require Tcl 8.2
+if {[catch {package require tcltest 2}]} {
+    puts stderr "Skipping tests in [info script].  tcltest 2 required."
+    return
 }
-
-if {[catch {package require msgcat 1.0}]} {
-    if {[info exist msgcat1]} {
-	catch {puts "Cannot load msgcat 1.0 package"}
-	return
-    } else {
-	catch {puts "Running msgcat 1.0 tests in slave interp"}
-	set interp [interp create msgcat1]
-	$interp eval [list set msgcat1 "running"]
-	$interp eval [list source [info script]]
-	interp delete $interp
-	return
-    }
+if {[catch {package require msgcat 1.3}]} {
+    puts stderr "Skipping tests in [info script].  No msgcat 1.3 found to test."
+    return
 }
 
-set oldlocale [::msgcat::mclocale]
-
-# some tests fail in tne environment variable LANG exists and is not C
-
-if {[info exists env(LANG)] && ($env(LANG) != "C")} {
-    set ::tcltest::testConstraints(LANGisC) 0
-} else {
-    set ::tcltest::testConstraints(LANGisC) 1
-}
-
-#
-# Test the various permutations of mclocale
-# and mcpreferences.
-#
-
-test msgcat-1.1 {::msgcat::mclocale default} {LANGisC} {
-    ::msgcat::mclocale
-} {c}
-test msgcat-1.2 {::msgcat::mcpreferences, single element} {LANGisC} {
-    ::msgcat::mcpreferences
-} {c}
-test msgcat-1.3 {::msgcat::mclocale, single element} {
-    ::msgcat::mclocale en
-} {en}
-test msgcat-1.4 {::msgcat::mclocale, single element} {
-    ::msgcat::mclocale
-} {en}
-test msgcat-1.5 {::msgcat::mcpreferences, single element} {
-    ::msgcat::mcpreferences
-} {en}
-test msgcat-1.6 {::msgcat::mclocale, two elements} {
-    ::msgcat::mclocale en_US
-} {en_us}
-test msgcat-1.7 {::msgcat::mclocale, two elements} {
-    ::msgcat::mclocale en_US
-    ::msgcat::mclocale
-} {en_us}
-test msgcat-1.8 {::msgcat::mcpreferences, two elements} {
-    ::msgcat::mcpreferences
-} {en_us en}
-test msgcat-1.9 {::msgcat::mclocale, three elements} {
-    ::msgcat::mclocale en_US_funky
-} {en_us_funky}
-test msgcat-1.10 {::msgcat::mclocale, three elements} {
-    ::msgcat::mclocale
-} {en_us_funky}
-test msgcat-1.11 {::msgcat::mcpreferences, three elements} {
-    ::msgcat::mcpreferences
-} {en_us_funky en_us en}
-
-#
-# Test mcset and mc, ensuring that namespace partitioning
-# is working.
-#
+namespace eval ::msgcat::test {
+    namespace import ::msgcat::*
+    namespace import ::tcltest::test
+    namespace import ::tcltest::cleanupTests
+    namespace import ::tcltest::temporaryDirectory
+    namespace import ::tcltest::make*
+    namespace import ::tcltest::remove*
+
+    # Tests msgcat-0.*: locale initialization
+
+    proc PowerSet {l} {
+	if {[llength $l] == 0} {return [list [list]]}
+	set element [lindex $l 0]
+	set rest [lrange $l 1 end]
+	set result [list]
+	foreach x [PowerSet $rest] {
+	    lappend result [linsert $x 0 $element]
+	    lappend result $x
+	}
+	return $result
+    }
 
-test msgcat-2.1 {::msgcat::mcset, global scope} {
-    ::msgcat::mcset  foo_BAR text1 text2
-} {text2}
-test msgcat-2.2 {::msgcat::mcset, global scope, default} {
-    ::msgcat::mcset  foo_BAR text3
-} {text3}
-test msgcat-2.2 {::msgcat::mcset, namespace overlap} {
-    namespace eval bar {::msgcat::mcset  foo_BAR con1 con1bar}
-    namespace eval baz {::msgcat::mcset  foo_BAR con1 con1baz}
-} {con1baz}
-test msgcat-2.3 {::msgcat::mcset, namespace overlap} {
-    ::msgcat::mclocale foo_BAR
-    namespace eval bar {::msgcat::mc con1}
-} {con1bar}
-test msgcat-2.4 {::msgcat::mcset, namespace overlap} {
-    ::msgcat::mclocale foo_BAR
-    namespace eval baz {::msgcat::mc con1}
-} {con1baz}
-test msgcat-2.5 {::msgcat::mcmset, global scope} {
-    ::msgcat::mcmset  foo_BAR {
-        src1 trans1
-        src2 trans2
+    variable envVars {LC_ALL LC_MESSAGES LANG}
+    variable count 0
+    variable body
+    variable result
+    variable setVars
+    foreach setVars [PowerSet $envVars] { 
+	set result [string tolower [lindex $setVars 0]]
+	if {[string length $result] == 0} {
+	    set result c
+	}
+	test msgcat-0.$count {
+	    locale initialization from environment variables
+	} -setup {
+	    variable var
+	    foreach var $envVars {
+		catch {variable $var $::env($var)}
+		catch {unset ::env($var)}
+	    }
+	    foreach var $setVars {
+		set ::env($var) $var
+	    }
+	    interp create [namespace current]::i
+	    i eval [list package ifneeded msgcat [package provide msgcat] \
+		    [package ifneeded msgcat [package provide msgcat]]]
+	    i eval package require msgcat
+	} -cleanup {
+	    interp delete [namespace current]::i
+	    foreach var $envVars {
+		catch {unset ::env($var)}
+		catch {set ::env($var) [set [namespace current]::$var]}
+	    }
+	} -body {i eval msgcat::mclocale} -result $result
+	incr count
     }
-    ::msgcat::mc src1
-} {trans1}
-test msgcat-2.6 {::msgcat::mcmset, namespace overlap} {
-    namespace eval bar {::msgcat::mcmset  foo_BAR {con2 con2bar}}
-    namespace eval baz {::msgcat::mcmset  foo_BAR {con2 con2baz}}
-} {2}
-test msgcat-2.7 {::msgcat::mcmset, namespace overlap} {
-    ::msgcat::mclocale foo_BAR
-    namespace eval baz {::msgcat::mc con2}
-} {con2baz}
+    catch {unset result}
+    
+    # Could add tests of initialization from Windows registry here.
+    # Use a fake registry package.
 
+    # Tests msgcat-1.*: [mclocale], [mcpreferences]
 
-#
-# Test mcset and mc, ensuring that more specific locales
-# (e.g. "en_UK") will search less specific locales
-# (e.g. "en") for translation strings.
-#
-# Do this for the 12 permutations of
-#     locales: {foo foo_BAR foo_BAR_baz}
-#     strings: {ov1 ov2 ov3 ov4}
-#     locale foo         defines ov1, ov2, ov3
-#     locale foo_BAR     defines      ov2, ov3
-#     locale foo_BAR_BAZ defines           ov3
-#     (ov4 is defined in none)
-# So,
-#     ov3 should be resolved in foo, foo_BAR, foo_BAR_baz
-#     ov2 should be resolved in foo, foo_BAR
-#     ov2 should resolve to foo_BAR in foo_BAR_baz
-#     ov1 should be resolved in foo
-#     ov1 should resolve to foo in foo_BAR, foo_BAR_baz
-#     ov4 should be resolved in none, and call mcunknown
-#
-
-test msgcat-3.1 {::msgcat::mcset, overlap} {
-    ::msgcat::mcset foo ov1 ov1_foo
-    ::msgcat::mcset foo ov2 ov2_foo
-    ::msgcat::mcset foo ov3 ov3_foo
-    ::msgcat::mcset foo_BAR ov2 ov2_foo_BAR
-    ::msgcat::mcset foo_BAR ov3 ov3_foo_BAR
-    ::msgcat::mcset foo_BAR_baz ov3 ov3_foo_BAR_baz
-} {ov3_foo_BAR_baz}
-# top level, locale foo
-test msgcat-3.2 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo
-    ::msgcat::mc ov1
-} {ov1_foo}
-test msgcat-3.3 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo
-    ::msgcat::mc ov2
-} {ov2_foo}
-test msgcat-3.4 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo
-    ::msgcat::mc ov3
-} {ov3_foo}
-test msgcat-3.5 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo
-    ::msgcat::mc ov4
-} {ov4}
-# second level, locale foo_BAR
-test msgcat-3.6 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo_BAR
-    ::msgcat::mc ov1
-} {ov1_foo}
-test msgcat-3.7 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo_BAR
-    ::msgcat::mc ov2
-} {ov2_foo_BAR}
-test msgcat-3.8 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo_BAR
-    ::msgcat::mc ov3
-} {ov3_foo_BAR}
-test msgcat-3.9 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo_BAR
-    ::msgcat::mc ov4
-} {ov4}
-# third level, locale foo_BAR_baz
-test msgcat-3.10 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo_BAR_baz
-    ::msgcat::mc ov1
-} {ov1_foo}
-test msgcat-3.11 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo_BAR_baz
-    ::msgcat::mc ov2
-} {ov2_foo_BAR}
-test msgcat-3.12 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo_BAR_baz
-    ::msgcat::mc ov3
-} {ov3_foo_BAR_baz}
-test msgcat-3.13 {::msgcat::mcset, overlap} {
-    ::msgcat::mclocale foo_BAR_baz
-    ::msgcat::mc ov4
-} {ov4}
+    test msgcat-1.3 {mclocale set, single element} -setup {
+	variable locale [mclocale]
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mclocale en
+    } -result en
+
+    test msgcat-1.4 {mclocale get, single element} -setup {
+	variable locale [mclocale]
+	mclocale en
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mclocale
+    } -result en
+
+    test msgcat-1.5 {mcpreferences, single element} -setup {
+	variable locale [mclocale]
+	mclocale en
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mcpreferences
+    } -result en
+
+    test msgcat-1.6 {mclocale set, two elements} -setup {
+	variable locale [mclocale]
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mclocale en_US
+    } -result en_us
+
+    test msgcat-1.7 {mclocale get, two elements} -setup {
+	variable locale [mclocale]
+	mclocale en_US
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mclocale
+    } -result en_us
+
+    test msgcat-1.8 {mcpreferences, two elements} -setup {
+	variable locale [mclocale]
+	mclocale en_US
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mcpreferences
+    } -result {en_us en}
+
+    test msgcat-1.9 {mclocale set, three elements} -setup {
+	variable locale [mclocale]
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mclocale en_US_funky
+    } -result en_us_funky
+
+    test msgcat-1.10 {mclocale get, three elements} -setup {
+	variable locale [mclocale]
+	mclocale en_US_funky
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mclocale
+    } -result en_us_funky
+
+    test msgcat-1.11 {mcpreferences, three elements} -setup {
+	variable locale [mclocale]
+	mclocale en_US_funky
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mcpreferences
+    } -result {en_us_funky en_us en}
+
+    # Tests msgcat-2.*: [mcset], [mcmset], namespace partitioning
+
+    test msgcat-2.1 {mcset, global scope} {
+	namespace eval :: ::msgcat::mcset  foo_BAR text1 text2
+    } {text2}
+
+    test msgcat-2.2 {mcset, global scope, default} {
+	namespace eval :: ::msgcat::mcset foo_BAR text3
+    } {text3}
+
+    test msgcat-2.2 {mcset, namespace overlap} {
+	namespace eval baz {::msgcat::mcset  foo_BAR con1 con1baz}
+    } {con1baz}
+
+    test msgcat-2.3 {mcset, namespace overlap} -setup {
+	namespace eval bar {::msgcat::mcset  foo_BAR con1 con1bar}
+	namespace eval baz {::msgcat::mcset  foo_BAR con1 con1baz}
+	variable locale [mclocale]
+	mclocale foo_BAR
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	namespace eval bar {::msgcat::mc con1}
+    } -result con1bar
+
+    test msgcat-2.4 {mcset, namespace overlap} -setup {
+	namespace eval bar {::msgcat::mcset  foo_BAR con1 con1bar}
+	namespace eval baz {::msgcat::mcset  foo_BAR con1 con1baz}
+	variable locale [mclocale]
+	mclocale foo_BAR
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	namespace eval baz {::msgcat::mc con1}
+    } -result con1baz
+
+    test msgcat-2.5 {mcmset, global scope} -setup {
+	namespace eval :: {
+	    ::msgcat::mcmset  foo_BAR {
+	        src1 trans1
+	        src2 trans2
+	    }
+	}
+	variable locale [mclocale]
+	mclocale foo_BAR
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	namespace eval :: {
+	    ::msgcat::mc src1
+	}
+    } -result trans1
+
+    test msgcat-2.6 {mcmset, namespace overlap} -setup {
+	namespace eval bar {::msgcat::mcmset  foo_BAR {con2 con2bar}}
+	namespace eval baz {::msgcat::mcmset  foo_BAR {con2 con2baz}}
+	variable locale [mclocale]
+	mclocale foo_BAR
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	namespace eval bar {::msgcat::mc con2}
+    } -result con2bar
+
+    test msgcat-2.7 {mcmset, namespace overlap} -setup {
+	namespace eval bar {::msgcat::mcmset  foo_BAR {con2 con2bar}}
+	namespace eval baz {::msgcat::mcmset  foo_BAR {con2 con2baz}}
+	variable locale [mclocale]
+	mclocale foo_BAR
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	namespace eval baz {::msgcat::mc con2}
+    } -result con2baz
+
+    # Tests msgcat-3.*: [mcset], [mc], catalog "inheritance"
+    #
+    # Test mcset and mc, ensuring that more specific locales
+    # (e.g. en_UK) will search less specific locales
+    # (e.g. en) for translation strings.
+    #
+    # Do this for the 12 permutations of
+    #     locales: {foo foo_BAR foo_BAR_baz}
+    #     strings: {ov1 ov2 ov3 ov4}
+    #     locale foo         defines ov1, ov2, ov3
+    #     locale foo_BAR     defines      ov2, ov3
+    #     locale foo_BAR_BAZ defines           ov3
+    #     (ov4 is defined in none)
+    # So,
+    #     ov3 should be resolved in foo, foo_BAR, foo_BAR_baz
+    #     ov2 should be resolved in foo, foo_BAR
+    #     ov2 should resolve to foo_BAR in foo_BAR_baz
+    #     ov1 should be resolved in foo
+    #     ov1 should resolve to foo in foo_BAR, foo_BAR_baz
+    #     ov4 should be resolved in none, and call mcunknown
+    #
+    variable count 2
+    variable result
+    array set result {
+	foo,ov1 ov1_foo foo,ov2 ov2_foo foo,ov3 ov3_foo foo,ov4 ov4
+	foo_BAR,ov1 ov1_foo foo_BAR,ov2 ov2_foo_BAR foo_BAR,ov3 ov3_foo_BAR
+	foo_BAR,ov4 ov4 foo_BAR_baz,ov1 ov1_foo foo_BAR_baz,ov2 ov2_foo_BAR
+	foo_BAR_baz,ov3 ov3_foo_BAR_baz foo_BAR_baz,ov4 ov4
+    }
+    variable loc
+    variable string
+    foreach loc {foo foo_BAR foo_BAR_baz} {
+	foreach string {ov1 ov2 ov3 ov4} {
+	    test msgcat-3.$count {mcset, overlap} -setup {
+		mcset foo ov1 ov1_foo
+		mcset foo ov2 ov2_foo
+		mcset foo ov3 ov3_foo
+		mcset foo_BAR ov2 ov2_foo_BAR
+		mcset foo_BAR ov3 ov3_foo_BAR
+		mcset foo_BAR_baz ov3 ov3_foo_BAR_baz
+		variable locale [mclocale]
+		mclocale $loc
+	    } -cleanup {
+		mclocale $locale
+	    } -body {
+		mc $string
+	    } -result $result($loc,$string)
+	    incr count
+	}
+    }
+    catch {unset result}
 
-#
-# Test mcunknown, first the default operation
-# and then with an overridden definition.
-#
+    # Tests msgcat-4.*: [mcunknown]
 
-test msgcat-4.1 {::msgcat::mcunknown, default} {
-    ::msgcat::mcset foo unk1 "unknown 1"
-} {unknown 1}
-test msgcat-4.2 {::msgcat::mcunknown, default} {
-    ::msgcat::mclocale foo
-    ::msgcat::mc unk1
-} {unknown 1}
-test msgcat-4.3 {::msgcat::mcunknown, default} {
-    ::msgcat::mclocale foo
-    ::msgcat::mc unk2
-} {unk2}
-test msgcat-4.4 {::msgcat::mcunknown, overridden} {
-    rename ::msgcat::mcunknown oldproc
-    proc ::msgcat::mcunknown {dom s} {
-        return "unknown:$dom:$s"
-    }
-    ::msgcat::mclocale foo
-    set result [::msgcat::mc unk1]
-    rename ::msgcat::mcunknown {}
-    rename oldproc ::msgcat::mcunknown
-    set result
-} {unknown 1}
-test msgcat-4.5 {::msgcat::mcunknown, overridden} {
-    rename ::msgcat::mcunknown oldproc
-    proc ::msgcat::mcunknown {dom s} {
-        return "unknown:$dom:$s"
+    test msgcat-4.2 {mcunknown, default} -setup {
+	mcset foo unk1 "unknown 1"
+	variable locale [mclocale]
+	mclocale foo
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mc unk1
+    } -result {unknown 1}
+
+    test msgcat-4.3 {mcunknown, default} -setup {
+	mcset foo unk1 "unknown 1"
+	variable locale [mclocale]
+	mclocale foo
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mc unk2
+    } -result unk2
+
+    test msgcat-4.4 {mcunknown, overridden} -setup {
+	rename ::msgcat::mcunknown SavedMcunknown
+	proc ::msgcat::mcunknown {dom s} {
+            return unknown:$dom:$s
+	}
+	mcset foo unk1 "unknown 1"
+	variable locale [mclocale]
+	mclocale foo
+    } -cleanup {
+	mclocale $locale
+	rename ::msgcat::mcunknown {}
+	rename SavedMcunknown ::msgcat::mcunknown
+    } -body {
+	mc unk1
+    } -result {unknown 1}
+
+    test msgcat-4.5 {mcunknown, overridden} -setup {
+	rename ::msgcat::mcunknown SavedMcunknown
+	proc ::msgcat::mcunknown {dom s} {
+            return unknown:$dom:$s
+	}
+	mcset foo unk1 "unknown 1"
+	variable locale [mclocale]
+	mclocale foo
+    } -cleanup {
+	mclocale $locale
+	rename ::msgcat::mcunknown {}
+	rename SavedMcunknown ::msgcat::mcunknown
+    } -body {
+	mc unk2
+    } -result {unknown:foo:unk2}
+
+    test msgcat-4.6 {mcunknown, uplevel context} -setup {
+	rename ::msgcat::mcunknown SavedMcunknown
+	proc ::msgcat::mcunknown {dom s} {
+            return "unknown:$dom:$s:[expr {[info level] - 1}]"
+	}
+	mcset foo unk1 "unknown 1"
+	variable locale [mclocale]
+	mclocale foo
+    } -cleanup {
+	mclocale $locale
+	rename ::msgcat::mcunknown {}
+	rename SavedMcunknown ::msgcat::mcunknown
+    } -body {
+	mc unk2
+    } -result unknown:foo:unk2:[info level]
+
+    # Tests msgcat-5.*: [mcload]
+
+    variable locales {foo foo_BAR foo_BAR_baz}
+    makeDirectory msgdir
+    foreach loc $locales {
+	makeFile "::msgcat::mcset $loc abc abc-$loc" \
+		[string tolower [file join msgdir $loc.msg]]
     }
-    ::msgcat::mclocale foo
-    set result [::msgcat::mc unk2]
-    rename ::msgcat::mcunknown {}
-    rename oldproc ::msgcat::mcunknown
-    set result
-} {unknown:foo:unk2}
-test msgcat-4.6 {::msgcat::mcunknown, uplevel context} {
-    rename ::msgcat::mcunknown oldproc
-    proc ::msgcat::mcunknown {dom s} {
-        return "unknown:$dom:$s:[info level]"
+    variable count 1
+    foreach loc {foo foo_BAR foo_BAR_baz} {
+	test msgcat-5.$count {mcload} -setup {
+	    variable locale [mclocale]
+	    mclocale $loc
+	} -cleanup {
+	    mclocale $locale
+	} -body {
+	    mcload [file join [temporaryDirectory] msgdir]
+	} -result $count
+	incr count
     }
-    ::msgcat::mclocale foo
-    set result [::msgcat::mc unk2]
-    rename ::msgcat::mcunknown {}
-    rename oldproc ::msgcat::mcunknown
-    set result
-} {unknown:foo:unk2:1}
-    
-
-#
-# Test mcload.  Need to set up an environment for
-# these tests by creating a temporary directory and
-# message files.
-#
 
-set locales {en en_US en_US_funky}
+    # Even though foo_BAR_notexist does not exist,
+    # foo_BAR and foo should be loaded.
+	test msgcat-5.4 {mcload} -setup {
+	    variable locale [mclocale]
+	    mclocale foo_BAR_notexist
+	} -cleanup {
+	    mclocale $locale
+	} -body {
+	    mcload [file join [temporaryDirectory] msgdir]
+	} -result 2
+
+	test msgcat-5.5 {mcload} -setup {
+	    variable locale [mclocale]
+	    mclocale no_FI_notexist
+	} -cleanup {
+	    mclocale $locale
+	} -body {
+	    mcload [file join [temporaryDirectory] msgdir]
+	} -result 0
+
+	test msgcat-5.6 {mcload} -setup {
+	    variable locale [mclocale]
+	    mclocale foo
+	} -cleanup {
+	    mclocale $locale
+	} -body {
+	    mc abc
+	} -result abc-foo
+
+	test msgcat-5.7 {mcload} -setup {
+	    variable locale [mclocale]
+	    mclocale foo_BAR
+	} -cleanup {
+	    mclocale $locale
+	} -body {
+	    mc abc
+	} -result abc-foo_BAR
+
+	test msgcat-5.8 {mcload} -setup {
+	    variable locale [mclocale]
+	    mclocale foo_BAR_baz
+	} -cleanup {
+	    mclocale $locale
+	} -body {
+	    mc abc
+	} -result abc-foo_BAR_baz
+
+	test msgcat-5.9 {mcload} -setup {
+	    rename ::msgcat::mcunknown SavedMcunknown
+	    proc ::msgcat::mcunknown {dom s} {
+		return unknown:$dom:$s
+	    }
+	    variable locale [mclocale]
+	    mclocale no_FI_notexist
+	} -cleanup {
+	    mclocale $locale
+	    rename ::msgcat::mcunknown {}
+	    rename SavedMcunknown ::msgcat::mcunknown
+	} -body {
+	    mc abc
+	} -result unknown:no_fi_notexist:abc
 
-catch {file mkdir msgdir}
-foreach l $locales {
-    set fd [open [string tolower [file join msgdir $l.msg]] w]
-    puts $fd "::msgcat::mcset $l abc abc-$l"
-    close $fd
-}
 
-test msgcat-5.1 {::msgcat::mcload} {
-    ::msgcat::mclocale en
-    ::msgcat::mcload msgdir
-} {1}
-test msgcat-5.2 {::msgcat::mcload} {
-    ::msgcat::mclocale en_US
-    ::msgcat::mcload msgdir
-} {2}
-test msgcat-5.3 {::msgcat::mcload} {
-    ::msgcat::mclocale en_US_funky
-    ::msgcat::mcload msgdir
-} {3}
-
-# Even though en_US_notexist does not exist,
-# en_US and en should be loaded.
-
-test msgcat-5.4 {::msgcat::mcload} {
-    ::msgcat::mclocale en_US_notexist
-    ::msgcat::mcload msgdir
-} {2}
-test msgcat-5.5 {::msgcat::mcload} {
-    ::msgcat::mclocale no_FI_notexist
-    ::msgcat::mcload msgdir
-} {0}
-test msgcat-5.6 {::msgcat::mcload} {
-    ::msgcat::mclocale en
-    ::msgcat::mc abc
-} {abc-en}
-test msgcat-5.7 {::msgcat::mcload} {
-    ::msgcat::mclocale en_US
-    ::msgcat::mc abc
-} {abc-en_US}
-test msgcat-5.8 {::msgcat::mcload} {
-    ::msgcat::mclocale en_US_funky
-    ::msgcat::mc abc
-} {abc-en_US_funky}
-test msgcat-5.9 {::msgcat::mcload} {
-    rename ::msgcat::mcunknown oldproc
-    proc ::msgcat::mcunknown {dom s} {
-        return "unknown:$dom:$s"
+    foreach loc $locales {
+	removeFile [string tolower [file join msgdir $loc.msg]]
     }
-    ::msgcat::mclocale no_FI_notexist
-    set result [::msgcat::mc abc]
-    rename ::msgcat::mcunknown {}
-    rename oldproc ::msgcat::mcunknown
-    set result
-} {unknown:no_fi_notexist:abc}
-
-# cleanup temp files
-foreach l $locales {
-    file delete [string tolower [file join msgdir $l.msg]]
-}
-# Clean out the msg catalogs
-file delete msgdir
+    removeDirectory msgdir
 
+    # Tests msgcat-6.*: [mcset], [mc] namespace inheritance
 #
 # Test mcset and mc, ensuring that resolution for messages
 # proceeds from the current ns to its parent and so on to the 
@@ -332,7 +459,7 @@
 #
 # Do this for the 12 permutations of
 #     locales: foo
-#     namespaces: ::foo ::foo::bar ::foo::bar::baz
+#     namespaces: foo foo::bar foo::bar::baz
 #     strings: {ov1 ov2 ov3 ov4}
 #     namespace ::foo            defines ov1, ov2, ov3
 #     namespace ::foo::bar       defines      ov2, ov3
@@ -347,82 +474,96 @@
 #     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
-test msgcat-7.4 {::msgcat::mc, extra args are given to unknown} {
-    ::msgcat::mc "this is a %s" "good test"
-} "this is a good test"
+    variable result
+    array set result {
+	foo,ov1 ov1_foo foo,ov2 ov2_foo foo,ov3 ov3_foo foo,ov4 ov4
+	foo::bar,ov1 ov1_foo foo::bar,ov2 ov2_foo_bar
+	foo::bar,ov3 ov3_foo_bar foo::bar,ov4 ov4 foo::bar::baz,ov1 ov1_foo
+	foo::bar::baz,ov2 ov2_foo_bar foo::bar::baz,ov3 ov3_foo_bar_baz
+	foo::bar::baz,ov4 ov4
+    }
+    variable count 1
+    variable ns
+    foreach ns {foo foo::bar foo::bar::baz} {
+	foreach string {ov1 ov2 ov3 ov4} {
+	    test msgcat-6.$count {mcset, overlap} -setup {
+		namespace eval foo {
+		    ::msgcat::mcset foo ov1 ov1_foo
+		    ::msgcat::mcset foo ov2 ov2_foo
+		    ::msgcat::mcset foo ov3 ov3_foo
+		    namespace eval bar {
+			::msgcat::mcset foo ov2 ov2_foo_bar
+			::msgcat::mcset foo ov3 ov3_foo_bar
+			namespace eval baz {
+			    ::msgcat::mcset foo ov3 "ov3_foo_bar_baz"
+			}
+		    }
+		    
+		}
+		variable locale [mclocale]
+		mclocale foo
+	    } -cleanup {
+		mclocale $locale
+		namespace delete foo
+	    } -body {
+		namespace eval $ns [list ::msgcat::mc $string]
+	    } -result $result($ns,$string)
+	    incr count
+	}
+    }
+
+    # Tests msgcat-7.*: [mc] extra args processed by [format]
 
-# Reset the locale
-::msgcat::mclocale $oldlocale
+    test msgcat-7.1 {mc extra args go through to format} -setup {
+	mcset foo format1 "this is a test"
+	mcset foo format2 "this is a %s"
+	mcset foo format3 "this is a %s %s"
+	variable locale [mclocale]
+	mclocale foo
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mc format1 "good test"
+    } -result "this is a test"
+
+    test msgcat-7.2 {mc extra args go through to format} -setup {
+	mcset foo format1 "this is a test"
+	mcset foo format2 "this is a %s"
+	mcset foo format3 "this is a %s %s"
+	variable locale [mclocale]
+	mclocale foo
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mc format2 "good test"
+    } -result "this is a good test"
+
+    test msgcat-7.3 {mc errors from format are propagated} -setup {
+	mcset foo format1 "this is a test"
+	mcset foo format2 "this is a %s"
+	mcset foo format3 "this is a %s %s"
+	variable locale [mclocale]
+	mclocale foo
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	catch {mc format3 "good test"}
+    } -result 1
+
+    test msgcat-7.4 {mc, extra args are given to unknown} -setup {
+	mcset foo format1 "this is a test"
+	mcset foo format2 "this is a %s"
+	mcset foo format3 "this is a %s %s"
+	variable locale [mclocale]
+	mclocale foo
+    } -cleanup {
+	mclocale $locale
+    } -body {
+	mc "this is a %s" "good test"
+    } -result "this is a good test"
 
-::tcltest::cleanupTests
+    cleanupTests
+}
+namespace delete ::msgcat::test
 return
+