Skip to main content

Django Localization

Localization (often abbreviated as l10n) is the process of adapting your Django application to specific regions or languages. While internationalization (i18n) prepares your app for translation, localization is the actual adaptation of your content to different cultures, languages, and regions.

In this tutorial, you'll learn how to effectively localize your Django applications to reach a global audience.

Introduction to Django Localization

Localization involves:

  • Translating text into different languages
  • Adapting date and time formats
  • Using appropriate number and currency formats
  • Handling pluralization rules specific to different languages
  • Customizing content based on cultural norms and preferences

Django provides a robust framework for localization that builds on its internationalization capabilities. By the end of this tutorial, you'll be able to deliver your Django application in multiple languages with appropriate regional customizations.

Prerequisites

Before diving into localization, ensure you have:

  • A basic Django project set up
  • Django's internationalization framework configured
  • Knowledge of how to mark strings for translation with gettext/gettext_lazy

If you're not familiar with these concepts, check out our "Django Internationalization" guide first.

Setting Up Localization in Django

1. Configure Language Settings

First, let's configure your Django project's settings to support localization:

python
# settings.py

# Internationalization settings
USE_I18N = True # Enable internationalization
USE_L10N = True # Enable localization
USE_TZ = True # Enable timezone support

# Available languages
from django.utils.translation import gettext_lazy as _
LANGUAGES = [
('en', _('English')),
('es', _('Spanish')),
('fr', _('French')),
('ja', _('Japanese')),
]

# Default language
LANGUAGE_CODE = 'en'

# Location of translation files
LOCALE_PATHS = [
BASE_DIR / 'locale',
]

2. Add the Locale Middleware

To enable Django to determine the user's preferred language, add the LocaleMiddleware:

python
# settings.py
MIDDLEWARE = [
# ...
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware', # Add this line AFTER SessionMiddleware and BEFORE CommonMiddleware
'django.middleware.common.CommonMiddleware',
# ...
]

The LocaleMiddleware examines the request to determine the user's preferred language, either from a language prefix in the URL, a cookie, or the browser's Accept-Language header.

Translating Content

Creating Translation Files

  1. Create message files for your languages:
bash
python manage.py makemessages -l es  # Creates Spanish message file
python manage.py makemessages -l fr # Creates French message file
python manage.py makemessages -l ja # Creates Japanese message file

This will create .po files in your project's locale directory.

  1. Edit the .po files to provide translations:
# locale/es/LC_MESSAGES/django.po
#: templates/myapp/welcome.html:5
msgid "Welcome to our website!"
msgstr "¡Bienvenido a nuestro sitio web!"
  1. Compile the translations:
bash
python manage.py compilemessages

This creates .mo files that Django uses to serve translations.

Translating Templates

Mark strings for translation in your templates:

html
{% load i18n %}

<h1>{% trans "Welcome to our website!" %}</h1>

{% blocktrans with user_name=user.name %}
Hello, {{ user_name }}! How are you today?
{% endblocktrans %}

Translating Python Code

Mark strings in Python code:

python
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy as _

# For immediate translation
welcome_message = _("Welcome to our website!")

# For lazy translation (useful in models and forms)
class Product(models.Model):
name = models.CharField(_("product name"), max_length=100)
description = models.TextField(_("product description"), blank=True)

Language-Specific Formatting

Django automatically formats dates, numbers, and currencies according to the selected locale.

Date Formatting

python
from django.utils import formats
from django.utils import timezone
import datetime

today = datetime.date.today()
formatted_date = formats.date_format(today) # Respects current locale format

In templates:

html
{% load i18n l10n %}

<p>{% trans "Today's date" %}: {{ today|date:"SHORT_DATE_FORMAT" }}</p>

Example output for US English: 10/15/2023 Example output for Spanish: 15/10/2023

Number Formatting

python
from django.utils import formats

value = 1234567.89
formatted_number = formats.number_format(value)

In templates:

html
{{ value|localize }}

Example output for US English: 1,234,567.89 Example output for German: 1.234.567,89

URL-based Language Selection

You can add language prefix to URLs to allow users to choose their language:

python
# urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path, include

urlpatterns = [
# URLs that should not be prefixed with language code
path('api/', include('myapp.api.urls')),
]

