Django translation tutorial

Settings and translation files

January 10, 2016



This post is part of a tutorial series.

  1. Introduction
  2. Settings and translation files
  3. Templates and Javascript
  4. Setting user language

In the previous post of the Django translation tutorial, we implemented a simple view which returns a message defined in the output variable.

def index(request):
    output = 'Welcome to my site.'
    return HttpResponse(output)

On this second post our goal will be to implement the translation of that message. For this we will make use of the Django project created in the previous post. If you did not follow the previous post, you may want to download the project from this link.

Configure settings

By default, Django projects created from scratch are already set up for internationalization (i18n) and localization (l10n). Despite that, open the project settings at langtests/settings.py and make sure that USE_I18N and USE_L10N are set to true:

# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/

LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'

USE_I18N = True
USE_L10N = True
USE_TZ = True

One relevant setting to note is the LANGUAGE_CODE, which defines the default language for the entire project. By default, it is set to 'en-us' but later we will be changing it to 'pt-pt' to test that translations are working.

The second thing we must do is add the laguages application to the list of installed apps. Although we could access the view without it, for translations it will be necessary:

# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'languages',
)

Translation files

In Django, all translatable strings have to be explicitly marked. In Python code, we specify translation strings by using the ugettext() function or the underscore alias. For this example, open languages/views.py and mark the output string as translatable.

from django.utils.translation import ugettext as _
from django.http import HttpResponse

def index(request):
    output = _('Welcome to my site.')
    return HttpResponse(output)

In addition to the import statement, the only significant change is the use of the underscore alias for ugettext.

Creating translation strings

Now that we have identified the translatable strings, we need to create the translation files themselves. In Django, translation files are called message files and have a .po file extension. Django comes with a tool (makemessages) that automates the creation and updates of these files. Although you can execute this script from several places, we'll keep it simple for this tutorial and use it inside the languages directory since it is the only application we have.

For instance, let's create a message file for Portuguese. Inside the languages directory do:

django-admin makemessages -l pt_pt

This will create the following structure in the languages application.

languages/
    locale/
        pt_pt/
            LC_MESSAGES/
                django.po
    migrations/
    (...)
Note that I used an underscore for the language directory (pt_pt instead of pt-pt). Although we use dashes such as pt-pt in Django settings, in directories we must use underscores.
If you get an error such as CommandError: Unable to find a locale path to store translations for file __init__.py, make sure to create the locale directory inside the languages directory (using mkdir locale).
If you get an error such as CommandError: Can't find msguniq. Make sure you have GNU gettext tools 0.15 or newer installed, you need to install the GNU gettext library.

For Linux you can probably use your distribution's package manager (sudo apt-get install gettext or similar). For OSX you can use Homebrew:
brew install gettext
brew link gettext --force

Doing translations

If you check the content of django.po you will find the string ready to be translated to portuguese. In simple terms, the Django makemessages script searches for occurrences of ugettext() and pulls out all strings marked for translation.

To do the translation, just fill the content of msgstr:

#: views.py:5
msgid "Welcome to my site."
msgstr "Bem-vindo ao meu site"

After translating all strings, we must compile the strings with the compilemessages script:

django-admin compilemessages

A new file django.mo will appear next to the message file.

languages/
    locale/
        pt_pt/
            LC_MESSAGES/
                django.mo
                django.po
    migrations/
    (...)

Testing translations

Although everything seems ready for translations, if we point the browser to http://localhost:8000/languages/, we're still going to get the "Welcome to my site." string in English. This is because we have not specified anywhere to which language we should translate the string.

Although there are several options to use for translations, such as allowing the user to select the language, we will test translations by changing the default project language to Portuguese. Open the project settings and change the language code to 'pt-pt':

# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/

LANGUAGE_CODE = 'pt-pt'  # Use pt_pt as default language
TIME_ZONE = 'UTC'

USE_I18N = True
USE_L10N = True
USE_TZ = True

Now point the browser to http://localhost:8000/languages/:

Django will return the string translated into Portuguese.