Django Language Settings
Introduction
When building web applications that need to reach users across different regions and languages, implementing proper internationalization (i18n) is crucial. Django provides robust support for creating multilingual websites through its language settings system.
In this tutorial, we'll explore how Django handles language preferences, how to configure supported languages, and how to implement language switching in your applications. By the end, you'll understand how to make your Django site accessible to users regardless of their language preference.
Understanding Django's Language Settings
Django uses a few key settings in your settings.py
file to determine which languages your application supports and which language to use for each request.
Core Language Settings
Let's start by looking at the essential settings that control language behavior:
# settings.py
# The default language code for your site
LANGUAGE_CODE = 'en-us'
# Whether to enable Django's translation system
USE_I18N = True
# Whether to enable localized formatting of data
USE_L10N = True
# Available languages for your site
LANGUAGES = [
('en', 'English'),
('es', 'Spanish'),
('fr', 'French'),
('de', 'German'),
]
# Location where Django should look for translation files
LOCALE_PATHS = [
BASE_DIR / 'locale',
]
Let's break down what each setting does:
- LANGUAGE_CODE: Sets the default language for the site. If Django can't determine which language to use for a request, it falls back to this.
- USE_I18N: When set to
True
, enables Django's translation system. - USE_L10N: When set to
True
, enables localized formatting of dates, numbers, etc. - LANGUAGES: Defines all languages your site supports. Each tuple contains a language code and its display name.
- LOCALE_PATHS: Tells Django where to find your translation files (
.po
and.mo
files).
Determining the Active Language
Django uses a specific algorithm to determine which language to use for each request. Understanding this process is key to properly implementing language switching.
The Language Resolution Process
Django follows these steps to decide which language to use:
- It checks for a language prefix in the URL (if using
i18n_patterns
) - It looks for a language code in the session
- It checks for a cookie named
django_language
- It examines the
Accept-Language
HTTP header - It falls back to the
LANGUAGE_CODE
setting
Let's set up our project to use these mechanisms.
Setting Up URL-based Language Switching
One of the most common approaches is to include the language code in the URL, like /en/about/
and /es/about/
.
1. Configure URL Patterns
First, modify your root URLconf to use i18n_patterns
:
# urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path, include
from . import views
urlpatterns = [
# URLs that don't need internationalization
path('admin/', admin.site.urls),
]
# URLs that should be translated
urlpatterns += i18n_patterns(
path('', views.homepage, name='home'),
path('about/', views.about, name='about'),
prefix_default_language=True, # Show prefix for default language too
)
The prefix_default_language
parameter determines whether to include the prefix for the default language. If set to False
, the default language won't have a prefix in the URL.
2. Add Language Selector
Let's create a simple language selector that users can use to switch languages:
<!-- In your template -->
<div class="language-selector">
<p>{% trans "Select your language" %}:</p>
<ul>
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<li>
<a href="{% url 'set_language' %}" data-language="{{ language.code }}" class="language-link">
{{ language.name_local }}
</a>
</li>
{% endfor %}
</ul>
</div>
<script>
document.querySelectorAll('.language-link').forEach(function(link) {
link.addEventListener('click', function(e) {
e.preventDefault();
const form = document.createElement('form');
form.action = this.getAttribute('href');
form.method = 'post';
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'language';
input.value = this.dataset.language;
const csrfInput = document.createElement('input');
csrfInput.type = 'hidden';
csrfInput.name = 'csrfmiddlewaretoken';
csrfInput.value = document.querySelector('[name=csrfmiddlewaretoken]').value;
form.appendChild(input);
form.appendChild(csrfInput);
document.body.appendChild(form);
form.submit();
});
});
</script>
3. Include Django's Language Views
Make sure to include Django's built-in language views in your URLs:
# urls.py
from django.conf.urls.i18n import i18n_patterns
urlpatterns = [
# ... other URL patterns ...
path('i18n/', include('django.conf.urls.i18n')),
]
Session and Cookie-based Language Selection
If you prefer not to use URL-based language selection, you can store the language preference in the session or a cookie.
Session-based Language
Add the LocaleMiddleware
to your middleware list:
# settings.py
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware', # Must come before LocaleMiddleware
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
# ... other middleware ...
]
Then create a view to set the language:
# views.py
from django.http import HttpResponseRedirect
from django.utils.translation import activate
def set_language_view(request):
if request.method == 'POST':
language = request.POST.get('language', 'en')
activate(language)
request.session['django_language'] = language
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
Cookie-based Language
Django's built-in set_language
view can set a cookie with the user's language preference:
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ request.path }}">
<select name="language" onchange="this.form.submit()">
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"
{% if language.code == LANGUAGE_CODE %}selected{% endif %}>
{{ language.name_local }}
</option>
{% endfor %}
</select>
</form>
Real-world Example: Multi-language Blog
Let's tie everything together with a simple blog application that supports multiple languages:
# 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'))
created_at = models.DateTimeField(_('Created at'), auto_now_add=True)
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_list(request):
posts = BlogPost.objects.all()
return render(request, 'blog/list.html', {
'title': _('Blog Posts'),
'posts': posts
})
<!-- templates/blog/list.html -->
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<h1>{% trans "Blog Posts" %}</h1>
<!-- Language selector -->
<div class="language-selector">
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ request.path }}">
<select name="language" onchange="this.form.submit()">
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"
{% if language.code == LANGUAGE_CODE %}selected{% endif %}>
{{ language.name_local }}
</option>
{% endfor %}
</select>
</form>
</div>
<!-- Blog posts -->
<div class="blog-list">
{% for post in posts %}
<article class="post">
<h2>{{ post.title }}</h2>
<div class="meta">
{% blocktrans with date=post.created_at %}
Published on {{ date }}
{% endblocktrans %}
</div>
<div class="content">
{{ post.content }}
</div>
</article>
{% empty %}
<p>{% trans "No posts found." %}</p>
{% endfor %}
</div>
{% endblock %}
After setting this up, you'll need to create translation files for each language using Django's makemessages
and compilemessages
commands.
Common Pitfalls and Solutions
1. Middleware Order
Make sure the middleware order is correct. LocaleMiddleware
should come after SessionMiddleware
but before CommonMiddleware
.
2. Missing Translation Files
If translations aren't showing up, ensure you've:
- Created translation files with
python manage.py makemessages -l es
(for Spanish, for example) - Edited the
.po
files with translations - Compiled them with
python manage.py compilemessages
3. Forgetting to Mark Strings for Translation
Only strings wrapped in gettext()
, gettext_lazy()
, or template tags like {% trans %}
will be translated.
Best Practices
- Use
gettext_lazy
for model fields: This defers translation until the value is actually accessed. - Create context for ambiguous translations: Use
pgettext()
when the same word might translate differently based on context. - Use
blocktrans
for complex phrases: When you need variables within translated text, use the{% blocktrans %}
template tag. - Keep translations up to date: Run
makemessages
regularly as you add new translatable strings.
Summary
Django's language settings provide a flexible system for creating multilingual applications. In this tutorial, we've covered:
- Core language settings in
settings.py
- How Django determines which language to use
- URL-based language switching with
i18n_patterns
- Session and cookie-based language preferences
- Creating a language selector for your users
- A real-world example with a multilingual blog
By implementing these techniques, you can make your Django application accessible to users around the world in their preferred languages.
Additional Resources
Exercises
- Create a Django project with at least two supported languages (e.g., English and Spanish).
- Implement URL-based language switching with
i18n_patterns
. - Add a dropdown language selector to your site's header.
- Create a model with translatable fields and generate the translation files.
- Challenge: Implement a user preference system that remembers each user's language preference.
By mastering Django's language settings, you'll be able to reach a global audience with your web applications!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)