Maurits van Rees:
http://maurits.vanrees.org/
Zest Software, The Netherlands:
http://zestsoftware.nl/
Sample code, including this talk:
https://github.com/mauritsvanrees/maurits.i18ntalk
- What is internationalization?
- Defining strings in templates,
Python
,GenericSetup
,XML
. - Extracting strings with
i18ndude
. - Making Plone aware of the translations.
- Overriding translations.
- Future.
internationalization i...18 characters..n
i18n
means making the web interface appear translated in your
local language instead of the default English.
No localization (l10n
).
No multilingual sites.
Plone 4.0-4.3
- Make strings translatable.
- Extract those strings with i18ndude into a po file (portable object).
- Translate the strings in the .po file.
<html ... xmlns:i18n="http://xml.zope.org/namespaces/i18n" i18n:domain="maurits.i18ntalk"> <span i18n:translate="">A simple message</span> <p i18n:translate="msg_string">This is a message.</p> <span i18n:translate="">Label:</span> <tal:block tal:content="some_content" /> <p i18n:domain="plone" i18n:translate=""> A message in the plone domain. </p> </html>
In the .pot/.po
file it becomes this:
#: browser/test.pt:3 msgid "A simple message" msgstr "Een eenvoudige boodschap" #. Default: "This is a message string." #: ./browser/bookview.pt:14 msgid "msg_string" msgstr "Dit is een boodschap."
These are not used:
#. Default: "This is a message." #: ./browser/bookview.pt:15 #, fuzzy msgid "msg_string" msgstr "Dit is een boodschap."
<p i18n:translate=""> This book has <tal:block i18n:name="stars" tal:content="context/getStars" /> stars. </p> msgid "This book has ${stars} stars." msgstr "Dit boek heeft ${stars} sterren."
If you forget the i18n:name
you get this in your .po
file:
msgid "This book has ${DYNAMIC_CONTENT} stars." msgstr "Dit boek heeft ${DYNAMIC_CONTENT} sterren."
And this translation does not show up.
__init__.py
:
from zope.i18nmessageid import MessageFactory i18ntalkMF = MessageFactory('maurits.i18ntalk')
In your python file:
from maurits.i18ntalk import i18ntalkMF as _ ... def title(self): return _(u"My latest books")
In a template:
<span tal:content="view/title" />
def book_message(self): number = 42 return _(u"There are ${books} books in total.", mapping={'books': number})
logger = ... from zope.i18n import translate msg = _("My books portlet is displayed.") translation = translate(msg, context=self.request) logger.info(translation) # def translate(msgid, domain=None, mapping=None, # context=None, target_language=None, default=None):
profiles/default/types/Book.xml
:
<?xml version="1.0"?> <object name="Book" meta_type="Factory-based Type Information with dynamic views" i18n:domain="maurits.i18ntalk" xmlns:i18n="http://xml.zope.org/namespaces/i18n"> <property name="title" i18n:translate="">Book</property> <property name="description" i18n:translate="">Information about a book</property>
Use your own domain for:
actions.xml controlpanel.xml types/YourType.xml
Use the plone domain for:
portal_atct.xml portlets.xml workflows/your_workflow/definition.xml
When in doubt, use the plone domain.
http://maurits.vanrees.org/weblog/archive/2010/10/i18n-plone-4
<configure xmlns:gs="http://namespaces.zope.org/genericsetup" i18n_domain="maurits.i18ntalk"> <gs:registerProfile name="default" title="Maurits' i18n talk" directory="profiles/default" description="Demo package by Maurits" provides="Products.GenericSetup.interfaces.EXTENSION" /> </configure>
- How to extract?
i18ndude
does not support this (yet).
<configure xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser" i18n_domain="maurits.i18ntalk"> <include package="plone.app.contentmenu" /> <browser:page for="maurits.i18ntalk.interfaces.IBook" name="book_view" ... /> <browser:menuItem for="maurits.i18ntalk.interfaces.IBook" menu="plone_displayviews" title="Book View" action="@@book_view" /> </configure> msgid "Book View"
atapi.IntegerField( 'stars', widget=..., schemata='starschema', ),
Put your translations manually in the plone domain:
#: content/book.py msgid "label_schema_starschema" msgstr "Sterrenschema"
Thanks, Reinout.
locales locales/yourdomain.pot locales/manual.pot locales/plone.pot locales/nl locales/nl/LC_MESSAGES locales/nl/LC_MESSAGES/yourdomain.po locales/nl/LC_MESSAGES/plone.po
buildout.cfg:
[i18ndude] recipe = zc.recipe.egg eggs = i18ndude
update_locales.sh
:
#! /bin/sh i18ndude rebuild-pot \ --pot locales/maurits.i18ntalk.pot \ --create maurits.i18ntalk \ --merge locales/manual.pot \ . for po in locales/*/LC_MESSAGES/maurits.i18ntalk.po; do i18ndude sync --pot locales/maurits.i18ntalk.pot $po done
# Maurits van Rees <maurits@vanrees.org>, 2012. msgid "" msgstr "" "Project-Id-Version: maurits.i18ntalk 1.0\n" "POT-Creation-Date: 2012-10-03 14:36+0000\n" "PO-Revision-Date: 2012-10-03 16:39 +0200\n" "Last-Translator: Maurits <maurits@vanrees.org>\n" "Language-Team: Plone NL <plone-nl@lists.plone.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0\n" "Language-Code: nl\n" "Language-Name: Nederlands\n" "Preferred-Encodings: utf-8 latin1\n" "Domain: maurits.i18ntalk\n"
msgfmt -c locales/nl/LC_MESSAGES/maurits.i18ntalk.po rm messages.mo
<configure xmlns="http://namespaces.zope.org/zope" xmlns:i18n="http://namespaces.zope.org/i18n"> <i18n:registerTranslations directory="locales" /> </configure>
Note:
- zcml:
http://namespaces.zope.org/i18n
- html:
http://xml.zope.org/namespaces/i18n
[instance] recipe = plone.recipe.zope2instance locales = ${buildout:directory}/locales environment-vars = PTS_LANGUAGES en nl zope_i18n_allowed_languages en nl zope_i18n_compile_mo_files true
- in version control: no
- released on PyPI: yes
MANIFEST.in
:
recursive-include collective * recursive-include docs * include * global-exclude *.pyc
easy_install or pip:
easy_install zest.releaser zest.pocompile
buildout:
[release] recipe = zc.recipe.egg eggs = zest.releaser zest.pocompile
Command:
fullrelease
Be the first! Order of loading:
$ cat parts/instance/etc/site.zcml <configure ... <!-- Load the configuration --> <include files="package-includes/*-configure.zcml" /> <five:loadProducts />
locales = ${buildout:directory}/locales
zcml = your.package
- Products alphabetically until and including
Products.CMFPlone
- packages registered with
z3c.autoinclude
- rest of the Products
i18n
folders (done byPlacelessTranslationService
)
- No more
i18n:translate="some_message_id"
. - Babel instead of i18ndude?
[babelpy] recipe = zc.recipe.egg eggs = babel lingua interpreter = babelpy
extract.ini
:
[lingua_python: **.py] [lingua_xml: **pt] [lingua_xml: **.xml] [lingua_zcml: **.zcml]
bin/babelpy setup.py extract_messages
command line options or setup.cfg
:
[extract_messages] mapping_file = extract.ini output_file = ...../locales/maurits.i18ntalk.pot sort_output = true
- Good: has zcml support
- Bad: currently extracts all domains
- support extracting zcml in i18ndude Code: https://github.com/collective/i18ndude
- improve babel or lingua
msgstr "Het einde" msgstr "Schluss" msgstr "La fin" msgstr "Los endos"
This talk plus the code:
https://github.com/mauritsvanrees/maurits.i18ntalk
An occasional blog entry about this Plone conference: