djangotemplatesinternationalizationjinja2python-babel

Switch language in jinja template


I'm migrating a multi lingual Django application from Django's template engine to Jinja2. In the templates I currently switch the active language on a per object basis using Django's language template tag i.e.:

{% load i18n %}
<h1>{% trans 'Page title' %}</h1>
<ul>
{% for obj in object_list %}
{% language obj.language_code %}
    <li><a href="{{ obj.get_absolute_url }}">{% trans 'view' %}: {{ obj.title }}</a>
{% endlanguage %}
{% endfor %}
</ul>

We also use i18n_patterns so the urls of each object are language specific as well.

I'm stuck on how to convert this to Jinja. I cannot use Django's i18n template tags and cannot find something equivalent for Jinja.

I was also looking at Babel to help with extracting messages from the templates. So a solution that works with Babel as well as with Django would be preferred.


Solution

  • It turns out it's fairly simple to do this by writing a custom jinja2 extension (I've based this on the example in the jinja2 docs):

    from django.utils import translation
    from jinja2.ext import Extension, nodes
    
    class LanguageExtension(Extension):
        tags = {'language'}
    
        def parse(self, parser):
            lineno = next(parser.stream).lineno
            # Parse the language code argument
            args = [parser.parse_expression()]
            # Parse everything between the start and end tag:
            body = parser.parse_statements(['name:endlanguage'], drop_needle=True)
            # Call the _switch_language method with the given language code and body
            return nodes.CallBlock(self.call_method('_switch_language', args), [], [], body).set_lineno(lineno)
    
        def _switch_language(self, language_code, caller):
            with translation.override(language_code):
                # Temporarily override the active language and render the body
                output = caller()
            return output
    
    # Add jinja2's i18n extension
    env.add_extension('jinja2.ext.i18n')
    # Install Django's translation module as the gettext provider
    env.install_gettext_translations(translation, newstyle=True)
    # Add the language extension to the jinja2 environment
    environment.add_extension(LanguageExtension)
    

    With this extension in place switching the active translation language is pretty much exactly like how you'd do it in Django:

    {% language 'en' %}{{ _('Hello World'){% endlanguage %}
    

    The only caveat is that when using Django as a gettext provider and Babel as a message extractor it's important to tell Babel to set the message domain to django when running init/update/compile_catalog.