Permalink
Browse files

1. add docs explaining how to get i18n to work with jinja2 templates

2. At present in i18n.py, the method get_localizer creates a default Translations object. The domain of these translations is the default 'messages'. If subsequent translations are found, they are added as fallbacks, so they cannot be accessed unless the domain is passed in. This causes trouble since jinja2 does not call dgettext, but rather gettext.  This patch changes get_localizer such that it finds all the translations in the translation dirs and puts them in the list. it uses the first one found as default, and calls translations.add passing in any additional translations objects found. If no translations were found, then we create the default Translations object with domain messages.

3. After this fix was made, two tests broke.  As it turns out there is a deformsite.mo file in the en directory, but it has German translations. I regenerated these files to have English in them.  A minor tweak was made to the test for get_localizer after testing a translation to German, a new request is created, the current_locale is set to english, and we call localizer.translate expecting english to be returned. it is returned.
  • Loading branch information...
1 parent 49315b6 commit e718d34a5372be2a9e791dfaf601217ae581496e @jeffmad committed Jan 16, 2011
View
@@ -1021,3 +1021,111 @@ For example:
.. note:: You can also add a custom locale negotiator via ZCML. See
:ref:`zcml_adding_a_locale_negotiator`
+
+i18n with Jinja2
+------------------
+In addition to all of the regular steps to get i18n
+working in pyramid, if you are using jinja2, there are
+three more steps to enable i18 in your templates:
+
+- Enable the jinja2 i18n extension in your application
+
+- Add a message extractor to ``setup.py``
+
+- Add translation tags to your templates
+
+The sections below explain how to complete these items.
+
+Enable the jinja2 i18n extenstion
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Jinja2 has an internationalization extension, but it is not
+enabled by default. To enable the extension, add a line to your
+``development.ini`` file just like line 4.
+
+.. code-block:: ini
+ :linenos:
+
+ [app:myapp]
+ #...
+ jinja2.directories = myapp:templates
+ jinja2.extensions = jinja2.ext.i18n
+
+If you want to enable more than one extension, then use a syntax like below, using a backslash to escape the newline.
+
+.. code-block:: text
+ :linenos:
+
+ [app:myapp]
+ #...
+ jinja2.directories = myapp:templates
+ jinja2.extensions = jinja2.ext.i18n\
+ jinja2.ext.do\
+ jinja2.ext.autoescape
+ another.setting = blue
+
+If you are not using ``paster`` and therefore do not have an ``.ini`` file,
+then you can add a setting to be passed into your ``Configurator``.
+
+Add a message extractor to ``setup.py``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order for the translation tags ``{% trans %}Translate me{% endtrans%}``
+in the jinja2 templates to be added to your :term:`gettext`
+files from your application, you need to add a message
+extractor function in your application's ``setup.py`` file (see line 10).
+It is assumed that ``Babel`` is installed.
+This additional message extractor is directing the extraction process
+to look in all files ending with ``.jinja2`` to be extracted by the function
+``babel_extract`` in the package ``jinja2.ext``. The last parameter,
+specified below as ``None`` is an optional dictionary of options to be passed
+into the jinja2 environment during the message extraction process..
+
+.. code-block:: python
+ :linenos:
+
+ setup(name="mypackage",
+ # ...
+ install_requires = [
+ # ...
+ 'Babel',
+ ],
+ message_extractors = { '.': [
+ ('**.py', 'chameleon_python', None ),
+ ('**.pt', 'chameleon_xml', None ),
+ ('**.jinja2', 'jinja2.ext:babel_extract', None)
+ ]},
+ )
+
+For jinja2, the options allow you to override various conventions.
+Here is an example providing some overrides.
+
+.. code-block:: python
+ :linenos:
+
+ setup(name="mypackage",
+ # ...
+ install_requires = [
+ # ...
+ 'Babel',
+ ],
+ message_extractors = { '.': [
+ ('**.py', 'chameleon_python', None ),
+ ('**.jinja2', 'jinja2.ext:babel_extract', {
+ 'block_start_string':'{%%',
+ 'block_end_string':'%%}',
+ 'newstyle_gettext':'True'
+ })
+ ]},
+ )
+
+Add translation tags to your templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The last thing to do is to add translation tags into your templates.
+You can consult the ``jinja2`` docs `here <http://jinja.pocoo.org/templates/#i18n-in-templates>`_.
+Most applications can make use of the default locale negotiator, which
+requires no additional coding or configuration.
+
+One thing to keep in mind is that unlike chameleon, jinja2 will not call translate on any of the ``TranslationString`` objects that you have passed into the template. It is up to you to call the :meth:`pyramid.i18n.Localizer.translate` method before passing the string to jinja2.
+
+Jinja2 will call ``gettext`` and ``ngettext`` on the translations object that was passed to it. This means that only the default domain of your messages can be accessed. If you need jinja2 to pass in the domain to perform a translation, then you will need to access the Jinja2 environment from the ``registry``, and use the Jinja2 api to install your own callables for ``gettext`` and ``ngettext``.
View
@@ -162,8 +162,7 @@ def get_localizer(request):
if localizer is None:
# no localizer utility registered yet
- translations = Translations()
- translations._catalog = {}
+ translations_found = []
tdirs = registry.queryUtility(ITranslationDirectories, default=[])
for tdir in tdirs:
locale_dirs = [ (lname, os.path.join(tdir, lname)) for lname in
@@ -181,7 +180,14 @@ def get_localizer(request):
mofp = open(mopath, 'rb')
domain = mofile[:-3]
dtrans = Translations(mofp, domain)
- translations.add(dtrans)
+ translations_found.append(dtrans)
+ if translations_found:
+ translations = translations_found[0]
+ for t in translations_found[1:]:
+ translations.add(t)
+ else:
+ translations = Translations()
+ translations._catalog = {}
localizer = Localizer(locale_name=current_locale_name,
translations=translations)
@@ -1,4 +1,4 @@
-# German translations for deformsite.
+# English translations for deformsite.
# Copyright (C) 2010 ORGANIZATION
# This file is distributed under the same license as the deformsite project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
@@ -19,13 +19,13 @@ msgstr ""
#: deformsite/__init__.py:458
msgid "Approve"
-msgstr "Genehmigen"
+msgstr "Approve"
#: deformsite/__init__.py:459
msgid "Show approval"
-msgstr "Zeigen Genehmigung"
+msgstr "Show approval"
#: deformsite/__init__.py:466
msgid "Submit"
-msgstr "Beugen"
+msgstr "Submit"
@@ -196,6 +196,9 @@ def test_locale_from_mo(self):
self.assertEqual(result.__class__, Localizer)
self.assertEqual(result.translate('Approve', 'deformsite'),
'Genehmigen')
+ request = DummyRequest()
+ request.locale_name = 'en'
+ result = self._callFUT(request)
self.assertEqual(result.translate('Approve'), 'Approve')
self.failUnless(hasattr(result, 'pluralize'))

0 comments on commit e718d34

Please sign in to comment.