1
0
mirror of https://github.com/videojs/video.js.git synced 2024-12-25 02:42:10 +02:00

Added localization support. closes #1360

This commit is contained in:
Tom Johnson 2014-08-05 17:07:46 -07:00 committed by Steve Heffernan
parent e4fbf27358
commit 6b612d8cdc
20 changed files with 165 additions and 30 deletions

View File

@ -31,7 +31,9 @@ module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: pkg,
lang: {
src: 'lang/*.json'
},
build: {
src: 'src/js/dependencies.js',
options: {
@ -331,11 +333,11 @@ module.exports = function(grunt) {
// grunt.loadTasks('./docs/tasks/');
// grunt.loadTasks('../videojs-doc-generator/tasks/');
grunt.registerTask('pretask', ['jshint', 'less', 'build', 'minify', 'usebanner']);
grunt.registerTask('pretask', ['jshint', 'less', 'lang', 'build', 'minify', 'usebanner']);
// Default task.
grunt.registerTask('default', ['pretask', 'dist']);
// Development watch task
grunt.registerTask('dev', ['jshint', 'less', 'build', 'qunit:source']);
grunt.registerTask('dev', ['jshint', 'less', 'lang', 'build', 'qunit:source']);
grunt.registerTask('test-qunit', ['pretask', 'qunit']);
// The test task will run `karma:saucelabs` when running in travis,
@ -425,7 +427,26 @@ module.exports = function(grunt) {
var fs = require('fs'),
gzip = require('zlib').gzip;
grunt.registerMultiTask('lang', 'Building Language Support', function() {
grunt.log.writeln('Building Language Support');
var langFiles = this.files;
var combined;
// Create a combined languages file
langFiles.forEach(function(result) {
combined += grunt.file.read(result.src);
});
combined = combined.replace('undefined', 'vjs.options["languages"] = ');
grunt.file.write('build/files/combined.languages.js', combined);
});
grunt.registerMultiTask('build', 'Building Source', function(){
// Fix windows file path delimiter issue
var i = sourceFiles.length;
while (i--) {
@ -439,6 +460,10 @@ module.exports = function(grunt) {
});
// Replace CDN version ref in js. Use major/minor version.
combined = combined.replace(/GENERATED_CDN_VSN/g, version.majorMinor);
// Add Combined Langauges
combined += grunt.file.read('build/files/combined.languages.js');
grunt.file.write('build/files/combined.video.js', combined);
// Copy over other files

28
lang/es.json Normal file
View File

@ -0,0 +1,28 @@
{'es':
{
'Play': 'Juego',
'Pause': 'Pausa',
'Current Time': 'Tiempo Actual',
'Duration Time': 'Tiempo de Duracion',
'Remaining Time': 'Tiempo Restante',
'Stream Type': 'Tipo de Transmision',
'LIVE': 'En Vivo',
'Loaded': 'Cargado',
'Progress': 'Progreso',
'Fullscreen': 'Pantalla Completa',
'Non-Fullscreen': 'No Pantalla Completa',
'Mute': 'Mudo',
'Unmuted': 'Activar sonido',
'Playback Rate': 'Reproduccion Cambio',
'Subtitles': 'Subtitulos',
'subtitles off': 'subtitulos fuera',
'Captions': 'Subtitulos',
'captions off': 'subtitulos fuera',
'Chapters': 'Capitulos',
'You aborted the video playback': 'Ha anulado la reproduccion de video',
'A network error caused the video download to fail part-way.': 'Un error en la red hizo que la descarga de video falle parte del camino.',
'The video could not be loaded, either because the server or network failed or because the format is not supported.': 'El video no se puede cargar, ya sea porque el servidor o la red fracasaron o porque el formato no es compatible.',
'The video playback was aborted due to a corruption problem or because the video used features your browser did not support.': 'La reproduccion de video se ha cancelado debido a un problema de corrupcion o porque el video utilizado cuenta con su navegador no soporta.',
'No compatible source was found for this video.': 'Ninguna fuente compatible se encontro para este video.'
}
}

View File

@ -45,7 +45,7 @@ vjs.Button.prototype.createEl = function(type, props){
this.controlText_ = vjs.createEl('span', {
className: 'vjs-control-text',
innerHTML: this.buttonText || 'Need Text'
innerHTML: this.localize(this.buttonText) || 'Need Text'
});
this.contentEl_.appendChild(this.controlText_);

View File

@ -195,6 +195,15 @@ vjs.Component.prototype.createEl = function(tagName, attributes){
return vjs.createEl(tagName, attributes);
};
vjs.Component.prototype.localize = function(string){
var lang = this.player_.language(),
languages = this.player_.languages();
if (languages && languages[lang] && languages[lang][string]) {
return languages[lang][string];
}
return string;
};
/**
* Get the component's DOM element
*

View File

@ -25,9 +25,9 @@ vjs.FullscreenToggle.prototype.buildCSSClass = function(){
vjs.FullscreenToggle.prototype.onClick = function(){
if (!this.player_.isFullscreen()) {
this.player_.requestFullscreen();
this.controlText_.innerHTML = 'Non-Fullscreen';
this.controlText_.innerHTML = this.localize('Non-Fullscreen');
} else {
this.player_.exitFullscreen();
this.controlText_.innerHTML = 'Fullscreen';
this.controlText_.innerHTML = this.localize('Fullscreen');
}
};

View File

@ -18,7 +18,7 @@ vjs.LiveDisplay.prototype.createEl = function(){
this.contentEl_ = vjs.createEl('div', {
className: 'vjs-live-display',
innerHTML: '<span class="vjs-control-text">Stream Type </span>LIVE',
innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '</span>' + this.localize('LIVE'),
'aria-live': 'off'
});

View File

@ -29,7 +29,7 @@ vjs.MuteToggle = vjs.Button.extend({
vjs.MuteToggle.prototype.createEl = function(){
return vjs.Button.prototype.createEl.call(this, 'div', {
className: 'vjs-mute-control vjs-control',
innerHTML: '<div><span class="vjs-control-text">Mute</span></div>'
innerHTML: '<div><span class="vjs-control-text">' + this.localize('Mute') + '</span></div>'
});
};
@ -53,12 +53,12 @@ vjs.MuteToggle.prototype.update = function(){
// This causes unnecessary and confusing information for screen reader users.
// This check is needed because this function gets called every time the volume level is changed.
if(this.player_.muted()){
if(this.el_.children[0].children[0].innerHTML!='Unmute'){
this.el_.children[0].children[0].innerHTML = 'Unmute'; // change the button text to "Unmute"
if(this.el_.children[0].children[0].innerHTML!=this.localize('Unmute')){
this.el_.children[0].children[0].innerHTML = this.localize('Unmute'); // change the button text to "Unmute"
}
} else {
if(this.el_.children[0].children[0].innerHTML!='Mute'){
this.el_.children[0].children[0].innerHTML = 'Mute'; // change the button text to "Mute"
if(this.el_.children[0].children[0].innerHTML!=this.localize('Mute')){
this.el_.children[0].children[0].innerHTML = this.localize('Mute'); // change the button text to "Mute"
}
}

View File

@ -34,12 +34,12 @@ vjs.PlayToggle.prototype.onClick = function(){
vjs.PlayToggle.prototype.onPlay = function(){
vjs.removeClass(this.el_, 'vjs-paused');
vjs.addClass(this.el_, 'vjs-playing');
this.el_.children[0].children[0].innerHTML = 'Pause'; // change the button text to "Pause"
this.el_.children[0].children[0].innerHTML = this.localize('Pause'); // change the button text to "Pause"
};
// OnPause - Add the vjs-paused class to the element so it can change appearance
vjs.PlayToggle.prototype.onPause = function(){
vjs.removeClass(this.el_, 'vjs-playing');
vjs.addClass(this.el_, 'vjs-paused');
this.el_.children[0].children[0].innerHTML = 'Play'; // change the button text to "Play"
this.el_.children[0].children[0].innerHTML = this.localize('Play'); // change the button text to "Play"
};

View File

@ -21,7 +21,7 @@ vjs.PlaybackRateMenuButton = vjs.MenuButton.extend({
vjs.PlaybackRateMenuButton.prototype.createEl = function(){
var el = vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-playback-rate vjs-menu-button vjs-control',
innerHTML: '<div class="vjs-control-content"><span class="vjs-control-text">Playback Rate</span></div>'
innerHTML: '<div class="vjs-control-content"><span class="vjs-control-text">' + this.localize('Playback Rate') + '</span></div>'
});
this.labelEl_ = vjs.createEl('div', {

View File

@ -124,7 +124,8 @@ vjs.LoadProgressBar = vjs.Component.extend({
vjs.LoadProgressBar.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-load-progress'
className: 'vjs-load-progress',
innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>'
});
};
@ -181,7 +182,7 @@ vjs.PlayProgressBar = vjs.Component.extend({
vjs.PlayProgressBar.prototype.createEl = function(){
return vjs.Component.prototype.createEl.call(this, 'div', {
className: 'vjs-play-progress',
innerHTML: '<span class="vjs-control-text">Progress: 0%</span>'
innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
});
};

View File

@ -31,7 +31,7 @@ vjs.CurrentTimeDisplay.prototype.createEl = function(){
vjs.CurrentTimeDisplay.prototype.updateContent = function(){
// Allows for smooth scrubbing, when player can't keep up.
var time = (this.player_.scrubbing) ? this.player_.getCache().currentTime : this.player_.currentTime();
this.contentEl_.innerHTML = '<span class="vjs-control-text">Current Time </span>' + vjs.formatTime(time, this.player_.duration());
this.contentEl_.innerHTML = '<span class="vjs-control-text">' + this.localize('Current Time') + '</span> ' + vjs.formatTime(time, this.player_.duration());
};
/**
@ -61,7 +61,7 @@ vjs.DurationDisplay.prototype.createEl = function(){
this.contentEl_ = vjs.createEl('div', {
className: 'vjs-duration-display',
innerHTML: '<span class="vjs-control-text">Duration Time </span>' + '0:00', // label the duration time for screen reader users
innerHTML: '<span class="vjs-control-text">' + this.localize('Duration Time') + '</span> ' + '0:00', // label the duration time for screen reader users
'aria-live': 'off' // tell screen readers not to automatically read the time as it changes
});
@ -72,7 +72,7 @@ vjs.DurationDisplay.prototype.createEl = function(){
vjs.DurationDisplay.prototype.updateContent = function(){
var duration = this.player_.duration();
if (duration) {
this.contentEl_.innerHTML = '<span class="vjs-control-text">Duration Time </span>' + vjs.formatTime(duration); // label the duration time for screen reader users
this.contentEl_.innerHTML = '<span class="vjs-control-text">' + this.localize('Duration Time') + '</span> ' + vjs.formatTime(duration); // label the duration time for screen reader users
}
};
@ -121,7 +121,7 @@ vjs.RemainingTimeDisplay.prototype.createEl = function(){
this.contentEl_ = vjs.createEl('div', {
className: 'vjs-remaining-time-display',
innerHTML: '<span class="vjs-control-text">Remaining Time </span>' + '-0:00', // label the remaining time for screen reader users
innerHTML: '<span class="vjs-control-text">' + this.localize('Remaining Time') + '</span> ' + '-0:00', // label the remaining time for screen reader users
'aria-live': 'off' // tell screen readers not to automatically read the time as it changes
});
@ -131,7 +131,7 @@ vjs.RemainingTimeDisplay.prototype.createEl = function(){
vjs.RemainingTimeDisplay.prototype.updateContent = function(){
if (this.player_.duration()) {
this.contentEl_.innerHTML = '<span class="vjs-control-text">Remaining Time </span>' + '-'+ vjs.formatTime(this.player_.remainingTime());
this.contentEl_.innerHTML = '<span class="vjs-control-text">' + this.localize('Remaining Time') + '</span> ' + '-'+ vjs.formatTime(this.player_.remainingTime());
}
// Allows for smooth scrubbing, when player can't keep up.

View File

@ -42,7 +42,7 @@ vjs.VolumeMenuButton.prototype.onClick = function(){
vjs.VolumeMenuButton.prototype.createEl = function(){
return vjs.Button.prototype.createEl.call(this, 'div', {
className: 'vjs-volume-menu-button vjs-menu-button vjs-control',
innerHTML: '<div><span class="vjs-control-text">Mute</span></div>'
innerHTML: '<div><span class="vjs-control-text">' + this.localize('Mute') + '</span></div>'
});
};
vjs.VolumeMenuButton.prototype.update = vjs.MuteToggle.prototype.update;

View File

@ -103,6 +103,11 @@ vjs.options = {
'errorDisplay': {}
},
'language': document.getElementsByTagName('html')[0].getAttribute('lang') || navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language || 'en',
// locales and their language translations
'languages': {},
// Default message to show when a video cannot be played.
'notSupportedMessage': 'No compatible source was found for this video.'
};

View File

@ -26,6 +26,6 @@ vjs.ErrorDisplay.prototype.createEl = function(){
vjs.ErrorDisplay.prototype.update = function(){
if (this.player().error()) {
this.contentEl_.innerHTML = this.player().error().message;
this.contentEl_.innerHTML = this.localize(this.player().error().message);
}
};

View File

@ -67,6 +67,7 @@ goog.exportProperty(vjs.Component.prototype, 'ready', vjs.Component.prototype.re
goog.exportProperty(vjs.Component.prototype, 'addClass', vjs.Component.prototype.addClass);
goog.exportProperty(vjs.Component.prototype, 'removeClass', vjs.Component.prototype.removeClass);
goog.exportProperty(vjs.Component.prototype, 'buildCSSClass', vjs.Component.prototype.buildCSSClass);
goog.exportProperty(vjs.Component.prototype, 'localize', vjs.Component.prototype.localize);
// Need to export ended to ensure it's not removed by CC, since it's not used internally
goog.exportProperty(vjs.Player.prototype, 'ended', vjs.Player.prototype.ended);
@ -76,6 +77,8 @@ goog.exportProperty(vjs.Player.prototype, 'preload', vjs.Player.prototype.preloa
goog.exportProperty(vjs.Player.prototype, 'remainingTime', vjs.Player.prototype.remainingTime);
goog.exportProperty(vjs.Player.prototype, 'supportsFullScreen', vjs.Player.prototype.supportsFullScreen);
goog.exportProperty(vjs.Player.prototype, 'currentType', vjs.Player.prototype.currentType);
goog.exportProperty(vjs.Player.prototype, 'language', vjs.Player.prototype.language);
goog.exportProperty(vjs.Player.prototype, 'languages', vjs.Player.prototype.languages);
goog.exportSymbol('videojs.MediaLoader', vjs.MediaLoader);
goog.exportSymbol('videojs.TextTrackDisplay', vjs.TextTrackDisplay);

View File

@ -45,6 +45,12 @@ vjs.Player = vjs.Component.extend({
// (tag must exist before Player)
options = vjs.obj.merge(this.getTagSettings(tag), options);
// Update Current Language
this.language_ = options['language'] || vjs.options['language'];
// Update Supported Languages
this.languages_ = options['languages'] || vjs.options['languages'];
// Cache for video property values.
this.cache_ = {};
@ -93,6 +99,41 @@ vjs.Player = vjs.Component.extend({
}
});
/**
* The players's stored language code
*
* @type {String}
* @private
*/
vjs.Player.prototype.language_;
/**
* The player's language code
* @param {String} languageCode The locale string
* @return {String} The locale string when getting
* @return {vjs.Player} self, when setting
*/
vjs.Player.prototype.language = function (languageCode) {
if (languageCode === undefined) {
return this.language_;
}
this.language_ = languageCode;
return this;
};
/**
* The players's stored language dictionary
*
* @type {Object}
* @private
*/
vjs.Player.prototype.languages_;
vjs.Player.prototype.languages = function(){
return this.languages_;
};
/**
* Player instance options, surfaced using vjs.options
* vjs.options = vjs.Player.prototype.options_

View File

@ -25,6 +25,7 @@
'test/unit/util.js',
'test/unit/events.js',
'test/unit/component.js',
'test/unit/button.js',
'test/unit/mediafaker.js',
'test/unit/player.js',
'test/unit/core.js',

View File

@ -183,12 +183,8 @@ test('fullscreenToggle does not depend on minified player methods', function(){
noop = function(){};
requestFullscreen = false;
exitFullscreen = false;
player = {
id: noop,
on: noop,
ready: noop,
reportUserActivity: noop
};
player = PlayerTest.makePlayer();
player['requestFullscreen'] = function(){
requestFullscreen = true;

22
test/unit/button.js Normal file
View File

@ -0,0 +1,22 @@
module('Button');
test('should localize its text', function(){
expect(1);
var player, testButton, el;
player = PlayerTest.makePlayer({
'language': 'es',
'languages': {
'es': {
'Play': 'Juego'
}
}
});
testButton = new vjs.Button(player);
testButton.buttonText = 'Play';
el = testButton.createEl();
ok(el.textContent, 'Juego', 'translation was successful');
});

View File

@ -9,6 +9,8 @@ test('should hide volume control if it\'s not supported', function(){
id: noop,
on: noop,
ready: noop,
language: noop,
languages: noop,
tech: {
features: {
'volumeControl': false
@ -32,6 +34,8 @@ test('should test and toggle volume control on `loadstart`', function(){
listeners = [];
player = {
id: noop,
language: noop,
languages: noop,
on: function(event, callback){
listeners.push(callback);
},