Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
64 changes: 64 additions & 0 deletions packages/hidp/hidp/translations/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import contextlib

from django.http import HttpResponseRedirect
from django.utils import translation


def _get_supported_language_variant(language_code):
with contextlib.suppress(LookupError):
return translation.get_supported_language_variant(language_code)


def _get_language_from_cookie(request):
language = request.COOKIES.get(UiLocalesMiddleware.LANGUAGE_COOKIE_NAME)
if language and _get_supported_language_variant(language):
return language
return None


def _get_language_from_ui_locales(request):
ui_locales = request.GET.get("ui_locales", "").strip()
for ui_locale in ui_locales.split():
if language := _get_supported_language_variant(ui_locale):
return language
return None


class UiLocalesMiddleware:
"""
Middleware that sets a cookie and activated the language based on the 'ui_locales'
query parameter.

As a workaround for a bug in Django Oauth Toolkit, the middleware also makes sure
that the 'ui_locales' query parameter is removed from the URL.
"""

LANGUAGE_COOKIE_NAME = "hidp_language"

def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
if "ui_locales" in request.GET:
# Remove the ui_locales query parameter from the URL.
# This is a workaround for a bug in Django OAuth Toolkit:
# https://github.com/jazzband/django-oauth-toolkit/issues/1468
query = request.GET.copy()
query.pop("ui_locales")
request.META["QUERY_STRING"] = query.urlencode()
response = HttpResponseRedirect(request.build_absolute_uri())

# Set the language cookie if the language is supported.
if language := _get_language_from_ui_locales(request):
# Set the language cookie.
response.set_cookie(
self.LANGUAGE_COOKIE_NAME,
language,
)

return response

if language := _get_language_from_cookie(request):
translation.activate(language)

return self.get_response(request)
1 change: 1 addition & 0 deletions packages/hidp/tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"hidp.rate_limit.middleware.RateLimitMiddleware",
"hidp.translations.middleware.UiLocalesMiddleware",
]

USE_TZ = True
Expand Down
Empty file.
115 changes: 115 additions & 0 deletions packages/hidp/tests/unit_tests/test_translations/test_middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
from unittest import mock

from django.test import RequestFactory, TestCase, override_settings
from django.utils import translation

from hidp.translations.middleware import UiLocalesMiddleware


@override_settings(LANGUAGES=[("en", "English"), ("fr", "French")])
class TestUiLocalesMiddleware(TestCase):
client_class = RequestFactory

def setUp(self):
self.get_response = mock.Mock()
self.middleware = UiLocalesMiddleware(self.get_response)

def test_no_preference(self):
"""
Do nothing if there is no language preference.
"""
active_language = translation.get_language()
response = self.middleware(self.client.get("/"))
self.assertEqual(
response,
self.get_response.return_value,
)
self.assertEqual(active_language, translation.get_language())

def test_drops_ui_locales_param(self):
"""
Removes the 'ui_locales' query parameter, keeps the rest.
"""
# This is a workaround for a bug in Django OAuth Toolkit:
# https://github.com/jazzband/django-oauth-toolkit/issues/1468
request = self.client.get("/?ui_locales=fr&foo=bar")
response = self.middleware(request)
# Does not call get_response
self.get_response.assert_not_called()
# Redirects to the same URL without the 'ui_locales' query parameter.
self.assertEqual(
response.status_code,
302,
)
self.assertEqual(
response.url,
f'http://{request.META["SERVER_NAME"]}/?foo=bar',
)

def test_sets_cookie_for_supported_langauge(self):
"""
Sets the language cookie if 'ui_locales' is present,
and the language is supported
"""
request = self.client.get("/?ui_locales=fr")
response = self.middleware(request)
# Sets the language cookie.
self.assertEqual(
response.cookies["hidp_language"].value,
"fr",
)

def test_picks_first_supported_language(self):
"""
Picks the first supported language if multiple are provided.
"""
request = self.client.get("/?ui_locales=de fr-be en")
response = self.middleware(request)
# Sets the language cookie.
self.assertEqual(
response.cookies["hidp_language"].value,
# 'de' is not supported, 'fr-be' is a variant of 'fr', so 'fr' is picked.
"fr",
)

def test_ignores_unsupported_language(self):
"""
Does not set the language cookie if the language is not supported.
"""
request = self.client.get("/?ui_locales=de")
response = self.middleware(request)
# Does not set the language cookie.
self.assertNotIn(
"hidp_language",
response.cookies,
)

def test_activates_language(self):
"""
Activates the language if it is supported.
"""
request = self.client.get("/", HTTP_COOKIE="hidp_language=fr")
response = self.middleware(request)
self.assertEqual(
response,
self.get_response.return_value,
)
self.assertEqual(
translation.get_language(),
"fr",
)

def test_does_not_activate_unsupported_language(self):
"""
Does not activate the language if it is not supported.
"""
request = self.client.get("/", HTTP_COOKIE="hidp_language=de")
response = self.middleware(request)
self.assertEqual(
response,
self.get_response.return_value,
)
self.assertNotEqual(
translation.get_language(),
"de",
)
5 changes: 5 additions & 0 deletions project/hidp_sandbox/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"hidp.rate_limit.middleware.RateLimitMiddleware",
"hidp.translations.middleware.UiLocalesMiddleware",
]

ROOT_URLCONF = "hidp_sandbox.urls"
Expand Down Expand Up @@ -176,6 +177,10 @@
# https://docs.djangoproject.com/en/4.2/topics/i18n/

LANGUAGE_CODE = "nl-nl"
LANGUAGES = [
("nl", "Nederlands"),
("en", "English"),
]

TIME_ZONE = "Europe/Amsterdam"

Expand Down