Remove the DarkLang middleware from the LMS Created and basic routing to the update_lang page for the GET Request TNL-4742 Basic form functionality Working example in LMS of the form to set the language Login now required to change the preview language, and corrected some minor bugs Updates to move the template code to lms and to correct minor defects TNL-4742 Added template for preview_lang.html to cms TNL-4742 Changed filename of darklang.py to api.py to match convention TNL-4742 Updated and refactored the Darklang tests TNL-4742 Updated comments in tests TNL-4742 Formating updates TNL-4742 Updated comments and formatting TNL-4742 Corrected i18n tests and corrected PEP8 format issues TNL-4742 Code Lint/PEP-8 corrections and upates TNL-4742 Removed constant that was not needed (to be squashed) TNL-4742 Added init method to clear up PEP8 Warnings (will squash) TNL-4742 PEP-8/Lint issue resolved (squash) Updated for i18n TNL-4742 Refactored the preview_lang.html page to use a common included template Refactoring and changes from PR comments TNL-4742 Correction for safecommit violation (Squash) TNL-4742 PR changes and refactoring (Squash) Updates to reduce changes made in the urls used TNL-4742 Removed unneeded aria-described by and bug in MAKO Template (squash) TNK-4742 Updated docstring comments Clarified form response text Minor PR request (Squash) Refactoring of how the responses are generated within the DarkLang views file A series of refactors in response to PR comments Method name change for clarity in reponse to PR comments (Squash) Updates to tests per PR requests (Squash) Minor comment updates for clarity and PR requests (Squash) Updated per PR comments and added a test for empty preview_language Layout and code style updates (Squash) Updated test to contain method in the request. Removed the Darklang preview-lang and clear-lang parameters and added the new DarkLang settings form at /update_lang Refactored tests and added some tests for coverage, corrected defect with empty input codes Removed unused and obsolete code Corrected test errors, resolved PR comments, and updated comments to clarify testing TNL-4742 Updated tests to deal with Pylint quality issue (Squash) Updated tests to better reflect test case and PR updates (Squash)
133 lines
5.1 KiB
Python
133 lines
5.1 KiB
Python
"""
|
|
Middleware for dark-launching languages. These languages won't be used
|
|
when determining which translation to give a user based on their browser
|
|
header, but can be selected by setting the Preview Languages on the Dark
|
|
Language setting page.
|
|
|
|
This middleware must be placed before the LocaleMiddleware, but after
|
|
the SessionMiddleware.
|
|
"""
|
|
from django.conf import settings
|
|
|
|
from dark_lang import DARK_LANGUAGE_KEY
|
|
from dark_lang.models import DarkLangConfig
|
|
from openedx.core.djangoapps.user_api.preferences.api import (
|
|
get_user_preference
|
|
)
|
|
from django.utils.translation.trans_real import parse_accept_lang_header
|
|
from django.utils.translation import LANGUAGE_SESSION_KEY
|
|
|
|
# If django 1.7 or higher is used, the right-side can be updated with new-style codes.
|
|
CHINESE_LANGUAGE_CODE_MAP = {
|
|
# The following are the new-style language codes for chinese language
|
|
'zh-hans': 'zh-CN', # Chinese (Simplified),
|
|
'zh-hans-cn': 'zh-CN', # Chinese (Simplified, China)
|
|
'zh-hans-sg': 'zh-CN', # Chinese (Simplified, Singapore)
|
|
'zh-hant': 'zh-TW', # Chinese (Traditional)
|
|
'zh-hant-hk': 'zh-HK', # Chinese (Traditional, Hongkong)
|
|
'zh-hant-mo': 'zh-TW', # Chinese (Traditional, Macau)
|
|
'zh-hant-tw': 'zh-TW', # Chinese (Traditional, Taiwan)
|
|
# The following are the old-style language codes that django does not recognize
|
|
'zh-mo': 'zh-TW', # Chinese (Traditional, Macau)
|
|
'zh-sg': 'zh-CN', # Chinese (Simplified, Singapore)
|
|
}
|
|
|
|
|
|
def _dark_parse_accept_lang_header(accept):
|
|
"""
|
|
The use of 'zh-cn' for 'Simplified Chinese' and 'zh-tw' for 'Traditional Chinese'
|
|
are now deprecated, as discussed here: https://code.djangoproject.com/ticket/18419.
|
|
The new language codes 'zh-hans' and 'zh-hant' are now used since django 1.7.
|
|
Although majority of browsers still use the old language codes, some new browsers
|
|
such as IE11 in Windows 8.1 start to use the new ones, which makes the current
|
|
chinese translations of edX don't work properly under these browsers.
|
|
This function can keep compatibility between the old and new language codes. If one
|
|
day edX uses django 1.7 or higher, this function can be modified to support the old
|
|
language codes until there are no browsers use them.
|
|
"""
|
|
browser_langs = parse_accept_lang_header(accept)
|
|
django_langs = []
|
|
for lang, priority in browser_langs:
|
|
lang = CHINESE_LANGUAGE_CODE_MAP.get(lang.lower(), lang)
|
|
django_langs.append((lang, priority))
|
|
|
|
return django_langs
|
|
|
|
|
|
class DarkLangMiddleware(object):
|
|
"""
|
|
Middleware for dark-launching languages.
|
|
|
|
This is configured by creating ``DarkLangConfig`` rows in the database,
|
|
using the django admin site.
|
|
"""
|
|
@property
|
|
def released_langs(self):
|
|
"""
|
|
Current list of released languages
|
|
"""
|
|
language_options = DarkLangConfig.current().released_languages_list
|
|
if settings.LANGUAGE_CODE not in language_options:
|
|
language_options.append(settings.LANGUAGE_CODE)
|
|
return language_options
|
|
|
|
def process_request(self, request):
|
|
"""
|
|
Prevent user from requesting un-released languages except by using the preview-lang query string.
|
|
"""
|
|
if not DarkLangConfig.current().enabled:
|
|
return
|
|
|
|
self._clean_accept_headers(request)
|
|
self._activate_preview_language(request)
|
|
|
|
def _fuzzy_match(self, lang_code):
|
|
"""Returns a fuzzy match for lang_code"""
|
|
match = None
|
|
if lang_code in self.released_langs:
|
|
match = lang_code
|
|
else:
|
|
lang_prefix = lang_code.partition('-')[0]
|
|
for released_lang in self.released_langs:
|
|
released_prefix = released_lang.partition('-')[0]
|
|
if lang_prefix == released_prefix:
|
|
match = released_lang
|
|
return match
|
|
|
|
def _clean_accept_headers(self, request):
|
|
"""
|
|
Remove any language that is not either in ``self.released_langs`` or
|
|
a territory of one of those languages.
|
|
"""
|
|
accept = request.META.get('HTTP_ACCEPT_LANGUAGE', None)
|
|
if accept is None or accept == '*':
|
|
return
|
|
|
|
new_accept = []
|
|
for lang, priority in _dark_parse_accept_lang_header(accept):
|
|
fuzzy_code = self._fuzzy_match(lang.lower())
|
|
if fuzzy_code:
|
|
# Formats lang and priority into a valid accept header fragment.
|
|
new_accept.append("{};q={}".format(fuzzy_code, priority))
|
|
|
|
new_accept = ", ".join(new_accept)
|
|
|
|
request.META['HTTP_ACCEPT_LANGUAGE'] = new_accept
|
|
|
|
def _activate_preview_language(self, request):
|
|
"""
|
|
Check the user's dark language setting in the session and apply it
|
|
"""
|
|
auth_user = request.user.is_authenticated()
|
|
preview_lang = None
|
|
if auth_user:
|
|
# Get the request user's dark lang preference
|
|
preview_lang = get_user_preference(request.user, DARK_LANGUAGE_KEY)
|
|
|
|
# User doesn't have a dark lang preference, so just return
|
|
if not preview_lang:
|
|
return
|
|
|
|
# Set the session key to the requested preview lang
|
|
request.session[LANGUAGE_SESSION_KEY] = preview_lang
|