Commit 99b883b7 by asadiqbal

WL-243 LMS Language Selection

parent 0c55f17d
......@@ -5,6 +5,8 @@ Middleware for Language Preferences
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference
from lang_pref import LANGUAGE_KEY
from django.utils.translation import LANGUAGE_SESSION_KEY
from django.utils.translation.trans_real import parse_accept_lang_header
from lang_pref.api import released_languages
class LanguagePreferenceMiddleware(object):
......@@ -26,3 +28,13 @@ class LanguagePreferenceMiddleware(object):
# Set it to the LANGUAGE_SESSION_KEY (Django-specific session setting governing language pref)
if user_pref:
request.session[LANGUAGE_SESSION_KEY] = user_pref
else:
# Setting the session language to the browser language, if it is supported.
preferred_language = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
lang_headers = [seq[0] for seq in parse_accept_lang_header(preferred_language)]
languages = released_languages()
for browser_lang in lang_headers:
if browser_lang in [seq[0] for seq in languages]:
if request.session.get(LANGUAGE_SESSION_KEY, None) != browser_lang:
request.session[LANGUAGE_SESSION_KEY] = unicode(browser_lang)
break
......@@ -4,9 +4,10 @@ from django.contrib.sessions.middleware import SessionMiddleware
from django.utils.translation import LANGUAGE_SESSION_KEY
from lang_pref.middleware import LanguagePreferenceMiddleware
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference, get_user_preference
from lang_pref import LANGUAGE_KEY
from student.tests.factories import UserFactory
import mock
class TestUserPreferenceMiddleware(TestCase):
......@@ -21,6 +22,7 @@ class TestUserPreferenceMiddleware(TestCase):
self.user = UserFactory.create()
self.request = RequestFactory().get('/somewhere')
self.request.user = self.user
self.request.META['HTTP_ACCEPT_LANGUAGE'] = 'ar;q=1.0' # pylint: disable=no-member
self.session_middleware.process_request(self.request)
def test_no_language_set_in_session_or_prefs(self):
......@@ -46,3 +48,21 @@ class TestUserPreferenceMiddleware(TestCase):
self.middleware.process_request(self.request)
self.assertEquals(self.request.session[LANGUAGE_SESSION_KEY], 'eo')
@mock.patch('lang_pref.middleware.released_languages',
mock.Mock(return_value=[('eo', 'dummy Esperanto'), ('ar', 'arabic')]))
def test_supported_browser_language_in_session(self):
"""
test: browser language should be set in user preferences if it is supported by system.
"""
self.assertEquals(get_user_preference(self.request.user, LANGUAGE_KEY), None)
self.middleware.process_request(self.request)
self.assertEqual(self.request.session[LANGUAGE_SESSION_KEY], 'ar') # pylint: disable=no-member
@mock.patch('lang_pref.middleware.released_languages', mock.Mock(return_value=[('en', 'english')]))
def test_browser_language_not_be_in_session(self):
"""
test: browser language should not be set in user preferences if it is not supported by system.
"""
self.middleware.process_request(self.request)
self.assertNotEqual(self.request.session.get(LANGUAGE_SESSION_KEY), 'ar') # pylint: disable=no-member
......@@ -124,3 +124,7 @@ else:
<%def name="is_request_in_themed_site()"><%
return is_request_in_themed_site()
%></%def>
<%def name="show_language_selector()"><%
return settings.FEATURES.get('SHOW_LANGUAGE_SELECTOR', False)
%></%def>
<form action="/i18n/setlang/" method="post" class="settings-language-form" id="language-settings-form">
<input title="preference api" type="hidden" id="preference-api-url" value="/api/user/v1/preferences/test1/">
<label><span class="sr">Choose Language</span>
<select class="input select" id="settings-language-value" name="language">
<option value="en" selected="selected">English</option>
<option value="ar" >Arabic</option>
</select>
</label>
</form>
/*global Language:true*/
(function() {
'use strict';
describe('Language change test for lang-edx.js', function () {
var lang_selector,
deferred;
beforeEach(function () {
loadFixtures('js/fixtures/lang-edx-fixture.html');
lang_selector = $('#settings-language-value');
deferred = $.Deferred();
});
it("can spy on language selector change event", function () {
spyOnEvent(lang_selector, 'change');
lang_selector.trigger('change');
expect('change').toHaveBeenTriggeredOn(lang_selector);
});
it("should make an AJAX request to the correct URL", function () {
spyOn($, 'ajax').andReturn(deferred);
Language.init();
lang_selector.trigger('change');
expect($.ajax.mostRecentCall.args[0].url).toEqual("/api/user/v1/preferences/test1/");
});
it("should make an AJAX request with correct type", function () {
spyOn($, 'ajax').andReturn(deferred);
Language.init();
lang_selector.trigger('change');
expect($.ajax.mostRecentCall.args[0].type).toEqual("PATCH");
});
it("should make an AJAX request with correct data", function () {
spyOn($, 'ajax').andReturn(deferred);
Language.init();
lang_selector.val('ar');
lang_selector.trigger('change');
expect($.ajax.mostRecentCall.args[0].data).toEqual('{"pref-lang":"ar"}');
// change to 'en' from 'ar'
lang_selector.val('en');
lang_selector.trigger('change');
expect($.ajax.mostRecentCall.args[0].data).toEqual('{"pref-lang":"en"}');
});
it("should call refresh on ajax failure", function () {
spyOn($, 'ajax').andCallFake(function () {
var d = $.Deferred();
d.reject();
return d.promise();
});
Language.init();
spyOn(Language, 'refresh');
lang_selector.trigger('change');
expect(Language.refresh).toHaveBeenCalled();
});
});
}).call(this);
var edx = edx || {},
Language = (function() {
'use strict';
var preference_api_url,
settings_language_selector,
self = null;
return {
init: function() {
preference_api_url = $('#preference-api-url').val();
settings_language_selector = $('#settings-language-value');
self = this;
this.listenForLanguagePreferenceChange();
},
/**
* Listener on changing language from selector.
* Send an ajax request to save user language preferences.
*/
listenForLanguagePreferenceChange: function() {
settings_language_selector.change(function(event) {
var language = this.value;
event.preventDefault();
$.ajax({
type: 'PATCH',
data: JSON.stringify({'pref-lang': language}) ,
url: preference_api_url,
dataType: 'json',
contentType: "application/merge-patch+json",
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", $('#csrf_token').val());
}
}).done(function () {
// User language preference has been set successfully
// Now submit the form in success callback.
$("#language-settings-form").submit();
}).fail(function() {
self.refresh();
});
});
},
/**
* refresh the page.
*/
refresh: function () {
// reloading the page so we can get the latest state of realsesd languages from model
location.reload();
}
};
})();
$(document).ready(function () {
'use strict';
Language.init();
});
......@@ -192,3 +192,14 @@ class DashboardPage(PageObject):
Click on `Account` link.
"""
self.q(css='.dropdown-menu li a').nth(2).click()
@property
def language_selector(self):
"""
return language selector
"""
self.wait_for_element_visibility(
'#settings-language-value',
'Language selector element is available'
)
return self.q(css='#settings-language-value')
......@@ -16,7 +16,9 @@ from ..helpers import (
load_data_str,
generate_course_key,
select_option_by_value,
element_has_text
element_has_text,
select_option_by_text,
get_selected_option_text
)
from ...pages.lms import BASE_URL
from ...pages.lms.account_settings import AccountSettingsPage
......@@ -1130,3 +1132,50 @@ class NotLiveRedirectTest(UniqueCourseTest):
'The course you are looking for does not start until',
page.banner_text
)
@attr('shard_1')
class LMSLanguageTest(UniqueCourseTest):
""" Test suite for the LMS Language """
def setUp(self):
super(LMSLanguageTest, self).setUp()
self.dashboard_page = DashboardPage(self.browser)
self.account_settings = AccountSettingsPage(self.browser)
AutoAuthPage(self.browser).visit()
def test_lms_language_change(self):
"""
Scenario: Ensure that language selection is working fine.
First I go to the user dashboard page in LMS. I can see 'English' is selected by default.
Then I choose 'Dummy Language' from drop down (at top of the page).
Then I visit the student account settings page and I can see the language has been updated to 'Dummy Language'
in both drop downs.
After that I select the 'English' language and visit the dashboard page again.
Then I can see that top level language selector persist its value to 'English'.
"""
self.dashboard_page.visit()
language_selector = self.dashboard_page.language_selector
self.assertEqual(
get_selected_option_text(language_selector),
u'English'
)
select_option_by_text(language_selector, 'Dummy Language (Esperanto)')
self.dashboard_page.wait_for_ajax()
self.account_settings.visit()
self.assertEqual(self.account_settings.value_for_dropdown_field('pref-lang'), u'Dummy Language (Esperanto)')
self.assertEqual(
get_selected_option_text(language_selector),
u'Dummy Language (Esperanto)'
)
# changed back to English language.
select_option_by_text(language_selector, 'English')
self.account_settings.wait_for_ajax()
self.assertEqual(self.account_settings.value_for_dropdown_field('pref-lang'), u'English')
self.dashboard_page.visit()
self.assertEqual(
get_selected_option_text(language_selector),
u'English'
)
......@@ -88,7 +88,8 @@
"MODE_CREATION_FOR_TESTING": true,
"AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING": true,
"ENABLE_COURSE_DISCOVERY": true,
"ENABLE_SPECIAL_EXAMS": true
"ENABLE_SPECIAL_EXAMS": true,
"SHOW_LANGUAGE_SELECTOR": true
},
"FEEDBACK_SUBMISSION_EMAIL": "",
"GITHUB_REPO_ROOT": "** OVERRIDDEN **",
......
......@@ -367,6 +367,9 @@ FEATURES = {
# Enable LTI Provider feature.
'ENABLE_LTI_PROVIDER': False,
# Show LMS Language selector
'SHOW_LANGUAGE_SELECTOR': False,
}
# Ignore static asset files on import which match this pattern
......@@ -1249,6 +1252,7 @@ base_application_js = [
'js/src/ie_shim.js',
'js/src/accessibility_tools.js',
'js/toggle_login_modal.js',
'js/src/lang_edx.js',
]
dashboard_js = (
......
......@@ -114,7 +114,7 @@
this.showNotificationMessage(successMessage);
if (this.options.refreshPageOnSave) {
document.location.reload();
location.reload(true);
}
var view = this;
......
......@@ -118,6 +118,10 @@ header.global {
margin-top: ($baseline/4);
padding-left: 0;
.settings-language-form {
margin-top: 4px;
}
> .primary {
display: block;
@include float(left);
......
......@@ -11,6 +11,7 @@ from lms.djangoapps.ccx.overrides import get_current_ccx
from branding import api as branding_api
# app that handles site status messages
from status.status import get_site_status_msg
from lang_pref.api import released_languages
%>
## Provide a hook for themes to inject branding on top.
......@@ -75,6 +76,28 @@ site_status_msg = get_site_status_msg(course_id)
</%block>
</ol>
<ol class="user">
% if static.show_language_selector():
<% languages = released_languages() %>
% if len(languages) > 1:
<li class="primary">
<form action="/i18n/setlang/" method="post" class="settings-language-form" id="language-settings-form">
<input type="hidden" id="csrf_token" name="csrfmiddlewaretoken" value="${csrf_token}">
<input title="preference api" type="hidden" id="preference-api-url" value="${reverse('preferences_api', kwargs={'username': user.username})}">
<label><span class="sr">${_("Choose Language")}</span>
<select class="input select" id="settings-language-value" name="language">
% for language in languages:
% if language[0] == LANGUAGE_CODE:
<option value="${language[0]}" selected="selected">${language[1]}</option>
% else:
<option value="${language[0]}" >${language[1]}</option>
% endif
% endfor
</select>
</label>
</form>
</li>
% endif
% endif
<li class="primary">
<a href="${reverse('dashboard')}" class="user-link">
<span class="sr">${_("Dashboard for:")}</span>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment