Commit b8eba419 by polesye

BLD-699: Fix i18n.

parent 4b56fc29
......@@ -11,6 +11,7 @@ from lxml import etree
from cache_toolbox.core import del_cached_content
from django.conf import settings
from django.utils.translation import ugettext as _
from xmodule.exceptions import NotFoundError
from xmodule.contentstore.content import StaticContent
......@@ -103,8 +104,10 @@ def get_transcripts_from_youtube(youtube_id):
data = requests.get(youtube_api['url'], params=youtube_api['params'])
if data.status_code != 200 or not data.text:
msg = "Can't receive transcripts from Youtube for {}. Status code: {}.".format(
youtube_id, data.status_code)
msg = _("Can't receive transcripts from Youtube for {youtube_id}. Status code: {statuc_code}.").format(
youtube_id=youtube_id,
statuc_code=data.status_code
)
raise GetTranscriptsFromYouTubeException(msg)
sub_starts, sub_ends, sub_texts = [], [], []
......@@ -162,7 +165,7 @@ def download_youtube_subs(youtube_subs, item):
highest_speed_subs = subs
if not highest_speed:
raise GetTranscriptsFromYouTubeException("Can't find any transcripts on the Youtube service.")
raise GetTranscriptsFromYouTubeException(_("Can't find any transcripts on the Youtube service."))
# When we exit from the previous loop, `highest_speed` and `highest_speed_subs`
# are the transcripts data for the highest speed available on the
......@@ -214,16 +217,16 @@ def generate_subs_from_source(speed_subs, subs_type, subs_filedata, item):
:returns: True, if all subs are generated and saved successfully.
"""
if subs_type != 'srt':
raise TranscriptsGenerationException("We support only SubRip (*.srt) transcripts format.")
raise TranscriptsGenerationException(_("We support only SubRip (*.srt) transcripts format."))
try:
srt_subs_obj = SubRipFile.from_string(subs_filedata)
except Exception as e:
raise TranscriptsGenerationException(
"Something wrong with SubRip transcripts file during parsing. "
"Inner message is {}".format(e.message)
msg = _("Something wrong with SubRip transcripts file during parsing. Inner message is {error_message}").format(
error_message=e.message
)
raise TranscriptsGenerationException(msg)
if not srt_subs_obj:
raise TranscriptsGenerationException("Something wrong with SubRip transcripts file during parsing.")
raise TranscriptsGenerationException(_("Something wrong with SubRip transcripts file during parsing."))
sub_starts = []
sub_ends = []
......
......@@ -15,6 +15,7 @@ from django.http import HttpResponse, Http404
from django.core.exceptions import PermissionDenied
from django.contrib.auth.decorators import login_required
from django.conf import settings
from django.utils.translation import ugettext as _
from xmodule.contentstore.content import StaticContent
from xmodule.exceptions import NotFoundError
......@@ -439,15 +440,15 @@ def _validate_transcripts_data(request):
"""
data = json.loads(request.GET.get('data', '{}'))
if not data:
raise TranscriptsRequestValidationException('Incoming video data is empty.')
raise TranscriptsRequestValidationException(_('Incoming video data is empty.'))
try:
item = _get_item(request, data)
except (ItemNotFoundError, InvalidLocationError, InsufficientSpecificationError):
raise TranscriptsRequestValidationException("Can't find item by locator.")
raise TranscriptsRequestValidationException(_("Can't find item by locator."))
if item.category != 'video':
raise TranscriptsRequestValidationException('Transcripts are supported only for "video" modules.')
raise TranscriptsRequestValidationException(_('Transcripts are supported only for "video" modules.'))
# parse data form request.GET.['data']['video'] to useful format
videos = {'youtube': '', 'html5': {}}
......
......@@ -33,6 +33,7 @@ src_paths:
# Paths to library JavaScript files (optional)
lib_paths:
- common_static/js/test/i18n.js
- common_static/coffee/src/ajax_prefix.js
- common_static/coffee/src/logger.js
- common_static/js/vendor/jasmine-jquery.js
......
......@@ -38,7 +38,7 @@
expect(timeControl).toHaveAttrs({
'role': 'slider',
'title': 'video position',
'title': 'Video position',
'aria-disabled': 'false'
});
......
......@@ -241,7 +241,7 @@
state.videoProgressSlider.notifyThroughHandleEnd({end: true});
expect(state.videoProgressSlider.handle.attr('title'))
.toBe('video ended');
.toBe('Video ended');
expect('focus').toHaveBeenTriggeredOn(
state.videoProgressSlider.handle
......@@ -252,7 +252,7 @@
state.videoProgressSlider.notifyThroughHandleEnd({end: false});
expect(state.videoProgressSlider.handle.attr('title'))
.toBe('video position');
.toBe('Video position');
expect('focus').not.toHaveBeenTriggeredOn(
state.videoProgressSlider.handle
......@@ -272,6 +272,30 @@
});
});
});
it('getTimeDescription', function () {
var cases = {
'0': '0 seconds',
'1': '1 second',
'10': '10 seconds',
'60': '1 minute 0 seconds',
'121': '2 minutes 1 second',
'3670': '1 hour 1 minute 10 seconds',
'21541': '5 hours 59 minutes 1 second',
},
getTimeDescription;
state = jasmine.initializePlayer();
getTimeDescription = state.videoProgressSlider.getTimeDescription;
$.each(cases, function(input, output) {
expect(getTimeDescription(input)).toBe(output);
});
});
});
}).call(this);
......@@ -6,6 +6,7 @@
oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice')
.andReturn(null);
});
afterEach(function () {
$('source').remove();
......@@ -15,14 +16,12 @@
describe('constructor', function () {
beforeEach(function () {
spyOn($.fn, 'slider').andCallThrough();
$.cookie.andReturn('75');
state = jasmine.initializePlayer();
});
it('initialize currentVolume to 100%', function () {
// Please note that:
// 0% -> 0
// 100% -> 1.0
expect(state.videoVolumeControl.currentVolume).toEqual(1);
it('initialize currentVolume to 75%', function () {
expect(state.videoVolumeControl.currentVolume).toEqual(75);
});
it('render the volume control', function () {
......@@ -45,13 +44,13 @@
it('add ARIA attributes to slider handle', function () {
var sliderHandle = $('div.volume-slider>a.ui-slider-handle'),
arr = [
'muted', 'very low', 'low', 'average', 'loud',
'very loud', 'maximum'
'Muted', 'Very low', 'Low', 'Average', 'Loud',
'Very loud', 'Maximum'
];
expect(sliderHandle).toHaveAttrs({
'role': 'slider',
'title': 'volume',
'title': 'Volume',
'aria-disabled': 'false',
'aria-valuemin': '0',
'aria-valuemax': '100'
......@@ -86,33 +85,33 @@
describe('onChange', function () {
var initialData = [{
range: 'muted',
range: 'Muted',
value: 0,
expectation: 'muted'
expectation: 'Muted'
}, {
range: 'in ]0,20]',
value: 10,
expectation: 'very low'
expectation: 'Very low'
}, {
range: 'in ]20,40]',
value: 30,
expectation: 'low'
expectation: 'Low'
}, {
range: 'in ]40,60]',
value: 50,
expectation: 'average'
expectation: 'Average'
}, {
range: 'in ]60,80]',
value: 70,
expectation: 'loud'
expectation: 'Loud'
}, {
range: 'in ]80,100[',
value: 90,
expectation: 'very loud'
expectation: 'Very loud'
}, {
range: 'maximum',
range: 'Maximum',
value: 100,
expectation: 'maximum'
expectation: 'Maximum'
}];
beforeEach(function () {
......
......@@ -16,19 +16,6 @@ define(
'video/01_initialize.js',
['video/03_video_player.js', 'video/00_cookie_storage.js'],
function (VideoPlayer, CookieStorage) {
// window.console.log() is expected to be available. We do not support
// browsers which lack this functionality.
// The function gettext() is defined by a vendor library. If, however, it
// is undefined, it is a simple wrapper. It is used to return a different
// version of the string passed (translated string, etc.). In the basic
// case, the original string is returned.
if (typeof(window.gettext) == 'undefined') {
window.gettext = function (s) {
return s;
};
}
/**
* @function
*
......
......@@ -81,7 +81,7 @@ function () {
// handle, behaves as a slider named 'video slider'.
state.videoControl.sliderEl.find('.ui-slider-handle').attr({
'role': 'slider',
'title': gettext('video slider')
'title': gettext('Video slider')
});
}
......
......@@ -42,7 +42,8 @@ function () {
onStop: onStop,
updatePlayTime: updatePlayTime,
updateStartEndTimeRegion: updateStartEndTimeRegion,
notifyThroughHandleEnd: notifyThroughHandleEnd
notifyThroughHandleEnd: notifyThroughHandleEnd,
getTimeDescription: getTimeDescription
};
state.bindTo(methodsDict, state.videoProgressSlider, state);
......@@ -72,7 +73,7 @@ function () {
// handle, behaves as a slider named 'video position'.
state.videoProgressSlider.handle.attr({
'role': 'slider',
'title': 'video position',
'title': gettext('Video position'),
'aria-disabled': false,
'aria-valuetext': getTimeDescription(state.videoProgressSlider
.slider.slider('option', 'value'))
......@@ -152,11 +153,15 @@ function () {
if (!this.videoProgressSlider.sliderRange) {
this.videoProgressSlider.sliderRange = $('<div />', {
class: 'ui-slider-range ' +
'ui-widget-header ' +
'ui-corner-all ' +
'slider-range'
}).css(rangeParams);
'class': 'ui-slider-range ' +
'ui-widget-header ' +
'ui-corner-all ' +
'slider-range'
})
.css({
left: rangeParams.left,
width: rangeParams.width
});
this.videoProgressSlider.sliderProgress
.after(this.videoProgressSlider.sliderRange);
......@@ -245,61 +250,50 @@ function () {
function notifyThroughHandleEnd(params) {
if (params.end) {
this.videoProgressSlider.handle
.attr('title', 'video ended')
.attr('title', gettext('Video ended'))
.focus();
} else {
this.videoProgressSlider.handle.attr('title', 'video position');
this.videoProgressSlider.handle
.attr('title', gettext('Video position'));
}
}
// Returns a string describing the current time of video in hh:mm:ss
// format.
// Returns a string describing the current time of video in
// `%d hours %d minutes %d seconds` format.
function getTimeDescription(time) {
var seconds = Math.floor(time),
minutes = Math.floor(seconds / 60),
hours = Math.floor(minutes / 60),
hrStr, minStr, secStr;
i18n = function (value, word) {
var msg;
switch(word) {
case 'hour':
msg = ngettext('%(value)s hour', '%(value)s hours', value);
break;
case 'minute':
msg = ngettext('%(value)s minute', '%(value)s minutes', value);
break;
case 'second':
msg = ngettext('%(value)s second', '%(value)s seconds', value);
break;
}
return interpolate(msg, {'value': value}, true);
};
seconds = seconds % 60;
minutes = minutes % 60;
hrStr = hours.toString(10);
minStr = minutes.toString(10);
secStr = seconds.toString(10);
if (hours) {
hrStr += (hours < 2 ? ' hour ' : ' hours ');
if (minutes) {
minStr += (minutes < 2 ? ' minute ' : ' minutes ');
} else {
minStr += ' 0 minutes ';
}
if (seconds) {
secStr += (seconds < 2 ? ' second ' : ' seconds ');
} else {
secStr += ' 0 seconds ';
}
return hrStr + minStr + secStr;
return i18n(hours, 'hour') + ' ' +
i18n(minutes, 'minute') + ' ' +
i18n(seconds, 'second');
} else if (minutes) {
minStr += (minutes < 2 ? ' minute ' : ' minutes ');
if (seconds) {
secStr += (seconds < 2 ? ' second ' : ' seconds ');
} else {
secStr += ' 0 seconds ';
}
return minStr + secStr;
} else if (seconds) {
secStr += (seconds < 2 ? ' second ' : ' seconds ');
return secStr;
return i18n(minutes, 'minute') + ' ' +
i18n(seconds, 'second');
}
return '0 seconds';
return i18n(seconds, 'second');
}
});
......
......@@ -94,7 +94,7 @@ function () {
volumeSliderHandleEl.attr({
'role': 'slider',
'title': 'volume',
'title': gettext('Volume'),
'aria-disabled': false,
'aria-valuemin': slider.slider('option', 'min'),
'aria-valuemax': slider.slider('option', 'max'),
......@@ -238,20 +238,27 @@ function () {
// Returns a string describing the level of volume.
function getVolumeDescription(vol) {
if (vol === 0) {
return 'muted';
// Translators: Volume level equals 0%.
return gettext('Muted');
} else if (vol <= 20) {
return 'very low';
// Translators: Volume level in range (0,20]%
return gettext('Very low');
} else if (vol <= 40) {
return 'low';
// Translators: Volume level in range (20,40]%
return gettext('Low');
} else if (vol <= 60) {
return 'average';
// Translators: Volume level in range (40,60]%
return gettext('Average');
} else if (vol <= 80) {
return 'loud';
// Translators: Volume level in range (60,80]%
return gettext('Loud');
} else if (vol <= 99) {
return 'very loud';
// Translators: Volume level in range (80,100)%
return gettext('Very loud');
}
return 'maximum';
// Translators: Volume level equals 100%.
return gettext('Maximum');
}
});
......
......@@ -28,6 +28,7 @@ prepend_path: lms/static
# Paths to library JavaScript files (optional)
lib_paths:
- xmodule_js/common_static/js/test/i18n.js
- xmodule_js/common_static/coffee/src/ajax_prefix.js
- xmodule_js/common_static/coffee/src/logger.js
- xmodule_js/common_static/js/vendor/jasmine-jquery.js
......
......@@ -58,8 +58,7 @@
</section>
<div class="video-player-post"></div>
<section class="video-controls is-hidden">
<div class="slider" title="Video position"></div>
<div class="slider" title="${_('Video position')}"></div>
<div>
<ul class="vcr">
<li><a class="video_control" href="#" title="${_('Play')}" role="button" aria-disabled="false"></a></li>
......
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