Evgenii Legotckoi
Evgenii Legotckoi1 июня 2022 г. 3:30

Django - Урок 059. Сохранение выбранного языка в настройках пользователя

В этой статье я бы хотел показать пример того, как можно сделать на сайте ссылку, чтобы пользователь мог переключаться между языками на сайте.

Но я покажу более продвинутый вариант подобного функционала, а именно сохранение выбранного языка в таблице пользователя, а также перенаправление пользователя на страницу с правильным языком, который был выбран пользователем, если они конечно авторизован. То есть, если пользователь настроил себе немецкий язык, то при переходе на сайт по английской ссылке, сайт автоматически перенаправит пользователя на страницу с немецким языком.

Для этого нам понадобится добавить поле кода языка в модель пользователя, надеюсь, что все уже используют свою модель пользователя в 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',
]

Заключение

Теперь все авторизованные пользователи всегда будут перенаправляться на страницы с тем языком, который они выбрали.

Конечно для неавторизованных пользователей так работать не будет, но они по крайней мере всегда смогут выбрать тот язык, который захотят из достцупных на вашем сайте.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

IscanderChe
  • 1 июня 2022 г. 3:54

Добрый день.

Помимо прочего, данный функционал будет полезен для того, чтобы формировать письма с правильным языком для зарегистрированных пользователей.

Уведомление об этой статье на почту мне пришло почему-то на английском.

Evgenii Legotckoi
  • 1 июня 2022 г. 4:57
  • (ред.)

Добрый день!
Потому что на этом сайте это не сделано :)
Я сейчас работаю над вторым проектом и делаю работу над ошибками :)
Позже буду ремонтировать и здесь, нужно ещё на более новую версию Django мигрировать.

Будет интересно посмотреть ваш новый проект, Евгений. Уж как никак, но ваши статьи самые лучшие и подходят на уровень детального создания, ведь по вашим статьям я делал много для своего проекта. Надеюсь у вас будут перспективы создать инструкции с новыми вашими познаниями и новым кодом :)

Благодарю за отзыв. Да, я понемногу буду писать статьи с информацией из нового проекта.
А сам проект позже анонсирую, также повешаю в сайдбаре анонсовый виджет.

Буду ждать, Евгений! Потому что буду разрабатывать новый проект, как альманах программиста новичка.)

NSProject
  • 5 июня 2022 г. 5:47

Можно пояснительную записку настроек. Что примерно указывается

                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,
Evgenii Legotckoi
  • 6 июня 2022 г. 5:18

Это всё настройки для языка в Django, по умолчанию ничего менять не нужно.
А сам этот кусок кода скопирован из оригинальной функции.

Подробнее написано в официальной документации Django settings .
Там написаны и значения по умолчанию.

NSProject
  • 6 июня 2022 г. 9:57

Я как так сказать новичёк в Python и Django в целом. Читал документацию и вот по этому мне показалось сразу что это оттуда. Немного маштабируемо по своему усмотрению. Однако все настройки из settings возможно индивидуальны. Это как я понимаю те настройки что я делаю сам.

Evgenii Legotckoi
  • 7 июня 2022 г. 2:06

Да, вы можете в своём settings файле определить все эти переменные и значения по умолчанию будут переписаны.

c
  • 1 декабря 2023 г. 16:34

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

Evgenii Legotckoi
  • 3 декабря 2023 г. 7:39

It is redirect from untranslated url to translated url. It is normal behavior for mutlilanguage web site based on the Django.

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
ДА

C++ - Тест 003. Условия и циклы

  • Результат:21баллов,
  • Очки рейтинга-10
АФ

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:30баллов,
  • Очки рейтинга-10
ПЩ

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80баллов,
  • Очки рейтинга4
Последние комментарии
k
kmssr9 февраля 2024 г. 5:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко5 февраля 2024 г. 12:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 21:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 19:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 декабря 2023 г. 8:01
Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
P
Pisych27 февраля 2023 г. 15:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 января 2024 г. 22:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…
BlinCT
BlinCT27 декабря 2023 г. 19:57
Растягивать Image на парент по высоте Ну и само собою дял включения scrollbar надо чтобы был Flickable. Так что выходит как то так Flickable{ id: root anchors.fill: parent clip: true property url linkFile p…
Дмитрий
Дмитрий10 января 2024 г. 15:18
Qt Creator загружает всю оперативную память Проблема решена. Удалось разобраться с помощью утилиты strace. Запустил ее: strace ./qtcreator Начал выводиться весь лог работы креатора. В один момент он начал считывать фай…
Evgenii Legotckoi
Evgenii Legotckoi12 декабря 2023 г. 17:48
Побуквенное сравнение двух строк Добрый день. Там случайно не высылается этот сигнал textChanged ещё и при форматировани текста? Если решиать в лоб, то можно просто отключать сигнал/слотовое соединение внутри слота и …

Следите за нами в социальных сетях