В этой статье я бы хотел показать пример того, как можно сделать на сайте ссылку, чтобы пользователь мог переключаться между языками на сайте.
Но я покажу более продвинутый вариант подобного функционала, а именно сохранение выбранного языка в таблице пользователя, а также перенаправление пользователя на страницу с правильным языком, который был выбран пользователем, если они конечно авторизован. То есть, если пользователь настроил себе немецкий язык, то при переходе на сайт по английской ссылке, сайт автоматически перенаправит пользователя на страницу с немецким языком.
Для этого нам понадобится добавить поле кода языка в модель пользователя, надеюсь, что все уже используют свою модель пользователя в Django вместо стандартной.
Переписать стандартную функцию установки языка Django, а также добавить Middleware, который будет обрабатывать запрашиваемые с сайта url.
Помимо прочего, данный функционал будет полезен для того, чтобы формировать письма с правильным языком для зарегистрированных пользователей.
Статья написана на основе Django 4 и может не подходить для более старших версий Django.
Настройка модели пользователя
Добавим в модель пользователя поле language, в которое будет добавлять код выбранного языка, по умолчанию будет тот язык, который является для вашего сайта основным.
Файл 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)
Соответственно создадим миграцию и выполним её.
python manage.py makemigrations python manage.py migrate
Добавим view для установки языка
Файл yourapp/yourauthapp/views.py
Здесь есть два важных момента:
- Оригинальная функциz предполагает использование формы и отправки POST сообщения. Я же предпочитаю ссылку с кодом языка.
- В случае, если пользователь авторизован,
# -*- 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
Подключите set_language в вашем главном файле urls.py
Теперь нужно подключить view, чтобы можно было генерировать ссылки для переключения языка в шаблонах.
# -*- coding: utf-8 -*- from django.urls import path from yourauthapp import views urlpatterns = [ path('lang/<str:lang_code>/', views.set_language, name='lang'), ... ]
Генерирование ссылок в шаблоне
Обратите внимание на то, что я добавляю ?next={{ request.path }} к ссылке, чтобы получить правильный редирект на исходную страницу, на которой пользователь решил переключить язык.
{% 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
А теперь напишем middleware, который будет автоматически произоводить редирект пользователя на страницу с соответствующим языком.
# -*- 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
Остаётся только подключить middleware в настройках сайта и всё будет работать.
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', ]
Заключение
Теперь все авторизованные пользователи всегда будут перенаправляться на страницы с тем языком, который они выбрали.
Конечно для неавторизованных пользователей так работать не будет, но они по крайней мере всегда смогут выбрать тот язык, который захотят из достцупных на вашем сайте.
Добрый день.
Уведомление об этой статье на почту мне пришло почему-то на английском.
Добрый день!
Потому что на этом сайте это не сделано :)
Я сейчас работаю над вторым проектом и делаю работу над ошибками :)
Позже буду ремонтировать и здесь, нужно ещё на более новую версию 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.