/** * 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; }