Lokalise and Django logos

Django i18n: A beginner’s guide

Are you looking to adapt your Django application to different linguistic and cultural settings? From a developer starting out on a project to one wanting to adapt an existing Django application, this beginner’s guide talks about the nuances of working using the process of i18n in Django.

First, we’ll cover how you can modify your Django application to enable i18n and then how to integrate your i18n with Lokalise. Then, we’ll talk about various techniques in Django that may help you with i18n concepts.

    You may also be interested in learning more advanced Django I18n concepts.

    Check out how our translation management system can help you translate your apps faster.

    Step 1: Prepare your Django Project for I18n with Lokalise

    1.1 Setup New Project

    Let us start by creating a project in Django using:

    django-admin startproject blog

    The directory structure of your project is as follows:

    blog
    ├── blog
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── db.sqlite3
    └── manage.py

    1.2 Enable I18n in Django

    The first task before we get started with any of the techniques is to enable i18n in Django. By default, Django is geared up to handle 18n. If you navigate to your project’s settings.py, you will notice that the variable USE_I18N is set to True. Even if your project does not use i18n, this can remain enabled in Django as it causes no overhead to your overall project.

    USE_I18N = True

    Also, you can set the default language using the variable LANGUAGE_CODE in the same settings.py file.

    LANGUAGE_CODE = 'en'

    Next, you can set up the languages that you plan to support in your Django application:

    LANGUAGES = [
        ('en','English'),
        ('bn', 'Bengali')
    ]

    You can also set the variable LANGUAGE_BIDI, which decides the direction in which the text is read. It should be set to True for right-to-left languages like Arabic, and to False otherwise.

    1.3 Start a New Django App

    Next, let’s start an application called blogs to test our i18n techniques on:

    python manage.py startapp blogs

    This creates the basic structure of an application within a Django project, as shown below:

    blog
    ├── blog
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── db.sqlite3
    ├── manage.py
    └── blogs
        ├── __init__.py
        ├── admin.py
        ├── apps.py
        ├── migrations
        │   └── __init__.py
        ├── models.py
        ├── templates
        ├── tests.py
        ├── urls.py
        └── views.py

    1.4 Create Translation Files in Django

    Next, we need to start a makemessages process in Django, which automatically creates and updates PO files for the application to use. You may encounter an error with the following command if gettext is not installed on your local system:

    ./manage.py makemessages -l en

    The last argument, en sets the language for translation. You can run this file from the root of the project or the application directory. This script essentially scans your project for all strings that require translation. The following is the updated structure of the project:

    blog
    ├── blog
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── db.sqlite3
    ├── manage.py
    └── blogs
        ├── __init__.py
        ├── admin.py
        ├── apps.py
        ├── locale
        │   └── en
        │       └── LC_MESSAGES
        │           └── django.po
        ├── migrations
        │   └── __init__.py
        ├── models.py
        ├── templates
        ├── tests.py
        ├── urls.py
        └── views.py

    Notice that a locale directory has been created within the project. In this, a directory for each language is created. The django.po file has been created within the LC_MESSAGES directory, which contains all strings for translation. We will now see how you can integrate your Django application with your Lokalise account.

    A PO file generated by Django looks like this:

    # blogs/locale/de/LC_MESSAGES/django.po
    
    #: templates/blogs/index.html:3
    msgid "Hello World"
    msgstr ""

    You can edit the PO file to fill in the msgstr values that store the translations.

    Step 2: Integrate with Lokalise

    2.1 Setup Lokalise CLI

    Before you can build an interface between your Django application and your Lokalise application, you need to install the Lokalise CLI client. Here are the installation instructions for the CLI client. The latest version of the CLI tool is now v2 — make sure you upgrade if you are using the older version.

    If you use a Mac, installation of the CLI client is carried out through Homebrew. Run the following commands to download and install v2 of the Lokalise CLI client:

    brew tap lokalise/brew
    brew install lokalise

    If you do not use Homebrew, you can download the installer directly.

    On Linux systems, you can download the installation script and run it in a single command as shown below:

    curl -sfL https://raw.githubusercontent.com/lokalise/lokalise-cli-2-go/master/install.sh | sh

    Alternately, you can install Lokalise CLI v2 using the installer for Linux (32 bit | 64 bit).

    If you are working on Windows, you can only install the CLI client using the installers (32 bit | 64 bit).

    2.2 Configure the CLI tool

    Once you have installed the CLI, the command lokalise2 is available for you to use. Any interaction that you perform with your Lokalise account is validated through a token. A token essentially mimics your user access and can be generated from your Lokalise account. Additionally, if you are working on a certain project within Lokalise, you also need to supply the project ID.

    Get Project ID and Token from Lokalise Profile
    Get Project ID and Token from Lokalise Profile

    You can save the token and project IDs in the /etc/lokalise.cfg file or supply them as parameters to every lokalise2 command. For instance:

    Token = "xxxxxxxxxx"
    Project = "xxxxx.xxxxx"

    Please note that supplying the token in your commands may save it to your bash history, which is a potential security risk.

    2.3 Exchange Files between Lokalise and your Django Application

    To upload a file to your Lokalise project, run the following command and replace <token> and <project_id> with your actual tokens and project IDs:

    lokalise2 
      --token <token> 
      --project-id <project_id> 
      file upload 
      --file "locale/en/LC_MESSAGES/default.po" 
      --lang-iso en

    You can download the files from your Lokalise project using the following command:

    lokalise2 
      --token <token> 
      --project-id <project_id> 
      file download 
      --format po 
      --filter-langs en 
      --original-filenames=true 
      --directory-prefix "" 
      --unzip-to "locale/en/LC_MESSAGES/"

    You will need to re-run the command to download the files for each language.

    Step 3: Set a Locale in Django

    3.1 Use Inbuilt Functions in Django

    Django comes with a default view django.conf.urls.i18n that sets the user’s language in Python. You can simply set a particular URL to trigger this function. For example:

    path('i18n/', include('django.conf.urls.i18n')),

    This view expects the language parameter as a POST variable, which saves the current user’s language preference in the session. In addition to this, there is an optional next parameter that sets the URL to which the view should redirect after setting the language.

    3.2 Set Locale Settings Manually

    While the default implementation is good for basic i18n, you may need to set additional parameters to set the locale to store variables, such as a geography, in addition to the language. To set such a variable, you would need to create a custom view. In this example, let’s manually set a language ID in our Django application. You can follow the same process to set any other variable related to the locale of the user.

    First, set the URL pattern in your application’s urls.py file as shown below. We set the variable language_id as a GET variable embedded within the URL. Whenever someone visits the URL /your_app/set_language/15/, Django will call up the set_language view with the parameter language_id as 15.

    urlpatterns = patterns(
        'my_app.views',
        (r'^set_language/(?P<language_id>d+)/$', 'set_language')
    )

    Now we’ll define the set_language() view to set a session variable to the language_id that has come as a part of the GET payload.

    def set_language(request):
        if request.GET.has_key('language_id'):  # Set language in Session variable
            # Redirect to home
            request.session['language_id'] = request.GET['language_id']
        return HttpResponseRedirect('/')

    Translation of Text: Integrate with gettext in Python

    We have explored the usage of the gettext module in Python i18n. Django has a wrapper in its translation module. The following view searches for the msgid “Hello World” in the current locale’s PO file and returns the output as an HTTP response.

    from django.utils.translation import gettext as _
    
    def display_message(request):
        output = _("Hello World")
        return HttpResponse(output)

    Here is the part of the PO file that is relevant to this translation and here is the translation if the language is set of Bengali:

    msgid "Hello World"
    msgstr "ওহে বিশ্ব"

    The HTTP response would simply be ওহে বিশ্ব, i.e., the translated string in the PO file.

    The argument to the gettext function may be any string. For instance, if you would like to print a string that displays how many days ago you last logged in, you can modify the function as shown below.

    def my_view(request, day):
        if day > 1:
            output = _('You logged in %(day)s days ago.') % {'day': day}
        else:
            output = _('You logged in %(day)s day ago.') % {'day': day}
    
        return HttpResponse(output)

    The corresponding files to handle such a situation in the PO file are shown below:

    msgid "You logged in %d day ago."
    msgid_plural "You logged in %d days ago."
    msgstr[0] "আপনি একদিন আগে লগ ইন করেছেন"
    msgstr[1] "আপনি %d দিন আগে লগ ইন করেছেন"

    Lazy Translation with Django

    If you follow this path of translation, Django translates all strings on the go. An interesting feature of Django is the ability to defer translation until it is absolutely necessary.

    Django has a pre-defined function, gettext_lazy(), which translates strings lazily. The translation takes place when the application needs to use the value of a string, and not when the view is called up.

    from django.utils.translation import gettext_lazy as _
    
    def display_message(request):
        output = _("Hello World")
        return HttpResponse(output)

    In this example, the translation does not occur until the value needs to be sent as an HTTP response.

    Django i18n in Templates

    While we have spoken extensively about i18n in the back end. In such use cases, you send the translated strings directly to the front end, which you can simply display in the templating engine. You can also translate a string from Django templates.

    To use this feature, you first need to enable it in the templating engine. You need to load the i18n module if you would like to translate a string using the templates. Next, use the trans keyword followed by the string to translate, as shown below:

    {% extends 'admin.html' %}
    {% load i18n %}
    {% block content %}
        <h1>{% trans 'Hello World' %}</h1>
    {% endblock %}

    Translations in Django with JavaScript

    The gettext module is available for you to use only through the backend in Python; therefore, it is not possible for you to translate using gettext in JavaScript. Furthermore, your JavaScript code would not have direct access to the translation files.

    Django provides a solution for passing the translations to JavaScript. This enables you to call the gettext() function from within your JavaScript code.

    First, enable the URL where JavaScript will get the required resources.

    from django.views.i18n import JavaScriptCatalog
    urlpatterns = [
        path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog')
    ]

    Next, call the catalog from your templates to fetch the translation resources.


    <script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>

    You can call the gettext() function, just like you would in Python, to get a related translation.

    $ console.log(gettext('Hello World'));
    ওহে বিশ্ব

    Here is a complete list of gettext related functions that you can call from within your JavaScript code.

    Final Thoughts on Django I18n

    In this tutorial, we explored the topic of i18n in Django. Here is what we learned to do:

    • Set up a Django project and enable I18n
    • Create and manage translations in Django
    • Integrate the translations in your Django application with your Lokalise account
    • Translate strings in Django and work with lazy translations
    • Translate strings on the go within templates
    • Integrate translations with JavaScript in Django

    Talk to one of our localization specialists

    Book a call with one of our localization specialists and get a tailored consultation that can guide you on your localization path.

    Get a demo

    Related posts

    Learn something new every two weeks

    Get the latest in localization delivered straight to your inbox.

    Related articles
    Localization made easy. Why wait?
    The preferred localization tool of 3000+ companies