Update jquery.i18n from upstream, use its default message store

In Iddef0805ab a custom message store was introduced for ULS, but
there were updates in jquery.i18n that allows us to use default
message store again.

(see https://github.com/wikimedia/jquery.i18n/pull/40)

Change-Id: I86cb7a44efa83e5811824cd1104c6be11b1e2925
This commit is contained in:
Santhosh Thottingal
2013-07-23 15:32:45 +05:30
parent f144cd5c16
commit 2159822145
7 changed files with 116 additions and 299 deletions

View File

@@ -129,6 +129,7 @@ $wgResourceModules['ext.uls.webfonts.repository'] = array(
$wgResourceModules['jquery.i18n'] = array( $wgResourceModules['jquery.i18n'] = array(
'scripts' => array( 'scripts' => array(
'lib/jquery.i18n/jquery.i18n.js', 'lib/jquery.i18n/jquery.i18n.js',
'lib/jquery.i18n/jquery.i18n.messages.js',
'lib/jquery.i18n/jquery.i18n.parser.js', 'lib/jquery.i18n/jquery.i18n.parser.js',
'lib/jquery.i18n/jquery.i18n.emitter.js', 'lib/jquery.i18n/jquery.i18n.emitter.js',
'lib/jquery.i18n/jquery.i18n.language.js', 'lib/jquery.i18n/jquery.i18n.language.js',

View File

@@ -16,13 +16,13 @@
( function ( $ ) { ( function ( $ ) {
'use strict'; 'use strict';
var nav, var nav, I18N,
slice = Array.prototype.slice; slice = Array.prototype.slice;
/** /**
* @constructor * @constructor
* @param {Object} options * @param {Object} options
*/ */
var I18N = function ( options ) { I18N = function ( options ) {
// Load defaults // Load defaults
this.options = $.extend( {}, I18N.defaults, options ); this.options = $.extend( {}, I18N.defaults, options );
@@ -43,13 +43,13 @@
var i18n; var i18n;
i18n = this; i18n = this;
i18n.messageStore.init( i18n.locale );
// Set locale of String environment // Set locale of String environment
String.locale = i18n.locale; String.locale = i18n.locale;
// Override String.localeString method // Override String.localeString method
String.prototype.toLocaleString = function () { String.prototype.toLocaleString = function () {
var localeParts, messageLocation, localePartIndex, value, locale, fallbackIndex; var localeParts, localePartIndex, value, locale, fallbackIndex,
_locale, message;
value = this.valueOf(); value = this.valueOf();
locale = i18n.locale; locale = i18n.locale;
@@ -62,24 +62,11 @@
localePartIndex = localeParts.length; localePartIndex = localeParts.length;
do { do {
var _locale = localeParts.slice( 0, localePartIndex ).join( '-' ); _locale = localeParts.slice( 0, localePartIndex ).join( '-' );
message = i18n.messageStore.get( _locale, value );
if ( i18n.options.messageLocationResolver ) {
messageLocation = i18n.options.messageLocationResolver( _locale, value );
if ( messageLocation &&
( !i18n.messageStore.isLoaded( _locale ,messageLocation ) )
) {
i18n.messageStore.load( messageLocation, _locale );
}
}
var message = i18n.messageStore.get( _locale, value );
if ( message ) { if ( message ) {
return message; return message;
} }
localePartIndex--; localePartIndex--;
} while ( localePartIndex ); } while ( localePartIndex );
@@ -89,7 +76,7 @@
locale = ( $.i18n.fallbacks[i18n.locale] && $.i18n.fallbacks[i18n.locale][fallbackIndex] ) || locale = ( $.i18n.fallbacks[i18n.locale] && $.i18n.fallbacks[i18n.locale][fallbackIndex] ) ||
i18n.options.fallbackLocale; i18n.options.fallbackLocale;
i18n.log( 'Trying fallback locale for ' + i18n.locale + ': ' + locale ); $.i18n.log( 'Trying fallback locale for ' + i18n.locale + ': ' + locale );
fallbackIndex++; fallbackIndex++;
} }
@@ -122,17 +109,12 @@
* null/undefined/false, * null/undefined/false,
* all cached messages for the i18n instance will get reset. * all cached messages for the i18n instance will get reset.
* *
* @param {String|Object|null} data * @param {String|Object} source
* @param {String} locale Language tag * @param {String} locale Language tag
* @returns {jQuery.Promise}
*/ */
load: function ( data, locale ) { load: function ( source, locale ) {
this.messageStore.load( data, locale ); return this.messageStore.load( source, locale );
},
log: function ( /* arguments */ ) {
if ( window.console && $.i18n.debug ) {
window.console.log.apply( window.console, arguments );
}
}, },
/** /**
@@ -204,18 +186,16 @@
var i18n = $.data( document, 'i18n' ); var i18n = $.data( document, 'i18n' );
String.locale = i18n.locale; String.locale = i18n.locale;
if ( !i18n ) { if ( !i18n ) {
i18n = new I18N( ); i18n = new I18N();
$.data( document, 'i18n', i18n ); $.data( document, 'i18n', i18n );
} }
return this.each( function () {
var $this = $( this );
if ( $this.data( 'i18n' ) ) { return this.each( function () {
var messageKey = $this.data( 'i18n' ), var $this = $( this ),
message = messageKey.toLocaleString(); messageKey = $this.data( 'i18n' );
if ( message !== '' ) {
$this.text( message ); if ( messageKey ) {
} $this.text( i18n.parse( messageKey ) );
} else { } else {
$this.find( '[data-i18n]' ).i18n(); $this.find( '[data-i18n]' ).i18n();
} }
@@ -247,19 +227,19 @@
}; };
$.i18n.debug = false; $.i18n.debug = false;
$.i18n.log = function ( /* arguments */ ) {
if ( window.console && $.i18n.debug ) {
window.console.log.apply( window.console, arguments );
}
};
/* Static members */ /* Static members */
I18N.defaults = { I18N.defaults = {
locale: String.locale, locale: String.locale,
fallbackLocale: 'en', fallbackLocale: 'en',
parser: $.i18n.parser, parser: $.i18n.parser,
messageStore: $.i18n.messageStore, messageStore: $.i18n.messageStore
/* messageLocationResolver - should be a function taking language code as argument and
* returning absolute or relative path to the localization file
*/
messageLocationResolver: null
}; };
// Expose constructor // Expose constructor
$.I18N = I18N; $.i18n.constructor = I18N;
}( jQuery ) ); }( jQuery ) );

View File

@@ -1,5 +1,5 @@
/** /**
* jQuery Internationalization library Message loading , parsing, retrieving utilities * jQuery Internationalization library - Message Store
* *
* Copyright (C) 2012 Santhosh Thottingal * Copyright (C) 2012 Santhosh Thottingal
* *
@@ -18,36 +18,12 @@
var MessageStore = function () { var MessageStore = function () {
this.messages = {}; this.messages = {};
this.sources = {}; this.sources = {};
this.locale = String.locale;
}; };
MessageStore.prototype = {
/** /**
* See https://github.com/wikimedia/jquery.i18n/wiki/Specification#wiki-Message_File_Loading * See https://github.com/wikimedia/jquery.i18n/wiki/Specification#wiki-Message_File_Loading
*
* @param locale
*/ */
init: function ( locale ) { MessageStore.prototype = {
var messageStore = this;
messageStore.locale = locale;
messageStore.log( 'initializing for ' + locale );
$( 'link' ).each( function ( index, element ) {
var $link = $( element ),
rel = ( $link.attr( 'rel' ) || '' ).toLowerCase().split( /\s+/ );
if ( $.inArray( 'localizations', rel ) !== -1 ) {
// multiple localizations
messageStore.load( $link.attr( 'href' ) );
} else if ( $.inArray( 'localization', rel ) !== -1 ) {
// single localization
messageStore.queue( ( $link.attr( 'hreflang' ) || '' ).toLowerCase(),
$link.attr( 'href' ) );
}
} );
},
/** /**
* General message loading API This can take a URL string for * General message loading API This can take a URL string for
@@ -55,7 +31,7 @@
* <code>load('path/to/all_localizations.json');</code> * <code>load('path/to/all_localizations.json');</code>
* *
* This can also load a localization file for a locale <code> * This can also load a localization file for a locale <code>
* load('path/to/de-messages.json', 'de' ); * load( 'path/to/de-messages.json', 'de' );
* </code> * </code>
* A data object containing message key- message translation mappings * A data object containing message key- message translation mappings
* can also be passed Eg: * can also be passed Eg:
@@ -65,168 +41,54 @@
* null/undefined/false, * null/undefined/false,
* all cached messages for the i18n instance will get reset. * all cached messages for the i18n instance will get reset.
* *
* @param {String|Object|null} data * @param {String|Object} source
* @param {String} locale Language tag * @param {String} locale Language tag
* @return {jQuery.Promise}
*/ */
load: function ( data, locale ) { load: function ( source, locale ) {
var key = null, var key = null,
messageStore = this, deferred = null,
hasOwn = Object.prototype.hasOwnProperty; deferreds = [],
messageStore = this;
if ( !data ) { if ( typeof source === 'string' ) {
// reset all localizations
messageStore.log( 'Resetting for locale ' + locale );
messageStore.messages = {};
return;
}
if ( typeof data === 'string' ) {
// This is a URL to the messages file. // This is a URL to the messages file.
messageStore.log( 'Loading messages from: ' + data ); $.i18n.log( 'Loading messages from: ' + source );
deferred = jsonMessageLoader( source )
messageStore.jsonMessageLoader( data ).done( function ( localization, textStatus ) { .done( function ( localization ) {
messageStore.load( localization, locale ); messageStore.set( locale, localization );
messageStore.queue( locale, data );
messageStore.markLoaded( locale, data );
} ); } );
} else {
// Data is either a group of messages for {locale}, return deferred.promise();
// or a group of languages with groups of messages inside. }
for ( key in data ) {
if ( hasOwn.call( data, key ) ) {
if ( locale ) { if ( locale ) {
// source is an key-value pair of messages for given locale
messageStore.set( locale, source );
// Lazy-init the object return $.Deferred().resolve();
if ( !messageStore.messages[locale] ) {
messageStore.messages[locale] = {};
}
// Update message object keys,
// don't overwrite the entire object.
messageStore.messages[locale][key] = data[key];
messageStore.log(
'[' + locale + '][' + key + '] : ' + data[key]
);
// No {locale} given, assume data is a group of languages,
// call this function again for each langauge.
} else { } else {
messageStore.load( data[key], key ); // source is a key-value pair of locales and their source
for ( key in source ) {
if ( Object.prototype.hasOwnProperty.call( source, key ) ) {
locale = key;
// No {locale} given, assume data is a group of languages,
// call this function again for each language.
deferreds.push( messageStore.load( source[key], locale ) );
} }
} }
return $.when.apply( $, deferreds );
} }
}
},
log: function ( /* arguments */ ) {
if ( window.console && $.i18n.debug ) {
window.console.log.apply( window.console, arguments );
}
}, },
/** /**
* Mark a message Location for a locale loaded * Set messages
*
* @param locale * @param locale
* @param messageLocation * @param messages
*/ */
markLoaded: function ( locale, messageLocation ) { set: function( locale, messages ) {
var i, this.messages[locale] = messages;
queue = this.sources[locale];
if ( !queue ) {
this.queue( locale, messageLocation );
queue = this.sources[locale];
}
this.sources[locale] = this.sources[locale] || [];
for ( i = 0; i < queue.length; i++ ) {
if ( queue[i].source.url === messageLocation ) {
queue[i].source.loaded = true;
return;
}
}
},
/**
* Register the message location for a locale, will be loaded when required
*
* @param locale
* @param messageLocation
*/
queue: function ( locale, messageLocation ) {
var i,
queue = this.sources[locale];
this.sources[locale] = this.sources[locale] || [];
if ( queue ) {
for ( i = 0; i < queue.length; i++ ) {
if ( queue[i].source.url === messageLocation ) {
return;
}
}
}
this.log( 'Source for: ' + locale + ' is ' + messageLocation + ' registered' );
this.sources[locale].push( {
source: {
url: messageLocation,
loaded: false
}
} );
},
/**
* Load the messages from the source queue for the locale
*
* @param {String} locale
*/
loadFromQueue: function ( locale ) {
var i,
queue = this.sources[locale];
if ( queue ) {
for ( i = 0; i < queue.length; i++ ) {
if ( !queue[i].source.loaded ) {
this.load( queue[i].source.url, locale );
this.sources[locale][i].source.loaded = true;
}
}
}
},
isLoaded: function ( locale, messageLocation ) {
var i,
sources = this.sources[locale],
result = false;
if ( sources ) {
for ( i = 0; i < sources.length; i++ ) {
if ( sources[i].source.url === messageLocation ) {
result = true;
}
}
}
return result;
},
jsonMessageLoader: function ( url ) {
var messageStore = this;
return $.ajax( {
url: url,
dataType: 'json',
//async: false
// This is unfortunate.
} ).fail( function ( jqxhr, settings, exception ) {
messageStore.log( 'Error in loading messages from ' + url + ' Exception: ' + exception );
} );
}, },
/** /**
@@ -236,15 +98,16 @@
* @returns {Boolean} * @returns {Boolean}
*/ */
get: function ( locale, messageKey ) { get: function ( locale, messageKey ) {
// load locale if not loaded
if ( !this.messages[locale] ) {
this.loadFromQueue( locale );
}
return this.messages[locale] && this.messages[locale][messageKey]; return this.messages[locale] && this.messages[locale][messageKey];
} }
}; };
function jsonMessageLoader( url ) {
return $.getJSON( url ).fail( function ( jqxhr, settings, exception ) {
$.i18n.log( 'Error in loading messages from ' + url + ' Exception: ' + exception );
} );
}
$.extend( $.i18n.messageStore, new MessageStore() ); $.extend( $.i18n.messageStore, new MessageStore() );
}( jQuery, window ) ); }( jQuery, window ) );

View File

@@ -46,7 +46,11 @@
}, },
ast: function ( message ) { ast: function ( message ) {
var pos = 0; var pipe, colon, backslash, anyCharacter, dollar, digits, regularLiteral,
regularLiteralWithoutBar, regularLiteralWithoutSpace, escapedOrLiteralWithoutBar,
escapedOrRegularLiteral, templateContents, templateName, openTemplate,
closeTemplate, expression, paramExpression, result,
pos = 0;
// Try parsers until one works, if none work return null // Try parsers until one works, if none work return null
function choice ( parserSyntax ) { function choice ( parserSyntax ) {
@@ -142,15 +146,15 @@
}; };
} }
var pipe = makeStringParser( '|' ); pipe = makeStringParser( '|' );
var colon = makeStringParser( ':' ); colon = makeStringParser( ':' );
var backslash = makeStringParser( '\\' ); backslash = makeStringParser( '\\' );
var anyCharacter = makeRegexParser( /^./ ); anyCharacter = makeRegexParser( /^./ );
var dollar = makeStringParser( '$' ); dollar = makeStringParser( '$' );
var digits = makeRegexParser( /^\d+/ ); digits = makeRegexParser( /^\d+/ );
var regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ ); regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ );
var regularLiteralWithoutBar = makeRegexParser( /^[^{}\[\]$\\|]/ ); regularLiteralWithoutBar = makeRegexParser( /^[^{}\[\]$\\|]/ );
var regularLiteralWithoutSpace = makeRegexParser( /^[^{}\[\]$\s]/ ); regularLiteralWithoutSpace = makeRegexParser( /^[^{}\[\]$\s]/ );
// There is a general pattern: // There is a general pattern:
// parse a thing; // parse a thing;
@@ -189,8 +193,8 @@
} }
choice( [ escapedLiteral, regularLiteralWithoutSpace ] ); choice( [ escapedLiteral, regularLiteralWithoutSpace ] );
var escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] ); escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] );
var escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] ); escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] );
function replacement () { function replacement () {
var result = sequence( [ dollar, digits ] ); var result = sequence( [ dollar, digits ] );
@@ -202,7 +206,7 @@
return [ 'REPLACE', parseInt( result[1], 10 ) - 1 ]; return [ 'REPLACE', parseInt( result[1], 10 ) - 1 ];
} }
var templateName = transform( templateName = transform(
// see $wgLegalTitleChars // see $wgLegalTitleChars
// not allowing : due to the need to catch "PLURAL:$1" // not allowing : due to the need to catch "PLURAL:$1"
makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ), makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ),
@@ -213,13 +217,14 @@
); );
function templateParam () { function templateParam () {
var result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] ); var expr,
result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] );
if ( result === null ) { if ( result === null ) {
return null; return null;
} }
var expr = result[1]; expr = result[1];
// use a "CONCAT" operator if there are multiple nodes, // use a "CONCAT" operator if there are multiple nodes,
// otherwise return the first node, raw. // otherwise return the first node, raw.
@@ -238,7 +243,7 @@
return result === null ? null : [ result[0], result[2] ]; return result === null ? null : [ result[0], result[2] ];
} }
var templateContents = choice( [ templateContents = choice( [
function () { function () {
var res = sequence( [ var res = sequence( [
// templates can have placeholders for dynamic // templates can have placeholders for dynamic
@@ -262,8 +267,8 @@
} }
] ); ] );
var openTemplate = makeStringParser( '{{' ); openTemplate = makeStringParser( '{{' );
var closeTemplate = makeStringParser( '}}' ); closeTemplate = makeStringParser( '}}' );
function template () { function template () {
var result = sequence( [ openTemplate, templateContents, closeTemplate ] ); var result = sequence( [ openTemplate, templateContents, closeTemplate ] );
@@ -271,8 +276,8 @@
return result === null ? null : result[1]; return result === null ? null : result[1];
} }
var expression = choice( [ template, replacement, literal ] ); expression = choice( [ template, replacement, literal ] );
var paramExpression = choice( [ template, replacement, literalWithoutBar ] ); paramExpression = choice( [ template, replacement, literalWithoutBar ] );
function start () { function start () {
var result = nOrMore( 0, expression )(); var result = nOrMore( 0, expression )();
@@ -284,7 +289,7 @@
return [ 'CONCAT' ].concat( result ); return [ 'CONCAT' ].concat( result );
} }
var result = start(); result = start();
/* /*
* For success, the pos must have gotten to the end of the input * For success, the pos must have gotten to the end of the input
@@ -301,5 +306,4 @@
}; };
$.extend( $.i18n.parser, new MessageParser() ); $.extend( $.i18n.parser, new MessageParser() );
}( jQuery ) ); }( jQuery ) );

View File

@@ -316,11 +316,9 @@
* @param {String} language Language code * @param {String} language Language code
*/ */
preview: function ( language ) { preview: function ( language ) {
var displaySettings = this, var displaySettings = this;
i18n = $.i18n();
i18n.locale = language; mw.uls.loadLocalization( language ).done( function () {
i18n.messageStore.load( i18n.locale ).done( function () {
displaySettings.i18n(); displaySettings.i18n();
} ); } );
}, },

View File

@@ -17,54 +17,31 @@
* @licence MIT License * @licence MIT License
*/ */
( function ( $, mw, undefined ) { ( function ( $, mw ) {
'use strict'; 'use strict';
mw.uls = mw.uls || {};
// jquery.i18n has CLDRPluralRuleParser but MediaWiki also has the same // jquery.i18n has CLDRPluralRuleParser but MediaWiki also has the same
// parser. Reuse it by aliasing it to window.pluralRuleParser // parser. Reuse it by aliasing it to window.pluralRuleParser
window.pluralRuleParser = mw.libs.pluralRuleParser; window.pluralRuleParser = mw.libs.pluralRuleParser;
/** // JavaScript side i18n initialization
* jquery.i18n message store for MediaWiki $.i18n( {
* locale: mw.config.get( 'wgUserLanguage' )
*/
var MWMessageStore = function () {
this.messages = {};
};
MWMessageStore.prototype = {
init: function () {},
get: function ( locale, messageKey ) {
return ( this.isLoaded( locale ) && this.messages[locale][messageKey] ) ||
'<' + messageKey + '>';
},
set: function( locale, messages ) {
this.messages[locale] = messages;
},
isLoaded: function ( locale ) {
return this.messages[locale];
},
load: function ( locale ) {
var store = this,
deferred = $.Deferred(),
url = mw.util.wikiScript( 'api' ) + '?action=ulslocalization&language=';
if ( store.isLoaded( locale ) ) {
return deferred.resolve();
}
deferred = $.getJSON( url + locale ).done( function ( data ) {
store.set( locale, data );
} ).fail( function ( jqxhr, settings, exception ) {
mw.log( 'Error in loading messages from ' + url + ' Exception: ' + exception );
} ); } );
return deferred.promise();
mw.uls.loadLocalization = function ( locale ) {
var i18n = $.i18n();
i18n.locale = locale;
if ( i18n.messageStore.messages[locale] ) {
return $.Deferred().resolve();
} }
return i18n.load(
mw.util.wikiScript( 'api' ) + '?action=ulslocalization&language=' + locale,
locale
);
}; };
mw.uls = mw.uls || {};
mw.uls.messageStore = new MWMessageStore();
}( jQuery, mediaWiki ) ); }( jQuery, mediaWiki ) );

View File

@@ -31,7 +31,7 @@
this.$languageFilter.addClass( 'noime' ); this.$languageFilter.addClass( 'noime' );
}; };
var jsonLoader, var jsonLoader = null,
initialized = false, initialized = false,
currentLang = mw.config.get( 'wgUserLanguage' ), currentLang = mw.config.get( 'wgUserLanguage' ),
logEventQueue = $.Callbacks( 'memory once' ); logEventQueue = $.Callbacks( 'memory once' );
@@ -211,14 +211,8 @@
*/ */
$.uls.data.addLanguage( 'als', { target: 'gsw' } ); $.uls.data.addLanguage( 'als', { target: 'gsw' } );
// JavaScript side i18n initialization
$.i18n( {
locale: currentLang,
messageStore: mw.uls.messageStore
} );
if ( !jsonLoader ) { if ( !jsonLoader ) {
jsonLoader = mw.uls.messageStore.load( currentLang ); jsonLoader = mw.uls.loadLocalization( currentLang );
} else { } else {
jsonLoader.done( function () { jsonLoader.done( function () {
initialized = true; initialized = true;