localize.dev

Docs / Django

Django template translation, automated

Wrap hardcoded copy in Django templates in translate and blocktranslate tags, fill the gettext .po catalogs, and switch on LocaleMiddleware, in one command or pull request.

How it works

Plain text becomes {% translate "…" %}; text with variables becomes {% blocktranslate with name=user.name %}…{% endblocktranslate %}, and {% load i18n %} is added after {% extends %}.

Translations go straight into locale/<lang>/LC_MESSAGES/django.po, keyed by the source text like makemessages would: existing entries are kept, empty ones filled and new ones appended. Run compilemessages to build them. (The catalog below is from the offline example, so its translations are empty.)

Before and after

The engine’s real output for this file. In a real run, the hosted model names the keys from context.

shop/templates/shop/cart.html · before
{% extends "base.html" %}
{% block content %}
<h1>Hi {{ user.first_name }}, welcome back</h1>
<input placeholder="Search products">
{% endblock %}
shop/templates/shop/cart.html · after
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<h1>{% blocktranslate with first_name=user.first_name %}Hi {{ first_name }}, welcome back{% endblocktranslate %}</h1>
<input placeholder="{% translate 'Search products' %}">
{% endblock %}
locale/es/LC_MESSAGES/django.po
msgid ""
msgstr ""
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Hi %(first_name)s, welcome back"
msgstr ""

msgid "Search products"
msgstr ""

What the setup adds

The first run sets up Django’s i18n (gettext) if the project doesn’t have it yet:

  • USE_I18N = True, LANGUAGES and LOCALE_PATHS in settings
  • LocaleMiddleware after the session middleware

Run it

The CLI needs Node.js 22 or later, nothing else: npx runs it without adding a package.json to the project. No Node on your machine? The GitHub Action below runs it for you.

npx localize-dev login

# Preview, then write the changes
npx localize-dev run --dry-run -l es,fr,de
npx localize-dev run -l es,fr,de

# Or open a pull request instead
npx localize-dev pr -l es,fr,de

Or add the GitHub Action and get a pull request on every push. Try it on your own code first in the playground.