490.md at [1bdb7e6598]

Login

File tip/490.md artifact f16e9bfb5a part of check-in 1bdb7e6598


# TIP 490: msgcat for TclOO
	Author:         Harald Oehlmann <[email protected]>
	State:          Final
	Type:           Project
	Vote:           Done
	Created:        07-Dec-2017
	Post-History:
	Keywords:       msgcat, oo
	Tcl-Version:    8.7
	Tcl-Branch:     tip490-msgcat-oo-2
-----

# Abstract

Package **msgcat** implements message catalogues for packages organized in nested namespaces.
This TIP proposes the extension to TclOO.

# Rationale

Since TclOO was included in the core, packages may also be defined as TclOO classes or classless objects.
The **msgcat** package should feature this.

A package should have its methods within a package namespace:

<pre>
namespace eval ::foo {
    oo::class create Foo
}
package provide foo 1.0
</pre>

The message catlog belongs to the package, not to an individual class.

<pre>
namespace eval ::foo {
    msgcat::mcload $dir/msgs
    oo::class create Foo {
        ...
    }
}
package provide foo 1.0
</pre>

# Key Use Cases

There are 4 use-cases to consider (which may be intermixed in the same package):

(with _'Servus!'_ as translation for _'Hi!'_)

1. The package does not use OO

	<pre>
	namespace import msgcat::*
	namespace eval ::N1 {
	    mcload $dir/msgs
	    proc m1 {} {
	        puts [mc Hi!]
	    }
	}

	% N1::m1
	-> Servus!
	</pre>

2. msgcat is called within a class definition script

	<pre>
	% namespace import msgcat::*
	% namespace eval ::N2 {
	    mcload $dir/msgs
	    oo::class create C1 {puts [mc Hi!]}
	}
	-> Servus!
	</pre>

3. msgcat is called from a method in an object and the method is defined in a class

	<pre>
	namespace import msgcat::*
	namespace eval ::N3Class {
	    mcload $dir/msgs
	    oo::class create C1
	    oo::define C1 method m1 {
	        puts [mc Hi!]
	    }
	}

	# The class object may be used in another namespace
	namespace eval ::N3Obj {
	    set O1 [::N3Class::C1 new]
	}

	% $N3Obj::O1 m1
	-> Servus!
	</pre>

4. msgcat is called from a method of a classless object

	<pre>
	namespace import msgcat::*
	namespace eval ::N4 {
	    mcload $dir/msgs
	    oo::object create O1
	    oo::objdefine O1 method m1 {} {
	        puts [mc Hi!]
	    }
	}

	% N4::O1 m1
	-> Servus!
	</pre>

Note that use-case 1 may emulate Use-cases 2 to 4 using `namespace eval`. Before this extension, a programmer for use-case 2 to 4 must have used `namespace eval` to explicitly specify the package namespace:

<pre>
namespace import msgcat::*
namespace eval ::N4 {
    mcload $dir/msgs
    oo::object create O1
    oo::objdefine O1 method m1 {} {
        puts [namespace eval ::N4 {mc Hi!}]
    }
}

% N4::O1 m1
-> Servus!
</pre>

This should still work with the new extension for compatibility reasons.

# Proposal

The following 4 extensions are proposed and covered by the TIP.


## Extension 1: Extend all msgcat commands to support all 4 use-cases.

So any **msgcat** command will detect the scenario on its own and extract the package namespace automatically.

The commands which are packet-namespace related are: **mc**, **mcexists**, **mcpackagelocale**, **mcforgetpackage**, **mcpackagenamespaceget** (new command, see below), **mcpackageconfig**, **mcset** and **mcmset**.

This has the following advantages (compared to the alternatives):

* no new commands, no learning
* if another foreign procedure is called and the procedure wants to use the callers message catalog, it may just use `uplevel 1 {msgcat tag}` and does not need to know if it is a class or not.

Here is an example for the second advantage:

