In this article, I would like to show an example of how you can make a link on the site so that the user can switch between languages on the site.
But I will show a more advanced version of this functionality, namely saving the selected language in the user table, as well as redirecting the user to a page with the correct language that was selected by the user, if they are of course authorized. That is, if the user has set the German language for himself, then when going to the site via an English link, the site will automatically redirect the user to the page with the German language.
To do this, we need to add a language code field to the user model, I hope that everyone is already using their user model in Django instead of the default one.
Rewrite the standard Django language setup function, and also add a Middleware that will process the urls requested from the site.
Among other things, this functionality will be useful in order to generate letters with the correct language for registered users.
This article is based on Django 4 and may not be suitable for older versions of Django.
Set up the user model
Let's add the language field to the user model, in which the code of the selected language will be added, by default it will be the language that is the main language for your site.
Файл yourapp/yourauthapp/models.py
# -*- coding: utf-8 -*- from django.conf import settings from django.contrib.auth.models import AbstractUser from django.db import models class Group(DjangoGroup): class Meta: proxy = True class User(AbstractUser): language = models.CharField(max_length=10, choices=settings.LANGUAGES, default=settings.LANGUAGE_CODE)
Accordingly, we will create a migration and execute it.
python manage.py makemigrations python manage.py migrate
Add a view to set the language
Файл yourapp/yourauthapp/views.py
There are two important points here:
- The original function is to use a form and send a POST message. I prefer the link with the language code.
- In case the user is authorized,
# -*- coding: utf-8 -*- from django.conf import settings from django.http import HttpResponseRedirect, HttpResponse from django.urls import translate_url from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import check_for_language def set_language(request, lang_code): next_url = request.POST.get('next', request.GET.get('next')) if ( (next_url or request.accepts('text/html')) and not url_has_allowed_host_and_scheme( url=next_url, allowed_hosts={request.get_host()}, require_https=request.is_secure(), ) ): next_url = request.META.get('HTTP_REFERER') if not url_has_allowed_host_and_scheme( url=next_url, allowed_hosts={request.get_host()}, require_https=request.is_secure(), ): next_url = '/' response = HttpResponseRedirect(next_url) if next_url else HttpResponse(status=204) if request.method == 'GET': # Change method from POST to GET, we want use url parameters for this funcionality if lang_code and check_for_language(lang_code): if next_url: next_trans = translate_url(next_url, lang_code) if next_trans != next_url: response = HttpResponseRedirect(next_trans) response.set_cookie( settings.LANGUAGE_COOKIE_NAME, lang_code, max_age=settings.LANGUAGE_COOKIE_AGE, path=settings.LANGUAGE_COOKIE_PATH, domain=settings.LANGUAGE_COOKIE_DOMAIN, secure=settings.LANGUAGE_COOKIE_SECURE, httponly=settings.LANGUAGE_COOKIE_HTTPONLY, samesite=settings.LANGUAGE_COOKIE_SAMESITE, ) # Important part of code, set language to user, if user is authenticated if request.user.is_authenticated: request.user.language = lang_code request.user.save(update_fields=['language']) return response
Include set_language in your main urls.py file
Now you need to connect the view so that you can generate links for switching the language in templates.
# -*- coding: utf-8 -*- from django.urls import path from yourauthapp import views urlpatterns = [ path('lang/<str:lang_code>/', views.set_language, name='lang'), ... ]
Generation of links in the template
Note that I add ?next={{ request.path }} to the link to get the correct redirect to the original page where the user chose to switch the language.
{% get_available_languages as LANGUAGES %} {% get_language_info_list for LANGUAGES as languages %} {% for language in languages %} <a href="{% url 'lang' language.code %}?next={{ request.path }}">{{ language.name_local }}</a> {% endfor %}
yourapp/yourauthapp/middleware.py
And now let's write a middleware that will automatically redirect the user to a page with the appropriate language.
# -*- coding: utf-8 -*- from django.http import HttpResponseRedirect from django.urls import translate_url from django.utils import translation from django.utils.deprecation import MiddlewareMixin class LocaleMiddleware(MiddlewareMixin): def process_response(self, request, response): user = getattr(request, 'user', None) if not user: return response if not user.is_authenticated: return response user_language = getattr(user, 'language', None) if not user_language: return response next_trans = translate_url(request.path, user_language) if next_trans != request.path: translation.activate(user_language) response = HttpResponseRedirect(next_trans) return response
It remains only to enable middleware in the site settings and everything will work.
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', # add your middleware for redirect user 'yourauthapp.middleware.LocaleMiddleware', ]
Conclusion
Now all authorized users will always be redirected to pages with the language they have chosen.
Of course, for unauthorized users this will not work, but at least they will always be able to choose the language they want from those available on your site.
Добрый день.
Уведомление об этой статье на почту мне пришло почему-то на английском.
Добрый день!
Потому что на этом сайте это не сделано :)
Я сейчас работаю над вторым проектом и делаю работу над ошибками :)
Позже буду ремонтировать и здесь, нужно ещё на более новую версию Django мигрировать.
Будет интересно посмотреть ваш новый проект, Евгений. Уж как никак, но ваши статьи самые лучшие и подходят на уровень детального создания, ведь по вашим статьям я делал много для своего проекта. Надеюсь у вас будут перспективы создать инструкции с новыми вашими познаниями и новым кодом :)
Благодарю за отзыв. Да, я понемногу буду писать статьи с информацией из нового проекта.
А сам проект позже анонсирую, также повешаю в сайдбаре анонсовый виджет.
Буду ждать, Евгений! Потому что буду разрабатывать новый проект, как альманах программиста новичка.)
Можно пояснительную записку настроек. Что примерно указывается
Это всё настройки для языка в Django, по умолчанию ничего менять не нужно.
А сам этот кусок кода скопирован из оригинальной функции.
Подробнее написано в официальной документации Django settings .
Там написаны и значения по умолчанию.
Я как так сказать новичёк в Python и Django в целом. Читал документацию и вот по этому мне показалось сразу что это оттуда. Немного маштабируемо по своему усмотрению. Однако все настройки из settings возможно индивидуальны. Это как я понимаю те настройки что я делаю сам.
Да, вы можете в своём settings файле определить все эти переменные и значения по умолчанию будут переписаны.
It tries to do language translation in API views. That's why it sends or receives the same API request twice. Do you have any suggestions on this? Example: stripe webhook.
"GET /warehouse/webhook/ HTTP/1.1" 302
"GET /en/warehouse/webhook/ HTTP/1.1" 200
It is redirect from untranslated url to translated url. It is normal behavior for mutlilanguage web site based on the Django.