diff --git a/Resources.php b/Resources.php index 5e80ac5d..53200577 100644 --- a/Resources.php +++ b/Resources.php @@ -129,6 +129,7 @@ $wgResourceModules['ext.uls.webfonts.repository'] = array( $wgResourceModules['jquery.i18n'] = array( 'scripts' => array( '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.emitter.js', 'lib/jquery.i18n/jquery.i18n.language.js', diff --git a/lib/jquery.i18n/jquery.i18n.js b/lib/jquery.i18n/jquery.i18n.js index 7c20f119..34ed7d4a 100644 --- a/lib/jquery.i18n/jquery.i18n.js +++ b/lib/jquery.i18n/jquery.i18n.js @@ -16,13 +16,13 @@ ( function ( $ ) { 'use strict'; - var nav, + var nav, I18N, slice = Array.prototype.slice; /** * @constructor * @param {Object} options */ - var I18N = function ( options ) { + I18N = function ( options ) { // Load defaults this.options = $.extend( {}, I18N.defaults, options ); @@ -43,13 +43,13 @@ var i18n; i18n = this; - i18n.messageStore.init( i18n.locale ); // Set locale of String environment String.locale = i18n.locale; // Override String.localeString method String.prototype.toLocaleString = function () { - var localeParts, messageLocation, localePartIndex, value, locale, fallbackIndex; + var localeParts, localePartIndex, value, locale, fallbackIndex, + _locale, message; value = this.valueOf(); locale = i18n.locale; @@ -62,24 +62,11 @@ localePartIndex = localeParts.length; do { - var _locale = localeParts.slice( 0, localePartIndex ).join( '-' ); - - 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 ); - + _locale = localeParts.slice( 0, localePartIndex ).join( '-' ); + message = i18n.messageStore.get( _locale, value ); if ( message ) { return message; } - localePartIndex--; } while ( localePartIndex ); @@ -89,7 +76,7 @@ locale = ( $.i18n.fallbacks[i18n.locale] && $.i18n.fallbacks[i18n.locale][fallbackIndex] ) || i18n.options.fallbackLocale; - i18n.log( 'Trying fallback locale for ' + i18n.locale + ': ' + locale ); + $.i18n.log( 'Trying fallback locale for ' + i18n.locale + ': ' + locale ); fallbackIndex++; } @@ -122,17 +109,12 @@ * null/undefined/false, * all cached messages for the i18n instance will get reset. * - * @param {String|Object|null} data + * @param {String|Object} source * @param {String} locale Language tag + * @returns {jQuery.Promise} */ - load: function ( data, locale ) { - this.messageStore.load( data, locale ); - }, - - log: function ( /* arguments */ ) { - if ( window.console && $.i18n.debug ) { - window.console.log.apply( window.console, arguments ); - } + load: function ( source, locale ) { + return this.messageStore.load( source, locale ); }, /** @@ -204,18 +186,16 @@ var i18n = $.data( document, 'i18n' ); String.locale = i18n.locale; if ( !i18n ) { - i18n = new I18N( ); + i18n = new I18N(); $.data( document, 'i18n', i18n ); } - return this.each( function () { - var $this = $( this ); - if ( $this.data( 'i18n' ) ) { - var messageKey = $this.data( 'i18n' ), - message = messageKey.toLocaleString(); - if ( message !== '' ) { - $this.text( message ); - } + return this.each( function () { + var $this = $( this ), + messageKey = $this.data( 'i18n' ); + + if ( messageKey ) { + $this.text( i18n.parse( messageKey ) ); } else { $this.find( '[data-i18n]' ).i18n(); } @@ -247,19 +227,19 @@ }; $.i18n.debug = false; - + $.i18n.log = function ( /* arguments */ ) { + if ( window.console && $.i18n.debug ) { + window.console.log.apply( window.console, arguments ); + } + }; /* Static members */ I18N.defaults = { locale: String.locale, fallbackLocale: 'en', parser: $.i18n.parser, - 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 + messageStore: $.i18n.messageStore }; // Expose constructor - $.I18N = I18N; + $.i18n.constructor = I18N; }( jQuery ) ); diff --git a/lib/jquery.i18n/jquery.i18n.messages.js b/lib/jquery.i18n/jquery.i18n.messages.js index f14602da..80b3c528 100644 --- a/lib/jquery.i18n/jquery.i18n.messages.js +++ b/lib/jquery.i18n/jquery.i18n.messages.js @@ -1,5 +1,5 @@ /** - * jQuery Internationalization library Message loading , parsing, retrieving utilities + * jQuery Internationalization library - Message Store * * Copyright (C) 2012 Santhosh Thottingal * @@ -18,44 +18,20 @@ var MessageStore = function () { this.messages = {}; this.sources = {}; - this.locale = String.locale; }; + /** + * See https://github.com/wikimedia/jquery.i18n/wiki/Specification#wiki-Message_File_Loading + */ MessageStore.prototype = { - /** - * See https://github.com/wikimedia/jquery.i18n/wiki/Specification#wiki-Message_File_Loading - * - * @param locale - */ - init: function ( locale ) { - 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 * the json formatted messages. * load('path/to/all_localizations.json'); * * This can also load a localization file for a locale - * load('path/to/de-messages.json', 'de' ); + * load( 'path/to/de-messages.json', 'de' ); * * A data object containing message key- message translation mappings * can also be passed Eg: @@ -65,168 +41,54 @@ * null/undefined/false, * all cached messages for the i18n instance will get reset. * - * @param {String|Object|null} data + * @param {String|Object} source * @param {String} locale Language tag + * @return {jQuery.Promise} */ - load: function ( data, locale ) { + load: function ( source, locale ) { var key = null, - messageStore = this, - hasOwn = Object.prototype.hasOwnProperty; + deferred = null, + deferreds = [], + messageStore = this; - if ( !data ) { - // reset all localizations - messageStore.log( 'Resetting for locale ' + locale ); - messageStore.messages = {}; - - return; - } - - if ( typeof data === 'string' ) { + if ( typeof source === 'string' ) { // This is a URL to the messages file. - messageStore.log( 'Loading messages from: ' + data ); + $.i18n.log( 'Loading messages from: ' + source ); + deferred = jsonMessageLoader( source ) + .done( function ( localization ) { + messageStore.set( locale, localization ); + } ); - messageStore.jsonMessageLoader( data ).done( function ( localization, textStatus ) { - messageStore.load( localization, locale ); - messageStore.queue( locale, data ); - messageStore.markLoaded( locale, data ); - } ); + return deferred.promise(); + } + + if ( locale ) { + // source is an key-value pair of messages for given locale + messageStore.set( locale, source ); + + return $.Deferred().resolve(); } else { - // Data is either a group of messages for {locale}, - // or a group of languages with groups of messages inside. - for ( key in data ) { - if ( hasOwn.call( data, key ) ) { - if ( locale ) { - - // Lazy-init the object - 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 { - 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 messageLocation + * @param messages */ - markLoaded: function ( locale, messageLocation ) { - var i, - 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 ); - } ); + set: function( locale, messages ) { + this.messages[locale] = messages; }, /** @@ -236,15 +98,16 @@ * @returns {Boolean} */ get: function ( locale, messageKey ) { - // load locale if not loaded - if ( !this.messages[locale] ) { - this.loadFromQueue( locale ); - } - 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() ); }( jQuery, window ) ); diff --git a/lib/jquery.i18n/jquery.i18n.parser.js b/lib/jquery.i18n/jquery.i18n.parser.js index f26109bd..7dc26e6e 100644 --- a/lib/jquery.i18n/jquery.i18n.parser.js +++ b/lib/jquery.i18n/jquery.i18n.parser.js @@ -46,7 +46,11 @@ }, 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 function choice ( parserSyntax ) { @@ -142,15 +146,15 @@ }; } - var pipe = makeStringParser( '|' ); - var colon = makeStringParser( ':' ); - var backslash = makeStringParser( '\\' ); - var anyCharacter = makeRegexParser( /^./ ); - var dollar = makeStringParser( '$' ); - var digits = makeRegexParser( /^\d+/ ); - var regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ ); - var regularLiteralWithoutBar = makeRegexParser( /^[^{}\[\]$\\|]/ ); - var regularLiteralWithoutSpace = makeRegexParser( /^[^{}\[\]$\s]/ ); + pipe = makeStringParser( '|' ); + colon = makeStringParser( ':' ); + backslash = makeStringParser( '\\' ); + anyCharacter = makeRegexParser( /^./ ); + dollar = makeStringParser( '$' ); + digits = makeRegexParser( /^\d+/ ); + regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ ); + regularLiteralWithoutBar = makeRegexParser( /^[^{}\[\]$\\|]/ ); + regularLiteralWithoutSpace = makeRegexParser( /^[^{}\[\]$\s]/ ); // There is a general pattern: // parse a thing; @@ -189,8 +193,8 @@ } choice( [ escapedLiteral, regularLiteralWithoutSpace ] ); - var escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] ); - var escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] ); + escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] ); + escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] ); function replacement () { var result = sequence( [ dollar, digits ] ); @@ -202,7 +206,7 @@ return [ 'REPLACE', parseInt( result[1], 10 ) - 1 ]; } - var templateName = transform( + templateName = transform( // see $wgLegalTitleChars // not allowing : due to the need to catch "PLURAL:$1" makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ), @@ -213,13 +217,14 @@ ); function templateParam () { - var result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] ); + var expr, + result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] ); if ( result === null ) { return null; } - var expr = result[1]; + expr = result[1]; // use a "CONCAT" operator if there are multiple nodes, // otherwise return the first node, raw. @@ -238,7 +243,7 @@ return result === null ? null : [ result[0], result[2] ]; } - var templateContents = choice( [ + templateContents = choice( [ function () { var res = sequence( [ // templates can have placeholders for dynamic @@ -262,8 +267,8 @@ } ] ); - var openTemplate = makeStringParser( '{{' ); - var closeTemplate = makeStringParser( '}}' ); + openTemplate = makeStringParser( '{{' ); + closeTemplate = makeStringParser( '}}' ); function template () { var result = sequence( [ openTemplate, templateContents, closeTemplate ] ); @@ -271,8 +276,8 @@ return result === null ? null : result[1]; } - var expression = choice( [ template, replacement, literal ] ); - var paramExpression = choice( [ template, replacement, literalWithoutBar ] ); + expression = choice( [ template, replacement, literal ] ); + paramExpression = choice( [ template, replacement, literalWithoutBar ] ); function start () { var result = nOrMore( 0, expression )(); @@ -284,7 +289,7 @@ return [ 'CONCAT' ].concat( result ); } - var result = start(); + result = start(); /* * For success, the pos must have gotten to the end of the input @@ -301,5 +306,4 @@ }; $.extend( $.i18n.parser, new MessageParser() ); - }( jQuery ) ); diff --git a/resources/js/ext.uls.displaysettings.js b/resources/js/ext.uls.displaysettings.js index fdc9cd1b..f5389120 100644 --- a/resources/js/ext.uls.displaysettings.js +++ b/resources/js/ext.uls.displaysettings.js @@ -316,11 +316,9 @@ * @param {String} language Language code */ preview: function ( language ) { - var displaySettings = this, - i18n = $.i18n(); + var displaySettings = this; - i18n.locale = language; - i18n.messageStore.load( i18n.locale ).done( function () { + mw.uls.loadLocalization( language ).done( function () { displaySettings.i18n(); } ); }, diff --git a/resources/js/ext.uls.i18n.js b/resources/js/ext.uls.i18n.js index 5f890c38..0b0c8db9 100644 --- a/resources/js/ext.uls.i18n.js +++ b/resources/js/ext.uls.i18n.js @@ -17,54 +17,31 @@ * @licence MIT License */ -( function ( $, mw, undefined ) { +( function ( $, mw ) { 'use strict'; + mw.uls = mw.uls || {}; + // jquery.i18n has CLDRPluralRuleParser but MediaWiki also has the same // parser. Reuse it by aliasing it to window.pluralRuleParser window.pluralRuleParser = mw.libs.pluralRuleParser; - /** - * jquery.i18n message store for MediaWiki - * - */ - var MWMessageStore = function () { - this.messages = {}; - }; + // JavaScript side i18n initialization + $.i18n( { + locale: mw.config.get( 'wgUserLanguage' ) + } ); - MWMessageStore.prototype = { - init: function () {}, + mw.uls.loadLocalization = function ( locale ) { + var i18n = $.i18n(); - 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(); + 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 ) ); diff --git a/resources/js/ext.uls.init.js b/resources/js/ext.uls.init.js index 90e72858..5be29d5b 100644 --- a/resources/js/ext.uls.init.js +++ b/resources/js/ext.uls.init.js @@ -31,7 +31,7 @@ this.$languageFilter.addClass( 'noime' ); }; - var jsonLoader, + var jsonLoader = null, initialized = false, currentLang = mw.config.get( 'wgUserLanguage' ), logEventQueue = $.Callbacks( 'memory once' ); @@ -211,14 +211,8 @@ */ $.uls.data.addLanguage( 'als', { target: 'gsw' } ); - // JavaScript side i18n initialization - $.i18n( { - locale: currentLang, - messageStore: mw.uls.messageStore - } ); - if ( !jsonLoader ) { - jsonLoader = mw.uls.messageStore.load( currentLang ); + jsonLoader = mw.uls.loadLocalization( currentLang ); } else { jsonLoader.done( function () { initialized = true;