# URLs that should have language prefix, e.g., /en/about/, /es/about/
urlpatterns += i18n_patterns(
path('about/', views.about, name='about'),
path('contact/', views.contact, name='contact'),
# ...
)

Language Switcher

Add a language switcher to your templates:

html
{% load i18n %}

<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>

Handling Plurals

Different languages have different pluralization rules. Django's translation system supports this:

python
from django.utils.translation import ngettext

def show_message(count):
return ngettext(
"You have %(count)d unread message",
"You have %(count)d unread messages",
count
) % {'count': count}

In templates:

html
{% blocktrans count counter=list|length %}
There is {{ counter }} item.
{% plural %}
There are {{ counter }} items.
{% endblocktrans %}

Real-World Example: Multilingual Blog

Let's put everything together in a basic multilingual blog:

python
# models.py
from django.db import models
from django.utils.translation import gettext_lazy as _

class BlogPost(models.Model):
title = models.CharField(_("title"), max_length=200)
content = models.TextField(_("content"))
published_date = models.DateTimeField(_("published date"))

class Meta:
verbose_name = _("blog post")
verbose_name_plural = _("blog posts")

def __str__(self):
return self.title
python
# views.py
from django.shortcuts import render
from django.utils.translation import gettext as _
from .models import BlogPost

def blog_home(request):
posts = BlogPost.objects.all()
welcome_message = _("Welcome to our multilingual blog!")
return render(request, 'blog/home.html', {
'posts': posts,
'welcome_message': welcome_message,
})
html
<!-- templates/blog/home.html -->
{% load i18n %}

<html>
<head>
<title>{% trans "Blog" %}</title>
</head>
<body>
<!-- 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>

<h1>{{ welcome_message }}</h1>

<div class="blog-posts">
{% for post in posts %}
<article>
<h2>{{ post.title }}</h2>
<time datetime="{{ post.published_date|date:'c' }}">
{{ post.published_date|date:"DATETIME_FORMAT" }}
</time>
<div class="content">
{{ post.content }}
</div>
</article>
{% empty %}
<p>{% trans "No posts available." %}</p>
{% endfor %}
</div>

<footer>
{% blocktrans with year=current_year %}
© {{ year }} - Our Multilingual Blog
{% endblocktrans %}
</footer>
</body>
</html>

Best Practices for Localization

  1. Context matters: Use context markers to disambiguate translations:

    python
    _("May", context="month name")  # vs "May" as in "might"
  2. Use placeholders: Don't concatenate strings for translation:

    python
    # Bad:
    _("Hello") + " " + user.name

    # Good:
    _("Hello %(name)s") % {'name': user.name}
  3. Provide context for translators: Add comments in the code to help translators:

    python
    # Translators: This message appears on the homepage
    welcome_message = _("Welcome to our website!")
  4. Test with different languages: Always test your application with right-to-left languages like Arabic or Hebrew to catch layout issues.

  5. Keep translations up to date: Run makemessages when new translatable content is added.

Common Localization Issues

  1. Hardcoded strings: Always use translation functions for user-visible text.

  2. Text in JavaScript: Use Django's JavaScript translation catalog for client-side text.

  3. Date/time formatting: Use Django's formatting functions rather than manual formatting.

  4. String concatenation: Avoid concatenating translated strings as word order differs between languages.

  5. Non-translatable content: Ensure images with text, PDFs, and other media are also localized.

Summary

You've learned how to:

  • Configure Django for localization
  • Create and manage translation files
  • Format dates, numbers, and currencies according to locale
  • Implement language switching in your application
  • Handle pluralization in different languages
  • Apply localization in a real-world example

With these skills, your Django application can now reach a global audience with a localized experience that feels native to each user.

Additional Resources and Exercises

Resources

Exercises

  1. Language Preference: Implement a user profile setting that allows users to save their language preference, overriding the browser's language.

  2. Content Translation: Create a blog application where posts can be translated into multiple languages.

  3. Locale-dependent Content: Modify your application to show different content based on the user's locale (e.g., region-specific promotions).

  4. RTL Support: Add support for right-to-left languages like Arabic and Hebrew, including appropriate CSS changes.

  5. Translation Coverage Report: Build a management command that reports on the percentage of your application that has been translated to each supported language.

Remember, localization is an ongoing process. As your application grows, continuously update your translation files and ensure new features are built with internationalization in mind from the start.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)