Django Language Selection
Introduction
Language selection is a key component of Django's internationalization (i18n) framework. It allows your application to adapt to the preferred language of your users, making your site more accessible and user-friendly for a global audience. In this tutorial, we'll explore how Django determines which language to display content in and how you can implement language selection functionality in your applications.
Django provides several mechanisms for language selection that work together in a priority-based system. Understanding these mechanisms will help you create truly multilingual applications that respect user preferences.
How Django Selects the Language
Django follows a specific order when determining which language to use for rendering content:
- Language specified in the URL parameter (if you use the
i18n_patterns
function) - Language from the session
- Language from a cookie
- Language from the Accept-Language HTTP header
- Default language specified in settings
Let's explore each of these methods in detail.
Setting Up Your Project for Language Selection
Before implementing language selection, ensure your project is properly configured for internationalization:
# settings.py
MIDDLEWARE = [
# ...
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
# ...
]
USE_I18N = True
LANGUAGES = [
('en', 'English'),
('es', 'Spanish'),
('fr', 'French'),
]
LANGUAGE_CODE = 'en' # Default language
The LocaleMiddleware
is crucial as it's responsible for detecting the user's preferred language based on the methods we'll discuss below.
Method 1: Language Prefix in URL Path
One of the most explicit ways to select a language is through URL prefixing. Django's i18n_patterns
function allows you to include the language code in the URL.
# urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path
from . import views
urlpatterns = [
# URLs not requiring language prefix
path('api/', include('myapp.api.urls')),
]
urlpatterns += i18n_patterns(
path('', views.home, name='home'),
path('about/', views.about, name='about'),
# more URL patterns...
)
With this configuration, URLs will look like:
/en/about/
(English)/es/about/
(Spanish)/fr/about/
(French)
If a user visits a URL with a language prefix, Django will use that language for that request.
Method 2: Language Selection via Session
You can store the user's language preference in their session. This approach is useful when you want the language selection to persist across multiple requests without changing URLs.
Here's a simple view that lets users change their language:
# views.py
from django.http import HttpResponseRedirect
from django.utils import translation
def set_language(request):
if request.method == 'POST':
language = request.POST.get('language', 'en')
translation.activate(language)
request.session[translation.LANGUAGE_SESSION_KEY] = language
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
And a corresponding form in your template:
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<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 }} ({{ language.code }})
</option>
{% endfor %}
</select>
</form>
Remember to add the URL pattern for this view:
# urls.py
urlpatterns = [
# ...
path('set-language/', views.set_language, name='set_language'),
# ...
]
Method 3: Using Cookies for Language Selection
Django can also store language preferences in a cookie. This is handled automatically by the LocaleMiddleware
if you use the set_language
view provided by Django:
# urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path, include
urlpatterns = [
# ...
path('i18n/', include('django.conf.urls.i18n')),
# ...
]
Then in your template:
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}">
<select name="language">
{% 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 }} ({{ language.code }})
</option>
{% endfor %}
</select>
<input type="submit" value="Go">
</form>
This built-in view will set a cookie named django_language
with the selected language code.
Method 4: Browser's Accept-Language Header
If no explicit language selection is made through any of the previous methods, Django will try to determine the user's preferred language from the Accept-Language
HTTP header sent by their browser.
For example, if a browser sends:
Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7
Django will try to match these language preferences with the languages available in your LANGUAGES
setting, choosing the best match.
No additional code is needed for this to work, as it's handled automatically by the LocaleMiddleware
.
Method 5: Default Language from Settings
If all else fails, Django will fall back to the language specified by LANGUAGE_CODE
in your settings.
# settings.py
LANGUAGE_CODE = 'en' # Default fallback language
Creating a Language Switcher Component
Let's create a reusable language switcher component that you can include in your templates:
# templatetags/i18n_tags.py
from django import template
from django.utils.translation import get_language
from django.conf import settings
register = template.Library()
@register.inclusion_tag('components/language_switcher.html')
def language_switcher(request):
current_language = get_language()
available_languages = settings.LANGUAGES
return {
'current_language': current_language,
'available_languages': available_languages,
'request': request,
}
And the corresponding template:
<!-- templates/components/language_switcher.html -->
<div class="language-switcher">
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ request.path }}">
<select name="language" onchange="this.form.submit()">
{% for code, name in available_languages %}
<option value="{{ code }}" {% if code == current_language %}selected{% endif %}>
{{ name }}
</option>
{% endfor %}
</select>
</form>
</div>
You can then use this component in your base template:
<!-- templates/base.html -->
{% load i18n_tags %}
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
<header>
<div class="container">
<nav>
<!-- Your navigation -->
</nav>
{% language_switcher request %}
</div>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<!-- Your footer -->
</footer>
</body>
</html>
Real-World Example: E-commerce Site with Language Selection
Let's look at a practical example of implementing language selection in an e-commerce site:
# settings.py
LANGUAGES = [
('en', 'English'),
('es', 'Español'),
('fr', 'Français'),
('de', 'Deutsch'),
]
LANGUAGE_CODE = 'en'
MIDDLEWARE = [
# ...
'django.middleware.locale.LocaleMiddleware',
# ...
]
LOCALE_PATHS = [
os.path.join(BASE_DIR, 'locale'),
]
# urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path, include
from . import views
urlpatterns = [
# Non-localized URLs
path('i18n/', include('django.conf.urls.i18n')),
path('api/', include('shop.api.urls')),
# Static files shouldn't be language-prefixed
path('media/', include('shop.media.urls')),
]
# Localized URLs
urlpatterns += i18n_patterns(
path('', views.homepage, name='home'),
path('products/', include('shop.products.urls')),
path('cart/', include('shop.cart.urls')),
path('account/', include('shop.accounts.urls')),
path('checkout/', include('shop.checkout.urls')),
prefix_default_language=True # Include prefix for default language too
)
# views.py
from django.shortcuts import render
from django.utils.translation import gettext as _
from .models import Product, Category
def homepage(request):
featured_products = Product.objects.filter(featured=True)[:8]
categories = Category.objects.all()
context = {
'title': _('Welcome to our store'),
'featured_products': featured_products,
'categories': categories,
'welcome_message': _('Find the best products at the best prices'),
}
return render(request, 'shop/homepage.html', context)
<!-- templates/shop/homepage.html -->
{% extends 'base.html' %}
{% load i18n %}
{% block content %}
<div class="hero">
<h1>{{ title }}</h1>
<p>{{ welcome_message }}</p>
</div>
<section class="featured-products">
<h2>{% trans "Featured Products" %}</h2>
<div class="product-grid">
{% for product in featured_products %}
<div class="product-card">
<img src="{{ product.image.url }}" alt="{{ product.name }}">
<h3>{{ product.name }}</h3>
<p class="price">{{ product.price }}</p>
<a href="{% url 'product_detail' product.slug %}" class="btn">
{% trans "View details" %}
</a>
<button class="btn add-to-cart" data-id="{{ product.id }}">
{% trans "Add to cart" %}
</button>
</div>
{% endfor %}
</div>
</section>
<section class="categories">
<h2>{% trans "Shop by Category" %}</h2>
<div class="category-list">
{% for category in categories %}
<a href="{% url 'category_detail' category.slug %}" class="category-item">
<img src="{{ category.image.url }}" alt="{{ category.name }}">
<span>{{ category.name }}</span>
</a>
{% endfor %}
</div>
</section>
{% endblock %}
This example shows how to:
- Configure multiple languages
- Use URL prefixes for language selection
- Translate content using
gettext
andtrans
tags - Maintain a consistent user experience across languages
Best Practices for Language Selection
-
Be consistent with your language selection mechanism: Choose one primary method (URL prefixes or session/cookie) and stick with it for better user experience.
-
Make the language switcher visible and intuitive: Place it in a consistent location, typically in the header or footer of your site.
-
Respect the user's preference: Once a user selects a language, maintain that preference across their session.
-
Handle untranslated content gracefully: When content isn't available in the selected language, consider showing it in the default language rather than showing nothing.
-
Test with multiple languages: Ensure your layouts work well with languages of different lengths (German words tend to be longer than English ones, for example).
-
Consider language-specific formatting: Dates, numbers, and currency should be formatted according to the selected language's conventions.
Common Issues and Troubleshooting
Language Not Changing
If the language isn't changing when selected:
- Check that
LocaleMiddleware
is properly positioned in yourMIDDLEWARE
setting - Verify that the language is in your
LANGUAGES
setting - Check for JavaScript that might be preventing form submission
- Inspect browser cookies and session to ensure the language preference is being stored
Missing Translations
If translations are missing:
- Ensure you've compiled your message files with
django-admin compilemessages
- Check that your
LOCALE_PATHS
setting is correct - Verify that the translation strings exist in your
.po
files
URL Pattern Issues
If you're having trouble with URL patterns:
- Make sure
i18n_patterns()
is used correctly - Check that you've included the language prefix in your templates:
{% url 'view-name' %}
- Consider whether
prefix_default_language=True
is appropriate for your application
Summary
Django's language selection system provides a flexible and powerful way to create multilingual applications. You can implement language selection through URL prefixes, session variables, cookies, or by respecting the browser's language preferences.
Key points to remember:
- The
LocaleMiddleware
is essential for language detection and selection - Django follows a priority order when determining which language to use
- URL prefixing provides the most explicit way to select a language
- User preferences can be stored in sessions or cookies
- The browser's Accept-Language header serves as a fallback
- The default language in settings is the last resort
By understanding and implementing these language selection methods, you can create Django applications that are truly global-ready and provide a personalized experience for users from different linguistic backgrounds.
Additional Resources
- Django Official Documentation on Translation
- Django Language Prefix URL Patterns
- Weblate - A web-based translation management system that can integrate with Django projects
- django-parler - A Django library for model translations
Exercises
-
Basic Language Switcher: Implement a basic language switcher using Django's built-in
set_language
view. -
Advanced Language Switcher: Create a dropdown language switcher that shows each language in its native name and includes a flag icon.
-
Language URL Patterns: Configure a Django project to use language prefixes in URLs and ensure all links in templates continue to work correctly.
-
Language Detection: Implement a view that detects the user's preferred language from their browser settings and redirects them to the appropriate localized version of the site.
-
Persistent Language Preference: Create a system that remembers a user's language preference between sessions using cookies.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)