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:
# 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
:
# 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
- Create message files for your languages:
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.
- 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!"
- Compile the translations:
python manage.py compilemessages
This creates .mo
files that Django uses to serve translations.
Translating Templates
Mark strings for translation in your templates:
{% 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:
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
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:
{% 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
from django.utils import formats
value = 1234567.89
formatted_number = formats.number_format(value)
In templates:
{{ 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:
# 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:
{% 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:
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:
{% 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:
# 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
# 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,
})
<!-- 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
-
Context matters: Use context markers to disambiguate translations:
python_("May", context="month name") # vs "May" as in "might"
-
Use placeholders: Don't concatenate strings for translation:
python# Bad:
_("Hello") + " " + user.name
# Good:
_("Hello %(name)s") % {'name': user.name} -
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!") -
Test with different languages: Always test your application with right-to-left languages like Arabic or Hebrew to catch layout issues.
-
Keep translations up to date: Run
makemessages
when new translatable content is added.
Common Localization Issues
-
Hardcoded strings: Always use translation functions for user-visible text.
-
Text in JavaScript: Use Django's JavaScript translation catalog for client-side text.
-
Date/time formatting: Use Django's formatting functions rather than manual formatting.
-
String concatenation: Avoid concatenating translated strings as word order differs between languages.
-
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
- Django's Official Documentation on Translation
- Django's Internationalization and Localization Guide
- Weblate - A web-based translation management system
- Transifex - A popular platform for managing translations
Exercises
-
Language Preference: Implement a user profile setting that allows users to save their language preference, overriding the browser's language.
-
Content Translation: Create a blog application where posts can be translated into multiple languages.
-
Locale-dependent Content: Modify your application to show different content based on the user's locale (e.g., region-specific promotions).
-
RTL Support: Add support for right-to-left languages like Arabic and Hebrew, including appropriate CSS changes.
-
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! :)