Skip to main content

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:

  1. Language specified in the URL parameter (if you use the i18n_patterns function)
  2. Language from the session
  3. Language from a cookie
  4. Language from the Accept-Language HTTP header
  5. 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:

python
# 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.

python
# 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:

python
# 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:

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

python
# 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:

python
# 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:

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

python
# 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:

python
# 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:

html
<!-- 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:

html
<!-- 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:

python
# 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'),
]
python
# 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
)
python
# 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)
html
<!-- 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:

  1. Configure multiple languages
  2. Use URL prefixes for language selection
  3. Translate content using gettext and trans tags
  4. Maintain a consistent user experience across languages

Best Practices for Language Selection

  1. Be consistent with your language selection mechanism: Choose one primary method (URL prefixes or session/cookie) and stick with it for better user experience.

  2. Make the language switcher visible and intuitive: Place it in a consistent location, typically in the header or footer of your site.

  3. Respect the user's preference: Once a user selects a language, maintain that preference across their session.

  4. Handle untranslated content gracefully: When content isn't available in the selected language, consider showing it in the default language rather than showing nothing.

  5. 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).

  6. 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:

  1. Check that LocaleMiddleware is properly positioned in your MIDDLEWARE setting
  2. Verify that the language is in your LANGUAGES setting
  3. Check for JavaScript that might be preventing form submission
  4. Inspect browser cookies and session to ensure the language preference is being stored

Missing Translations

If translations are missing:

  1. Ensure you've compiled your message files with django-admin compilemessages
  2. Check that your LOCALE_PATHS setting is correct
  3. Verify that the translation strings exist in your .po files

URL Pattern Issues

If you're having trouble with URL patterns:

  1. Make sure i18n_patterns() is used correctly
  2. Check that you've included the language prefix in your templates: {% url 'view-name' %}
  3. 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

Exercises

  1. Basic Language Switcher: Implement a basic language switcher using Django's built-in set_language view.

  2. Advanced Language Switcher: Create a dropdown language switcher that shows each language in its native name and includes a flag icon.

  3. Language URL Patterns: Configure a Django project to use language prefixes in URLs and ensure all links in templates continue to work correctly.

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

  5. 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! :)