/**
* jQuery Internationalization library
*
* Copyright (C) 2012 Santhosh Thottingal
*
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
* anything special to choose one license or the other and you don't have to
* notify anyone which license you are using. You are free to use
* UniversalLanguageSelector in commercial projects as long as the copyright
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
*
* @licence GNU General Public Licence 2.0 or later
* @licence MIT License
*/
( function ( $, window, undefined ) {
"use strict";
var I18N = function ( options ) {
// Load defaults
this.options = $.extend( {}, $.i18n.defaults, options );
this.parser = this.options.parser;
this.messageStore = this.options.messageStore;
this.languages = {};
this.locale = this.options.locale;
this.init();
};
I18N.prototype = {
/**
* Initialize by loading locales and setting up toLocaleString
*/
init: function () {
var that = this;
this.messageStore.init( this.locale );
// Override String.localeString method
String.prototype.toLocaleString = function () {
var value = this.valueOf();
var locale = that.locale;
var fallbackIndex = 0;
while ( locale ) {
// Iterate through locales starting at most-specific until
// localization is found. As in fi-Latn-FI, fi-Latn and fi.
var localeParts = locale.toLowerCase().split( "-" );
var localePartIndex = localeParts.length;
do {
var _locale = localeParts.slice( 0, localePartIndex ).join( "-" );
if ( !that.messageStore.messages[_locale] && that.options.messageLocationResolver ) {
// FIXME If messageloading gives 404, it keep on trying to
// load the file again and again
that.messageStore.load(
that.options.messageLocationResolver( _locale ), _locale );
}
var message = that.messageStore.get( _locale, value );
if ( message ) {
return message;
}
localePartIndex--;
} while (localePartIndex);
if ( locale === "en" ) {
break;
}
locale = ( $.i18n.fallbacks[that.locale] && $.i18n.fallbacks[that.locale][fallbackIndex] )
|| that.options.fallbackLocale;
that.log( "Trying fallback locale for " + that.locale + ": " + locale );
fallbackIndex++;
}
return value; // fallback the original string value
};
String.locale = this.locale;
},
destroy: function () {
$( 'body' ).data( 'i18n', null );
},
/**
* General message loading API This can take a URL string for the json formatted messages.
* Eg: load('path/to/all_localizations.json');
*
* This can also load a localization file for a locale Eg: load('path/to/de-messages.json',
* 'de' );
*
* A data object containing message key- message translation mappings can also be passed Eg:
* load( { 'hello' : 'Hello' }, optionalLocale ); If the data argument is
* null/undefined/false, all cached messages for the i18n instance will get reset.
*
* @param {String|Object|null} data
* @param {String} locale Language tag
*/
load: function ( data, locale ) {
this.messageStore.load( data, locale );
},
log: function (/* arguments */) {
var hasConsole = window.console !== undefined;
if ( hasConsole && $.i18n.debug ) {
window.console.log.apply( window.console, arguments );
}
},
/**
* Does parameter and magic word substitution.
*
* @param {String}
* key Message key
* @param {Array}
* parameters Message parameters
* @return
* @string
*/
parse: function ( key, parameters ) {
var message = key.toLocaleString();
this.parser.language = $.i18n.languages[$.i18n().locale] || $.i18n.languages['default'];
return this.parser.parse( message, parameters );
}
};
String.locale = String.locale || $( 'html' ).attr( 'lang' );
if ( !String.locale ) {
if ( typeof window.navigator !== undefined ) {
var nav = window.navigator;
String.locale = nav.language || nav.userLanguage || "";
} else {
String.locale = "";
}
}
$.i18n = function ( key, parameter_1 /* [, parameter_2] */) {
var parameters = [], i18n = $( 'body' ).data( 'i18n' );
var options = typeof key === 'object' && key;
if ( options && options.locale && i18n && i18n.locale !== options.locale ) {
String.locale = i18n.locale = options.locale;
}
if ( !i18n ) {
$( 'body' ).data( 'i18n', ( i18n = new I18N( options ) ) );
$( '[data-i18n]' ).each( function ( e ) {
var $this = $( this );
if ( $this.data( 'i18n' ) ) {
var messageKey = $this.data( 'i18n' );
var message = $.i18n( messageKey );
if ( message !== messageKey ) {
$this.text( message );
}
}
} );
}
if ( !key ) {
return i18n;
}
// Support variadic arguments
if ( parameter_1 !== undefined ) {
parameters = $.makeArray( arguments );
parameters.shift();
}
if ( typeof key === 'string' ) {
return i18n.parse( key, parameters );
} else {
return i18n;
}
};
$.fn.i18n = function ( option ) {
return this.each( function () {
var $this = $( this );
if ( $this.data( 'i18n' ) ) {
var messageKey = $this.data( 'i18n' );
var message = $.i18n( messageKey );
if ( message !== messageKey ) {
$this.text( message );
}
} else {
$this.find( '[data-i18n]' ).i18n();
}
} );
};
// The default parser only handles variable substitution
var defaultParser = {
parse: function ( message, parameters ) {
return message.replace( /\$(\d+)/g, function ( str, match ) {
var index = parseInt( match, 10 ) - 1;
return parameters[index] !== undefined ? parameters[index] : '$' + match;
} );
}
};
$.i18n.languages = {};
$.i18n.messageStore = $.i18n.messageStore || {};
$.i18n.parser = defaultParser;
$.i18n.parser.emitter = {};
$.i18n.debug = false;
$.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/ relative path to the localization file
*/
messageLocationResolver: null
};
$.i18n.Constructor = I18N;
/**
* Convenient alias
*/
window._ = window._ || $.i18n;
}( jQuery, window ) );
/**
* jQuery Internationalization library Message loading , parsing, retrieving utilities
*
* Copyright (C) 2012 Santhosh Thottingal
*
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do anything special to
* choose one license or the other and you don't have to notify anyone which license you are using.
* You are free to use UniversalLanguageSelector in commercial projects as long as the copyright
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
*
* @licence GNU General Public Licence 2.0 or later
* @licence MIT License
*/
( function ( $, window, undefined ) {
"use strict";
var MessageStore = function () {
this.messages = {};
this.sources = {};
this.locale = String.locale;
};
MessageStore.prototype = {
/**
* See https://github.com/wikimedia/jquery.i18n/wiki/Specification#wiki-Message_File_Loading
*
* @param locale
*/
init: function ( locale ) {
this.locale = locale;
var that = this;
var $links = $( "link" );
var linksCount = $links.length;
this.log( "initializing for " + locale );
// Check for load('path/to/all_localizations.json');
*
* This can also load a localization file for a locale
* load('path/to/de-messages.json', 'de' );
*
* A data object containing message key- message translation mappings can also be passed Eg:
*
* load( { 'hello' : 'Hello' }, optionalLocale );
* If the data argument is
* null/undefined/false, all cached messages for the i18n instance will get reset.
*
* @param {String|Object|null} data
* @param {String} locale Language tag
*/
load: function ( data, locale ) {
var that = this;
var hasOwn = Object.prototype.hasOwnProperty;
if ( !data ) {
// reset all localizations
this.log( "Resetting for locale" + locale );
that.messages = {};
return;
}
var dataType = typeof data;
if ( locale && this.locale !== locale ) {
// queue loading locale if not needed
if ( ! ( locale in this.sources ) ) {
this.sources[locale] = [];
}
this.log( "Queueing: " + locale + " Current locale " + this.locale );
this.sources[locale].push( data );
return;
}
if ( arguments.length > 0 && dataType !== "number" ) {
if ( dataType === "string" ) {
// This is a URL to the messages file.
this.log( "Loading messages from: " + data );
this.jsonMessageLoader( data ).done( function ( localization, textStatus ) {
that.load( localization, locale );
delete that.sources[locale];
} );
} else {
// data is Object
// Extend current localizations instead of completely
// overwriting them
var localization = data;
for ( var messageKey in localization ) {
if ( !hasOwn.call( localization, messageKey ) ) {
continue;
}
var messageKeyType = typeof messageKey;
if ( messageKeyType === "string" && locale ) {
that.log( "[" + locale + "][" + messageKey + "] : "
+ localization[messageKey] );
that.messages[locale] = that.messages[locale] || [];
that.messages[locale][messageKey] = localization[messageKey];
} else {
var passedLocale = messageKey;
this.log( "Loading locale: " + passedLocale );
that.load( localization[passedLocale], passedLocale );
}
}
}
}
},
log: function (/* arguments */) {
var hasConsole = window.console !== undefined;
if ( hasConsole && $.i18n.debug ) {
window.console.log.apply( window.console, arguments );
}
},
/**
* Load the messages from the source queue for the locale
*
* @param {String} locale
*/
loadFromQueue: function ( locale ) {
var that = this;
var queue = that.sources[locale];
for ( var i = 0; i < queue.length; i++ ) {
that.load( queue[i], locale );
}
delete that.sources[locale];
},
jsonMessageLoader: function ( url ) {
var that = this;
return $.ajax( {
url: url,
dataType: "json",
async: false
// that is unfortunate
} ).fail( function ( jqxhr, settings, exception ) {
that.log( "Error in loading messages from " + url + " Exception: " + exception );
} );
},
/**
*
* @param locale
* @param messageKey
* @returns {Boolean}
*/
get: function ( locale, messageKey ) {
// load locale if not loaded
if ( this.sources[locale] ) {
// We need to switch to this locale
this.locale = locale;
this.loadFromQueue( locale );
}
return this.messages[locale] && this.messages[locale][messageKey];
}
};
$.extend( $.i18n.messageStore, new MessageStore() );
}( jQuery, window ) );
/**
* jQuery Internationalization library
*
* Copyright (C) 2012 Santhosh Thottingal
*
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do anything special to
* choose one license or the other and you don't have to notify anyone which license you are using.
* You are free to use UniversalLanguageSelector in commercial projects as long as the copyright
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
*
* @licence GNU General Public Licence 2.0 or later
* @licence MIT License
*/
( function ( $, undefined ) {
$.i18n = $.i18n || {};
$.i18n.fallbacks = {
"ab": ["ru"],
"ace": ["id"],
"aln": ["sq"],
"als": ["gsw", "de"],
"an": ["es"],
"anp": ["hi"],
"arn": ["es"],
"arz": ["ar"],
"av": ["ru"],
"ay": ["es"],
"ba": ["ru"],
"bar": ["de"],
"bat-smg": ["sgs", "lt"],
"bcc": ["fa"],
"be-x-old": ["be-tarask"],
"bh": ["bho"],
"bjn": ["id"],
"bm": ["fr"],
"bpy": ["bn"],
"bqi": ["fa"],
"bug": ["id"],
"cbk-zam": ["es"],
"ce": ["ru"],
"crh": ["crh-latn"],
"crh-cyrl": ["ru"],
"csb": ["pl"],
"cv": ["ru"],
"de-at": ["de"],
"de-ch": ["de"],
"de-formal": ["de"],
"dsb": ["de"],
"dtp": ["ms"],
"egl": ["it"],
"eml": ["it"],
"ff": ["fr"],
"fit": ["fi"],
"fiu-vro": ["vro", "et"],
"frc": ["fr"],
"frp": ["fr"],
"frr": ["de"],
"fur": ["it"],
"gag": ["tr"],
"gan": ["gan-hant", "zh-hant", "zh-hans"],
"gan-hans": ["zh-hans"],
"gan-hant": ["zh-hant", "zh-hans"],
"gl": ["pt"],
"glk": ["fa"],
"gn": ["es"],
"gsw": ["de"],
"hif": ["hif-latn"],
"hsb": ["de"],
"ht": ["fr"],
"ii": ["zh-cn", "zh-hans"],
"inh": ["ru"],
"iu": ["ike-cans"],
"jut": ["da"],
"jv": ["id"],
"kaa": ["kk-latn", "kk-cyrl"],
"kbd": ["kbd-cyrl"],
"khw": ["ur"],
"kiu": ["tr"],
"kk": ["kk-cyrl"],
"kk-arab": ["kk-cyrl"],
"kk-latn": ["kk-cyrl"],
"kk-cn": ["kk-arab", "kk-cyrl"],
"kk-kz": ["kk-cyrl"],
"kk-tr": ["kk-latn", "kk-cyrl"],
"kl": ["da"],
"ko-kp": ["ko"],
"koi": ["ru"],
"krc": ["ru"],
"ks": ["ks-arab"],
"ksh": ["de"],
"ku": ["ku-latn"],
"ku-arab": ["ckb"],
"kv": ["ru"],
"lad": ["es"],
"lb": ["de"],
"lbe": ["ru"],
"lez": ["ru"],
"li": ["nl"],
"lij": ["it"],
"liv": ["et"],
"lmo": ["it"],
"ln": ["fr"],
"ltg": ["lv"],
"lzz": ["tr"],
"mai": ["hi"],
"map-bms": ["jv", "id"],
"mg": ["fr"],
"mhr": ["ru"],
"min": ["id"],
"mo": ["ro"],
"mrj": ["ru"],
"mwl": ["pt"],
"myv": ["ru"],
"mzn": ["fa"],
"nah": ["es"],
"nap": ["it"],
"nds": ["de"],
"nds-nl": ["nl"],
"nl-informal": ["nl"],
"no": ["nb"],
"os": ["ru"],
"pcd": ["fr"],
"pdc": ["de"],
"pdt": ["de"],
"pfl": ["de"],
"pms": ["it"],
"pt": ["pt-br"],
"pt-br": ["pt"],
"qu": ["es"],
"qug": ["qu", "es"],
"rgn": ["it"],
"rmy": ["ro"],
"roa-rup": ["rup"],
"rue": ["uk", "ru"],
"ruq": ["ruq-latn", "ro"],
"ruq-cyrl": ["mk"],
"ruq-latn": ["ro"],
"sa": ["hi"],
"sah": ["ru"],
"scn": ["it"],
"sg": ["fr"],
"sgs": ["lt"],
"sli": ["de"],
"sr": ["sr-ec"],
"srn": ["nl"],
"stq": ["de"],
"su": ["id"],
"szl": ["pl"],
"tcy": ["kn"],
"tg": ["tg-cyrl"],
"tt": ["tt-cyrl", "ru"],
"tt-cyrl": ["ru"],
"ty": ["fr"],
"udm": ["ru"],
"ug": ["ug-arab"],
"uk": ["ru"],
"vec": ["it"],
"vep": ["et"],
"vls": ["nl"],
"vmf": ["de"],
"vot": ["fi"],
"vro": ["et"],
"wa": ["fr"],
"wo": ["fr"],
"wuu": ["zh-hans"],
"xal": ["ru"],
"xmf": ["ka"],
"yi": ["he"],
"za": ["zh-hans"],
"zea": ["nl"],
"zh": ["zh-hans"],
"zh-classical": ["lzh"],
"zh-cn": ["zh-hans"],
"zh-hant": ["zh-hans"],
"zh-hk": ["zh-hant", "zh-hans"],
"zh-min-nan": ["nan"],
"zh-mo": ["zh-hk", "zh-hant", "zh-hans"],
"zh-my": ["zh-sg", "zh-hans"],
"zh-sg": ["zh-hans"],
"zh-tw": ["zh-hant", "zh-hans"],
"zh-yue": ["yue"]
};
}( jQuery ) );
/**
* jQuery Internationalization library
*
* Copyright (C) 2012 Santhosh Thottingal
*
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
* anything special to choose one license or the other and you don't have to
* notify anyone which license you are using. You are free to use
* UniversalLanguageSelector in commercial projects as long as the copyright
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
*
* @licence GNU General Public Licence 2.0 or later
* @licence MIT License
*/
( function ( $ ) {
"use strict";
var MessageParser = function ( options ) {
this.options = $.extend( {}, $.i18n.parser.defaults, options );
this.language = $.i18n.languages[$.i18n().locale];
this.emitter = $.i18n.parser.emitter;
};
MessageParser.prototype = {
constructor: MessageParser,
simpleParse: function ( message, parameters ) {
return message.replace( /\$(\d+)/g, function ( str, match ) {
var index = parseInt( match, 10 ) - 1;
return parameters[index] !== undefined ? parameters[index] : '$' + match;
} );
},
parse: function ( message, replacements ) {
if ( message.indexOf( '{{' ) < 0 ) {
return this.simpleParse( message, replacements );
}
this.emitter.language = $.i18n.languages[$.i18n().locale]
|| $.i18n.languages['default'];
return this.emitter.emit( this.ast( message ), replacements );
},
ast: function ( message ) {
var pos = 0;
// Try parsers until one works, if none work return null
function choice ( parserSyntax ) {
return function () {
for ( var i = 0; i < parserSyntax.length; i++) {
var result = parserSyntax[i]();
if ( result !== null ) {
return result;
}
}
return null;
};
}
// Try several parserSyntax-es in a row.
// All must succeed; otherwise, return null.
// This is the only eager one.
function sequence ( parserSyntax ) {
var originalPos = pos;
var result = [];
for ( var i = 0; i < parserSyntax.length; i++) {
var res = parserSyntax[i]();
if ( res === null ) {
pos = originalPos;
return null;
}
result.push( res );
}
return result;
}
// Run the same parser over and over until it fails.
// Must succeed a minimum of n times; otherwise, return null.
function nOrMore ( n, p ) {
return function () {
var originalPos = pos;
var result = [];
var parsed = p();
while (parsed !== null) {
result.push( parsed );
parsed = p();
}
if ( result.length < n ) {
pos = originalPos;
return null;
}
return result;
};
}
// Helpers -- just make parserSyntax out of simpler JS builtin types
function makeStringParser ( s ) {
var len = s.length;
return function () {
var result = null;
if ( message.substr( pos, len ) === s ) {
result = s;
pos += len;
}
return result;
};
}
function makeRegexParser ( regex ) {
return function () {
var matches = message.substr( pos ).match( regex );
if ( matches === null ) {
return null;
}
pos += matches[0].length;
return matches[0];
};
}
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]/ );
// There is a general pattern -- parse a thing, if that worked,
// apply transform, otherwise return null.
// But using this as a combinator seems to cause problems when
// combined with nOrMore().
// May be some scoping issue
function transform ( p, fn ) {
return function () {
var result = p();
return result === null ? null : fn( result );
};
}
// Used to define "literals" without spaces, in space-delimited
// situations
function literalWithoutSpace () {
var result = nOrMore( 1, escapedOrLiteralWithoutSpace )();
return result === null ? null : result.join( '' );
}
// Used to define "literals" within template parameters. The pipe
// character is the parameter delimeter, so by default
// it is not a literal in the parameter
function literalWithoutBar () {
var result = nOrMore( 1, escapedOrLiteralWithoutBar )();
return result === null ? null : result.join( '' );
}
function literal () {
var result = nOrMore( 1, escapedOrRegularLiteral )();
return result === null ? null : result.join( '' );
}
function escapedLiteral () {
var result = sequence( [ backslash, anyCharacter ] );
return result === null ? null : result[1];
}
var escapedOrLiteralWithoutSpace = choice( [ escapedLiteral, regularLiteralWithoutSpace ] );
var escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] );
var escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] );
function replacement () {
var result = sequence( [ dollar, digits ] );
if ( result === null ) {
return null;
}
return [ 'REPLACE', parseInt( result[1], 10 ) - 1 ];
}
var templateName = transform(
// see $wgLegalTitleChars
// not allowing : due to the need to catch "PLURAL:$1"
makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ),
function ( result ) {
return result.toString();
} );
function templateParam () {
var result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] );
if ( result === null ) {
return null;
}
var expr = result[1];
// use a "CONCAT" operator if there are multiple nodes,
// otherwise return the first node, raw.
return expr.length > 1 ? [ "CONCAT" ].concat( expr ) : expr[0];
}
function templateWithReplacement () {
var result = sequence( [ templateName, colon, replacement ] );
return result === null ? null : [ result[0], result[2] ];
}
function templateWithOutReplacement () {
var result = sequence( [ templateName, colon, paramExpression ] );
return result === null ? null : [ result[0], result[2] ];
}
var templateContents = choice( [
function () {
var res = sequence( [
// templates can have placeholders for dynamic
// replacement eg: {{PLURAL:$1|one car|$1 cars}}
// or no placeholders eg:
// {{GRAMMAR:genitive|{{SITENAME}}}
choice( [ templateWithReplacement, templateWithOutReplacement ] ),
nOrMore( 0, templateParam ) ] );
return res === null ? null : res[0].concat( res[1] );
}, function () {
var res = sequence( [ templateName, nOrMore( 0, templateParam ) ] );
if ( res === null ) {
return null;
}
return [ res[0] ].concat( res[1] );
} ] );
var openTemplate = makeStringParser( '{{' );
var closeTemplate = makeStringParser( '}}' );
function template () {
var result = sequence( [ openTemplate, templateContents, closeTemplate ] );
return result === null ? null : result[1];
}
var expression = choice( [ template, replacement, literal ] );
var paramExpression = choice( [ template, replacement, literalWithoutBar ] );
function start () {
var result = nOrMore( 0, expression )();
if ( result === null ) {
return null;
}
return [ "CONCAT" ].concat( result );
}
var result = start();
return result;
}
};
$.extend( $.i18n.parser, new MessageParser() );
}( jQuery ) );
/**
* jQuery Internationalization library
*
* Copyright (C) 2012 Santhosh Thottingal
*
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
* anything special to choose one license or the other and you don't have to
* notify anyone which license you are using. You are free to use
* UniversalLanguageSelector in commercial projects as long as the copyright
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
*
* @licence GNU General Public Licence 2.0 or later
* @licence MIT License
*/
( function ( $ ) {
"use strict";
var MessageParserEmitter = function () {
this.language = $.i18n.languages[$.i18n().locale] || $.i18n.languages['default'];
};
MessageParserEmitter.prototype = {
constructor: MessageParserEmitter,
/**
* (We put this method definition here, and not in prototype, to make
* sure it's not overwritten by any magic.) Walk entire node structure,
* applying replacements and template functions when appropriate
*
* @param {Mixed}
* abstract syntax tree (top node or subnode)
* @param {Array}
* replacements for $1, $2, ... $n
* @return {Mixed} single-string node or array of nodes suitable for
* jQuery appending
*/
emit: function ( node, replacements ) {
var ret = null;
var that = this;
switch (typeof node) {
case 'string':
case 'number':
ret = node;
break;
case 'object':
// node is an array of nodes
var subnodes = $.map( node.slice( 1 ), function ( n ) {
return that.emit( n, replacements );
} );
var operation = node[0].toLowerCase();
if ( typeof that[operation] === 'function' ) {
ret = that[operation]( subnodes, replacements );
} else {
throw new Error( 'unknown operation "' + operation + '"' );
}
break;
case 'undefined':
// Parsing the empty string (as an entire expression, or as a
// paramExpression in a template) results in undefined
// Perhaps a more clever parser can detect this, and return the
// empty string? Or is that useful information?
// The logical thing is probably to return the empty string here
// when we encounter undefined.
ret = '';
break;
default:
throw new Error( 'unexpected type in AST: ' + typeof node );
}
return ret;
},
/**
* Parsing has been applied depth-first we can assume that all nodes
* here are single nodes Must return a single node to parents -- a
* jQuery with synthetic span However, unwrap any other synthetic spans
* in our children and pass them upwards
*
* @param {Array}
* nodes - mixed, some single nodes, some arrays of nodes
* @return String
*/
concat: function ( nodes ) {
var result = "";
$.each( nodes, function ( i, node ) {
// strings, integers, anything else
result += node;
} );
return result;
},
/**
* Return escaped replacement of correct index, or string if
* unavailable. Note that we expect the parsed parameter to be
* zero-based. i.e. $1 should have become [ 0 ]. if the specified
* parameter is not found return the same string (e.g. "$99" ->
* parameter 98 -> not found -> return "$99" ) TODO throw error if
* nodes.length > 1 ?
*
* @param {Array}
* of one element, integer, n >= 0
* @return {String} replacement
*/
replace: function ( nodes, replacements ) {
var index = parseInt( nodes[0], 10 );
if ( index < replacements.length ) {
// replacement is not a string, don't touch!
return replacements[index];
} else {
// index not found, fallback to displaying variable
return '$' + ( index + 1 );
}
},
/**
* Transform parsed structure into pluralization n.b. The first node may
* be a non-integer (for instance, a string representing an Arabic
* number). So convert it back with the current language's
* convertNumber.
*
* @param {Array}
* of nodes, [ {String|Number}, {String}, {String} ... ]
* @return {String} selected pluralized form according to current
* language
*/
plural: function ( nodes ) {
var count = parseFloat( this.language.convertNumber( nodes[0], 10 ) );
var forms = nodes.slice( 1 );
return forms.length ? this.language.convertPlural( count, forms ) : '';
},
/**
* Transform parsed structure into gender Usage
* {{gender:gender|masculine|feminine|neutral}}.
*
* @param {Array}
* of nodes, [ {String}, {String}, {String} , {String} ]
* @return {String} selected gender form according to current language
*/
gender: function ( nodes ) {
var gender = nodes[0];
var forms = nodes.slice( 1 );
return this.language.gender( gender, forms );
},
/**
* Transform parsed structure into grammar conversion. Invoked by
* putting {{grammar:form|word}} in a message
*
* @param {Array}
* of nodes [{Grammar case eg: genitive}, {String word}]
* @return {String} selected grammatical form according to current
* language
*/
grammar: function ( nodes ) {
var form = nodes[0];
var word = nodes[1];
return word && form && this.language.convertGrammar( word, form );
}
};
$.extend( $.i18n.parser.emitter, new MessageParserEmitter() );
}( jQuery ) );
/* global pluralRuleParser */
( function ( $ ) {
"use strict";
var language = {
// CLDR plural rules generated using
// http://i18ndata.appspot.com/cldr/tags/unconfirmed/supplemental/plurals?action=browse&depth=-1
// and compressed
pluralRules: {
gv: {
one: "n mod 10 in 1..2 or n mod 20 is 0"
},
gu: {
one: "n is 1"
},
rof: {
one: "n is 1"
},
ga: {
few: "n in 3..6",
many: "n in 7..10",
two: "n is 2",
one: "n is 1"
},
gl: {
one: "n is 1"
},
lg: {
one: "n is 1"
},
lb: {
one: "n is 1"
},
xog: {
one: "n is 1"
},
ln: {
one: "n in 0..1"
},
lo: "",
brx: {
one: "n is 1"
},
tr: "",
ts: {
one: "n is 1"
},
tn: {
one: "n is 1"
},
to: "",
lt: {
few: "n mod 10 in 2..9 and n mod 100 not in 11..19",
one: "n mod 10 is 1 and n mod 100 not in 11..19"
},
tk: {
one: "n is 1"
},
th: "",
ksb: {
one: "n is 1"
},
te: {
one: "n is 1"
},
ksh: {
zero: "n is 0",
one: "n is 1"
},
fil: {
one: "n in 0..1"
},
haw: {
one: "n is 1"
},
kcg: {
one: "n is 1"
},
ssy: {
one: "n is 1"
},
yo: "",
de: {
one: "n is 1"
},
ko: "",
da: {
one: "n is 1"
},
dz: "",
dv: {
one: "n is 1"
},
guw: {
one: "n in 0..1"
},
shi: {
few: "n in 2..10",
one: "n within 0..1"
},
el: {
one: "n is 1"
},
eo: {
one: "n is 1"
},
en: {
one: "n is 1"
},
ses: "",
teo: {
one: "n is 1"
},
ee: {
one: "n is 1"
},
kde: "",
fr: {
one: "n within 0..2 and n is not 2"
},
eu: {
one: "n is 1"
},
et: {
one: "n is 1"
},
es: {
one: "n is 1"
},
seh: {
one: "n is 1"
},
ru: {
few: "n mod 10 in 2..4 and n mod 100 not in 12..14",
many: "n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14",
one: "n mod 10 is 1 and n mod 100 is not 11"
},
kl: {
one: "n is 1"
},
sms: {
two: "n is 2",
one: "n is 1"
},
smn: {
two: "n is 2",
one: "n is 1"
},
smj: {
two: "n is 2",
one: "n is 1"
},
smi: {
two: "n is 2",
one: "n is 1"
},
fy: {
one: "n is 1"
},
rm: {
one: "n is 1"
},
ro: {
few: "n is 0 OR n is not 1 AND n mod 100 in 1..19",
one: "n is 1"
},
bn: {
one: "n is 1"
},
sma: {
two: "n is 2",
one: "n is 1"
},
be: {
few: "n mod 10 in 2..4 and n mod 100 not in 12..14",
many: "n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14",
one: "n mod 10 is 1 and n mod 100 is not 11"
},
bg: {
one: "n is 1"
},
ms: "",
wa: {
one: "n in 0..1"
},
ps: {
one: "n is 1"
},
wo: "",
bm: "",
jv: "",
bo: "",
bh: {
one: "n in 0..1"
},
kea: "",
asa: {
one: "n is 1"
},
cgg: {
one: "n is 1"
},
br: {
few: "n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99",
many: "n mod 1000000 is 0 and n is not 0",
two: "n mod 10 is 2 and n mod 100 not in 12,72,92",
one: "n mod 10 is 1 and n mod 100 not in 11,71,91"
},
bs: {
few: "n mod 10 in 2..4 and n mod 100 not in 12..14",
many: "n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14",
one: "n mod 10 is 1 and n mod 100 is not 11"
},
ja: "",
om: {
one: "n is 1"
},
fa: "",
vun: {
one: "n is 1"
},
or: {
one: "n is 1"
},
xh: {
one: "n is 1"
},
nso: {
one: "n in 0..1"
},
ca: {
one: "n is 1"
},
cy: {
few: "n is 3",
zero: "n is 0",
many: "n is 6",
two: "n is 2",
one: "n is 1"
},
cs: {
few: "n in 2..4",
one: "n is 1"
},
zh: "",
lv: {
zero: "n is 0",
one: "n mod 10 is 1 and n mod 100 is not 11"
},
pt: {
one: "n is 1"
},
wae: {
one: "n is 1"
},
tl: {
one: "n in 0..1"
},
chr: {
one: "n is 1"
},
pa: {
one: "n is 1"
},
ak: {
one: "n in 0..1"
},
pl: {
few: "n mod 10 in 2..4 and n mod 100 not in 12..14",
many: "n is not 1 and n mod 10 in 0..1 or n mod 10 in 5..9 or n mod 100 in 12..14",
one: "n is 1"
},
hr: {
few: "n mod 10 in 2..4 and n mod 100 not in 12..14",
many: "n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14",
one: "n mod 10 is 1 and n mod 100 is not 11"
},
am: {
one: "n in 0..1"
},
ti: {
one: "n in 0..1"
},
hu: "",
hi: {
one: "n in 0..1"
},
jmc: {
one: "n is 1"
},
ha: {
one: "n is 1"
},
he: {
one: "n is 1"
},
mg: {
one: "n in 0..1"
},
fur: {
one: "n is 1"
},
bem: {
one: "n is 1"
},
ml: {
one: "n is 1"
},
mo: {
few: "n is 0 OR n is not 1 AND n mod 100 in 1..19",
one: "n is 1"
},
mn: {
one: "n is 1"
},
mk: {
one: "n mod 10 is 1 and n is not 11"
},
ur: {
one: "n is 1"
},
bez: {
one: "n is 1"
},
mt: {
few: "n is 0 or n mod 100 in 2..10",
many: "n mod 100 in 11..19",
one: "n is 1"
},
uk: {
few: "n mod 10 in 2..4 and n mod 100 not in 12..14",
many: "n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14",
one: "n mod 10 is 1 and n mod 100 is not 11"
},
mr: {
one: "n is 1"
},
ta: {
one: "n is 1"
},
my: "",
sah: "",
ve: {
one: "n is 1"
},
af: {
one: "n is 1"
},
vi: "",
is: {
one: "n is 1"
},
iu: {
two: "n is 2",
one: "n is 1"
},
it: {
one: "n is 1"
},
kn: "",
ii: "",
ar: {
few: "n mod 100 in 3..10",
zero: "n is 0",
many: "n mod 100 in 11..99",
two: "n is 2",
one: "n is 1"
},
zu: {
one: "n is 1"
},
saq: {
one: "n is 1"
},
az: "",
tzm: {
one: "n in 0..1 or n in 11..99"
},
id: "",
ig: "",
pap: {
one: "n is 1"
},
nl: {
one: "n is 1"
},
nn: {
one: "n is 1"
},
no: {
one: "n is 1"
},
nah: {
one: "n is 1"
},
nd: {
one: "n is 1"
},
ne: {
one: "n is 1"
},
ny: {
one: "n is 1"
},
naq: {
two: "n is 2",
one: "n is 1"
},
nyn: {
one: "n is 1"
},
kw: {
two: "n is 2",
one: "n is 1"
},
nr: {
one: "n is 1"
},
tig: {
one: "n is 1"
},
kab: {
one: "n within 0..2 and n is not 2"
},
mas: {
one: "n is 1"
},
rwk: {
one: "n is 1"
},
kaj: {
one: "n is 1"
},
lag: {
zero: "n is 0",
one: "n within 0..2 and n is not 0 and n is not 2"
},
syr: {
one: "n is 1"
},
kk: {
one: "n is 1"
},
ff: {
one: "n within 0..2 and n is not 2"
},
fi: {
one: "n is 1"
},
fo: {
one: "n is 1"
},
ka: "",
gsw: {
one: "n is 1"
},
ckb: {
one: "n is 1"
},
ss: {
one: "n is 1"
},
sr: {
few: "n mod 10 in 2..4 and n mod 100 not in 12..14",
many: "n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14",
one: "n mod 10 is 1 and n mod 100 is not 11"
},
sq: {
one: "n is 1"
},
sw: {
one: "n is 1"
},
sv: {
one: "n is 1"
},
km: "",
st: {
one: "n is 1"
},
sk: {
few: "n in 2..4",
one: "n is 1"
},
sh: {
few: "n mod 10 in 2..4 and n mod 100 not in 12..14",
many: "n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14",
one: "n mod 10 is 1 and n mod 100 is not 11"
},
so: {
one: "n is 1"
},
sn: {
one: "n is 1"
},
ku: {
one: "n is 1"
},
sl: {
few: "n mod 100 in 3..4",
two: "n mod 100 is 2",
one: "n mod 100 is 1"
},
sg: "",
nb: {
one: "n is 1"
},
se: {
two: "n is 2",
one: "n is 1"
}
},
/**
* Plural form transformations, needed for some languages.
*
* @param count
* integer Non-localized quantifier
* @param forms
* array List of plural forms
* @return string Correct form for quantifier in this language
*/
convertPlural: function ( count, forms ) {
var pluralFormIndex = 0;
if ( !forms || forms.length === 0 ) {
return '';
}
var pluralRules = this.pluralRules[$.i18n().locale];
if ( !pluralRules ) {
// default fallback.
return ( count === 1 ) ? forms[0] : forms[1];
}
pluralFormIndex = this.getPluralForm( count, pluralRules );
pluralFormIndex = Math.min( pluralFormIndex, forms.length - 1 );
return forms[pluralFormIndex];
},
/**
* For the number, get the plural for index
*
* @param number
* @param pluralRules
* @return plural form index
*/
getPluralForm: function ( number, pluralRules ) {
var pluralForms = [ 'zero', 'one', 'two', 'few', 'many', 'other' ];
var pluralFormIndex = 0;
for ( var i = 0; i < pluralForms.length; i++) {
if ( pluralRules[pluralForms[i]] ) {
if ( pluralRuleParser( pluralRules[pluralForms[i]], number ) ) {
return pluralFormIndex;
}
pluralFormIndex++;
}
}
return pluralFormIndex;
},
/**
* Converts a number using digitTransformTable.
*
* @param {num}
* number Value to be converted
* @param {boolean}
* integer Convert the return value to an integer
*/
'convertNumber': function ( num, integer ) {
// Set the target Transform table:
var transformTable = this.digitTransformTable( $.i18n().locale ), numberString = ''
+ num, convertedNumber = '';
if ( !transformTable ) {
return num;
}
// Check if the "restore" to Latin number flag is set:
if ( integer ) {
if ( parseFloat( num, 10 ) === num ) {
return num;
}
var tmp = [];
for ( var item in transformTable) {
tmp[transformTable[item]] = item;
}
transformTable = tmp;
}
for ( var i = 0; i < numberString.length; i++) {
if ( transformTable[numberString[i]] ) {
convertedNumber += transformTable[numberString[i]];
} else {
convertedNumber += numberString[i];
}
}
return integer ? parseFloat( convertedNumber, 10 ) : convertedNumber;
},
/**
* Grammatical transformations, needed for inflected languages.
* Invoked by putting {{grammar:form|word}} in a message.
* forms can be computed dynamically by overriding this method per language
*
* @param word {String}
* @param form {String}
* @return {String}
*/
convertGrammar: function ( word, form ) {
return word + form;
},
/**
* Provides an alternative text depending on specified gender. Usage
* {{gender:[gender|user object]|masculine|feminine|neutral}}. If second
* or third parameter are not specified, masculine is used.
*
* These details may be overriden per language.
*
* @param gender
* string male, female, or anything else for neutral.
* @param forms
* array List of gender forms
*
* @return string
*/
'gender': function ( gender, forms ) {
if ( !forms || forms.length === 0 ) {
return '';
}
while (forms.length < 2) {
forms.push( forms[forms.length - 1] );
}
if ( gender === 'male' ) {
return forms[0];
}
if ( gender === 'female' ) {
return forms[1];
}
return ( forms.length === 3 ) ? forms[2] : forms[0];
},
/**
* Get the digit transform table for the given language
* See http://cldr.unicode.org/translation/numbering-systems
* @param language
* @returns Array of digits in the passed language representation
*/
digitTransformTable: function ( language ) {
var tables = {
ar: "۰۱۲۳۴۵۶۷۸۹",
ml: "൦൧൨൩൪൫൬൭൮൯",
kn: "೦೧೨೩೪೫೬೭೮೯",
lo: "໐໑໒໓໔໕໖໗໘໙",
or: "୦୧୨୩୪୫୬୭୮୯",
kh: "០១២៣៤៥៦៧៨៩",
pa: "੦੧੨੩੪੫੬੭੮੯",
gu: "૦૧૨૩૪૫૬૭૮૯",
hi: "०१२३४५६७८९",
my: "၀၁၂၃၄၅၆၇၈၉",
ta: "௦௧௨௩௪௫௬௭௮௯",
te: "౦౧౨౩౪౫౬౭౮౯",
th: "๐๑๒๓๔๕๖๗๘๙", //FIXME use iso 639 codes
bo: "༠༡༢༣༤༥༦༧༨༩" //FIXME use iso 639 codes
};
if ( !tables[language] ) {
return null;
}
return tables[language].split( "" );
}
};
$.extend( $.i18n.languages, {
'default': language
} );
}( jQuery ) );
/**
* Bosnian (bosanski) language functions
*/
( function ( $ ) {
"use strict";
var bosanski = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch (form) {
case 'instrumental': // instrumental
word = 's ' + word;
break;
case 'lokativ': // locative
word = 'o ' + word;
break;
}
return word;
}
} );
$.extend( $.i18n.languages, {
'bs': bosanski
} );
}( jQuery ) );
/**
* Lower Sorbian (Dolnoserbski) language functions
*/
( function ( $ ) {
"use strict";
var dsb = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch (form) {
case 'instrumental': // instrumental
word = 'z ' + word;
break;
case 'lokatiw': // lokatiw
word = 'wo ' + word;
break;
}
return word;
}
} );
$.extend( $.i18n.languages, {
'dsb': dsb
} );
}( jQuery ) );
/**
* Finnish (Suomi) language functions
*
* @author Santhosh Thottingal
*/
( function ( $ ) {
"use strict";
var finnish = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
// vowel harmony flag
var aou = word.match( /[aou][^äöy]*$/i );
var origWord = word;
if ( word.match( /wiki$/i ) ) {
aou = false;
}
// append i after final consonant
if ( word.match( /[bcdfghjklmnpqrstvwxz]$/i ) ) {
word += 'i';
}
switch (form) {
case 'genitive':
word += 'n';
break;
case 'elative':
word += ( aou ? 'sta' : 'stä' );
break;
case 'partitive':
word += ( aou ? 'a' : 'ä' );
break;
case 'illative':
// Double the last letter and add 'n'
word += word.substr( word.length - 1 ) + 'n';
break;
case 'inessive':
word += ( aou ? 'ssa' : 'ssä' );
break;
default:
word = origWord;
break;
}
return word;
}
} );
$.extend( $.i18n.languages, {
'fi': finnish
} );
}( jQuery ) );
/**
* Irish (Gaeilge) language functions
*/
( function ( $ ) {
"use strict";
var ga = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
if ( form === 'ainmlae' ) {
switch (word) {
case 'an Domhnach':
word = 'Dé Domhnaigh';
break;
case 'an Luan':
word = 'Dé Luain';
break;
case 'an Mháirt':
word = 'Dé Mháirt';
break;
case 'an Chéadaoin':
word = 'Dé Chéadaoin';
break;
case 'an Déardaoin':
word = 'Déardaoin';
break;
case 'an Aoine':
word = 'Dé hAoine';
break;
case 'an Satharn':
word = 'Dé Sathairn';
break;
}
}
return word;
}
} );
$.extend( $.i18n.languages, {
'ga': ga
} );
}( jQuery ) );
/**
* Hebrew (עברית) language functions
*/
( function ( $ ) {
"use strict";
var hebrew = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch (form) {
case 'prefixed':
case 'תחילית': // the same word in Hebrew
// Duplicate prefixed "Waw", but only if it's not already double
if ( word.substr( 0, 1 ) === "ו" && word.substr( 0, 2 ) !== "וו" ) {
word = "ו" + word;
}
// Remove the "He" if prefixed
if ( word.substr( 0, 1 ) === "ה" ) {
word = word.substr( 1, word.length );
}
// Add a hyphen (maqaf) before numbers and non-Hebrew letters
if ( word.substr( 0, 1 ) < "א" || word.substr( 0, 1 ) > "ת" ) {
word = "־" + word;
}
}
return word;
}
} );
$.extend( $.i18n.languages, {
'he': hebrew
} );
}( jQuery ) );
/**
* Upper Sorbian (Hornjoserbsce) language functions
*/
( function ( $ ) {
"use strict";
var hsb = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch (form) {
case 'instrumental': // instrumental
word = 'z ' + word;
break;
case 'lokatiw': // lokatiw
word = 'wo ' + word;
break;
}
return word;
}
} );
$.extend( $.i18n.languages, {
'hsb': hsb
} );
}( jQuery ) );
/**
* Hungarian language functions
*
* @author Santhosh Thottingal
*/
( function ( $ ) {
"use strict";
var hu = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch (form) {
case 'rol':
word += 'ról';
break;
case 'ba':
word += 'ba';
break;
case 'k':
word += 'k';
break;
}
return word;
}
} );
$.extend( $.i18n.languages, {
'hu': hu
} );
}( jQuery ) );
/**
* Armenian (Հայերեն) language functions
*/
( function ( $ ) {
"use strict";
var hy = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
if ( form === 'genitive' ) {// սեռական հոլով
if ( word.substr( -1 ) === 'ա' ) {
word = word.substr( 0, word.length - 1 ) + 'այի';
} else if ( word.substr( -1 ) === 'ո' ) {
word = word.substr( 0, word.length - 1 ) + 'ոյի';
} else if ( word.substr( -4 ) === 'գիրք' ) {
word = word.substr( 0, word.length - 4 ) + 'գրքի';
} else {
word = word + 'ի';
}
}
return word;
}
} );
$.extend( $.i18n.languages, {
'hy': hy
} );
}( jQuery ) );
/**
* Latin (lingua Latina) language functions
*
* @author Santhosh Thottingal
*/
( function ( $ ) {
"use strict";
var la = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch (form) {
case 'genitive':
// only a few declensions, and even for those mostly the singular only
word = word.replace( /u[ms]$/i, 'i' ); // 2nd declension singular
word = word.replace( /ommunia$/i, 'ommunium' ); // 3rd declension neuter plural (partly)
word = word.replace( /a$/i, 'ae' ); // 1st declension singular
word = word.replace( /libri$/i, 'librorum' ); // 2nd declension plural (partly)
word = word.replace( /nuntii$/i, 'nuntiorum' ); // 2nd declension plural (partly)
word = word.replace( /tio$/i, 'tionis' ); // 3rd declension singular (partly)
word = word.replace( /ns$/i, 'ntis' );
word = word.replace( /as$/i, 'atis' );
word = word.replace( /es$/i, 'ei' ); // 5th declension singular
break;
case 'accusative':
// only a few declensions, and even for those mostly the singular only
word = word.replace( /u[ms]$/i, 'um' ); // 2nd declension singular
word = word.replace( /ommunia$/i, 'am' ); // 3rd declension neuter plural (partly)
word = word.replace( /a$/i, 'ommunia' ); // 1st declension singular
word = word.replace( /libri$/i, 'libros' ); // 2nd declension plural (partly)
word = word.replace( /nuntii$/i, 'nuntios' );// 2nd declension plural (partly)
word = word.replace( /tio$/i, 'tionem' ); // 3rd declension singular (partly)
word = word.replace( /ns$/i, 'ntem' );
word = word.replace( /as$/i, 'atem' );
word = word.replace( /es$/i, 'em' ); // 5th declension singular
break;
case 'ablative':
// only a few declensions, and even for those mostly the singular only
word = word.replace( /u[ms]$/i, 'o' ); // 2nd declension singular
word = word.replace( /ommunia$/i, 'ommunibus' ); // 3rd declension neuter plural (partly)
word = word.replace( /a$/i, 'a' ); // 1st declension singular
word = word.replace( /libri$/i, 'libris' ); // 2nd declension plural (partly)
word = word.replace( /nuntii$/i, 'nuntiis' ); // 2nd declension plural (partly)
word = word.replace( /tio$/i, 'tione' ); // 3rd declension singular (partly)
word = word.replace( /ns$/i, 'nte' );
word = word.replace( /as$/i, 'ate' );
word = word.replace( /es$/i, 'e' ); // 5th declension singular
break;
}
return word;
}
} );
$.extend( $.i18n.languages, {
'la': la
} );
}( jQuery ) );
/**
* Ossetian (Ирон) language functions
*
* @author Santhosh Thottingal
*/
( function ( $ ) {
"use strict";
var os = $.extend( {}, $.i18n.languages['default'],
{
convertGrammar: function ( word, form ) {
// Ending for allative case
var end_allative = 'мæ';
// Variable for 'j' beetwen vowels
var jot = '';
// Variable for "-" for not Ossetic words
var hyphen = '';
// Variable for ending
var ending = '';
// Checking if the $word is in plural form
if ( word.match( /тæ$/i ) ) {
word = word.substring( 0, word.length - 1 );
end_allative = 'æм';
}
// Works if word is in singular form.
// Checking if word ends on one of the vowels: е, ё, и, о, ы, э, ю,
// я.
else if ( word.match( /[аæеёиоыэюя]$/i ) ) {
jot = 'й';
}
// Checking if word ends on 'у'. 'У' can be either consonant 'W' or
// vowel 'U' in cyrillic Ossetic.
// Examples: {{grammar:genitive|аунеу}} = аунеуы,
// {{grammar:genitive|лæппу}} = лæппуйы.
else if ( word.match( /у$/i ) ) {
if ( !word.substring( word.length - 2, word.length - 1 ).match(
/[аæеёиоыэюя]$/i ) ) {
jot = 'й';
}
} else if ( !word.match( /[бвгджзйклмнопрстфхцчшщьъ]$/i ) ) {
hyphen = '-';
}
switch (form) {
case 'genitive':
ending = hyphen + jot + 'ы';
break;
case 'dative':
ending = hyphen + jot + 'æн';
break;
case 'allative':
ending = hyphen + end_allative;
break;
case 'ablative':
if ( jot === 'й' ) {
ending = hyphen + jot + 'æ';
} else {
ending = hyphen + jot + 'æй';
}
break;
case 'superessive':
ending = hyphen + jot + 'ыл';
break;
case 'equative':
ending = hyphen + jot + 'ау';
break;
case 'comitative':
ending = hyphen + 'имæ';
break;
}
return word + ending;
}
} );
$.extend( $.i18n.languages, {
'os': os
} );
}( jQuery ) );
/**
* Russian (Русский) language functions
*/
( function ( $ ) {
"use strict";
var ru = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
if ( form === 'genitive' ) { // родительный падеж
if ( ( word.substr( word.length - 4 ) === 'вики' )
|| ( word.substr( word.length - 4 ) === 'Вики' ) ) {
} else if ( word.substr( word.length - 1 ) === 'ь' ) {
word = word.substr( 0, word.length - 1 ) + 'я';
} else if ( word.substr( word.length - 2 ) === 'ия' ) {
word = word.substr( 0, word.length - 2 ) + 'ии';
} else if ( word.substr( word.length - 2 ) === 'ка' ) {
word = word.substr( 0, word.length - 2 ) + 'ки';
} else if ( word.substr( word.length - 2 ) === 'ти' ) {
word = word.substr( 0, word.length - 2 ) + 'тей';
} else if ( word.substr( word.length - 2 ) === 'ды' ) {
word = word.substr( 0, word.length - 2 ) + 'дов';
} else if ( word.substr( word.length - 3 ) === 'ник' ) {
word = word.substr( 0, word.length - 3 ) + 'ника';
}
}
return word;
}
} );
$.extend( $.i18n.languages, {
'ru': ru
} );
}( jQuery ) );
/**
* Slovenian (Slovenščina) language functions
*/
( function ( $ ) {
"use strict";
var sl = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch (form) {
case 'mestnik': // locative
word = 'o ' + word;
break;
case 'orodnik': // instrumental
word = 'z ' + word;
break;
}
return word;
}
} );
$.extend( $.i18n.languages, {
'sl': sl
} );
}( jQuery ) );
/**
* Ukrainian (Українська) language functions
*/
( function ( $ ) {
"use strict";
var uk = $.extend( {}, $.i18n.languages['default'], {
convertGrammar: function ( word, form ) {
switch (form) {
case 'genitive': // родовий відмінок
if ( ( word.substr( word.length - 4 ) === 'вікі' )
|| ( word.substr( word.length - 4 ) === 'Вікі' ) ) {
} else if ( word.substr( word.length - 1 ) === 'ь' ) {
word = word.substr( 0, word.length - 1 ) + 'я';
} else if ( word.substr( word.length - 2 ) === 'ія' ) {
word = word.substr( 0, word.length - 2 ) + 'ії';
} else if ( word.substr( word.length - 2 ) === 'ка' ) {
word = word.substr( 0, word.length - 2 ) + 'ки';
} else if ( word.substr( word.length - 2 ) === 'ти' ) {
word = word.substr( 0, word.length - 2 ) + 'тей';
} else if ( word.substr( word.length - 2 ) === 'ды' ) {
word = word.substr( 0, word.length - 2 ) + 'дов';
} else if ( word.substr( word.length - 3 ) === 'ник' ) {
word = word.substr( 0, word.length - 3 ) + 'ника';
}
break;
case 'accusative': // знахідний відмінок
if ( ( word.substr( word.length - 4 ) === 'вікі' )
|| ( word.substr( word.length - 4 ) === 'Вікі' ) ) {
} else if ( word.substr( word.length - 2 ) === 'ія' ) {
word = word.substr( 0, word.length - 2 ) + 'ію';
}
break;
}
return word;
}
} );
$.extend( $.i18n.languages, {
'uk': uk
} );
}( jQuery ) );
/**
* cldrpluralparser.js
* A parser engine for CLDR plural rules.
*
* Copyright 2012 GPLV3+, Santhosh Thottingal
*
* @version 0.1.0-alpha
* @source https://github.com/santhoshtr/CLDRPluralRuleParser
* @author Santhosh Thottingal
* @author Timo Tijhof
* @author Amir Aharoni
*/
/**
* Evaluates a plural rule in CLDR syntax for a number
* @param rule
* @param number
* @return true|false|null
*/
function pluralRuleParser(rule, number) {
/*
Syntax: see http://unicode.org/reports/tr35/#Language_Plural_Rules
-----------------------------------------------------------------
condition = and_condition ('or' and_condition)*
and_condition = relation ('and' relation)*
relation = is_relation | in_relation | within_relation | 'n'
is_relation = expr 'is' ('not')? value
in_relation = expr ('not')? 'in' range_list
within_relation = expr ('not')? 'within' range_list
expr = 'n' ('mod' value)?
range_list = (range | value) (',' range_list)*
value = digit+
digit = 0|1|2|3|4|5|6|7|8|9
range = value'..'value
*/
// Indicates current position in the rule as we parse through it.
// Shared among all parsing functions below.
var pos = 0;
var whitespace = makeRegexParser(/^\s+/);
var digits = makeRegexParser(/^\d+/);
var _n_ = makeStringParser('n');
var _is_ = makeStringParser('is');
var _mod_ = makeStringParser('mod');
var _not_ = makeStringParser('not');
var _in_ = makeStringParser('in');
var _within_ = makeStringParser('within');
var _range_ = makeStringParser('..');
var _comma_ = makeStringParser(',');
var _or_ = makeStringParser('or');
var _and_ = makeStringParser('and');
function debug() {
/* console.log.apply(console, arguments);*/
}
debug('pluralRuleParser', rule, number);
// Try parsers until one works, if none work return null
function choice(parserSyntax) {
return function () {
for (var i = 0; i < parserSyntax.length; i++) {
var result = parserSyntax[i]();
if (result !== null) {
return result;
}
}
return null;
};
}
// Try several parserSyntax-es in a row.
// All must succeed; otherwise, return null.
// This is the only eager one.
function sequence(parserSyntax) {
var originalPos = pos;
var result = [];
for (var i = 0; i < parserSyntax.length; i++) {
var res = parserSyntax[i]();
if (res === null) {
pos = originalPos;
return null;
}
result.push(res);
}
return result;
}
// Run the same parser over and over until it fails.
// Must succeed a minimum of n times; otherwise, return null.
function nOrMore(n, p) {
return function () {
var originalPos = pos;
var result = [];
var parsed = p();
while (parsed !== null) {
result.push(parsed);
parsed = p();
}
if (result.length < n) {
pos = originalPos;
return null;
}
return result;
};
}
// Helpers -- just make parserSyntax out of simpler JS builtin types
function makeStringParser(s) {
var len = s.length;
return function () {
var result = null;
if (rule.substr(pos, len) === s) {
result = s;
pos += len;
}
return result;
};
}
function makeRegexParser(regex) {
return function () {
var matches = rule.substr(pos).match(regex);
if (matches === null) {
return null;
}
pos += matches[0].length;
return matches[0];
};
}
function n() {
var result = _n_();
if (result === null) {
debug(" -- failed n");
return result;
}
result = parseInt(number, 10);
debug(" -- passed n ", result);
return result;
}
var expression = choice([mod, n]);
function mod() {
var result = sequence([n, whitespace, _mod_, whitespace, digits]);
if (result === null) {
debug(" -- failed mod");
return null;
}
debug(" -- passed mod");
return parseInt(result[0], 10) % parseInt(result[4], 10);
}
function not() {
var result = sequence([whitespace, _not_]);
if (result === null) {
debug(" -- failed not");
return null;
} else {
return result[1];
}
}
function is() {
var result = sequence([expression, whitespace, _is_, nOrMore(0, not), whitespace, digits]);
if (result !== null) {
debug(" -- passed is");
if (result[3][0] === 'not') {
return result[0] !== parseInt(result[5], 10);
} else {
return result[0] === parseInt(result[5], 10);
}
}
debug(" -- failed is");
return null;
}
function rangeList() {
// range_list = (range | value) (',' range_list)*
var result = sequence([choice([range, digits]), nOrMore(0, rangeTail)]);
var resultList = [];
if (result !== null) {
resultList = resultList.concat(result[0], result[1][0]);
return resultList;
}
debug(" -- failed rangeList");
return null;
}
function rangeTail() {
// ',' range_list
var result = sequence([_comma_, rangeList]);
if (result !== null) {
return result[1];
}
debug(" -- failed rangeTail");
return null;
}
function range() {
var result = sequence([digits, _range_, digits]);
if (result !== null) {
debug(" -- passed range");
var array = [];
var left = parseInt(result[0], 10);
var right = parseInt(result[2], 10);
for ( i = left; i <= right; i++) {
array.push(i);
}
return array;
}
debug(" -- failed range");
return null;
}
function _in() {
// in_relation = expr ('not')? 'in' range_list
var result = sequence([expression, nOrMore(0, not), whitespace, _in_, whitespace, rangeList]);
if (result !== null) {
debug(" -- passed _in");
var range_list = result[5];
for (var i = 0; i < range_list.length; i++) {
if (parseInt(range_list[i], 10) === result[0]) {
return (result[1][0] !== 'not');
}
}
return (result[1][0] === 'not');
}
debug(" -- failed _in ");
return null;
}
function within() {
var result = sequence([expression, whitespace, _within_, whitespace, rangeList]);
if (result !== null) {
debug(" -- passed within ");
var range_list = result[4];
return (parseInt( range_list[0],10 )<= result[0] && result[0] <= parseInt( range_list[1], 10));
}
debug(" -- failed within ");
return null;
}
var relation = choice([is, _in, within]);
function and() {
var result = sequence([relation, whitespace, _and_, whitespace, condition]);
if (result) {
debug(" -- passed and");
return result[0] && result[4];
}
debug(" -- failed and");
return null;
}
function or() {
var result = sequence([relation, whitespace, _or_, whitespace, condition]);
if (result) {
debug(" -- passed or");
return result[0] || result[4];
}
debug(" -- failed or");
return null;
}
var condition = choice([and, or, relation]);
function isInt(n) {
return parseFloat(n) % 1 === 0;
}
function start() {
if (!isInt(number)) {
return false;
}
var result = condition();
return result;
}
var result = start();
/*
* For success, the pos must have gotten to the end of the rule
* and returned a non-null.
* n.b. This is part of language infrastructure, so we do not throw an internationalizable message.
*/
if (result === null || pos !== rule.length) {
// throw new Error("Parse error at position " + pos.toString() + " in input: " + rule + " result is " + result);
}
return result;
}
/* For module loaders, e.g. NodeJS, NPM */
if (typeof module !== 'undefined' && module.exports) {
module.exports = pluralRuleParser;
}