Personal tools
You are here: Home Documentation How-Tos How to internationalize your application

How to internationalize your application

This How-to applies to: Any version.
This How-to is intended for: Developer

In this howto, you will learn how to internationalize your code, extract translatable strings and translate your application into an other language.

In this example, a project HelloWorld was created with grokproject HelloWorld.

Internationalizing Strings in Python Code

You need to use a MessageFactory that marks all the strings you want to be able to translate.

in __init__.py, add:

from zope.i18nmessageid import MessageFactory
HelloWorldMessageFactory = MessageFactory('helloworld')

in your code (i.e. app.py) add:

from HelloWorld import HelloWorldMessageFactory as _

now to internationalize your strings in Python code, change:

message = u'Hello World'

to:

message = _(u'Hello World')

You will have in your generated POT:

msgid "Hello World"
msgstr ""

or you can define both a msgid and default:

message = _(u'hello_msg', default=u'Hello World')

You will have in your generated POT:

#. Default: "Hello World"
msgid "hello_msg"
msgstr ""

Example, in app.py:

class Index(grok.View):
    def update(self):
        self.message = _(u'Hello World')

and in app_templates/index.pt:

<html>
<head>
</head>
<body>
  <p tal:content="view/message" />
</body>
</html>

To internationalize text in a page template, you add the i18n:domain attribute to the html-tag. Then you mark each tag who's text you want to translate with the attribute i18n:translate:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
    xmlns:tal="http://xml.zope.org/namespaces/tal"
    xmlns:i18n="http://xml.zope.org/namespaces/i18n"
    lang="en"
    i18n:domain="helloworld">
<head>
    <title>My project</title>
</head>
<body>
  <a tal:attributes="href python: view.url('creategame')"
     i18n:translate="link_create_new_game"
     >Create new game</a>
  <a tal:attributes="href python: view.url('createcontact')"
     i18n:translate=""
     >Create new contact</a>
</body>
</html>

You will have in your generated POT:

#. Default: "Create new game"
msgid "link_create_new_game"
msgstr ""

msgid "Create new contact"
msgstr ""

Setting the locales directory

To activate translations you need to let Grok know what translation domain you are using and where the translation files are. src/helloworld/configure.zcml should look like this:

<configure xmlns="http://namespaces.zope.org/zope"
           xmlns:grok="http://namespaces.zope.org/grok"
           xmlns:i18n="http://namespaces.zope.org/i18n">
  <include package="grok" />
  <includeDependencies package="." />
  <grok:grok package="." />
  <i18n:registerTranslations directory="locales" />
</configure>

Extracting strings with z3c.recipe.i18n:i18n

Grokproject has created two files in your HelloWorld/bin-folder called: i18nextract and i18nmerge.

1. Building the POT file

In your project directory, to extract all internationalizable strings:

./bin/i18nextract

This creates the src/helloworld/locales directory and generates src/helloworld/locales/helloworld.pot

2. Creating new translation

Begin to translate into french:

mkdir -p src/helloworld/locales/fr/LC_MESSAGES/
msginit -i src/helloworld/locales/helloworld.pot -o src/helloworld/locales/fr/LC_MESSAGES/helloworld.po

translate src/helloworld/locales/fr/LC_MESSAGES/HelloWorld.po with poEdit, KBabel... NOTE: If you use an editor such as poEdit, it will generates your compiled .mo translation files when saving, this saves you having to compile them manually.

To use default text strings that you have put in your PageTemplates and python-code you need to create a translation for your default language and generate the .mo file, BUT you don't translate any of the strings.

3. Updating existing translation

To update all PO files, in your buildout directory:

./bin/i18nextract
./bin/i18nmergeall

4. Generating MO files

To finish, you have to generate a MO file for each PO file:

cd locales/fr/LC_MESSAGES/
msgfmt -o helloworld.mo helloworld.po

If you want to generate all mo file, you can use this command:

for po in `find . -name "*.po"` ; do msgfmt -o `dirname $po`/`basename $po .po`.mo $po; done

As mentioned previously, this is done automatically by poEdit when you save changes.

Restart your server and see the translation in french.

There is a new feature in zope.i18n 3.5.0, the changelog says: "Feature: Added optional automatic compilation of mo files from po files. You need to depend on the zope.i18n [compile] extra and set an environment variable called zope_i18n_compile_mo_files to any True value to enable this option."

How Does the Server Know What Language is Used?

Grok/Zope3 uses the Accept-Language header of the HTTP-request to determine what language to present to the user. This is set by the browser and in Macosx you change this in System Preferences / International / Language. In order to test your translations you probably need to change there and restart the browser.