Use Babel to translate your python package¶
I use egg-containing buildouts for all my plone packages. Since i18ndude does not extract msgids from zcml files, i tried Babel and succeeded.
Problem¶
You want to translate titles in Plone’s diplay menu by adding
browser:menuItem
in your zcml file as shown
here.
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:plone="http://namespaces.plone.org/plone"
xmlns:i18n="http://namespaces.zope.org/i18n"
i18n:domain="my.package">
<browser:page
for="plone.folder.interfaces.IOrderableFolder"
name="a_new_view"
class=".demo_view.DemoView"
permission="zope2.View"
template="templates/demo_view.pt"
/>
<!-- Entry in display menu -->
<browser:menuItem
for="plone.folder.interfaces.IOrderableFolder"
menu="plone_displayviews"
title="A new view"
action="@@a_new_view"
description="I want a view with translated title and description"
i18n:attributes="title; description"
/>
</configure>
i18ndude does not extract message ids from zcml files, so i tried a diffent solution using Babel.
Update your setup.py file¶
First of all you should tune your setup.py
file as shown below. Just
replace my.package
in all code-snippets with your package name. You
need to add an additional requirement to lingua.
setup(name='my.package',
version=version,
...
install_requires=[
...
'lingua==1.6', # lingua 2.0 has a new api
],
extras_require={
'test': [
'plone.app.testing [robot]',
],
},
message_extractors={
'src': [
("**.py", "lingua_python", None),
("**.pt", "lingua_xml", None),
('**.zcml', "lingua_xml", None),
('**.xml', "lingua_xml", None),
],
},
entry_points="""
# -*- Entry points: -*-
[z3c.autoinclude.plugin]
target = plone
""",
)
Configure buildout¶
You’ll need to update your egg-contained buildout.cfg
to generate a
new interpreter which includes Babel and a small script - i’ll
call it update_translations
- which does all the magic.
[buildout]
# ...
develop = .
parts =
# ...
babelpy
update_translations
# ...
[babelpy]
recipe = zc.recipe.egg
eggs =
setuptools
Babel
lingua==1.6
interpreter = babelpy
[update_translations]
recipe = collective.recipe.template
output = ${buildout:directory}/bin/update_translations
input = inline:
#!/bin/bash
# XXX: Update your package name and path here
# --------------------------------------------
DOMAIN="my.package"
BASE_PATH=${buildout:directory}/src/my/package
# --------------------------------------------
# No modifications below
${buildout:directory}/bin/${babelpy:interpreter} ${buildout:directory}/setup.py extract_messages \
-o $BASE_PATH/locales/$DOMAIN.pot \
-w 79
# sync all locales, create if they do not exist
for PO_FILE in `find $BASE_PATH/locales -maxdepth 1 -mindepth 1 -type d \
| grep -v .svn \
| sed -e "s/.*locales\/\(.*\)$/\1/"`; do
if [ ! -f $BASE_PATH/locales/$PO_FILE/LC_MESSAGES/$DOMAIN.po ]; then
echo "Create $BASE_PATH/locales/$PO_FILE/LC_MESSAGES/$DOMAIN.po"
touch $BASE_PATH/locales/$PO_FILE/LC_MESSAGES/$DOMAIN.po
fi
${buildout:directory}/bin/${babelpy:interpreter} \
${buildout:directory}/setup.py update_catalog \
-l $PO_FILE -i $BASE_PATH/locales/$DOMAIN.pot \
-o $BASE_PATH/locales/$PO_FILE/LC_MESSAGES/$DOMAIN.po
done
mode = 755
Now re-run buildout:
$ bin/buildout
Extract msgids and sync translations¶
After buildout succeeded you should have two new executeables in your
bin/
directory. You can ignore bin/babelpy
it will just be used by
your generated script.
$ bin/update_translations
running extract_messages
extracting messages from src/my/__init__.py
extracting messages from src/my/package/__init__.py
extracting messages from src/my/package/configure.zcml
extracting messages from src/my/package/testing.py
extracting messages from src/my/package/browser/__init__.py
extracting messages from src/my/package/browser/configure.zcml
extracting messages from src/my/package/browser/demo_view.py
extracting messages from src/my/package/browser/templates/demo_view.pt
extracting messages from src/my/package/browser/templates/demo_viewlet.pt
extracting messages from src/my/package/content/__init__.py
extracting messages from src/my/package/content/configure.zcml
extracting messages from src/my/package/content/demo.py
extracting messages from src/my/package/profiles/default/metadata.xml
extracting messages from src/my/package/profiles/default/propertiestool.xml
extracting messages from src/my/package/profiles/default/types.xml
extracting messages from src/my/package/profiles/default/types/my.package.xml
extracting messages from src/my/package/tests/__init__.py
extracting messages from src/my/package/tests/test_demo.py
writing PO template file to /home/daniel/workspace/my.package/src/my/package/locales/my.package.pot
If you create multiple language folders in your
src/my/package/locales
directory, all languages will be updated
automatically. Even if there is no .po
file in there it will be
created for you and you’ll see following additional lines when calling
update_translations
(for example having de and fr as additional
languages):
running update_catalog
updating catalog '/home/daniel/workspace/my.package/src/my/package/locales/de/LC_MESSAGES/my.package.po' based on '/Users/daniel/Workspace/my.package/src/my/package/locales/my.package.pot'
running update_catalog
updating catalog '/home/daniel/workspace/my.package/src/my/package/locales/fr/LC_MESSAGES/my.package.po' based on '/Users/daniel/Workspace/my.package/src/my/package/locales/my.package.pot'
Update *.zcml files and include locales folder¶
Don’t forget to include your locales folder in configure.zcml
file.
<configure
xmlns:i18n="http://namespaces.zope.org/i18n"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
i18n:domain="my.package">
<i18n:registerTranslations directory="locales" />
</configure>
Important: You’ll have to use i18n:domain
in your zcml files
instead of i18n_domain
otherwise Babel will not extract your zcml
message ids.
Note
Version note: Because there were a lot of
changes in
lingua >= 2.0
, this tutorial ist for lingua < 2.0
only.