.. -*- coding: utf-8 -*-

Internationalization (I18N)
===========================

Page templates support the i18n attribute language. The implementation
is based on this document:

  * http://wiki.zope.org/zope3/ZPTInternationalizationSupport

With the exception of i18n:data and i18n:source, the implementation is
complete.

API
---

To demonstrate the high-level API, the following template requires
translation of tag contents.

  >>> from chameleon.zpt.template import PageTemplate
  >>> template = PageTemplate("""\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" i18n:translate="msgid">Default</span>
  ... </div>""")

Without passing a language, we simply get the default value.

  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>Default</span>
  </div>

To translate messages, we define a translation function. The following
describes the method signature:

  >>> def translate(msgid, domain=None, mapping=None, context=None,
  ...               target_language=None, default=None):
  ...     return msgid.upper()

Let's initialize a new template instance, this time passing in a
translation function.

  >>> template_with_translation = PageTemplate(template.body, translate=translate)

If we now render the template, our translation message id should be
uppercased.

  >>> print template_with_translation.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>MSGID</span>
  </div>

When ``zope.i18n`` is available (separately available from the Python
Package Index), this framework is used for translation. We will use
this for the remainder of this test.

The following sets up a translation domain that provides mock
translations for the German language code, ``"de"``.

  >>> from zope import interface
  >>> from chameleon.core import utils

  >>> from zope.i18n.interfaces import ITranslationDomain
  >>> class MockTranslationDomain(object):
  ...     interface.implements(ITranslationDomain)
  ...
  ...     def translate(self, msgid, mapping=None, context=None,
  ...                   target_language=None, default=None):
  ...         if target_language != 'de':
  ...             return default
  ...
  ...         mock ="Mock translation of '%s'" % \
  ...             utils.htmlescape(msgid)
  ...         if mapping:
  ...             mock += ' mapping=%s' % mapping
  ...         return mock + '.'

  >>> from zope import component
  >>> component.provideUtility(MockTranslationDomain(), ITranslationDomain, name="test")

Translation of tag contents
---------------------------

Now we'll render the template again---passing German as the language.

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>Mock translation of 'msgid'.</span>
  </div>

Let's try infering the translation message id from the tag body.

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" i18n:translate="">
  ...     Default
  ...   </span>
  ... </div>"""

Not passing a language:

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>
      Default
    </span>
  </div>

Passing German:

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>Mock translation of 'Default'.</span>
  </div>

We could also add a named block inside the tag.

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <p i18n:domain="test" i18n:translate="">
  ...     <span i18n:name="count">18</span> bananas.
  ...   </p>
  ...   <p>
  ...     <span i18n:name="count">18</span> bananas.
  ...   </p>
  ... </div>"""

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <p>
      <span>18</span> bananas.
    </p>
    <p>
      <span>18</span> bananas.
    </p>
  </div>

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <p>Mock translation of '${count} bananas.'
       mapping={'count': u'<span>18</span>'}.</p>
    <p>
      <span>18</span> bananas.
    </p>
  </div>

Or two:

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" i18n:translate="">
  ...     I want <span i18n:name="bananas">12</span> bananas and
  ...     <span i18n:name="apples">8</span> apples.
  ...   </span>
  ... </div>"""

Without a language this gives:

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>
       I want <span>12</span> bananas and
       <span>8</span> apples.
    </span>
  </div>

In German:

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>Mock translation of 'I want ${bananas} bananas and ${apples} apples.'
      mapping={'bananas': u'<span>12</span>', 'apples': u'<span>8</span>'}.</span>
  </div>

Here's an example from a template for a calendar widget:

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <div i18n:translate="" tal:omit-tag="">
  ...       <span i18n:name="monthname"
  ...             i18n:translate=""
  ...             tal:content="monthname"
  ...             tal:omit-tag="">monthname</span>
  ...       <span i18n:name="year"
  ...             i18n:translate=""
  ...             tal:content="year"
  ...             tal:omit-tag="">year</span>
  ...   </div>
  ... </div>"""

