kopia lustrzana https://github.com/wagtail/wagtail
Activate user's preferred language in JavaScriptCatalog view
rodzic
4f37b011f5
commit
9c4136635b
|
@ -5,6 +5,7 @@ Changelog
|
|||
~~~~~~~~~~~~~~~~
|
||||
|
||||
* Add `WAGTAIL_` prefix to Wagtail-specific tag settings (Aayushman Singh)
|
||||
* Fix: Take preferred language into account for translatable strings in client-side code (Bernhard Bliem, Sage Abdullah)
|
||||
* Docs: Add missing `django.contrib.admin` to list of apps in "add to Django project" guide (Mohamed Rabiaa)
|
||||
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ depth: 1
|
|||
|
||||
### Bug fixes
|
||||
|
||||
* ...
|
||||
* Take preferred language into account for translatable strings in client-side code (Bernhard Bliem, Sage Abdullah)
|
||||
|
||||
### Documentation
|
||||
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import types
|
||||
from functools import wraps
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import override as override_tz
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import override
|
||||
|
||||
from wagtail.admin import messages
|
||||
from wagtail.admin.localization import get_localized_response
|
||||
from wagtail.log_actions import LogContext
|
||||
from wagtail.permissions import page_permission_policy
|
||||
|
||||
|
@ -135,42 +133,8 @@ def require_admin_access(view_func):
|
|||
|
||||
if user.has_perms(["wagtailadmin.access_admin"]):
|
||||
try:
|
||||
preferred_language = None
|
||||
if hasattr(user, "wagtail_userprofile"):
|
||||
preferred_language = (
|
||||
user.wagtail_userprofile.get_preferred_language()
|
||||
)
|
||||
time_zone = user.wagtail_userprofile.get_current_time_zone()
|
||||
else:
|
||||
time_zone = settings.TIME_ZONE
|
||||
with override_tz(time_zone), LogContext(user=user):
|
||||
if preferred_language:
|
||||
with override(preferred_language):
|
||||
response = view_func(request, *args, **kwargs)
|
||||
else:
|
||||
response = view_func(request, *args, **kwargs)
|
||||
|
||||
if hasattr(response, "render"):
|
||||
# If the response has a render() method, Django treats it
|
||||
# like a TemplateResponse, so we should do the same
|
||||
# In this case, we need to guarantee that when the TemplateResponse
|
||||
# is rendered, it is done within the override context manager
|
||||
# or the user preferred_language/timezone will not be used
|
||||
# (this could be replaced with simply rendering the TemplateResponse
|
||||
# for simplicity but this does remove some of its middleware modification
|
||||
# potential)
|
||||
render = response.render
|
||||
|
||||
def overridden_render(response):
|
||||
with override_tz(time_zone):
|
||||
if preferred_language:
|
||||
with override(preferred_language):
|
||||
return render()
|
||||
return render()
|
||||
|
||||
response.render = types.MethodType(overridden_render, response)
|
||||
# decorate the response render method with the override context manager
|
||||
return response
|
||||
with LogContext(user=user):
|
||||
return get_localized_response(view_func, request, *args, **kwargs)
|
||||
|
||||
except PermissionDenied:
|
||||
if request.headers.get("x-requested-with") == "XMLHttpRequest":
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import functools
|
||||
import types
|
||||
|
||||
import zoneinfo
|
||||
from django import VERSION as DJANGO_VERSION
|
||||
from django.conf import settings
|
||||
from django.utils.dates import MONTHS, WEEKDAYS, WEEKDAYS_ABBR
|
||||
from django.utils.timezone import override as override_tz
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import override
|
||||
|
||||
# Wagtail languages with >=90% coverage
|
||||
# This list is manually maintained
|
||||
|
@ -117,3 +120,41 @@ def get_available_admin_time_zones():
|
|||
return getattr(
|
||||
settings, "WAGTAIL_USER_TIME_ZONES", sorted(zoneinfo.available_timezones())
|
||||
)
|
||||
|
||||
|
||||
def get_localized_response(view_func, request, *args, **kwargs):
|
||||
user = request.user
|
||||
preferred_language = None
|
||||
if hasattr(user, "wagtail_userprofile"):
|
||||
preferred_language = user.wagtail_userprofile.get_preferred_language()
|
||||
time_zone = user.wagtail_userprofile.get_current_time_zone()
|
||||
else:
|
||||
time_zone = settings.TIME_ZONE
|
||||
with override_tz(time_zone):
|
||||
if preferred_language:
|
||||
with override(preferred_language):
|
||||
response = view_func(request, *args, **kwargs)
|
||||
else:
|
||||
response = view_func(request, *args, **kwargs)
|
||||
|
||||
if hasattr(response, "render"):
|
||||
# If the response has a render() method, Django treats it
|
||||
# like a TemplateResponse, so we should do the same
|
||||
# In this case, we need to guarantee that when the TemplateResponse
|
||||
# is rendered, it is done within the override context manager
|
||||
# or the user preferred_language/timezone will not be used
|
||||
# (this could be replaced with simply rendering the TemplateResponse
|
||||
# for simplicity but this does remove some of its middleware modification
|
||||
# potential)
|
||||
render = response.render
|
||||
|
||||
def overridden_render(response):
|
||||
with override_tz(time_zone):
|
||||
if preferred_language:
|
||||
with override(preferred_language):
|
||||
return render()
|
||||
return render()
|
||||
|
||||
response.render = types.MethodType(overridden_render, response)
|
||||
# decorate the response render method with the override context manager
|
||||
return response
|
||||
|
|
|
@ -6,6 +6,7 @@ from wagtail.admin.forms.auth import PasswordResetForm
|
|||
from wagtail.admin.tests.test_forms import CustomLoginForm, CustomPasswordResetForm
|
||||
from wagtail.models import Page
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
from wagtail.users.models import UserProfile
|
||||
|
||||
|
||||
class TestLoginView(WagtailTestUtils, TestCase):
|
||||
|
@ -224,10 +225,24 @@ class TestPasswordResetView(TestCase):
|
|||
)
|
||||
|
||||
|
||||
class TestJsi18nView(TestCase):
|
||||
class TestJsi18nView(WagtailTestUtils, TestCase):
|
||||
def test_jsi18n_does_not_require_login(self):
|
||||
response = self.client.get(reverse("wagtailadmin_javascript_catalog"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# get content type header without the "charset" suffix
|
||||
content_type = response["content-type"].split(";")[0]
|
||||
self.assertEqual(content_type, "text/javascript")
|
||||
|
||||
def test_jsi18n_is_localized(self):
|
||||
user = self.login()
|
||||
profile = UserProfile.get_for_user(user)
|
||||
# If a non-default language is activated, the response should contain this:
|
||||
pattern = "const newcatalog = "
|
||||
# If the preferred language is the default language, the catalog should be empty
|
||||
response = self.client.get(reverse("wagtailadmin_javascript_catalog"))
|
||||
self.assertNotContains(response, pattern)
|
||||
# Changing the language should make the catalog appear
|
||||
profile.preferred_language = "de"
|
||||
profile.save()
|
||||
response = self.client.get(reverse("wagtailadmin_javascript_catalog"))
|
||||
self.assertContains(response, pattern)
|
||||
|
|
|
@ -6,7 +6,6 @@ from django.urls import include, path, re_path
|
|||
from django.views.decorators.cache import never_cache
|
||||
from django.views.defaults import page_not_found
|
||||
from django.views.generic import TemplateView
|
||||
from django.views.i18n import JavaScriptCatalog
|
||||
|
||||
from wagtail import hooks
|
||||
from wagtail.admin.api import urls as api_urls
|
||||
|
@ -20,6 +19,7 @@ from wagtail.admin.urls import workflows as wagtailadmin_workflows_urls
|
|||
from wagtail.admin.views import account, chooser, dismissibles, home, tags
|
||||
from wagtail.admin.views.bulk_action import index as bulk_actions
|
||||
from wagtail.admin.views.generic.preview import StreamFieldBlockPreview
|
||||
from wagtail.admin.views.i18n import localized_js_catalog
|
||||
from wagtail.admin.views.pages import listing
|
||||
from wagtail.utils.urlpatterns import decorate_urlpatterns
|
||||
|
||||
|
@ -147,7 +147,7 @@ urlpatterns += [
|
|||
# JS translation catalog
|
||||
path(
|
||||
"jsi18n/",
|
||||
JavaScriptCatalog.as_view(packages=["wagtail.admin"]),
|
||||
localized_js_catalog,
|
||||
name="wagtailadmin_javascript_catalog",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
from django.views.i18n import JavaScriptCatalog
|
||||
|
||||
from wagtail.admin.localization import get_localized_response
|
||||
|
||||
js_catalog = JavaScriptCatalog.as_view(packages=["wagtail.admin"])
|
||||
|
||||
|
||||
def localized_js_catalog(request, *args, **kwargs):
|
||||
"""
|
||||
Django's JavaScriptCatalog that has been decorated to ensure it is localized
|
||||
in the user's preferred language.
|
||||
https://github.com/wagtail/wagtail/issues/11074
|
||||
"""
|
||||
return get_localized_response(js_catalog, request, *args, **kwargs)
|
Ładowanie…
Reference in New Issue