The tklib package "tooltip" may invoke `msgcat::mc msg` for all text to get eventual translations (it does something like that but I don't understand it, IMHO broken).
The package namespace of the caller should be used (not the one of the tooltip package).

So:
<pre>
proc ::tooltip::tooltip {widget message} {
    ...
    set message [uplevel 1 {::msgcat::mc $message}]
}
</pre>

This will work in all use-cases, e.g. if `tooltip::tooltip` is called by a method following use-case 1 to 4.

## Extension 2: new command to get package namespace

The "magic" to extract the package namespace is exposed by the command:

<pre>
mcpackagenamespaceget
</pre>

This may be used:

* for the same case like the upper tooltip example, but for late binding
* for introspection and debugging

The upper tooltip example, where the translation is extracted when the tooltip is actually shown (to show an updated message if the current locale changed)

<pre>
proc ::tooltip::tooltip {widget message} {
    ...
    set messagenamespace [uplevel 1 {::msgcat::mcpackagenamespaceget}]
    ...
    bind $widget <Enter> [list ::tooltip::show $widget $messagenamespace $message]
}

proc ::tooltip::show {widget messagenamespace message} {
    ...
    set message [namespace eval $messagenamespace [list ::msgcat::mc $message]]
    ...
}
</pre>


## Extension 3: new command to get a translation with a package namespace as argument

A new command is proposed to get a translation with an explicit namespace:

<pre>
mcn ns src args...
</pre>

with the arguments:

* ns: package namespace to do the translation for
* src: the translation source string (like mc command)
* args: eventual arguments to contained format patterns (like mc command)

This command is identical to the `mc` command, with the difference, that the package namespace is not found by an implicit call to `mcpackagenamespaceget`, but may be explicitly specified as first argument

Then, the `mc` command may be expressed like:
<pre>
mcn [mcpackagenamespaceget] src args...
</pre>

There are the following purposes for this command:

* foreign packages. The package namespace is known (for example by a call to `mcpackagenamespaceget`). The translation may be retrieved by a call to `mcn` without any `namespace eval $ns` around it.
* Authors of C packages required to specify the namespace explicitly.
* Optimizations in an eventual time critical path. The speed of the old msgcat is beaten by `mcn [namespace current]..`

An example for the case of a foreign package is the tooltip package described above.

The contained call:

<pre>
proc ::tooltip::show {widget messagenamespace message} {
    ...
    set message [namespace eval $messagenamespace [list ::msgcat::mc $message]]
}
</pre>

may be expressed like:

<pre>
proc ::tooltip::show {widget messagenamespace message} {
    ...
    set message [::msgcat::mcn $messagenamespace $message]
}
</pre>


## Extension 4: Command "mcexists" should get a parameter -namespace to explicitly specify the namespace

The command `mcexists` has currently the syntax:

<pre>
mcexists ?-exactnamespace? ?-exactlocale? src
</pre>

A switch, **-namespace** _ns_, is added to specify the namespace explicitly:

<pre>
mcexists ?-exactnamespace? ?-exactlocale? ?-namespace ns? src
</pre>

This may be useful in similar situations as the `mcn` command.

# Implementation

Within **msgcat**, the package namespace is currently extracted by:

<pre>
proc msgcat::mc {src args} {
    ...
    set ns [uplevel 1 {namespace current}]
</pre>

This is replaced by:

<pre>
proc msgcat::mc {src args} {
    ...
    set ns [PackageNamespaceGet]
    ...
}
proc ::msgcat::PackageNamespaceGet {} {
    uplevel 2 {
	# Check for no object
	switch -exact -- [namespace which self] {
	    {::oo::define::self} {
		# We are within a class definition
		return [namespace qualifiers [self]]
	    }
	    {::oo::Helpers::self} {
		# We are within an object
		set Class [info object class [self]]
		# Check for classless defined object
		if {$Class eq {::oo::object}} {
		    return [namespace qualifiers [self]]
		}
		# Class defined object
		return [namespace qualifiers $Class]
	    }
	    default {
		# Not in object environment
		return [namespace current]
	    }
	}
    }
}
</pre>

The implementation is in tcl fossil in branch
[tip490-msgcat-oo-2](https://core.tcl-lang.org/tcl/timeline?r=tip490-msgcat-oo-2).

There are tests but no man page changes yet.
Please use this text as man-page.

# Discussion

See [this page](490-discussion.md) for further discussion.

# Credits

* René Zaumseil: initiative and partial implementation
* Eric Boudallier: alternate implementation
* Donal Fellows: implementation and examples
* Ashok P. Nadkarni: teach me oo by his excellent book


# Copyright

This document has been placed in the public domain.