6

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.

Machavity
  • 30,841
  • 27
  • 92
  • 100
jaap3
  • 2,696
  • 19
  • 34
  • Did you see how it's implemented in `django-jinja` app? – Sergei Zherevchuk Jul 03 '17 at 11:04
  • I'm assuming you mean [this django-jinja](https://github.com/niwinz/django-jinja)? I cannot find an implementation of the `language` template tag in there. – jaap3 Jul 03 '17 at 12:26
  • have you tried this syntax? http://jinja.pocoo.org/docs/2.9/templates/#i18n – HassenPy Jul 06 '17 at 11:37
  • The translating bit is not the issue. I'm looking for a way to switch between the active language for the message catalog during the template rendering. So part of the template uses i.e. German translations while some parts in the same template use English translations. – jaap3 Jul 06 '17 at 14:23

2 Answers2

3

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.

jaap3
  • 2,696
  • 19
  • 34
1

I have this code snippet to switch between languages in jinja2.

def change_lang(request, lang=None, *args, **kwargs):
"""
Get active page's url by a specified language, it activates 
Usage: {{ change_lang(request, 'en') }}
"""

path = request.path
url_parts = resolve(path)

url = path
cur_language = get_language()
try:
    activate(lang)
    url = reverse(url_parts.view_name, kwargs=url_parts.kwargs)
finally:
    activate(cur_language)

return "%s" % url

in settings.py

TEMPLATES = [
{
    "BACKEND": "django_jinja.backend.Jinja2",
    'DIRS': [
        os.path.join(BASE_DIR, 'templates/jinja'),
    ],
    "OPTIONS": {
        # Match the template names ending in .html but not the ones in the admin folder.
        "match_extension": ".html",
        "match_regex": r"^(?!admin/).*",
        "newstyle_gettext": True,
        "extensions": [
            "jinja2.ext.do",
            "jinja2.ext.loopcontrols",
            "jinja2.ext.with_",
            "jinja2.ext.i18n",
            "jinja2.ext.autoescape",
            "django_jinja.builtins.extensions.CsrfExtension",
            "django_jinja.builtins.extensions.CacheExtension",
            "django_jinja.builtins.extensions.TimezoneExtension",
            "django_jinja.builtins.extensions.UrlsExtension",
            "django_jinja.builtins.extensions.StaticFilesExtension",
            "django_jinja.builtins.extensions.DjangoFiltersExtension",
        ],
        'globals': {
            'change_lang': 'drug.utils.change_lang'
        },
        "bytecode_cache": {
            "name": "default",
            "backend": "django_jinja.cache.BytecodeCache",
            "enabled": False,
        },
        "autoescape": True,
        "auto_reload": DEBUG,
        "translation_engine": "django.utils.translation",
        "context_processors": [
            "dashboard.context_processors.auth",
            # "django.template.context_processors.debug",
            "django.template.context_processors.i18n",
            # "django.template.context_processors.media",
            # "django.template.context_processors.static",
            # "django.template.context_processors.tz",
            "django.contrib.messages.context_processors.messages",
        ]
    }
},
{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [
        os.path.join(BASE_DIR, 'templates'),
    ],
    'APP_DIRS': True,
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            'django.template.context_processors.request',
            'django.contrib.auth.context_processors.auth',
            'django.contrib.messages.context_processors.messages',
        ]
    },
},]

and then you can use this in anywhere in your templates {{ _('Hello World') }}

heykarimoff
  • 709
  • 8
  • 14