Evgenii Legotckoi
Evgenii Legotckoi1 июня 2022 г. 4: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 г. 4:54

Добрый день.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

c
  • 1 декабря 2023 г. 17: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 г. 8:39

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

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
1
  • 12333
  • 18 июля 2024 г. 11:34

Qt - Тест 001. Сигналы и слоты

  • Результат:63баллов,
  • Очки рейтинга-1
1
  • 12333
  • 18 июля 2024 г. 11:25

C++ - Тест 005. Структуры и Классы

  • Результат:50баллов,
  • Очки рейтинга-4
AM

C++ - Тест 005. Структуры и Классы

  • Результат:33баллов,
  • Очки рейтинга-10
Последние комментарии
d
dblas55 июля 2024 г. 17:02
QML - Урок 016. База данных SQLite и работа с ней в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr9 февраля 2024 г. 0:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко5 февраля 2024 г. 7:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 16:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 14:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
Сейчас обсуждают на форуме
F
Fynjy22 июля 2024 г. 10:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCT25 июня 2024 г. 7:00
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
Evgenii Legotckoi
Evgenii Legotckoi24 июня 2024 г. 21:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
BlinCT
BlinCT5 мая 2024 г. 11:46
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii Legotckoi2 мая 2024 г. 20:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

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