Evgenii Legotckoi
01 червня 2022 р. 14:30

Django - Урок 059. Збереження вибраної мови в установках користувача

У цій статті я хотів би показати приклад того, як можна зробити на сайті посилання, щоб користувач міг перемикатися між мовами на сайті.

Але я покажу більш просунутий варіант подібного функціоналу, а саме збереження вибраної мови в таблиці користувача, а також перенаправлення користувача на сторінку з правильною мовою, яку вибрано користувачем, якщо вони звичайно авторизовані. Тобто, якщо користувач налаштував собі німецьку мову, то при переході на сайт за англійським посиланням сайт автоматично перенаправить користувача на сторінку з німецькою мовою.

Для цього нам знадобиться додати поле коду мови в модель користувача, сподіваюся, що всі вже використовують свою модель користувача Django замість стандартної.
Переписати стандартну функцію встановлення мови Django, а також додати Middleware, який оброблятиме запитані з сайту url.

Крім того, цей функціонал буде корисним для того, щоб формувати листи з правильною мовою для зареєстрованих користувачів.

Стаття написана на основі Django 4 і може не підходити для старших версій Django.

Налаштування моделі пользователя

Додамо в модель користувача поле language, в яке додаватиме код обраної мови, за замовчуванням буде та мова, яка є для вашого сайту основною.

Файл yourapp/yourauthapp/models.py

  1. # -*- coding: utf-8 -*-
  2.  
  3. from django.conf import settings
  4. from django.contrib.auth.models import AbstractUser
  5. from django.db import models
  6.  
  7.  
  8.  
  9. class Group(DjangoGroup):
  10. class Meta:
  11. proxy = True
  12.  
  13.  
  14. class User(AbstractUser):
  15. language = models.CharField(max_length=10,
  16. choices=settings.LANGUAGES,
  17. default=settings.LANGUAGE_CODE)

Відповідно створимо міграцію та виконаємо її.

  1. python manage.py makemigrations
  2. python manage.py migrate

Додамо view для встановлення мови

Файл yourapp/yourauthapp/views.py

Тут є два важливі моменти:

  • Оригінальна функція передбачає використання форми і відправки POST повідомлення. Я ж віддаю перевагу посиланню з кодом мови.
  • У випадку, якщо користувач авторизований,
  1. # -*- coding: utf-8 -*-
  2.  
  3. from django.conf import settings
  4. from django.http import HttpResponseRedirect, HttpResponse
  5. from django.urls import translate_url
  6. from django.utils.http import url_has_allowed_host_and_scheme
  7. from django.utils.translation import check_for_language
  8.  
  9.  
  10. def set_language(request, lang_code):
  11.  
  12. next_url = request.POST.get('next', request.GET.get('next'))
  13. if (
  14. (next_url or request.accepts('text/html')) and
  15. not url_has_allowed_host_and_scheme(
  16. url=next_url,
  17. allowed_hosts={request.get_host()},
  18. require_https=request.is_secure(),
  19. )
  20. ):
  21. next_url = request.META.get('HTTP_REFERER')
  22. if not url_has_allowed_host_and_scheme(
  23. url=next_url,
  24. allowed_hosts={request.get_host()},
  25. require_https=request.is_secure(),
  26. ):
  27. next_url = '/'
  28. response = HttpResponseRedirect(next_url) if next_url else HttpResponse(status=204)
  29.  
  30. if request.method == 'GET': # Change method from POST to GET, we want use url parameters for this funcionality
  31.  
  32. if lang_code and check_for_language(lang_code):
  33. if next_url:
  34. next_trans = translate_url(next_url, lang_code)
  35. if next_trans != next_url:
  36. response = HttpResponseRedirect(next_trans)
  37. response.set_cookie(
  38. settings.LANGUAGE_COOKIE_NAME, lang_code,
  39. max_age=settings.LANGUAGE_COOKIE_AGE,
  40. path=settings.LANGUAGE_COOKIE_PATH,
  41. domain=settings.LANGUAGE_COOKIE_DOMAIN,
  42. secure=settings.LANGUAGE_COOKIE_SECURE,
  43. httponly=settings.LANGUAGE_COOKIE_HTTPONLY,
  44. samesite=settings.LANGUAGE_COOKIE_SAMESITE,
  45. )
  46.  
  47. # Important part of code, set language to user, if user is authenticated
  48. if request.user.is_authenticated:
  49. request.user.language = lang_code
  50. request.user.save(update_fields=['language'])
  51.  
  52. return response

Підключіть set_language у вашому головному файлі urls.py

