Django: automate translations with Deeplr

published: 3/6/2025

I’ve never had a translator on any of the software projects I’ve worked on. And yet, translations had to be added. And often, to simplify things, I had the content in French and translated it myself.
With Deeplr, it’s very simple: I paste my content into the application and I get my translation.
But Deeplr also provides an API, and I’m going to show you how to use it to automate translations simply and easily.
Preparing your Django project for internationalization
To activate internationalization with Django, simply add this setting to the settings.py
file
USE_I18N = True
Here’s an example of strings marked for translation.
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class DashboardConfig(AppConfig):
name = "dashboard"
verbose_name = _("Dashboard")
For Python code, we import the translation method and use it on the string to be translated.
{% load i18n %}
<h2 class="title has-text-centered">{% translate "Login" %}</h2>
For templates, we add the tag that loads the internationalization and put the strings to be translated in a translate
tag.
Generating translation files
To generate translation files, use Django’s built-in command.
django-admin makemessages -l fr
And fr is the language for which you will generate the translation files.
Deeplr API configuration
The API key
To retrieve the API key, simply go to the Deeplr website and create an account.
Once the account has been created, go to the account and API key menu to generate our key.
Deeplr Python library
Next, we’ll add the Deeplr library to our project.
pip install deepl
Configuration in Django
For security reasons, we strongly advise against putting the API key directly into our settings.py
file.
We’ll add it to an .env
file and import it into our configuration files using the decouple
library.
from decouple import config
DEEPL_API_KEY = config("DEEPL_API_KEY")
Translation automation
Translation method
import deepl
from django.conf import settings
translator = deepl.DeepLClient(settings.DEEPL_API_KEY)
def translate(text, target_lang):
result = translator.translate_text(text, target_lang=target_lang)
return result.text
We initialize a DeepLClient
class with our API key and then use the translate_text
method with our string to translate and the target language.
The translation command
Django translation files use the .po
extension, so we’ll use the polib
library to browse them.
pip install polib
Using the administration command system, we’ll create an update_po
command to update our translations.
import os
from django.core.management.base import BaseCommand, CommandError
from core.translator import translate
from django.conf import settings
class Command(BaseCommand):
help = "Met à jour les fichiers .po avec des traductions Deeplr"
def add_arguments(self, parser):
parser.add_argument(
"-l", "--locale", type=str, help="Langue cible (ex: fr, de, es)"
)
def handle(self, *args, **options):
try:
if settings.DEBUG is False:
raise Exception("Must be used in development only")
import polib
locale = options["locale"]
base_dir = os.getcwd()
for app in os.listdir(base_dir):
locale_path = os.path.join(
base_dir, app, "locale", locale, "LC_MESSAGES"
)
po_file_path = os.path.join(locale_path, "django.po")
if not os.path.exists(po_file_path):
continue
po = polib.pofile(po_file_path)
for entry in po:
if not entry.translated():
translated_text = translate(entry.msgid, locale)
entry.msgstr = translated_text
self.stdout.write(
self.style.SUCCESS(
f"[{app}] Traduit: {entry.msgid} -> {translated_text}"
)
)
po.save()
self.stdout.write(
self.style.SUCCESS(f"Mise à jour terminée pour {po_file_path}")
)
except Exception as e:
print(e)
raise CommandError("Error during translation")
This command allows us to update one language at a time throughout our application. For example, here in French.
python manage.py update_po --locale fr
Using polib
, we browse our .po
files containing the strings to be translated. If a translated string is empty, we send the string to be translated to Deeplr and retrieve the result.
Once all the strings in the file have been translated, we save the file.
The polib
library will not be installed on production environments. This is why it is loaded into the handle
function, after a check of the DEBUG
configuration variable and not at the beginning of the file.
The translation pipeline
Django lets you call administration commands within a single command. This allows us to perform the whole chain of a move for a given language.
makemessages
: Update string files to be translatedupdate_po
: Translate additionscompilemessages
: Compile translations
python manage.py make_translations --lang fr
Here’s the command file.
from django.core.management.base import BaseCommand, CommandError
from django.core.management import call_command
class Command(BaseCommand):
help = "Mise à jour complète des traductions"
def add_arguments(self, parser):
parser.add_argument(
"-l", "--lang", type=str, help="Langue cible (ex: fr, de, es)"
)
def handle(self, *args, **options):
try:
lang = options["lang"]
self.stdout.write(self.style.WARNING("Execute makemessages"))
call_command("makemessages", locale=[lang])
self.stdout.write(self.style.SUCCESS(f"makemessages done for {lang}."))
self.stdout.write(self.style.WARNING("Update translations..."))
call_command("update_po", locale=lang)
self.stdout.write(self.style.WARNING("Translations updated"))
self.stdout.write(
self.style.WARNING("Start compiling translations")
)
call_command("compilemessages")
self.stdout.write(self.style.SUCCESS("Compilation is complete."))
except Exception as e:
print(e)
raise CommandError("Error during translation")
Conclusion
Thanks to this system, it’s much easier to translate our application into other languages automatically.
It doesn’t replace a real translator, of course. I’ve had a few surprises while browsing the translations, but on the whole it covers my needs.