Django Integration

Learn how to integrate i18next with Django applications for internationalization.

Integrate internationalization with your Django application to support multiple languages using Lengrise-managed translations, with both server-side rendering and template-based capabilities.

Prerequisites

  • Django project (v3.2+)
  • Python (v3.7+)
  • Package manager (pip, poetry)
  • Translations downloaded from Lengrise

Pull Translation Files

First, you must pull your translation files from Lengrise API. These files will contain all the translated text for your application.

  1. Go to the Installation Guide and follow the steps to download your translations
  2. Convert the downloaded JSON files to Django locale format or use them directly with a custom solution
  3. Place the translated files in your project as shown in the project structure below

Integration Setup

Django comes with built-in internationalization support, but you may need to install additional packages for JSON handling:

python pip install django-rosetta

Project Structure

manage.pyadd
requirements.txtadd
projectadd
settings.pyadd
urls.pyadd
wsgi.pyadd
__init__.pyadd
appadd
views.pyadd
models.pyadd
admin.pyadd
__init__.pyadd
templatesadd
base.htmladd
index.htmladd
localeadd
enadd
LC_MESSAGESadd
django.poadd
django.moadd
esadd
LC_MESSAGESadd
django.poadd
django.moadd
staticadd
jsadd
language-switcher.jsadd
localesadd
en.jsonadd
es.jsonadd

Configuration Steps

1. Configure Django Settings

Update your settings.py file to enable internationalization:

from django.utils.translation import gettext_lazy as _
from pathlib import Path
import os

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# Internationalization settings
LANGUAGE_CODE = 'en'

# Enable translations
USE_I18N = True

# Enable localization
USE_L10N = True

# Enable timezone support
USE_TZ = True

# Available languages
LANGUAGES = [
    ('en', _('English')),
    ('es', _('Spanish')),
]

# Path to locale files
LOCALE_PATHS = [
    os.path.join(BASE_DIR, 'locale'),
]

# Add the LocaleMiddleware
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',  # Add this line
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

# Templates configuration
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'app/templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'django.template.context_processors.i18n',  # Add this line
            ],
        },
    },
]

# Optional, if using Rosetta for translation management
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app',
    'rosetta',  # Add this line if using Rosetta
]

2. Update URLs Configuration

Update your project/urls.py file to include language selection URLs:

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

urlpatterns = [
    # Non-localized URLs
    path('i18n/', include('django.conf.urls.i18n')),  # Language switching
]

# Localized URLs - will include language prefix
urlpatterns += i18n_patterns(
    path(_('admin/'), admin.site.urls),
    path('', include('app.urls')),
    path('rosetta/', include('rosetta.urls')),  # Optional, if using Rosetta
    prefix_default_language=True,  # Set to False to omit prefix for default language
)

3. Create a Base Template with Language Switcher

Create a file at app/templates/base.html:

{% load i18n %}
{% load static %}
<!DOCTYPE html>
<html lang="{{ LANGUAGE_CODE }}">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}{% trans "My Multilingual Site" %}{% endblock %}</title>
    <style>
        .language-switcher {
            margin: 1rem 0;
        }
        .language-switcher ul {
            display: flex;
            list-style-type: none;
            padding: 0;
        }
        .language-switcher li {
            margin-right: 1rem;
        }
        .language-switcher a {
            text-decoration: none;
            color: #333;
        }
        .language-switcher a.active {
            font-weight: bold;
            color: #0066cc;
        }
    </style>
</head>
<body>
    <header>
        <h1>{% trans "My Multilingual Django Site" %}</h1>
        <nav>
            <ul>
                <li><a href="{% url 'home' %}">{% trans "Home" %}</a></li>
                <li><a href="{% url 'about' %}">{% trans "About" %}</a></li>
                <li><a href="{% url 'contact' %}">{% trans "Contact" %}</a></li>
            </ul>
        </nav>

        <!-- Language Switcher -->
        <div class="language-switcher">
            <form action="{% url 'set_language' %}" method="post">
                {% csrf_token %}
                <input name="next" type="hidden" value="{{ redirect_to }}">
                <select name="language" onchange="this.form.submit()">
                    {% get_current_language as CURRENT_LANGUAGE %}
                    {% get_available_languages as LANGUAGES %}
                    {% for lang_code, lang_name in LANGUAGES %}
                        <option value="{{ lang_code }}" {% if lang_code == CURRENT_LANGUAGE %}selected{% endif %}>
                            {{ lang_name }}
                        </option>
                    {% endfor %}
                </select>
            </form>
        </div>
    </header>

    <main>
        {% block content %}{% endblock %}
    </main>

    <footer>
        <p>{% blocktrans with year=current_year %}© {{ year }} My Website. All rights reserved.{% endblocktrans %}</p>
    </footer>
</body>
</html>

4. Create Views

Update your app/views.py file:

from django.shortcuts import render
from django.utils.translation import gettext as _
from django.utils import timezone

def home(request):
    current_year = timezone.now().year

    # Example of translatable content with variables
    message = _("Hello, welcome to our site!")
    items_count = 5
    items_message = _("You have %(count)d item") % {'count': items_count}
    if items_count != 1:
        items_message = _("You have %(count)d items") % {'count': items_count}

    context = {
        'message': message,
        'items_message': items_message,
        'current_year': current_year,
    }

    return render(request, 'index.html', context)

def about(request):
    current_year = timezone.now().year
    return render(request, 'about.html', {'current_year': current_year})

def contact(request):
    current_year = timezone.now().year
    return render(request, 'contact.html', {'current_year': current_year})

5. Create URLs for your App

