← Blog

How to translate a Django project: makemessages, .po files and automatic translation

Translate the .po files of your Django project into English, Portuguese and French with python manage.py translate: extract, translate, write and compile. With the real output.

· 2 min de lectura

Translating a Django project usually goes like this: makemessages, opening each .po, copying the msgid into a translator or ChatGPT, pasting the msgstr one by one, praying that no %(name)s breaks and running compilemessages. And repeating it with every new text.

In this guide we do it with a single command, python manage.py translate, using the translate-projects-django package. Everything you see below (terminal and .po files) is the real output of a run on a test project.

What you need

  • Python 3.8 or higher and Django 3.2 or higher.
  • GNU gettext (xgettext and msgfmt), the same as for any translation in Django: sudo apt-get install gettext or brew install gettext.
  • A free Translate Projects account with a project and its API token ([Getting started](https://doc.translateprojects.dev/docs/primeros-pasos?utm_source=blog&utm_medium=articulo&utm_campaign=blog-django)).
  • The AI API key of your provider (Claude, OpenAI, DeepSeek or Gemini). There are no plans or credit card: you only pay the tokens you use with your provider.

1. Install the package

pip install translate-projects-django

2. Configure settings.py

import os

INSTALLED_APPS = [
    # ...
    "translate_projects_django",
]

MIDDLEWARE = [
    # ...
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.locale.LocaleMiddleware",
    "django.middleware.common.CommonMiddleware",
    # ...
]

USE_I18N = True
LANGUAGE_CODE = "es"  # idioma base
LANGUAGES = [
    ("es", "Español"),
    ("en", "English"),
    ("pt-br", "Português (Brasil)"),
    ("fr", "Français"),
]
LOCALE_PATHS = [BASE_DIR / "locale"]
TRANSLATE_PROJECTS_API_KEY = os.environ.get("TRANSLATE_PROJECTS_API_KEY")

Django codes (pt-br, zh-hans) are converted automatically to those of the API. LANGUAGE_CODE is your base language and is never translated.

3. Mark your texts as usual

Nothing you already do in Django changes: gettext in Python and {% translate %} in the templates.

# views.py (ejemplo)
from django.utils.translation import gettext as _

def home(request):
    message = _("Cerrar sesión")
{# templates/home.html (ejemplo) #}
{% load i18n %}
<h1>{% translate "Bienvenido a tu panel" %}</h1>
<p>{% blocktranslate %}Hola {{ name }}, tienes mensajes nuevos{% endblocktranslate %}</p>

4. Translate

export TRANSLATE_PROJECTS_API_KEY=tu_token
python manage.py translate

Real output (6 texts to English, Portuguese and French):

[05:19:47 PM] 🎯 Start Generate Translations
              📂 6 texts found in es
[05:19:48 PM] ✅ Sources unchanged, upload skipped
📝 Translating (en)    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
📝 Translating (pt-br) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
📝 Translating (fr)    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
[05:19:50 PM] ⏱ extract 0.0s · upload 0.4s · compile 0.0s · total 2.5s · 7 HTTP requests
                 en: 6 filled, 0 missing, 0 rejected · translate 1.4s · download 0.4s ·
              write 0.0s
                 pt-br: 6 filled, 0 missing, 0 rejected · translate 1.6s · download 0.1s ·
                 fr: 6 filled, 0 missing, 0 rejected · translate 1.8s · download 0.3s ·
              🎉 Translations completed ✅

Two and a half seconds in total. The command runs makemessages, translates up to 3 languages in parallel, writes the .po files and finishes with compilemessages. This is how the French looks:

# locale/fr/LC_MESSAGES/django.po
#: demo/templates/demo.html:2
msgid "Bienvenido a tu panel"
msgstr "Bienvenue sur votre tableau de bord"

#: demo/templates/demo.html:3
#, python-format
msgid "Hola %(name)s, tienes mensajes nuevos"
msgstr "Bonjour %(name)s, vous avez de nouveaux messages"

#: demo/templates/demo.html:4
msgid "<b>Guardar</b> cambios"
msgstr "<b>Enregistrer</b> les modifications"

#: demo/templates/demo.html:5
#, python-format
msgid "100 %% gratis"
msgstr "100 %% gratuit"

#: demo/views.py:3
msgid "Cerrar sesión"
msgstr "Se déconnecter"

#: demo/views.py:3
msgid "Configuración de la cuenta"
msgstr "Paramètres du compte"

Files it generates

locale/
├── en/LC_MESSAGES/django.po    ← y su django.mo
├── pt_BR/LC_MESSAGES/django.po
├── fr/LC_MESSAGES/django.po
└── .translate-projects.json    ← caché

Commit the .po files and .translate-projects.json so that other machines and your CI skip the languages that are already up to date.

Useful commands

python manage.py translate --locale en   # solo un idioma
python manage.py translate --force       # descarga todo otra vez
  • A second run without new texts makes a single request and skips the complete languages: what is already translated doesn't spend tokens.
  • Don't edit the .po files by hand: they are rebuilt on every run. Edit the translation in the panel and run with --force.
  • Current limitation: plurals (ngettext, blocktranslate count) are not translated yet and stay in the base language.

Common errors

  • TRANSLATE_PROJECTS_API_KEY is not set in settings.py: export the variable and read it in settings.py.
  • The source language is not the default: LANGUAGE_CODE must be the project's default language.
  • MISSING_API_KEY: configure provider, model and AI key in the project.
  • CommandError: Can't find msguniq o xgettext: GNU gettext is missing.

The full guide is available at doc.translateprojects.dev. Create your project for free and translate your Django project today.