Adding multiple languages in Django can be a bit challenging when you have never done this before. In this easy tutorial, we will walk through the steps that you will have to take to support multiple languages. We will start with the English language and then add the Dutch language as well (as I am Dutch :) ), but you can use any language you want. The steps are the same! Okay, let’s get started. I will start with a fresh project, but you can skip this if you already have your poject ready.

Create a project

Create a new folder somewhere on your computer and navigate to that folder (cd ~/languages/).

I am not getting into the details of setting up your environment for Django, but here is the code you’ll need on a Linux/Mac computer:

virtualenv .env
source .env/bin/activate
pip install django
django-admin startproject languages
cd languages
python manage.py migrate

Your environment should now be prepped and should see the default page when you launch your server (python manage.py runserver).

You should be greeted with the launch icon on the Django home page:

Django launch screen

Let’s create a demo page and URL. First, create a new project in your app:

python manage.py startapp home

Add home to your settings.py:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # add here:
    'home'
]

In your home app, we will create a new view (in your views.py file):

def index(request):
    return render(request, 'home.html')

In your languages/urls.py file you can add this line in your urlpatterns array:

path('', index)

Don’t forget to import the view with from home.views import index.

Finally, we need to create a home.html page to display something. Create a home/templates/home.html file and add this (slightly modified) default template that I grabbed from here:

<!doctype html>
<html class="no-js" lang="">

<head>
  <meta charset="utf-8">
  <title></title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="manifest" href="site.webmanifest">
  <meta name="theme-color" content="#fafafa">
</head>

<body>
  <!--[if IE]>
    <p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="https://browsehappy.com/">upgrade your browser</a> to improve your experience and security.</p>
  <![endif]-->

  <p>Hello world! This is HTML5 Boilerplate.</p>
</body>

</html>

This should display Hello world! This is a HTML5 Boilerplate. when you load http://localhost:8000/. That’s the text we will translate.

Enable i18n and l10n

This is actually enabled by default in Django (at least in Django 2.2.x). Just in case they change this in future, find the following settings in your settings.py file:

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

Make sure at least USE_I18N and USE_L10N are set to True. We need both to make multi-language work.

Translating

We can translate text both in the views and templates.

Translating in templates

Go to a .html file in one of your template folders. Then add {% load i18n %} at the top of that template. This loads the Django library to translate your text.

In the example we created, we have the text Hello world! This is a HTML5 Boilerplate.. Let’s translate that to something else. To be able to do that, we will have wrap some code around that string. That part would become this:

{% trans "Hello world! This is a HTML5 Boilerplate." %}

When you add that and then refresh the page…. nothing has changed. That’s because it will just default back to the text within the quotes if no language is set up. We will go further on this in a bit.

Translating in views

Translating text in templates is only half of what you can do. If you have text in views that you, for instance, dynamically change based on behavior/values, then you also translate these in your views - before you push them to the template. You can do that with gettext(). To be specific. You can do it like this:

from django.utils.translation import gettext as _

def index(request):
    text = _("this is some random text")
    return render(request, 'home.html', { 'text': text })

And add the text to your home.html file.

If you want to save on your resources, you can also load text “lazy”. Use gettext_lazy(), but be careful! It sort of caches the translations, which means that it will sometimes put out the wrong text if you change text often. You can use gettext_lazy() safely in things that don’t change. Think of helptext in models. For views.py files, I would recommend to use gettext().

Now… before we can actually use gettext, you will have to install it on your computer, otherwise, you will run into errors.

If you are on Mac, you will need to do this:

brew install gettext
brew link --force gettext

For Windows, you can download and install it here.

Create translations with Django

We have now set up our project to work with translations. Next step is to create the translation file and create the translations for it.

First, we need to create a new directory to store the translations in:

mkdir -p locale

Then, run this command in your project to create the translation files:

django-admin makemessages --ignore="static" --ignore=".env"  -l nl

Note that we are ignoring the static and .env folders. By default, Django will go through all folders to find things to translate. These two folders are unnecessary and therefore we will skip them. The only exception for the .env folder to not be included, if you are translating in a language that is not supported by Django by default, then it could make sense to translate the default Django text as well.

We use -l to define the language that we would like to translate. As mentioned before, that is Dutch for me right now.

Note: you might get an error that it can’t find the folder we created earlier. An error message like this:

CommandError: Unable to find a locale path to store translations for file home/__init__.py

To fix that, we need to add this to our settings.py file:

LOCALE_PATHS = ( os.path.join(BASE_DIR, 'locale'), )

When you then run the previous command to create the translation files, it will output this:

processing locale nl

If you look closely, you can see that your locale folder has changed now. It should look like this now:

locale
    `-- nl
        `-- LC_MESSAGES
            `-- django.po

The django.po file is important. It’s the file where all translations are in (or should be in soon enough). If you open that, you will see a pattern. It will look like this:

#: home/templates/home.html:19
msgid "Hello world! This is a HTML5 Boilerplate."
msgstr ""

The msgid is the ID that is used to link all strings in the code to your translated lines. We need to complete the msgstr. That’s the tranlsation for the Dutch version. In this case, it will look like this:

#: home/templates/home.html:19
msgid "Hello world! This is a HTML5 Boilerplate."
msgstr "Hallo wereld! Dit is een HTML5 Boilerplate."

You will need to do that for all strings in that file. Once you have done that, we need to compile the files to files that Django uses to do the translations with. We can do that with this simple command:

django-admin compilemessages

That will result in this:

processing file django.po in ~/blog-projects/languages/locale/nl/LC_MESSAGES

It has now created a django.mo file which you can’t see or edit. That’s fine. Remember though that you will always have to re-run the previous command after you made changes to your translating file.

Now, to fully test out, if our translating in Django actually works, we will have to change your settings.py file. In my case, I will set it to nl. Change this:

LANGUAGE_CODE = 'en-us'

To this (for Dutch):

LANGUAGE_CODE = 'nl'

When you start the server, you will see that lines have changed:

Django launch screen

And that’s it! You might want to change the language based on the user’s preference. For that, I would highly suggest you check out Django’s docs about that.

Let me know if you have questions down below.