84 lines
2.5 KiB
Python
84 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
from contextlib import suppress
|
|
from urllib.parse import urlparse
|
|
|
|
from django.conf import settings
|
|
from django.http import HttpRequest, HttpResponseRedirect
|
|
from django.urls import translate_url
|
|
from django.utils.http import url_has_allowed_host_and_scheme
|
|
from django.utils.translation import activate
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
|
|
|
|
def _normalize_language_code(lang: str | None) -> str:
|
|
if not lang:
|
|
return settings.LANGUAGE_CODE
|
|
|
|
lang = lang.replace("_", "-").lower()
|
|
|
|
overrides = getattr(settings, "LANGUAGE_URL_OVERRIDES", {}) or {}
|
|
if lang in overrides:
|
|
lang = overrides[lang].lower()
|
|
|
|
supported = {code.lower() for code, _ in getattr(settings, "LANGUAGES", [])}
|
|
if lang not in supported:
|
|
primary = lang.split("-")[0]
|
|
if primary in overrides and overrides[primary].lower() in supported:
|
|
return overrides[primary].lower()
|
|
return settings.LANGUAGE_CODE
|
|
|
|
return lang
|
|
|
|
|
|
def _safe_next_url(request: HttpRequest) -> str:
|
|
next_url = (
|
|
request.POST.get("next")
|
|
or request.GET.get("next")
|
|
or request.META.get("HTTP_REFERER")
|
|
or "/"
|
|
)
|
|
if not url_has_allowed_host_and_scheme(
|
|
url=next_url,
|
|
allowed_hosts={request.get_host()},
|
|
require_https=request.is_secure(),
|
|
):
|
|
return "/"
|
|
return next_url
|
|
|
|
|
|
LANGUAGE_SESSION_KEY = "_language"
|
|
|
|
|
|
@csrf_exempt
|
|
def set_language(request: HttpRequest):
|
|
language = request.POST.get("language") or request.GET.get("language")
|
|
normalized = _normalize_language_code(language)
|
|
|
|
response = HttpResponseRedirect("/")
|
|
if hasattr(request, "session"):
|
|
request.session[LANGUAGE_SESSION_KEY] = normalized
|
|
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, normalized)
|
|
|
|
activate(normalized)
|
|
|
|
next_url = _safe_next_url(request)
|
|
|
|
if translate_url is not None:
|
|
with suppress(Exception):
|
|
next_url = translate_url(next_url, normalized)
|
|
else:
|
|
parsed = urlparse(next_url)
|
|
parts = [p for p in parsed.path.split("/") if p]
|
|
supported = {code.lower() for code, _ in getattr(settings, "LANGUAGES", [])}
|
|
if parts and parts[0].lower() in supported:
|
|
parts[0] = normalized
|
|
path = (
|
|
"/" + "/".join(parts) + "/"
|
|
if parsed.path.endswith("/")
|
|
else "/" + "/".join(parts)
|
|
)
|
|
next_url = parsed._replace(path=path).geturl()
|
|
|
|
response["Location"] = next_url
|
|
return response
|