зеркало из https://github.com/mozilla/bedrock.git
Set correct lang-picker options when different locales for a given path may be served by the CMS or the Django fallback view
* Expand prefer_cms to take args that tell us what locales are available in the Django fallback version of the page * In prefer_cms annotate the request with lists of locales available in a CMS-backed version of the page and a Django-backed version * Add a new jinja helper to select the most appropriate list of locales to pass to the lang picker UI element
This commit is contained in:
Родитель
d78a2c4110
Коммит
6e2a8a51dd
|
@ -4,12 +4,16 @@
|
|||
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
#}
|
||||
|
||||
{% if translations|length > 1 %}
|
||||
|
||||
|
||||
{%- set available_languages = get_lang_switcher_options(request, translations) -%}
|
||||
|
||||
{% if available_languages|length > 1 %}
|
||||
<form id="lang_form" class="mzp-c-language-switcher" method="get" action="#">
|
||||
<a class="mzp-c-language-switcher-link" href="{{ url('mozorg.locales') }}">{{ ftl('footer-language') }}</a>
|
||||
<label for="page-language-select">{{ ftl('footer-language') }}</label>
|
||||
<select id="page-language-select" class="mzp-js-language-switcher-select" name="lang" dir="ltr" data-testid="footer-language-select">
|
||||
{% for code, label in translations|dictsort -%}
|
||||
{% for code, label in available_languages|dictsort -%}
|
||||
{# Don't escape the label as it may include entity references
|
||||
# like "Português (do Brasil)" (Bug 861149) #}
|
||||
<option lang="{{ code }}" value="{{ code }}"{{ code|ifeq(LANG, " selected") }}>{{ label|safe|replace('English (US)', 'English') }}</option>
|
||||
|
|
|
@ -17,6 +17,7 @@ from markupsafe import Markup
|
|||
|
||||
from bedrock.base import waffle
|
||||
from bedrock.utils import expand_locale_groups
|
||||
from lib.l10n_utils import get_translations_native_names
|
||||
|
||||
from ..urlresolvers import reverse
|
||||
|
||||
|
@ -153,3 +154,22 @@ def alternate_url(path, locale):
|
|||
return alt_paths[path][locale]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@library.global_function
|
||||
def get_lang_switcher_options(request, translations):
|
||||
# For purely Django-rendered pages, or purely CMS-backed pages, we can just
|
||||
# rely on the `translations` var in the render context to know what locales
|
||||
# are viable for the page being rendered. Great! \o/
|
||||
available_locales = translations
|
||||
|
||||
# However, if a URL route is decorated with bedrock.cms.decorators.prefer_cms
|
||||
# that means that a page could come from the CMS or from Django depending on
|
||||
# the locale being requested. In this situation _locales_available_via_cms
|
||||
# and _locales_for_django_fallback_view are annotated onto the request.
|
||||
# We need to use these to create a more accurate view of what locales are
|
||||
# available
|
||||
if hasattr(request, "_locales_available_via_cms") and hasattr(request, "_locales_for_django_fallback_view"):
|
||||
available_locales = get_translations_native_names(sorted(set(request._locales_available_via_cms + request._locales_for_django_fallback_view)))
|
||||
|
||||
return available_locales
|
||||
|
|
|
@ -9,19 +9,41 @@ from django.http import Http404
|
|||
from wagtail.views import serve as wagtail_serve
|
||||
|
||||
from bedrock.base.i18n import remove_lang_prefix
|
||||
from lib.l10n_utils.fluent import get_active_locales
|
||||
|
||||
from .utils import get_cms_locales_for_path
|
||||
|
||||
HTTP_200_OK = 200
|
||||
|
||||
|
||||
def prefer_cms(view_func):
|
||||
def prefer_cms(view_func=None, fallback_ftl_files=None, fallback_lang_codes=None):
|
||||
"""
|
||||
A decorator that helps us migrate from pure Django-based views
|
||||
to CMS views.
|
||||
to CMS views, or support having _some_ locales served from the CMS and
|
||||
other / evergreen content in other locales coming from Django views .
|
||||
|
||||
It will try to see if `wagtail.views.serve` can find a live CMS page for the
|
||||
URL path that matches the current Django view's path, and if so, will
|
||||
return that. If not, it will let the regular Django view run.
|
||||
|
||||
Args:
|
||||
view_func - the function to wrap
|
||||
fallback_ftl_files (optional) - a list of the names of the Fluent files used by
|
||||
the Django view that's being wrapped. It's a little repetitive, but
|
||||
from those we can work out what locales the page is availble in
|
||||
across the CMS and Django views
|
||||
fallback_lang_codes (optional) - a list of strings of language codes that
|
||||
show what locales are available for the Django view being wrapped.
|
||||
This is useful if, for some reason, the Fluent files are not a reliable
|
||||
way to determine the available locales for a page
|
||||
(e.g. the Fluent files cover 20 locales for some strings which appear
|
||||
on the page, but the main localized content is only in two languages,
|
||||
because the contnet doesn't come from Fluent - such as Legal Docs,
|
||||
which comes from a git repo)
|
||||
|
||||
Note that setting both fallback_lang_codes and fallback_ftl_files will cause
|
||||
an exception to be raised - only one should be set, not both.
|
||||
|
||||
Workflow:
|
||||
|
||||
1. This decorator is added to the target view that will be replaced with CMS content
|
||||
|
@ -33,42 +55,100 @@ def prefer_cms(view_func):
|
|||
|
||||
Example for a function-based view:
|
||||
|
||||
@prefer_cms
|
||||
@prefer_cms(fallback_ftl_files=[...]) # or fallback_lang_codes=["fr", "es-MX",]
|
||||
def some_path(request):
|
||||
...
|
||||
|
||||
Or, in a URLconf for a regular Django view:
|
||||
|
||||
...
|
||||
path("some/path/", prefer_cms(views.some_view)),
|
||||
path("some/path/", prefer_cms(views.some_view, fallback_ftl_files=[...])),
|
||||
|
||||
# or
|
||||
|
||||
path("some/path/", prefer_cms(views.some_view, fallback_lang_codes=["fr", "pt-BR",])),
|
||||
|
||||
...
|
||||
|
||||
Or, in a URLconf with Bedrock's page() helper:
|
||||
page(
|
||||
"about/leadership/",
|
||||
"mozorg/about/leadership/index.html",
|
||||
ftl_files=["some/path/to/fluent/data"],
|
||||
decorators=[prefer_cms],
|
||||
)
|
||||
|
||||
(Note that no fallback_ftl_files is needed here - we'll just pick up the regular
|
||||
ftl_files kwarg that's already used for page(). ALSO: fallback_ftl_files is NOT
|
||||
supported for the page() helper, as we shouldn't need it in real use)
|
||||
"""
|
||||
|
||||
@wraps(view_func)
|
||||
def wrapped_view(request, *args, **kwargs):
|
||||
path = remove_lang_prefix(request.path_info)
|
||||
try:
|
||||
# Does Wagtail have a route that matches this? If so, show that page
|
||||
wagtail_response = wagtail_serve(request, path)
|
||||
if wagtail_response.status_code == HTTP_200_OK:
|
||||
return wagtail_response
|
||||
except Http404:
|
||||
pass
|
||||
if fallback_ftl_files and fallback_lang_codes:
|
||||
raise RuntimeError("The prefer_cms decorator can be configured with either fallback_ftl_files or fallback_lang_codes but not both.")
|
||||
|
||||
# If not, call the original view function, but remember to
|
||||
# un-mark the request as being for a CMS page. This is so to avoid
|
||||
# lib.l10n_utils.render() incorrectly looking for available translations
|
||||
# based on CMS data - we're falling back to non-CMS pages, so we want
|
||||
# the Fluent translations that are normal for a Django-rendered page
|
||||
request.is_cms_page = False
|
||||
return view_func(request, *args, **kwargs)
|
||||
fallback_ftl_files = fallback_ftl_files or []
|
||||
fallback_lang_codes = fallback_lang_codes or []
|
||||
|
||||
return wrapped_view
|
||||
def _get_django_locales_available(
|
||||
fallback_ftl_files,
|
||||
fallback_lang_codes,
|
||||
kwargs,
|
||||
):
|
||||
# Prefer explicit list of lang codes over everything else
|
||||
if fallback_lang_codes:
|
||||
return fallback_lang_codes
|
||||
|
||||
# If we're looking at Fluent files, prefer the ftl_files kwarg (as used
|
||||
# with page()) but also accept an explicitly added fallback_ftl_files param
|
||||
_ftl_files = kwargs.get("ftl_files", fallback_ftl_files)
|
||||
return get_active_locales(_ftl_files, force=True)
|
||||
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapped_view(request, *args, **kwargs):
|
||||
path = remove_lang_prefix(request.path_info)
|
||||
|
||||
# Annotate the request with the Django/fallback locales, as we'll
|
||||
# need them for the language picket in the footer when rendering
|
||||
# the Wagtail response IF there is a Wagtail match
|
||||
request._locales_for_django_fallback_view = _get_django_locales_available(
|
||||
fallback_ftl_files=fallback_ftl_files,
|
||||
fallback_lang_codes=fallback_lang_codes,
|
||||
kwargs=kwargs,
|
||||
)
|
||||
|
||||
try:
|
||||
# Does Wagtail have a route that matches this? If so, show that page
|
||||
wagtail_response = wagtail_serve(request, path)
|
||||
if wagtail_response.status_code == HTTP_200_OK:
|
||||
return wagtail_response
|
||||
except Http404:
|
||||
pass
|
||||
|
||||
# If the page does not exist in Wagtail, call the original view function and...
|
||||
#
|
||||
# 1) Un-mark this request as being for a CMS page (which happened
|
||||
# via wagtail_serve()) to avoid lib.l10n_utils.render() incorrectly
|
||||
# looking for available translations based on CMS data, rather than
|
||||
# Fluent files
|
||||
request.is_cms_page = False
|
||||
|
||||
# 2) Make extra sure this request is still annotated with any CMS-backed
|
||||
# locale versions that are available, so that we can populate the
|
||||
# language picker appropriately. (The annotation also happened via
|
||||
# wagtail_serve() thanks to AbstractBedrockCMSPage._patch_request_for_bedrock
|
||||
request._locales_available_via_cms = getattr(
|
||||
request,
|
||||
"_locales_available_via_cms",
|
||||
get_cms_locales_for_path(request),
|
||||
)
|
||||
return func(request, *args, **kwargs)
|
||||
|
||||
return wrapped_view
|
||||
|
||||
# If view_func is None, the decorator was called with parameters
|
||||
if view_func is None:
|
||||
return decorator
|
||||
else:
|
||||
# Otherwise, apply the decorator directly to view_func
|
||||
return decorator(view_func)
|
||||
|
|
|
@ -59,7 +59,7 @@ class AbstractBedrockCMSPage(WagtailBasePage):
|
|||
# Quick annotation to help us track the origin of the page
|
||||
request.is_cms_page = True
|
||||
|
||||
# Patch in a list of CMS-available locales for pages that are translations, not just aliases
|
||||
# Patch in a list of available locales for pages that are translations, not just aliases
|
||||
request._locales_available_via_cms = get_locales_for_cms_page(self)
|
||||
return request
|
||||
|
||||
|
|
|
@ -1,11 +1,26 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
from django.http import Http404
|
||||
|
||||
|
||||
def get_page_for_path(request, path):
|
||||
from wagtail.models import Site
|
||||
|
||||
site = Site.find_for_request(request)
|
||||
try:
|
||||
page, args, kwargs = site.root_page.specific.route(request, path)
|
||||
return page
|
||||
except Http404:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_locales_for_cms_page(page):
|
||||
# Patch in a list of CMS-available locales for pages that are
|
||||
# translations, not just aliases
|
||||
|
||||
locales_available_via_cms = [page.locale.language_code]
|
||||
try:
|
||||
_actual_translations = (
|
||||
|
@ -21,3 +36,12 @@ def get_locales_for_cms_page(page):
|
|||
pass
|
||||
|
||||
return locales_available_via_cms
|
||||
|
||||
|
||||
def get_cms_locales_for_path(request):
|
||||
locales = []
|
||||
|
||||
if page := get_page_for_path(request=request, path=request.path):
|
||||
locales = get_locales_for_cms_page(page)
|
||||
|
||||
return locales
|
||||
|
|
|
@ -39,12 +39,18 @@ urlpatterns = (
|
|||
# VPN Resource Center
|
||||
path(
|
||||
"vpn/resource-center/",
|
||||
prefer_cms(views.resource_center_landing_view),
|
||||
prefer_cms(
|
||||
views.resource_center_landing_view,
|
||||
fallback_lang_codes=["de", "en-US", "es-ES", "fr", "it", "ja", "nl", "pl", "pt-BR", "ru", "zh-CN"],
|
||||
),
|
||||
name="products.vpn.resource-center.landing",
|
||||
),
|
||||
path(
|
||||
"vpn/resource-center/<slug:slug>/",
|
||||
prefer_cms(views.resource_center_article_view),
|
||||
prefer_cms(
|
||||
views.resource_center_article_view,
|
||||
fallback_lang_codes=["de", "en-US", "es-ES", "fr", "it", "ja", "nl", "pl", "pt-BR", "ru", "zh-CN"],
|
||||
),
|
||||
name="products.vpn.resource-center.article",
|
||||
),
|
||||
path("monitor/waitlist-plus/", views.monitor_waitlist_plus_page, name="products.monitor.waitlist-plus"),
|
||||
|
|
Загрузка…
Ссылка в новой задаче