Django - Translation Tutorial (Internationalization) (Python)
How to create multilingual websites using Django.
Updated Nov 2, 2023

Introduction

This tutorial shows how to translate texts, URLs and database content.

For example, here is an English blog post:

And here is its German translation:

The text "Title" is defined in the post_detail.html template and translated to "Titel" using Django's default translation system.

The actual title, body, and the slug are stored in the database and translated using the admin (with the help of the django-modeltranslation package).

Prerequisities

Use this to setup a base project:

Django - Blog App Tutorial (Python)

Update Settings

Edit mysite/settings.py and add this line:

LOCALE_PATHS = [BASE_DIR / 'locale'] # here
LANGUAGE_CODE = 'en-us' 
  • LOCALE_PATHS setting is used to specify the directories where the application should look for translation files.
  • LANGUAGE_CODE specifies the default language for the project. Set it to LANGUAGE_CODE = 'de' if you want German to be the default language.

URL Prefix

Edit mysite/settings.py and add LocaleMiddleware above CommonMiddleware:

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

Edit mysite/urls.py, import i18n_patterns and use it like this:

from django.contrib import admin
from django.urls import include, path
from django.conf.urls.i18n import i18n_patterns # here

# wrap the paths with i18n_patterns:
urlpatterns = i18n_patterns(
    path('blog/', include('blog.urls')),
    path('admin/', admin.site.urls),
    prefix_default_language=False
)                     
  • i18n_patterns prepends the current active language code to all URL patterns defined inside the function.
  • prefix_default_language=False argument hides the language prefix for the default language.

Translate Text In Templates

Edit templates/blog/post_detail.html. Load the i18n tag, and use the trans tag to translate a string:

{% load i18n %}
<div class="post">
    <h1>{% trans "Title" %}: {{ post.title }}</h1>
    <div class="body">{{ post.body }}</div>
</div>

Create a directory in the project's root for the translation files and execute the makemessages command:

mkdir locale
django-admin makemessages -l de --ignore venv

Are you getting an error like this: CommandError: Can't find msguniq. Make sure you have GNU gettext tools 0.15 or newer installed.?

Windows

Install gettext from here: https://mlocati.github.io/articles/gettext-iconv-windows.html

You probably want the 64 bit static installer.

macOS, Linux

Run these commands:

sudo apt-get update
sudo apt-get install gettext

Then, you can create translation files:

django-admin makemessages -l fi --ignore venv

Edit locale/de/LC_MESSAGES/django.po and add the translation:

#: .\templates\blog\post_detail.html:3
msgid "Title"
msgstr "Titel" # here

Run this command:

django-admin compilemessages --ignore venv

Restart the development server.

Visit /de/blog/how-to-make-money/ and you should see this:

This is something you might actually want. You might want the slug to be in English for all languages.

Slug And Content Translation

On the other hand, it might make more sense to do something like this:

  • /blog/how-to-make-money/ (English version)
  • /de/blog/wie-man-geld-verdient/ (German version)

Install django-modeltranslation:

pip install django-modeltranslation

Edit mysite/settings.py:

INSTALLED_APPS = [
    'modeltranslation', # here, put it on top
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
]

# add languages to the project:
from django.utils.translation import gettext_lazy as _
LANGUAGES = (
    ('en', _('English')),
    ('de', _('German')),
)
  • gettext_lazy is used to translate the actual language names.
  • If we don't specify the LANGUAGES setting, Django's default value is used, which includes a lot of languages.

Create blog/translation.py:

Create a translation class for the Post model and register it:

from modeltranslation.translator import translator, TranslationOptions
from .models import Post

class PostTranslationOptions(TranslationOptions):
    fields = ('title', 'body', 'slug') # specify the fields you want to translate

translator.register(Post, PostTranslationOptions)

Run these commands:

python manage.py makemigrations
python manage.py migrate
python manage.py update_translation_fields
  • Run update_translation_fields if you have an existing project and you have already synced the model fields (that you are trying to translate) with the database.

Restart the development server.

Visit /admin/ and edit a blog post:

The first "Title" field shows the currently active translation:

http://127.0.0.1:8000/de/admin/blog/post/1/change/

Now we have the English post...

...and its German translation:

Admin Translation

The admin UI is translated automatically for the added languages.

You can see this in action by visiting /de/admin:

/admin/ provides the login form in the default language:

Admin URL Translation

You can also translate the admin url: /de/verwaltung/

Edit the mysite/urls.py file, and make these changes:

from django.contrib import admin
from django.urls import include, path
from django.conf.urls.i18n import i18n_patterns
from django.utils.translation import gettext_lazy as _ # here


urlpatterns = i18n_patterns(
    path('blog/', include('blog.urls')),
    path(_('admin/'), admin.site.urls), # here
    prefix_default_language=False
)  
  • The gettext_lazy function translates the string when the value is accessed, not when we call the function.

Run the makemessages command:

django-admin makemessages -l de --ignore venv

Edit locale/de/LC_MESSAGES/django.po and translate the admin/ path:

#: .\mysite\urls.py:25
msgid "admin/"
msgstr "verwaltung/" # here

Run the compilemessages command:

django-admin compilemessages --ignore venv

Now you can find the admin page translated to German in here:

http://127.0.0.1:8000/de/verwaltung/

The English translated admin can be accessed here:

http://127.0.0.1:8000/admin/

More Django Tutorials