summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstanton <stanton>1998-12-07 20:56:53 (GMT)
committerstanton <stanton>1998-12-07 20:56:53 (GMT)
commit082e3f3ed08ece41e41c7d8344e768918e7ae155 (patch)
tree9b1c3c73241a1c8d4c870555ec5282babf9ff57e
parent3dcc2202e14e378b8390380674353bbbec870325 (diff)
downloadtcl-082e3f3ed08ece41e41c7d8344e768918e7ae155.zip
tcl-082e3f3ed08ece41e41c7d8344e768918e7ae155.tar.gz
tcl-082e3f3ed08ece41e41c7d8344e768918e7ae155.tar.bz2
cleaned up docs
* tests/msgcat.test: Added message catalog test suite. * library/msgcat1.0/msgcat.tcl: minor bug fixes, integrated latest changes from Mark Harrison.
-rw-r--r--doc/msgcat.n72
-rw-r--r--library/msgcat/msgcat.tcl29
-rw-r--r--library/msgcat1.0/msgcat.tcl29
-rw-r--r--tests/msgcat.test333
4 files changed, 414 insertions, 49 deletions
diff --git a/doc/msgcat.n b/doc/msgcat.n
index ea9005a..ce72a74 100644
--- a/doc/msgcat.n
+++ b/doc/msgcat.n
@@ -11,7 +11,7 @@
.BS
'\" Note: do not modify the .SH NAME line immediately below!
.SH NAME
-msgcat \- Tcl Message Catalog
+msgcat \- Tcl message catalog
.SH SYNOPSIS
\fB::msgcat::mc src-string\fR
.sp
@@ -40,19 +40,28 @@ the message catalog.
Use of the message catalog is optional by any application
or package, but is encouraged if the application or package
wishes to be enabled for multi-lingual applications.
+
.SH COMMANDS
.TP
\fB::msgcat::mc src-string\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. The 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
+application. Instead of using an English string directly, an
+applicaton can pass the English string through \fB::msgcat::mc\fR and
+use the result. If an application is written for a single language in
+this fashion, then it is easy to add support for additional languages
+later simply by defining new message catalog entries.
.TP
-\fB::msgcat::mclocale \fR?\fInewLocale\fR?
-The locale defaults to the locale specified in the
-user's environment. This function sets the locale
-to \fInewLocale\fR. If \fInewLocale\fR is omitted, the
-current locale is returned.
+\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 new locale
+is returned. The initial locale defaults to the locale specified in
+the user's environment. See \fBLOCALE AND SUBLOCALE 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
@@ -62,21 +71,26 @@ preference. If the user has specified LANG=en_US_funky,
this procedure would return {en_US_funky en_US en}.
.TP
\fB::msgcat::mcload \fIdirname\fR
-Searches the specified directory for files which match
-the language specifications returned by mcpreferences.
+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
+and were loaded is returned.
.TP
\fB::msgcat::mcset \fIlocale src-string \fR?\fItranslate-string\fR?
-Sets the translation for \fIsrc-string\fR to \fItranlate-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.
+specified, \fIsrc-string\fR is used for both. The function
+return \fItranslate-string\fR.
.TP
\fB::msgcat::mcunknown \fIlocale src-string\fR
This routine is called by \fB::msgcat::mc\fR in the case when
a translation for \fIsrc-string\fR is not defined in the
current locale. The default action is to return
-\fIsrc-string\fR. This command can be redefined by the
-application.
+\fIsrc-string\fR. This procedure can be redefined by the
+application, for example to log error messages for each unknown
+string.
+
.SH "LOCALE AND SUBLOCALE SPECIFICATION"
.PP
The locale is specified by a locale string.
@@ -87,14 +101,17 @@ codes are specified in standards ISO-639 and ISO-3166.
For example, the locale ``en'' specifies English and
``en_US'' specifes U.S. English.
.PP
-The locale defaults to the value in env(LANG) at the time the
-\fBmsgcat\fR package is loaded. If env(LANG) is not defined, then the
+The locale defaults to the value in \fBenv(LANG)\fR at the time the
+\fBmsgcat\fR package is loaded. If \fBenv(LANG)\fR is not defined, then the
locale defaults to ``C''.
.PP
-When a locale is specified by the user, a ``best match''
-search is performed. If a user specifies en_UK_Funky, the
-locales ``en_UK_Funky'', ``en_UK'', and ``en'' are searched
-in order.
+When a locale is specified by the user, a ``best match'' search is
+performed during string translation. For example, if a user specifies
+en_UK_Funky, the locales ``en_UK_Funky'', ``en_UK'', and ``en'' are
+searched in order until a matching translation string is found. If no
+translation string is available, then \fB::msgcat::unknown\fR is
+called.
+
.SH "NAMESPACES AND MESSAGE CATALOGS"
.PP
Strings stored in the message catalog are stored relative
@@ -106,8 +123,8 @@ error.
.PP
For example, executing the code
.CS
-mcset EN hello "hello from ::"
-namespace eval foo {mcset EN hello "hello from ::foo"}
+mcset en hello "hello from ::"
+namespace eval foo {mcset en hello "hello from ::foo"}
puts [mc hello]
namespace eval foo {puts [mc hello]}
.CE
@@ -116,6 +133,7 @@ will print
hello from ::
hello from ::foo
.CE
+
.SH "LOCATION AND FORMAT OF MESSAGE FILES"
.PP
Message files can be located in any directory, subject
@@ -135,10 +153,11 @@ necessary translation strings for the language. For example:
.CS
::msgcat::mcset es "Free Beer!" "Cerveza Gracias!"
.CE
+
.SH "RECOMMENDED MESSAGE SETUP FOR PACKAGES"
.PP
If a package is installed into a subdirectory of the
-tcl_pkgPath and loaded via ``package require'', the
+\fBtcl_pkgPath\fR and loaded via \fBpackage require\fR, the
following procedure is recommended.
.IP [1]
During package installation, create a subdirectory
@@ -150,12 +169,13 @@ Copy your *.msg files into that directory.
initialization script:
.CS
# load language files, stored in lang subdirectory
-::msgcat::mcloadmsgs [file join [file dirname [info script]] lang]
+::msgcat::mcload [file join [file dirname [info script]] lang]
.CE
+
.SH "POSTITIONAL CODES FOR FORMAT AND SCAN COMMANDS"
.PP
It is possible that a message string used as an argument
-to \fBformat\fR might have positionally dependent parameters which
+to \fBformat\fR might have positionally dependent parameters that
might need to be repositioned. For example, it might be
syntactically desirable to rearrange the sentence structure
while translating.
@@ -173,10 +193,12 @@ format "In location %2\\\\$s we produced %1\\\\$d units" $num $city
.PP
Similarly, positional parameters can be used with \fBscan\fR to
extract values from internationalized strings.
+
.SH "SEE ALSO"
format(n), scan(n), namespace(n), package(n)
+
.SH CREDITS
.PP
The message catalog code was developed by Mark Harrison.
.SH KEYWORDS
-internationalization i18n message text translation
+internationalization, i18n, localization, l10n, message, text, translation
diff --git a/library/msgcat/msgcat.tcl b/library/msgcat/msgcat.tcl
index 330002d..61b8327 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.1.2.2 1998/12/04 21:58:08 stanton Exp $
+# RCS: @(#) $Id: msgcat.tcl,v 1.1.2.3 1998/12/07 20:56:53 stanton Exp $
package provide msgcat 1.0
@@ -27,13 +27,6 @@ namespace eval msgcat {
# array key is of the form "<locale>,<namespace>,<src>" and the value is
# the translated string.
array set msgs {}
-
- # set default locale, try to get from environment
- if {[info exists ::env(LANG)]} {
- mclocale $::env(LANG)
- } else {
- mclocale "C"
- }
}
# msgcat::mc --
@@ -114,16 +107,18 @@ proc msgcat::mcpreferences {} {
# langdir The directory to search.
#
# Results:
-# None.
+# Returns the number of message catalogs that were loaded.
proc msgcat::mcload {langdir} {
+ set x 0
foreach p [::msgcat::mcpreferences] {
set langfile [file join $langdir $p.msg]
if {[file exists $langfile]} {
+ incr x
uplevel [list source $langfile]
}
}
- return
+ return $x
}
# msgcat::mcset --
@@ -137,7 +132,7 @@ proc msgcat::mcload {langdir} {
# the source string is used.
#
# Results:
-# None.
+# Returns the new locale.
proc msgcat::mcset {locale src {dest ""}} {
if {$dest == ""} {
@@ -147,7 +142,7 @@ proc msgcat::mcset {locale src {dest ""}} {
set ns [uplevel {namespace current}]
set ::msgcat::msgs($locale,$ns,$src) $dest
- return
+ return $dest
}
# msgcat::mcunknown --
@@ -168,3 +163,13 @@ proc msgcat::mcunknown {locale src} {
return $src
}
+# Initialize the default locale
+
+namespace eval msgcat {
+ # set default locale, try to get from environment
+ if {[info exists ::env(LANG)]} {
+ mclocale $::env(LANG)
+ } else {
+ mclocale "C"
+ }
+}
diff --git a/library/msgcat1.0/msgcat.tcl b/library/msgcat1.0/msgcat.tcl
index 330002d..61b8327 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.1.2.2 1998/12/04 21:58:08 stanton Exp $
+# RCS: @(#) $Id: msgcat.tcl,v 1.1.2.3 1998/12/07 20:56:53 stanton Exp $
package provide msgcat 1.0
@@ -27,13 +27,6 @@ namespace eval msgcat {
# array key is of the form "<locale>,<namespace>,<src>" and the value is
# the translated string.
array set msgs {}
-
- # set default locale, try to get from environment
- if {[info exists ::env(LANG)]} {
- mclocale $::env(LANG)
- } else {
- mclocale "C"
- }
}
# msgcat::mc --
@@ -114,16 +107,18 @@ proc msgcat::mcpreferences {} {
# langdir The directory to search.
#
# Results:
-# None.
+# Returns the number of message catalogs that were loaded.
proc msgcat::mcload {langdir} {
+ set x 0
foreach p [::msgcat::mcpreferences] {
set langfile [file join $langdir $p.msg]
if {[file exists $langfile]} {
+ incr x
uplevel [list source $langfile]
}
}
- return
+ return $x
}
# msgcat::mcset --
@@ -137,7 +132,7 @@ proc msgcat::mcload {langdir} {
# the source string is used.
#
# Results:
-# None.
+# Returns the new locale.
proc msgcat::mcset {locale src {dest ""}} {
if {$dest == ""} {
@@ -147,7 +142,7 @@ proc msgcat::mcset {locale src {dest ""}} {
set ns [uplevel {namespace current}]
set ::msgcat::msgs($locale,$ns,$src) $dest
- return
+ return $dest
}
# msgcat::mcunknown --
@@ -168,3 +163,13 @@ proc msgcat::mcunknown {locale src} {
return $src
}
+# Initialize the default locale
+
+namespace eval msgcat {
+ # set default locale, try to get from environment
+ if {[info exists ::env(LANG)]} {
+ mclocale $::env(LANG)
+ } else {
+ mclocale "C"
+ }
+}
diff --git a/tests/msgcat.test b/tests/msgcat.test
new file mode 100644
index 0000000..92e5c4b
--- /dev/null
+++ b/tests/msgcat.test
@@ -0,0 +1,333 @@
+# Commands covered: ::msgcat::mc ::msgcat::mclocale
+# ::msgcat::mcpreferences ::msgcat::mcload
+# ::msgcat::mcset ::msgcat::mcunknown
+#
+# This file contains a collection of tests for the msgcat script library.
+# 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 by Scriptics Corporation.
+#
+# 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.1.2.1 1998/12/07 20:56:54 stanton Exp $
+
+if {[string compare test [info procs test]] == 1} then {source defs}
+
+if {[catch {package require msgcat 1.0}]} {
+ if {[info exist msgcat1]} {
+ catch {puts stderr "Cannot load msgcat 1.0 package"}
+ return
+ } else {
+ catch {puts stderr "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
+ }
+}
+
+set oldlocale [::msgcat::mclocale]
+
+#
+# Test the various permutations of mclocale
+# and mcpreferences.
+#
+
+test msgcat-1.1 {::msgcat::mclocale default} {
+ ::msgcat::mclocale
+} {C}
+
+test msgcat-1.2 {::msgcat::mcpreferences, single element} {
+ ::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}
+
+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 mcc, ensuring that namespace partitioning
+# is working.
+#
+
+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 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 mcunknown, first the default operation
+# and then with an overridden definition.
+#
+
+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"
+ }
+ ::msgcat::mclocale foo
+ set result [::msgcat::mc unk2]
+ rename ::msgcat::mcunknown {}
+ rename oldproc ::msgcat::mcunknown
+ set result
+} {unknown:foo:unk2}
+
+#
+# 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}
+
+catch {file mkdir msgdir}
+foreach l $locales {
+ set fd [open [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"
+ }
+ ::msgcat::mclocale no_FI_notexist
+ set result [::msgcat::mc abc]
+ rename ::msgcat::mcunknown {}
+ rename oldproc ::msgcat::mcunknown
+ set result
+} {unknown:no_FI_notexist:abc}
+
+#
+# Clean up the test files
+#
+
+foreach l $locales {
+ file delete [file join msgdir $l.msg]
+}
+
+# Clean out the msg catalogs
+
+
+::msgcat::mclocale $oldlocale
+file delete msgdir