Тепер потрібно підключити view, щоб можна було генерувати посилання для перемикання мови шаблонів.

  1. # -*- coding: utf-8 -*-
  2.  
  3. from django.urls import path
  4.  
  5. from yourauthapp import views
  6.  
  7.  
  8. urlpatterns = [
  9. path('lang/<str:lang_code>/', views.set_language, name='lang'),
  10. ...
  11. ]

Генерування посилань у шаблоні

Зверніть увагу, що я додаю ?next={{ request.path }} до посилання, щоб отримати правильний редирект на вихідну сторінку, на якій користувач вирішив переключити мову.

  1. {% get_available_languages as LANGUAGES %}
  2. {% get_language_info_list for LANGUAGES as languages %}
  3. {% for language in languages %}
  4. <a href="{% url 'lang' language.code %}?next={{ request.path }}">{{ language.name_local }}</a>
  5. {% endfor %}

yourapp/yourauthapp/middleware.py

А тепер напишемо middleware, який автоматично вироблятиме редирект користувача на сторінку з відповідною мовою.

  1. # -*- coding: utf-8 -*-
  2.  
  3. from django.http import HttpResponseRedirect
  4. from django.urls import translate_url
  5. from django.utils import translation
  6. from django.utils.deprecation import MiddlewareMixin
  7.  
  8.  
  9. class LocaleMiddleware(MiddlewareMixin):
  10.  
  11. def process_response(self, request, response):
  12. user = getattr(request, 'user', None)
  13. if not user:
  14. return response
  15.  
  16. if not user.is_authenticated:
  17. return response
  18.  
  19. user_language = getattr(user, 'language', None)
  20. if not user_language:
  21. return response
  22.  
  23. next_trans = translate_url(request.path, user_language)
  24. if next_trans != request.path:
  25. translation.activate(user_language)
  26. response = HttpResponseRedirect(next_trans)
  27.  
  28. return response

Залишається тільки підключити middleware у налаштуваннях сайту і все буде працювати.

  1. MIDDLEWARE = [
  2. 'django.middleware.security.SecurityMiddleware',
  3. 'django.contrib.sessions.middleware.SessionMiddleware',
  4. 'django.middleware.common.CommonMiddleware',
  5. 'django.middleware.csrf.CsrfViewMiddleware',
  6. 'django.middleware.locale.LocaleMiddleware',
  7. 'django.contrib.auth.middleware.AuthenticationMiddleware',
  8. 'django.contrib.messages.middleware.MessageMiddleware',
  9. 'django.middleware.clickjacking.XFrameOptionsMiddleware',
  10.  
  11. # add your middleware for redirect user
  12. 'yourauthapp.middleware.LocaleMiddleware',
  13. ]

Висновок

Тепер всі авторизовані користувачі завжди будуть перенаправлятися на сторінки з мовою, яку вони вибрали.

Звичайно для неавторизованих користувачів так працювати не буде, але вони принаймні завжди зможуть вибрати ту мову, яку захочуть з доступних на вашому сайті.

По статті запитували0питання

6

Вам це подобається? Поділіться в соціальних мережах!

IscanderChe
  • 01 червня 2022 р. 14:54

Добрый день.

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

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

Evgenii Legotckoi
  • 01 червня 2022 р. 15:57
  • (відредаговано)

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

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

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

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

NSProject
  • 05 червня 2022 р. 16:47

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

  1. path=settings.LANGUAGE_COOKIE_PATH,
  2. domain=settings.LANGUAGE_COOKIE_DOMAIN,
  3. secure=settings.LANGUAGE_COOKIE_SECURE,
  4. httponly=settings.LANGUAGE_COOKIE_HTTPONLY,
  5. samesite=settings.LANGUAGE_COOKIE_SAMESITE,
Evgenii Legotckoi
  • 06 червня 2022 р. 16:18

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

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

NSProject
  • 06 червня 2022 р. 20:57

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

Evgenii Legotckoi
  • 07 червня 2022 р. 13:06

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

c
  • 02 грудня 2023 р. 04: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
  • 03 грудня 2023 р. 19:39

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
  • Останні коментарі
  • Evgenii Legotckoi
    16 квітня 2025 р. 17:08
    Благодарю за отзыв. И вам желаю всяческих успехов!
  • IscanderChe
    12 квітня 2025 р. 17:12
    Добрый день. Спасибо Вам за этот проект и отдельно за ответы на форуме, которые мне очень помогли в некоммерческих пет-проектах. Профессиональным программистом я так и не стал, но узнал мно…
  • AK
    01 квітня 2025 р. 11:41
    Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
  • Evgenii Legotckoi
    09 березня 2025 р. 21:02
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    09 березня 2025 р. 16:14
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…