We'll set up the tests such that pass in an i18n message for the month
name.

  >>> from zope.i18nmessageid import Message

  >>> year = 2008
  >>> monthname = Message(u'month_nov', domain="test", default=u"November")

Without passing a language.

  >>> template = PageTemplate(body)
  >>> print template.render(year=year, monthname=monthname)
  <div xmlns="http://www.w3.org/1999/xhtml">
      November
      2008
  </div>

Passing German:

  >>> print template.render(year=year, monthname=monthname, target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
      Mock translation of 'month_nov'.
      2008
  </div>

Entities and dynamic translation strings:

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" i18n:translate="">
  ...     <sup>&reg;</sup> &gt;
  ...     <sup>&lt;</sup>
  ...   </span>
  ... </div>"""

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>
      <sup>&reg;</sup> &gt;
      <sup>&lt;</sup>
    </span>
  </div>

Translating entities:

  >>> template = PageTemplate("""\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span tal:omit-tag="" i18n:domain="test" i18n:translate="">
  ...     &reg;
  ...     &lt;
  ...   </span>
  ... </div>""")

  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    &reg;
    &lt;
  </div>

  >>> print template.render(year=year, monthname=monthname, target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    Mock translation of '&reg;  &lt;'.
  </div>

If  we're  replacing  or   inserting  content  dynamically,  and  this
evaluates to ``None``, omit the tag even if we're translating.

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" tal:content="None" i18n:translate="">
  ...     Will appear as an empty tag.
  ...   </span>
  ...   <span i18n:domain="test" tal:replace="None" i18n:translate="">
  ...     Won't appear.
  ...   </span>
  ... </div>"""

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span></span>
  </div>

If we have dynamic content in a translation, and it doesn't match a
specific message id, it is left untouched:

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" i18n:translate="">
  ...     The book is on the <span tal:content="string:desk" i18n:name="place">table</span>.
  ...   </span>
  ... </div>"""

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>
      The book is on the <span>desk</span>.
    </span>
  </div>

Dynamic content and sub-tags in a translation:

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" i18n:translate="">
  ...     The <b>book</b> is on the <span tal:content="string:desk" i18n:name="place">table</span>.
  ...   </span>
  ... </div>"""

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>
      The <b>book</b> is on the <span>desk</span>.
    </span>
  </div>

If a message id is supplied, unnamed blocks are rendered as-is.

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" i18n:translate="msg_id" tal:omit-tag="">
  ...     The book is on the <span tal:content="'table'" />.
  ...   </span>
  ... </div>"""

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    The book is on the <span>table</span>.
  </div>

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    Mock translation of 'msg_id'.
  </div>

Nested translation.

  >>> template = PageTemplate("""\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <h4 i18n:domain="test" i18n:translate="">
  ...     <em i18n:name="amount" i18n:translate="">
  ...         (1 van <tal:weight i18n:name="weight" replace="weight" />)
  ...     </em>
  ...   </h4>
  ... </div>""")

  >>> print template.render(weight=32)
  <div xmlns="http://www.w3.org/1999/xhtml">
    <h4>
      <em>
         (1 van 32)
      </em>
    </h4>
  </div>

Using METAL and i18n together:

  >>> template = PageTemplate("""\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <div metal:define-macro="macro">
  ...     <p i18n:domain="test" i18n:translate="">
  ...       <span i18n:name="count" tal:content="count|default">18</span> bananas.
  ...     </p>
  ...   </div>
  ...   <div tal:define="count 42" metal:use-macro="macros.macro" />
  ... </div>""")

  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <div>
      <p>
        <span>18</span> bananas.
      </p>
    </div>
    <div>
      <p>
        <span>42</span> bananas.
      </p>
    </div>
  </div>

Translation of tag attributes
-----------------------------

A simple example to start with.

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" title="Simple Title" i18n:attributes="title">
  ...     Default
  ...   </span>
  ... </div>"""

Not passing a language:

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Simple Title">
      Default
    </span>
  </div>

Passing German:

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Mock translation of 'Simple Title'.">
      Default
    </span>
  </div>

Use an explicit msgid:

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" title="Simple Title"
  ...         i18n:attributes="title title_simple">
  ...     Default
  ...   </span>
  ... </div>"""

Not passing a language:

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Simple Title">
      Default
    </span>
  </div>

Passing German:

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Mock translation of 'title_simple'.">
      Default
    </span>
  </div>

Use an explicit msgid with a trailing semicolon.

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" title="Simple Title"
  ...         i18n:attributes="title title_simple;">
  ...     Default
  ...   </span>
  ... </div>"""

Not passing a language:

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Simple Title">
      Default
    </span>
  </div>

Passing German:

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Mock translation of 'title_simple'.">
      Default
    </span>
  </div>

Use multiple attributes on the same tag.

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" title="Simple Title"
  ...         longdesc="A not so short description."
  ...         i18n:attributes="title title_simple; longdesc desc_short">
  ...     Default
  ...   </span>
  ... </div>"""

Not passing a language:

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Simple Title" longdesc="A not so short description.">
      Default
    </span>
  </div>

Passing German:

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Mock translation of 'title_simple'."
          longdesc="Mock translation of 'desc_short'.">
      Default
    </span>
  </div>

Tag attributes which are messages (even with interpolation, but only
if there's no surrounding text).

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <div title="Simple Title"
  ...        tal:attributes="title monthname">
  ...     <img alt="${monthname}" src="#" />
  ...     <img alt="${abc | monthname}" src="#" />
  ...     <img alt="not translated: ${monthname}" src="#" />
  ...   </div>
  ... </div>"""
  >>> template = PageTemplate(body)
  >>> from zope.i18nmessageid import Message
  >>> monthname = Message(u'month_nov', domain="test", default=u"November")
  >>> print template.render(target_language='de', monthname=monthname)
  <div xmlns="http://www.w3.org/1999/xhtml">
    <div title="Mock translation of 'month_nov'.">
      <img alt="Mock translation of 'month_nov'." src="#" />
      <img alt="Mock translation of 'month_nov'." src="#" />
      <img alt="not translated: month_nov" src="#" />
    </div>
  </div>

Translation of tag content and tag attributes
---------------------------------------------

A simple example to start with.

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
  ...      xmlns:tal="http://xml.zope.org/namespaces/tal">
  ...   <span i18n:domain="test" i18n:translate="tid"
  ...         title="Title" i18n:attributes="title aid">
  ...     Default, "default", 'default'
  ...   </span>
  ...   <span i18n:domain="test" i18n:translate=""
  ...         tal:content="string:tid">
  ...     Default, "default", 'default'
  ...   </span>
  ...   <span i18n:domain="test" i18n:translate="">
  ...     ${'tid'}
  ...   </span>
  ...   <span i18n:domain="test" i18n:translate="">
  ...     t${'i'}${'d'}
  ...   </span>
  ... </div>"""

Not passing a language:

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Title">
      Default, "default", 'default'
    </span>
    <span>tid</span>
    <span>tid</span>
    <span>tid</span>    
  </div>

Passing German:

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Mock translation of 'aid'.">Mock translation of 'tid'.</span>
    <span>Mock translation of 'tid'.</span>
    <span>Mock translation of 'tid'.</span>
    <span>Mock translation of 'tid'.</span>
  </div>

Make sure translations play nice with loops.

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
  ...      xmlns:tal="http://xml.zope.org/namespaces/tal" i18n:domain="test">
  ...   <div tal:repeat="i range(1)">
  ...     <span i18n:translate="tid">Default</span>
  ...   </div>
  ... </div>"""

Not passing a language:

  >>> print PageTemplate(body).render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <div>
      <span>Default</span>
    </div>
  </div>

Messages that contain the formatting character '%' (edge-case):

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
  ...      i18n:domain="test">
  ...     <span i18n:translate="">%</span>
  ...     <span i18n:translate="%">default</span>
  ... </div>"""

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>%</span>
    <span>default</span>
  </div>

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>Mock translation of '%'.</span>
    <span>Mock translation of '%'.</span>
  </div>

When a message id is given and ``i18n:name`` is omitted from one or
more elements, only the element text is subjected to translation;
elements appearing after the element text are rendered individually:

  >>> template = PageTemplate("""
  ... <label i18n:translate="label_login_name" i18n:domain="test"
  ...           tal:attributes="for ac_name">Login Name <input type="text"
  ...           size="15"
  ...           class="autofocus"
  ...           tal:attributes="name ac_name;
  ...                           id ac_name;
  ...                           value login_name;"/></label>
  ... """)

Without passing a language:

  >>> print template(ac_name="test", login_name="test")
  <label for="test">Login Name <input type="text" size="15" class="autofocus" name="test" id="test" value="test" /></label>

Passing German:

  >>> print template(ac_name="test", login_name="test", target_language='de')
  <label for="test">Mock translation of 'label_login_name'.</label>

To include the element in the translation process, use ``i18n:name``:

  >>> template = PageTemplate("""
  ... <label i18n:translate="label_login_name" i18n:domain="test"
  ...           tal:attributes="for ac_name">Login Name <input type="text"
  ...           size="15"
  ...           class="autofocus"
  ...           i18n:name="input"
  ...           tal:attributes="name ac_name;
  ...                           id ac_name;
  ...                           value login_name;"/></label>
  ... """)
  >>> print template(ac_name="test", login_name="test", target_language='de')
  <label for="test">Mock translation of 'label_login_name' mapping={'input': u'<input type="text" size="15" class="autofocus" name="test" id="test" value="test" />'}.</label>

Without a message id:

  >>> template = PageTemplate("""
  ... <label i18n:translate="" i18n:domain="test"
  ...           tal:attributes="for ac_name">Login Name <input type="text"
  ...           size="15"
  ...           class="autofocus"
  ...           tal:attributes="name ac_name;
  ...                           id ac_name;
  ...                           value login_name;"/></label>
  ... """)

Without passing a language:

  >>> print template(ac_name="test", login_name="test")
  <label for="test">Login Name <input type="text" size="15" class="autofocus" name="test" id="test" value="test" /></label>

Passing German (notice that the translation string is escaped by the
mock translation domain):

  >>> print template(ac_name="test", login_name="test", target_language='de')
  <label for="test">Mock translation of 'Login Name &lt;input type="text" size="15" class="autofocus" name="test" id="test" value="test" /&gt;'.</label>

Translation of non-ASCII tag attributes
---------------------------------------

A simple example to start with.

  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span i18n:domain="test" title="Español" i18n:attributes="title">
  ...     Default
  ...   </span>
  ... </div>"""

Not passing a language:

  >>> template = PageTemplate(body)
  >>> print template.render()
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Español">
      Default
    </span>
  </div>

Passing German:

  >>> print template.render(target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span title="Mock translation of 'Español'.">
      Default
    </span>
  </div>

Message interpolation
---------------------

Messages without a default translation just output the message
string (identifier).

  >>> who = Message(u"The Dutch", domain="test")
  >>> body = """\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span>${who}</span>
  ... </div>"""

Not passing a language:

  >>> template = PageTemplate(body)
  >>> print template.render(who=who)
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>The Dutch</span>
  </div>

Passing German:

  >>> print template.render(who=who, target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>Mock translation of 'The Dutch'.</span>
  </div>

Using ``tal:content``:

  >>> template = PageTemplate("""\
  ... <div xmlns="http://www.w3.org/1999/xhtml"
  ...      xmlns:i18n="http://xml.zope.org/namespaces/i18n">
  ...   <span tal:content="who" />
  ... </div>""")

Not passing a language:

  >>> print template(who=who)
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>The Dutch</span>
  </div>

Passing German:

  >>> print template(who=who, target_language='de')
  <div xmlns="http://www.w3.org/1999/xhtml">
    <span>Mock translation of 'The Dutch'.</span>
  </div>
