Automating Django translations with DeepLr. The complete guide

Django: automate translations with Deeplr

Written by Fabien Schlegel

Fabien Schlegel

4 min

published: 3/6/2025

Django: automate translations with Deeplr

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 translated
  • update_po : Translate additions
  • compilemessages : 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.

Related Articles