Create a file at app/urls.py:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
    path('about/', views.about, name='about'),
    path('contact/', views.contact, name='contact'),
]

6. Create a Home Page Template

Create a file at app/templates/index.html:

{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Home" %} -
{{ block.super }}{% endblock %} {% block content %}
<h1>{% trans "Welcome to our site" %}</h1>
<p>{{ message }}</p>

<section>
  <h2>{% trans "Features" %}</h2>
  <ul>
    <li>{% trans "Easy to use" %}</li>
    <li>{% trans "Fast translation" %}</li>
    <li>{% trans "Flexible integration" %}</li>
  </ul>
</section>

<p>{{ items_message }}</p>

<p>
  {% blocktrans with name="Django Developer" %}Hello, {{ name }}!{%
  endblocktrans %}
</p>
{% endblock %}

Working with Translations

1. Extracting Translatable Strings

Run the following command to extract strings from your templates and Python code:

python manage.py makemessages -l en -l es

This will create or update your .po files in the locale directory.

2. Editing Translation Files

Edit the .po files in locale/es/LC_MESSAGES/django.po to add Spanish translations:

#: templates/base.html:7
msgid "My Multilingual Site"
msgstr "Mi Sitio Multilingüe"

#: templates/base.html:18
msgid "My Multilingual Django Site"
msgstr "Mi Sitio Django Multilingüe"

#: templates/base.html:21
msgid "Home"
msgstr "Inicio"

#: templates/base.html:22
msgid "About"
msgstr "Acerca de"

#: templates/base.html:23
msgid "Contact"
msgstr "Contacto"

#: templates/index.html:6
msgid "Welcome to our site"
msgstr "Bienvenido a nuestro sitio"

#: views.py:8
msgid "Hello, welcome to our site!"
msgstr "¡Hola, bienvenido a nuestro sitio!"

#: views.py:10
#, python-format
msgid "You have %(count)d item"
msgstr "Tienes %(count)d elemento"

#: views.py:12
#, python-format
msgid "You have %(count)d items"
msgstr "Tienes %(count)d elementos"

#: templates/index.html:14
msgid "Easy to use"
msgstr "Fácil de usar"

#: templates/index.html:15
msgid "Fast translation"
msgstr "Traducción rápida"

#: templates/index.html:16
msgid "Flexible integration"
msgstr "Integración flexible"

#: templates/index.html:21
#, python-format
msgid "Hello, %(name)s!"
msgstr "¡Hola, %(name)s!"

3. Compiling Translations

After editing the .po files, compile them to .mo files:

python manage.py compilemessages

4. Using Translations in Python Code

In your Python code, you can use various translation functions:

from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy as _lazy
from django.utils.translation import ngettext

# For regular strings
message = _("Hello")

# For strings in class definitions, models, etc.
class MyModel(models.Model):
    title = models.CharField(_lazy("Title"), max_length=100)

# For pluralization
item_count = 5
message = ngettext(
    'There is %(count)d item',
    'There are %(count)d items',
    item_count
) % {'count': item_count}

5. Using Translations in Templates

In your Django templates, you can use translation tags:

{% load i18n %}

<!-- Simple translation -->
<h1>{% trans "Welcome" %}</h1>

<!-- Translation with variables -->
{% blocktrans with user_name=user.username %} Hello, {{ user_name }}! {%
endblocktrans %}

<!-- Pluralization in templates -->
{% blocktrans count counter=items.count %} There is {{ counter }} item. {%
plural %} There are {{ counter }} items. {% endblocktrans %}

Using JSON Files for JavaScript

If you want to use your Lengrise JSON files directly for JavaScript translations:

1. Create a JavaScript Language Switcher

Create a file at static/js/language-switcher.js:

document.addEventListener("DOMContentLoaded", function () {
  // Load translations for the current language
  const currentLanguage = document.documentElement.lang || "en";
  let translations = {};

  // Fetch translations from your JSON file
  fetch(`/static/locales/${currentLanguage}.json`)
    .then((response) => response.json())
    .then((data) => {
      translations = data;
      // Translate all elements with data-i18n attribute
      translateElements();
    })
    .catch((error) => console.error("Error loading translations:", error));

  // Function to translate elements
  function translateElements() {
    document.querySelectorAll("[data-i18n]").forEach((element) => {
      const key = element.getAttribute("data-i18n");
      const translation = getNestedTranslation(key, translations);
      if (translation) {
        element.textContent = translation;
      }
    });
  }

  // Helper function to handle nested keys (e.g. "user.greeting")
  function getNestedTranslation(key, obj) {
    return key.split(".").reduce((p, c) => (p && p[c] ? p[c] : null), obj);
  }

  // Function to translate with parameters
  window.translate = function (key, params = {}) {
    let translation = getNestedTranslation(key, translations);
    if (!translation) return key;

    // Replace parameters
    Object.keys(params).forEach((param) => {
      translation = translation.replace(
        new RegExp(`{{\\s*${param}\\s*}}`, "g"),
        params[param]
      );
    });

    return translation;
  };
});

2. Include the Script in Your Template

Add this to your base.html:

{% load static %}
<!-- Add this near the end of your body tag -->
<script src="{% static 'js/language-switcher.js' %}"></script>

3. Using JavaScript Translations

In your HTML, you can use data attributes for translation:

<div data-i18n="welcome.title"></div>
<div data-i18n="features.easy"></div>

<!-- For dynamic translations with JavaScript -->
<script>
  document.getElementById("greeting").textContent = translate("greeting", {
    name: "JavaScript User",
  });
</script>

Resources

For more detailed information, check out these resources: