Using zope.i18nmessageid

Rationale

To translate any text, we must be able to discover the source domain of the text. A source domain is an identifier that identifies a project that produces program source strings. Source strings occur as literals in python programs, text in templates, and some text in XML data. The project implies a source language and an application context.

Messages and Domains

We can think of a source domain as a collection of messages and associated translation strings. The domain helps to disambiguate messages based on context: for instance, the message whose source string is draw means one thing in a first-person shooter game, and quite another in a graphics package: in the first case, the domain for the message might be ok_corral, while in the second it might be gimp.

We often need to create unicode strings that will be displayed by separate views. The view cannot translate the string without knowing its source domain. A string or unicode literal carries no domain information, therefore we use instances of the Message class. Messages are unicode strings which carry a translation source domain and possibly a default translation.

Message Factories

Messages are created by a message factory belonging to a given translation domain. Each message factory is created by instantiating a MessageFactory, passing the domain corresponding to the project which manages the corrresponding translations.

>>> from zope.i18nmessageid import MessageFactory
>>> factory = MessageFactory('myproject')
>>> foo = factory('foo')
>>> foo.domain
'myproject'

The Zope project uses the zope domain for its messages. This package exports an already-created factory for that domain:

>>> from zope.i18nmessageid import ZopeMessageFactory as _z_
>>> foo = _z_('foo')
>>> foo.domain
'zope'

Example Usage

In this example, we create a message factory and assign it to _. By convention, we use _ as the name of our factory to be compatible with translatable string extraction tools such as xgettext. We then call _ with a string that needs to be translatable:

>>> from zope.i18nmessageid import MessageFactory, Message
>>> _ = MessageFactory("futurama")
>>> robot = _(u"robot-message", u"${name} is a robot.")

Messages at first seem like they are text strings:

>>> robot == 'robot-message'
True
>>> isinstance(robot, str)
True

The additional domain, default and mapping information is available through attributes:

>>> robot.default == '${name} is a robot.'
True
>>> robot.mapping
>>> robot.domain
'futurama'

The message’s attributes are considered part of the immutable message object. They cannot be changed once the message id is created:

>>> robot.domain = "planetexpress"
Traceback (most recent call last):
...
AttributeError: readonly attribute

>>> robot.default = u"${name} is not a robot."
Traceback (most recent call last):
...
AttributeError: readonly attribute

>>> robot.mapping = {'name': 'Bender'}
Traceback (most recent call last):
...
AttributeError: readonly attribute

If you need to change their information, yo’ll have to make a new message id object:

>>> new_robot = Message(robot, mapping={'name': 'Bender'})
>>> new_robot == 'robot-message'
True
>>> new_robot.domain
'futurama'
>>> new_robot.default == '${name} is a robot.'
True
>>> new_robot.mapping == {'name': 'Bender'}
True

Last but not least, messages are reduceable for pickling:

>>> callable, args = new_robot.__reduce__()
>>> callable is Message
True
>>> args == ('robot-message',
...          'futurama',
...          '${name} is a robot.',
...          {'name': 'Bender'},
...          None,
...          None,
...          None)
True

>>> fembot = Message('fembot')
>>> callable, args = fembot.__reduce__()
>>> callable is Message
True
>>> args == ('fembot', None, None, None, None, None, None)
True

Pickling and unpickling works, which means we can store message IDs in a database:

>>> from pickle import dumps, loads
>>> pystate = dumps(new_robot)
>>> pickle_bot = loads(pystate)
>>> (pickle_bot,
...  pickle_bot.domain,
...  pickle_bot.default,
...  pickle_bot.mapping) == ('robot-message',
...                          'futurama',
...                          '${name} is a robot.',
...                          {'name': 'Bender'})
True
>>> pickle_bot.__reduce__()[0] is Message
True