i18n, locales and Plone 3.0

published Sep 25, 2007, last modified Mar 05, 2013

More and more products are developed as python packages instead of Zope products. This means they should not be put in the Products directory anymore, but in the lib/python/ directory of your instance. How you handle translations has changed a bit because of that. Also with Zope 2.10 and higher the translation machinery has changed a bit. So how should you handle translations now?

For the most recent version focusing on Plone 4, see my talk at the Plone Conference 2012.

For a version from 2010 focusing on Plone 3.3 and 4, see a different article on my blog.

How should you handle translations now when dealing with locales, i18n, products, packages and Plone 3.0?

For clarity: I use the following terms here: a product is in the Products dir, a package is in the lib/python dir.

Please correct me if anything I write here is wrong.


Most translations should now be put in the locales directory of your product or package. This directory must be registered in zcml before it is picked up on Zope startup. So in your configure.zcml add:


The locales directory differs a bit from the i18n directory. i18n has for example:


In locales that should be:


You can rebuild the .pot file with:

i18ndude rebuild-pot --pot locales/mydomain.pot --create mydomain .

and you sync the .po files with:

i18ndude sync --pot locales/mydomain.pot locales/*/LC_MESSAGES/mydomain.po

Domain plone

If you want to add translations to the plone domain you could add locales/plone.pot and locales/nl/LC_MESSAGES/plone.po (or locales/plone-mydomain.pot and locales/nl/LC_MESSAGES/plone-mydomain.po if you want). That works: your translations for the plone domain are then available. But now the default translations for the plone domain (so those from PloneTranslations) are overridden. This is because your translations for the plone domain are picked up first by the zope translation machinery; the PlacelessTranslationService that normally loads the translations from PloneTranslations then gets ignored for the plone domain. So half your site is in English even though your browser is set to Dutch.

So extra translations for the plone domain should not be done in the locales directory. You can still put them in the i18n directory though. In Plone 3.5 this is likely to change.

As Hanno Schlichting tells me, the same is of course true for the other domains in PloneTranslations, like atcontenttypes, cmfeditions, plonelanguagetool, etcetera.


If you put an i18n dir in a package, it will be ignored. Doing i18n:registerTranslations for this directory does not work. You can only use an i18n dir in a product.

GenericSetup profiles

Several .xml files in the profiles can handle i18n as well. For instance in a types definition like types/JobPerformanceInterview.xml:

The i18n:domain should be plone and not for instance mydomain as these translations are used in templates of plone itself. In fact, I think that the only use for having these i18n commands in the .xml files is that they can then be extracted by i18ndude (version 3.0 I think, which is best installed in a workingenv).

Compiling .po files

.po files in i18n are compiled on zope startup time by the PlacelessTranslationService. With compiling I mean: turning them into .mo files so they are usable by zope. This automatic compiling does not happen .po files in packages. So it is better to compile those files yourself. You can do that like this:

# Compile po files
for lang in $(find locales -mindepth 1 -maxdepth 1 -type d); do
    if test -d $lang/LC_MESSAGES; then
        msgfmt -o $lang/LC_MESSAGES/${PRODUCTNAME}.mo $lang/LC_MESSAGES/${PRODUCTNAME}.po


  • For both products and packages: put the translations for your domain in the locales directory.
  • Put extra translations for the plone domain in an i18n directory in a product for the Products dir.