Language Reference: Translation (I18N)

Translation of template contents and attributes is supported via the i18n namespace and message objects.


The translation machinery defines a message as any object which is not a string or a number and which does not provide an __html__ method.

When any such object is inserted into the template, the translate function is invoked first to see if it needs translation. The result is always coerced to a native string before it’s inserted into the template.

Translation function

The simplest way to hook into the translation machinery is to provide a translation function to the template constructor or at render-time. In either case it should be passed as the keyword argument translate.

The function has the following signature:

def translate(msgid, domain=None, mapping=None, context=None, target_language=None, default=None):

The result should be a string or None. If another type of object is returned, it’s automatically coerced into a string.

If zope.i18n is available, the translation machinery defaults to using its translation function. Note that this function requires messages to conform to the message class from zope.i18nmessageid; specifically, messages must have attributes domainmapping and default. Example use:

from zope.i18nmessageid import MessageFactory
_ = MessageFactory("food")

apple = _(u"Apple")

There’s currently no further support for other translation frameworks.

Using Zope’s translation framework

The translation function from zope.i18n relies on translation domains to provide translations.

These are components that are registered for some translation domain identifier and which implement a translate method that translates messages for that domain.

Note: To register translation domain components, the Zope Component Architecture must be used (see zope.component).

The easiest way to configure translation domains is to use the the registerTranslations ZCML-directive; this requires the use of the zope.configuration package. This will set up translation domains and gettext catalogs automatically:

<configure xmlns=""

   <i18n:registerTranslations directory="locales" />


The ./locales directory must follow a particular directory structure:


In each of the LC_MESSAGES directories, one GNU gettext file in the .po format must be present per translation domain:

# ./locales/de/LC_MESSAGES/food.po

msgid ""
msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

msgid "Apple"
msgstr "Apfel"

It may be necessary to compile the message catalog using the msgfmt utility. This will produce a .mo file.

Translation domains without gettext

The following example demonstrates how to manually set up and configure a translation domain for which messages are provided directly:

from zope import component
from zope.i18n.simpletranslationdomain import SimpleTranslationDomain

