Un commutateur de langue pour les sites Wagtail multilingues

La création d'un commutateur de langue qui revient à la page d'accueil dans la langue souhaitée est relativement facile. Le faire revenir à la même page dans la langue souhaitée nécessite un peu plus de travail.

12 Juillet 2020 09:53
Thèmes: Plusieurs langues

Django possède de nombreux utilitaires liés aux langues. Dans ce tutoriel, on va utiliser certains d'entre eux pour créer un commutateur de langue qu'on peut activer via un lien dans notre menu de navigation et qui renverra la page sur laquelle on était dans la langue choisie. Définir une vue dans notre views.py qui renvoie la page d'accueil dans la langue choisie est relativement simple:

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

Cela suppose qu'on utilise des préfixes de langue pour nos URL. On vérifie d'abord si le language_code souhaité est dans les LANGUAGES de nos paramètres. Ensuite, on définit l'URL de la page d'accueil, la langue et définit un cookie de langue.

Si on veut revenir à la page où on était, mais dans une langue différente, la première chose qu'on doit faire est de récupérer la page où on était:

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 +'/'

Si on ne peut pas trouver une page précédente, on revient par défaut à la page d'accueil. S'il y a une page précédente et la langue actuelle est déjà la langue demandée, alors on revient immédiatement. On récupère la langue courante avec la fonction get_language de Django.

Maintenant, il y a deux options: soit previous appartient à une page qui fait partie de l'arbre Wagtail, soit elle n'appartient pas. Pour trouver previous dans l'arbre Wagtail, il faut un peu d'ingénierie d'URL. Commencez par séparer le chemin de l'URL avec la fonction urlparse de Python:

from urllib.parse import urlparse

prev_path = urlparse(previous).path

Maintenant, on peut rechercher cela dans l'arbre Wagtail, en utilisant le champ url_path. Cependant, il y a une petite complication: dans les sites multilingues, wagtailtrans a préfixé le chemin racine traduisible à url_path. On peut trouver le site actuel en utilisant la méthode find_for_request du modèle de site de Wagtail. Donc notre recherche devient (les deux chemins ont un début et une fin /, on en supprime un avec le 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)

Maintenant, on peut récupérer la version traduite de cette page en utilisant les champs canonical_page et language du modèle TranslatablePage, comme on l'a fait dans notre menu de navigation.

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

Cela termine le cas où la page précédente fait partie de l'arbre Wagtail. Les pages multilingues de notre projet en dehors de l'arbre doivent être construites avec des modèles i18n. Django fournit une fonction pour traduire leurs URL:

from django import urls

next_url = urls.translate_url(previous, language_code)

Cette fonction est également utilisée dans l'implémentation de set_language de Django. Si translate_url ne trouve pas de traduction, il renvoie l'url d'origine. On a déjà pris soin du cas où language_code est égal à la langue actuelle, donc si cela se produit alors apparemment il n'y a pas de traduction. Dans ce cas, on redirige vers la page d'accueil:

if next_url == previous:
    next_url = '/' + language_code + '/'

La combinaison de ces deux cas et la gestion des exceptions nous donne le code complet qu'on met dans le fichier views.py de notre application:

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

Dans l'urls.py de notre application, on crée un chemin vers cette fonction:

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'),
]

On peut inclure ces modèles d'URL dans notre urls.py du project en ajoutant:

urlpatterns += i18n_patterns(
   path('language/', include('cms.urls')),
)

Si on aurait plus d'entrées dans notre urls.py de l'application, on ferait probablement cela différemment, peut-être créer une application distincte, mais pour l'instant cela suffira. On peut maintenant activer la fonction en créant des liens vers /language/en/, /language/fr/ etc. où on veut le commutateur de langue. Dans un didacticiel précédent, on a créé un générateur de menus; on peut créer un menu language, définir les liens de langue pour chaque élément de menu, y ajouter des icônes avec des drapeaux des différentes langues et ajouter ce menu comme entrée à notre menu principal. Ajouter une nouvelle langue à l'avenir est alors juste une question de l'ajouter au menu. Regardez la vidéo pour voir cela en action.

Lisez plus si vous souhaitez autoriser les utilisateurs à ajouter des commentaires aux articles de votre site.

Commentez cet article (connectez-vous d'abord ou confirmez par nom et email ci-dessous)