Wednesday, December 24, 2008

Disguising context.translate() as _

Edit (Dec. 2009): I currently think that it's better to define MyProductMessageFactory in __init__.py, and import that as _ in your scripts and filesystem code.
from zope.i18nmessageid import MessageFactory
MyProductMessageFactory = MessageFactory('MyProduct')

In the case of scripts, you'll have to add a security declaration:

from AccessControl.SecurityInfo import ModuleSecurityInfo
security = ModuleSecurityInfo('Products.MyProductMessageFactory')
security.declarePublic('MyProductMessageFactory')

And of course, see also the link that Mikko added: http://worldcookery.com/files/fivei18n/

The outdated manual



To make i18ndude aware of the strings you pass context.translate(), you can wrap these calls in a '_' method like so (example taken from PloneInvite):
def _(message):
""" Fake MessageFactory, allows calling context.translate() as _(u"") in
order to be able to use i18ndude on this script.

We want to use context.translate, but if we do, its input strings aren't
recognized by i18ndude as translatable strings. If we call it as _(u""),
i18ndude will recognize it and create translation strings in the .pot file.
"""
domain = 'PloneInvite'
msgid = message
return context.translate(default=message, domain=domain, msgid=msgid)

Now you can call it as portalmessage = _(u"Sent invitation to %s.") % invite_to_address, and i18ndude will create the appropriate strings in the .pot file.

If you have several scripts in your product which produce messages that should be translated, you might want to consider moving the wrapper method to a separate class. In PloneInvite, i added this to the __init__.py:
# FakeMessageFactory for translating strings in scripts
from AccessControl import ModuleSecurityInfo
from fakemessagefactory import FakeMessageFactory
ModuleSecurityInfo('Products.PloneInvite').declarePublic('FakeMessageFactory')

fakemessagefactory.py reads:
class FakeMessageFactory:

def __init__(self, context, domain):
self.context = context
self.domain = domain

def __call__(self, message):
msgid = message
return self.context.translate(default=message, domain=self.domain, msgid=msgid)

And i call it in the script like this:
from Products.PloneInvite import FakeMessageFactory
_ = FakeMessageFactory(context,'PloneInvite')

portalmessage = _(u"Sent invitation to %s.") % invite_to_address

1 comment:

Mikko Ohtamaa said...

Thanks for the great tip!

See also:

http://worldcookery.com/files/fivei18n/