Internationalization is a process to make your code locale and language aware. Usually this means supplying translation files for text strings used in the code.
Plone internally uses UNIX standard gettext tool to perform i18n.
There are two separate gettext systems. Both use .po <http://www.gnu.org/software/hello/manual/gettext/PO-Files.html> file format to describe translations.
Note that this chapter concerns only code-level translations. Content translations are managed by Products.LinguaPlone add-on product.
Plone (at least 3.3) uses only file path and name to search the translation files. Information in the .po file headers is ignored.
Each module declares its own MessageFactory which is a callable and marks strings with translation domain. MessageFactory is declared at main __init__.py file of your package.
from zope.i18nmessageid import MessageFactory
# your.app.package must match domain declaration in .po files
yourAppMessageFactory = MessageFactory('your.app.package')
You also need to have the following ZCML entry:
<configure
xmlns:i18n="http://namespaces.zope.org/i18n">
<i18n:registerTranslations directory="locales" />
</configure>
After the set-up above you can use message factory to mark strings with translation domains. i18ndude translation utilities use underscore _ to mark translatable strings (gettext message ids). Message ids must be unicode strings.
from your.app.package import yourAppMessageFactory as _
my_translatable_text = _(u"My text") # my_text is zope.
The object will still look like a string:
>>> my_translatable_text
u'Test'
But in reality it is zope.i18nmessageid.message.Message object.
>>> my_translatable_text.__class__ <type 'zope.i18nmessageid.message.Message'>>>> my_translatable_text.domain 'your.app.package'
Plone will automatically perform translation for message ids which are outputted in page templates.
The following code would translate my_translateable_text to the native language activated for the current page.
<span tal:content="view/my_translateable_text">
Note that since my_translateable_text is zope.i18nmessageid.message.Message containing its own gettext domain information, the i18n:domain attribute in page templates does not affect message ids declared through message factories.
If you need to manipulate translated text outside page templates, you need to perform the final translation manually.
Translation always needs context (i.e. under which site the translation happens), as the active language and other preferences are read from the HTTP request object and site object settings.
Translation can be performed using context.translate() method:
# Translate some text
msgid = _(u"My text") # my_text is zope.
# Use inherited translate() function to get the final text string
translated = self.context.translate(msgid)
# translated is now u"Käännetty teksti" (in Finnish)
There are also other message id markers in code outside Python domain
Translation string substitutions must be used when the final translated message contains variable strings in it.
Plone content classes inherit translate() function which can be used to get the final translated string. It will use the currently activate language. Translation domain will be taken from the msgid object itself, which is string-like zope.i18nmessageid instance.
Message ids are immutable (read-only) objects so you need to always create a new message id if you use different variable substituion mappings.
Python code:
from saariselka.app import appMessageFactory as _
class SomeView(BrowserView):
def do_stuff(self):
msgid = _(u"search_results_found_msg", default=u"Found ${results} results", mapping={ u"results" : len(self.contents)})
# Use inherited translate() function to get the final text string
translated = self.context.translate(msgid)
# Show the final result count to the user as a portal status message
messages = IStatusMessage(self.request)
messages.addStatusMessage(translated, type="info")
Corresponding .po file entry:
#. Default: "Found ${results} results"
#: ./browser/accommondationsummaryview.py:429
msgid "search_results_found_msg"
msgstr "Löytyi ${results} majoituskohdetta"
For more information, see
i18ndude is developer command-line utility to manage .po and .mo files.
Usually you build our own shell script wrapper around i18ndude to automatize generation of .mo files of your product .po files.
Examples
Example:
mkdir locales
mkdir locales/fi
mkdir locales/en
mkdir locales/fi/LC_MESSAGES
mkdir locales/en/LC_MESSAGES
Example:
i18ndude rebuild-pot --pot locales/mydomain.pot --create your.app.package .
Example shell script to manage i18n files. Change CATALOGNAME to reflect the actual package of your product:
#!/bin/sh
#
# Shell script to manage .po files.
#
# Run this file in the folder main __init__.py of product
#
# E.g. if your product is yourproduct.name
# you run this file in yourproduct.name/yourproduct/name
#
#
# Copyright 2009 Twinapex Research http://www.twinapex.com
#
# Assume the product name is the current folder name
CURRENT_PATH=`pwd`
CATALOGNAME="yourproduct.app"
# List of languages
LANGUAGES="en fi"
# Create locales folder structure for languages
install -d locales
for lang in $LANGUAGES; do
install -d locales/$lang/LC_MESSAGES
done
# Rebuild .pot
i18ndude rebuild-pot --pot locales/$CATALOGNAME.pot --create $CATALOGNAME .
# Compile po files
for lang in $(find locales -mindepth 1 -maxdepth 1 -type d); do
if test -d $lang/LC_MESSAGES; then
PO=$lang/LC_MESSAGES/${CATALOGNAME}.po
# Create po file if not exists
touch $PO
# Sync po file
echo "Syncing $PO"
i18ndude sync --pot locales/$CATALOGNAME.pot $PO
# Compile .po to .mo
MO=$lang/LC_MESSAGES/${CATALOGNAME}.mo
echo "Compiling $MO"
msgfmt -o $MO $lang/LC_MESSAGES/${CATALOGNAME}.po
fi
done