chameleon
chameleon copied to clipboard
Double translation
After the discussion in #328, I switched our translation service to return any messages that are not of type str
, yielding mostly the expected result. However, if we do set an i18n
attribute on a tag that obtains the content from something other than a string, the translation is called twice. To demonstrate, I adjusted an example of Dieter Maurer:
from chameleon.zpt.template import PageTemplate
def translate(msgid, *args, **kw):
print('translate called with', msgid.__class__, msgid)
if not isinstance(msgid, str):
return msgid
return "translated"
class Something:
def __init__(self, text):
self.text = text
def __str__(self):
return self.text
x = Something("x")
ts = """<div i18n:translate="" tal:content="x" />"""
t = PageTemplate(ts)
t(translate=translate, x=x)
This yields
translate called with <class '__main__.Something'> x
translate called with <class '__main__.Something'> x
When looking into the cache file created by Chameleon, this is due to the following two lines:
__content = translate(__content, default=None, domain=__i18n_domain, context=__i18n_context, target_language=getitem('
__content = __quote(__content, None, '\xad', None, None)
If these two lines were switched, I would get the desired result - __quote
would find that __content
is not a string or number and has no __html__
method, so it would call the translation. Since the translation returned the original object, __quote
would fall back to returning str(target)
, so the call to translate
that is due to the explicit i18n:translate
attrlibute would be handled a string and actually do a translation.
Is there a reason for this order? Also, I cannot imagine that it is intended that the translation is called twice here. Maybe the specification that everything that is not a string or has an __html__
method is considered to be a message to be translated could be reconsidered?
I think you're supposed to always return a string from the translate
function. This would then mean that __quote
would not attempt to convert (i.e. translate) twice.
If in doubt, consult z3c.pt
which has existed as an option for people in Zope/Plone to use Chameleon for ages.
The chameleon cache file contains inside __quote
the lines
__converted = convert(target)
target = (str(target) if (target is __converted) else __converted)
suggesting that the translation (convert
is a partial on translate
) is allowed to return the object itself to indicate that it will not handle it.
Anyhow, if I return str(msgid)
instead of msgid
in the case of msgid
not being a string, the translation is only called once, but I still am unable to differentiate between
<div i18n:translate="" tal:content="x"></div>
and
<div tal:content="x"></div>
Of course, this is actually what the documentation says. x
is considered to be in need of translation irrespective of the explict i18n:translate
attribute simply due to it being neither a string nor an object with an __html__
method. Whether an explicit translation requesting attribute was also given, is then irrelevant and my translate
function is not given any information about whether the attribute is in fact present (short of inspecting the call stack).
I can live with this since in the context of Zope 4, I can only cause this behavior if naming a File
object as content for a tag and at the same time requesting a translation using an explicit i18n:translate
. I doubt that we actually have this situation anywhere in our code. It is just something that is now different than it was in Zope 2 due to this different definition of what needs to be translated.
Maybe if more such problems arise, you could consider a chameleon flag that changes this behavior to be more compatible to Zope, so it only translates messages with explicit i18n
attributes?