I spent the last couple of hours trying to extract the transation messages from a django project that uses Jinja2 into a .PO file, so I decided to write this small post to explain how I did it.

First, django does not extract the strings from Jinja2 templates (c.f. Django Doc i18n/translation ). The doc says you'll need Babel to extract them so I went and installed it on my project.

Also, if you're using jinja2 templates in a django project, you're most probablly using the django_jinja package. This package implements the most useful extensions from django templates in your jinja2 templates, like: csrf tag, url resolver, static tag, cache, etc. My settings for the templates looks something like:

from django_jinja.builtins import DEFAULT_EXTENSIONS

        "NAME": "jinja2",
        "BACKEND": "django_jinja.backend.Jinja2",
        "APP_DIRS": True,
        "OPTIONS": {
            "match_extension": ".jinja2",
            "app_dirname": "jinja2",
            "auto_reload": True,  # Set to False in prod
            "extensions": DEFAULT_EXTENSIONS + ["pipeline.jinja2.PipelineExtension"],

The problem I had with babel is that it doesn't recognize the template tags that were added by django_jinja or the tags and filters that I created myself. To fix that, you need to add all the extensions into the babel.cfg file:

# Extraction from Python source files
[python: **.py]

# Extraction from Jinja2 templates
[jinja2: **.jinja2]
extensions = jinja2.ext.i18n,pipeline.jinja2...., etc
silent = False 

("silent = False" is quite useful, It will rise an exception if babel can't parse a template file)

This will do the work, however you'll have a super-long-not-readable line in you babel.cfg file. To make it prettier I defined a new extract function that will read the template engine extensions and pass them to the Jinja2 extract function:

# babel.cfg

custom = somewhere.in.my.project:babel_extract

[custom: **.jinja2]
silent = False

# somewhere/in/my/project.py

import django
from jinja2.ext import babel_extract as jinja_babel_extract


def babel_extract(fileobj, keywords, comment_tags, options):
    from django.template import engines
    options['extensions'] = ",".join(engines['jinja2'].env.extensions.keys())
    return jinja_babel_extract(fileobj, keywords, comment_tags, options)

This function will read the django settings and will extract the configured jinja2 extensions used in your project. Now you can run pybabel extract and hopefully it will extract all the translation strings.

Now all I need to learn how to use Pootle XD