Creating a language switch that returns to the home page in the desired language is relatively easy. Making it return to the same page in the desired language requires some more work.
Django has many language related utilities. In this tutorial we will use some of them to create a language switch that we can activate via a link in our navigation menu and that will return the page that we were on in the chosen language. Defining a view in our views.py
that returns the home page in the chosen language is relatively straightforward:
from django.http import HttpResponseRedirect
from django.utils import translation
def set_language_from_url(request, language_code):
if not language_code in [lang[0] for lang in settings.LANGUAGES]:
return HttpResponseRedirect('/')
next_url = '/' + language_code +'/'
translation.activate(language_code)
response = HttpResponseRedirect(next_url)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language_code)
return response
This assumes that we are using language prefixes for our urls. We first check whether the desired language_code
is in the LANGUAGES
of our settings. Then we set the homepage url to go to, activate the language and set a language cookie.
If we would like to return to the page we where on, but in a different language, the first thing we have to do is retrieve the page that we where on:
try:
# get the full path of the referring page; go back if requested language equals current language
previous = request.META['HTTP_REFERER']
if language_code == translation.get_language():
return HttpResponseRedirect(previous)
CODE FOLLOWS
except KeyError:
# if for some reason the previous page cannot be found, go to the home page
next_url = '/' + language_code +'/'
If we cannot find a previous page, then we default to the homepage. If there is a previous page and the current language is already the requested language, then we return immediately. We retrieve the current language with Django's function get_language
.
Now there are two options: either previous
belongs to a page that is part of the Wagtail tree, or it does not. To find previous
in the Wagtail tree requires a little url-engineering. First split off the path of the url with Python's function urlparse
:
from urllib.parse import urlparse
prev_path = urlparse(previous).path
Now we can look this up in the Wagtail tree, using the field url_path
. However, there is one small complication: in multilingual sites wagtailtrans prefixed the translatable root path to url_path
. We can find the current site using the method find_for_request
of Wagtail's Site model. So our lookup becomes (both paths have a beginning and ending /
, we remove one with the slice [1:]
):
from wagtail.core.models import Site
prev_url_path = Site.find_for_request(request).root_page.url_path + prev_path[1:]
prev_page = TranslatablePage.objects.get(url_path=prev_url_path)
Now we can retrieve the translated version of this page using the TranslatablePage
fields canonical_page
and language
, just as we did in our navigation menu.
can_page = prev_page.canonical_page if prev_page.canonical_page else prev_page
language = Language.objects.get(code=language_code)
next_url = can_page.url if language_code == settings.LANGUAGE_CODE else TranslatablePage.objects.get(language=language, canonical_page=can_page).url
That finishes the case where the previous page is part of the Wagtail tree. Multilingual pages in our project outside of the tree should be constructed with i18n patterns. Django provides a function to translate their urls:
from django import urls
next_url = urls.translate_url(previous, language_code)
This function is also used in Django's set_language
implementation. If translate_url
cannot find a translation, then it returns the original url. We already took care of the case where language_code
equals the current language, so if this happens then apparently there is no translation. In that case we redirect to the home page:
if next_url == previous:
next_url = '/' + language_code + '/'
Combining these two cases and taking care of exceptions gives us the full code that we put in the views.py
file of our app:
from django import urls
from django.conf import settings
from django.http import HttpResponseRedirect
from django.utils import translation
from urllib.parse import urlparse
from wagtail.core.models import Site
from wagtailtrans.models import TranslatablePage, Language
def set_language_from_url(request, language_code):
if not language_code in [lang[0] for lang in settings.LANGUAGES]:
return HttpResponseRedirect('/')
try:
# get the full path of the referring page; go back if requested language equals current language
previous = request.META['HTTP_REFERER']
if language_code == translation.get_language():
return HttpResponseRedirect(previous)
try:
# split off the path of the previous page
prev_path = urlparse(previous).path
# wagtailtrans prefixes the translatable root's url_path, so we need to do that as well
prev_url_path = Site.find_for_request(request).root_page.url_path + prev_path[1:]
prev_page = TranslatablePage.objects.get(url_path=prev_url_path)
# if the current page is not canonical, get the canonical page
can_page = prev_page if prev_page.is_canonical else prev_page.canonical_page
# if the requested language is the canonical (default) language, use the canonical page, else find the translated page
language = Language.objects.get(code=language_code)
next_url = can_page.url if language_code == settings.LANGUAGE_CODE else TranslatablePage.objects.get(language=language, canonical_page=can_page).url
except (TranslatablePage.DoesNotExist, Language.DoesNotExist):
# previous page is not a TranslatablePage, try if previous path can be translated by changing the language code
next_url = urls.translate_url(previous, language_code)
# if no translation is found, translate_url will return the original url
# in that case, go to the home page in the requested language
if next_url == previous:
next_url = '/' + language_code + '/'
except KeyError:
# if for some reason the previous page cannot be found, go to the home page
next_url = '/' + language_code +'/'
translation.activate(language_code)
response = HttpResponseRedirect(next_url)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language_code)
return response
In the urls.py
of our app we create a path to this function:
from django.urls import path
from .views import set_language_from_url
urlpatterns = [
path('<str:language_code>/', set_language_from_url, name='set_language_from_url'),
]
We can include these url patterns in our project urls.py
by adding:
urlpatterns += i18n_patterns(
path('language/', include('cms.urls')),
)
If we would have more entries in our app urls.py
then we would probably do this differently, perhaps create a separate app, but for now this will do. We can now activate the function by creating links to /language/en/
, /language/fr/
etc. wherever we want the language switch. In a previous tutorial we have created a menu builder; we can create a menu language
, define the language links for every menu item, add icons with flags of the different languages to it, and add this menu as an entry to our main menu. Adding a new language in the future is then just a matter of adding it to the menu. View the video to see this in action.
Read on if you want to allow users to add comments to articles on your site.
Comment on this article (sign in first or confirm by name and email below)