food = SimpleTranslationDomain("food", {
    ('de', u'Apple'): u'Apfel',

component.provideUtility(food, food.domain)

An example of a custom translation domain class:

from zope import interface

class TranslationDomain(object):

     def translate(self, msgid, mapping=None, context=None,
                  target_language=None, default=None):


component.provideUtility(TranslationDomain(), name="custom")

This approach can be used to integrate other translation catalog implementations.


The i18n namespace URI and recommended prefix are currently defined as:


This is not a URL, but merely a unique identifier. Do not expect a browser to resolve it successfully.


The allowable i18n statements are:

  • i18n:translate
  • i18n:domain
  • i18n:context
  • i18n:source
  • i18n:target
  • i18n:name
  • i18n:attributes
  • i18n:data
  • i18n:comment
  • i18n:ignore
  • i18n:ignore-attributes


This attribute is used to mark units of text for translation. If this attribute is specified with an empty string as the value, the message ID is computed from the content of the element bearing this attribute. Otherwise, the value of the element gives the message ID.


The i18n:domain attribute is used to specify the domain to be used to get the translation. If not specified, the translation services will use a default domain. The value of the attribute is used directly; it is not a TALES expression.


The i18n:context attribute is used to specify the context to be used to get the translation. If not specified, the translation services will use a default context. The context is generally use to distinguish identical texts in different context (because in a translation this may not be the case.) The value of the attribute is used literally; it is not an expression.


The i18n:source attribute specifies the language of the text to be translated. The default is nothing, which means we don’t provide this information to the translation services.


The i18n:target attribute specifies the language of the translation we want to get. If the value is default, the language negotiation services will be used to choose the destination language. If the value is nothing, no translation will be performed; this can be used to suppress translation within a larger translated unit. Any other value must be a language code.

The attribute value is a TALES expression; the result of evaluating the expression is the language code or one of the reserved values.

Note: i18n:target is primarily used for hints to text extraction tools and translation teams. If you had some text that should only be translated to e.g. German, then it probably shouldn’t be wrapped in an i18n:translate span.


Name the content of the current element for use in interpolation within translated content. This allows a replaceable component in content to be re-ordered by translation. For example:

<span i18n:translate=''>
  <span tal:replace='' i18n:name='name' /> was born in
  <span tal:replace='context.country_of_birth' i18n:name='country' />.

would cause this text to be passed to the translation service:

"${name} was born in ${country}."


This attribute will allow us to translate attributes of HTML tags, such as the alt attribute in the img tag. The i18n:attributes attribute specifies a list of attributes to be translated with optional message IDs for each; if multiple attribute names are given, they must be separated by semicolons. Message IDs used in this context must not include whitespace.

Note that the value of the particular attributes come either from the HTML attribute value itself or from the data inserted by tal:attributes.

If an attibute is to be both computed using tal:attributes and translated, the translation service is passed the result of the TALES expression for that attribute.

An example:

<img src="" alt="Visit us"
     tal:attributes="alt context.greeting"

In this example, we let tal:attributes set the value of the alt attribute to the text “Stop by for a visit!”. This text will be passed to the translation service, which uses the result of language negotiation to translate “Stop by for a visit!” into the requested language. The example text in the template, “Visit us”, will simply be discarded.

Another example, with explicit message IDs:

<img src="../icons/uparrow.png" alt="Up"
     i18n:attributes="src up-arrow-icon; alt up-arrow-alttext"

Here, the message ID up-arrow-icon will be used to generate the link to an icon image file, and the message ID ‘up-arrow-alttext’ will be used for the “alt” text.


Since TAL always returns strings, we need a way in ZPT to translate objects, one of the most obvious cases being datetime objects. The data attribute will allow us to specify such an object, and i18n:translate will provide us with a legal format string for that object. If data is used, i18n:translate must be used to give an explicit message ID, rather than relying on a message ID computed from the content.


The i18n:comment attribute can be used to add extra comments for translators. It is not used by Chameleon for processing, but will be picked up by tools like lingua.

An example:

<h3 i18n:comment="Header for the news section"


The i18n:ignore attribute can be used to inform translation extraction tools like i18ndude to not give a warning/error on the given tag if there is no i18n:translate attribute.

An example:

<h1 i18n:ignore="">News</h3>


The i18n:ignore-attributes, just like i18n:ignore is expected to be used by translation extraction tools like i18ndude. If i18n:ignore makes text within a tag to be ignored, i18n:ignore-attributesmarks the given attributes as ignored.

An example:

<a href=""
title="Python!" i18n:ignore-attributes="title">Python website</a>

Relation with TAL processing

The attributes defined in the i18n namespace modify the behavior of the TAL interpreter for the tal:attributestal:contenttal:repeat, and tal:replace attributes, but otherwise do not affect TAL processing.

Since these attributes only affect TAL processing by causing translations to occur at specific times, using these with a TAL processor which does not support the i18n namespace degrades well; the structural expectations for a template which uses the i18n support is no different from those for a page which does not. The only difference is that translations will not be performed in a legacy processor.

Relation with METAL processing

When using translation with METAL macros, the internationalization context is considered part of the specific documents that page components are retrieved from rather than part of the combined page. This makes the internationalization context lexical rather than dynamic, making it easier for a site builder to understand the behavior of each element with respect to internationalization.

Let’s look at an example to see what this means:

<html i18n:translate='' i18n:domain='EventsCalendar'

  <div metal:fill-slot='additional-notes'>
    <ol tal:condition="context.notes">
      <li tal:repeat="note context.notes">
         <tal:block tal:omit-tag=""
           <strong tal:content="note.heading">
             Note heading goes here
           <br />
         <span tal:replace="note/description">
           Some longer explanation for the note goes here.


And the macro source:

<html i18n:domain='CalendarService'>
  <div tal:replace='python:DateTime().Month()'

  <!-- really hairy TAL code here ;-) -->

  <div define-slot="additional-notes">
    Place for the application to add additional notes if desired.


Note that the macro is using a different domain than the application (which it should be). With lexical scoping, no special markup needs to be applied to cause the slot-filler in the application to be part of the same domain as the rest of the application’s page components. If dynamic scoping were used, the internationalization context would need to be re-established in the slot-filler.

Extracting translatable message

Translators use PO files when translating messages. To create and update PO files you need to do two things: extract all messages from python and templates files and store them in a .pot file, and for each language update its .po file. Chameleon facilitates this by providing extractors for Babel. To use this you need modify For example:

from setuptools import setup

      install_requires = [
      message_extractors = { "src": [
            ("**.py",   "chameleon_python", None ),
            ("**.pt",   "chameleon_xml", None ),

This tells Babel to scan the src directory while using the chameleon_python extractor for all .pyfiles and the chameleon_xml extractor for all .pt files.

You can now use Babel to manage your PO files:

python extract_messages --output-file=i18n/mydomain.pot
python update_catalog \
          -l nl \
          -i i18n/mydomain.pot \
          -o i18n/nl/LC_MESSAGES/mydomain.po
python compile_catalog \
          --directory i18n --locale nl

You can also configure default options in a setup.cfg file. For example:

domain = mydomain
directory = i18n

copyright_holder = Acme Inc.
output_file = i18n/mydomain.pot
charset = UTF-8

domain = mydomain
input_file = i18n/mydomain.pot
output_dir = i18n

domain = mydomain
input_file = i18n/mydomain.pot
output_dir = i18n
previous = true

You can now use the Babel commands directly:

python extract_messages
python update_catalog
python compile_catalog

Leave a Comment

Your email address will not be published.