Jforrester has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/223045

Change subject: Update jQuery.i18n to v1.0.3
......................................................................

Update jQuery.i18n to v1.0.3

  https://github.com/wikimedia/jquery.i18n/releases/tag/v1.0.3

Change-Id: I531f49bd8c2f60bd569c411d7d7025f91d1551e9
---
M lib/jquery.i18n/CREDITS
A lib/jquery.i18n/README.md
M lib/jquery.i18n/libs/CLDRPluralRuleParser/src/CLDRPluralRuleParser.js
M lib/jquery.i18n/package.json
A lib/jquery.i18n/src/jquery.i18n.emitter.bidi.js
M lib/jquery.i18n/src/jquery.i18n.fallbacks.js
M lib/jquery.i18n/src/jquery.i18n.js
M lib/jquery.i18n/src/jquery.i18n.language.js
M lib/jquery.i18n/src/jquery.i18n.messagestore.js
M lib/jquery.i18n/src/jquery.i18n.parser.js
M lib/jquery.i18n/src/languages/os.js
11 files changed, 1,414 insertions(+), 559 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor 
refs/changes/45/223045/1

diff --git a/lib/jquery.i18n/CREDITS b/lib/jquery.i18n/CREDITS
index 4f6b73f..3a4eb5e 100644
--- a/lib/jquery.i18n/CREDITS
+++ b/lib/jquery.i18n/CREDITS
@@ -6,5 +6,4 @@
 Siebrand Mazeland
 Niklas Laxström
 Neil Kandalgaonkar
-
-
+David Chan
diff --git a/lib/jquery.i18n/README.md b/lib/jquery.i18n/README.md
new file mode 100644
index 0000000..da82c2b
--- /dev/null
+++ b/lib/jquery.i18n/README.md
@@ -0,0 +1,432 @@
+jQuery.i18n
+===========
+
+jQuery.i18n is a jQuery based Javascript internationalization library. It 
helps you to internationalize your web applications easily.
+
+This is a project by Wikimedia foundation's [Language Engineering 
team](http://wikimediafoundation.org/wiki/Language_Engineering_team) and used 
in some of the Wikimedia Foundation projects like Universal Language Selector.
+
+The jquery.i18n library uses a json based localization file format, "banana", 
which is used as the localization file format for  MediaWiki and other projects.
+
+Features
+========
+* Simple file format - JSON. Easily readable for humans and machines.
+* Author and metadata information is not lost anywhere. There are other file 
formats using comments to store this.
+* Uses MediaWiki convention for placeholders. Easily readable and proven 
convention. Example: ```There are $1 cars```
+* Supports plural conversion without using extra messages for all plural 
forms. Plural rule handling is done using CLDR. Covers a wide range of languages
+* Supports gender. By passing the gender value, you get correct sentences 
according to gender.
+* Supports grammar forms. jquery.i18n has a basic but extensible grammar 
conversion support
+* Fallback chains for all languages.
+* Data api- the message key. Example: ```<li data-i18n="message-key"></li>```.
+* Dynamic change of interface language without refreshing a webpage.
+* Nestable grammar, plural, gender support. These constructs can be nested to 
any arbitrary level for supporting sophisticated message localization
+* Message documentation through special language code ```qqq```
+* Extensible message parser to add or customize magic words in the messages. 
Example: ```{sitename}``` or ```[[link]]``
+
+
+Quick start
+-----------
+
+```bash
+git clone https://github.com/wikimedia/jquery.i18n.git
+cd jquery.i18n
+git submodule update --init
+```
+
+Testing
+-------
+
+```shell
+npm install
+```
+
+To run tests locally, run `npm test`, and this will run the tests.
+
+Message File Format
+===================
+
+The message files are json formatted. As a convention you can have a folder 
named i18n inside your source code. For each language or locale, have a file 
named like languagecode.json.
+
+Example:
+```
+App
+       |--src
+       |--doc
+       |--i18n
+               |--ar.json
+               |--de.json
+               |--en.json
+               |--he.json
+               |--hi.json
+               |--fr.json
+               |--qqq.json
+```
+
+A simple en.json file example is given below
+
+```json
+{
+       "@metadata": {
+               "authors": [
+                       "Alice",
+                       "David",
+                       "Santhosh"
+               ],
+               "last-updated": "2012-09-21",
+               "locale": "en",
+               "message-documentation": "qqq",
+               "AnotherMetadata": "AnotherMedatadataValue"
+       },
+       "appname-title": "Example Application",
+       "appname-sub-title": "An example application with jquery.i18n",
+       "appname-header-introduction": "Introduction",
+       "appname-about": "About this application",
+       "appname-footer": "Footer text"
+}
+```
+
+The json file should be a valid json. The ```@metadata``` holds all kind of 
data that are not messages. You can store author information, copyright, 
updated date or anything there.
+
+Messages are key value pairs. It is a good convention to prefix your appname 
to message keys to make the messages unique. It acts as the namespace for the 
message keys. It is also a good convention to have the message keys with 
```-``` separated words, all in lower case.
+
+If you are curious to see some real jquery.i18n message file from other 
projects:
+
+- message files of MediaWiki 
https://github.com/wikimedia/mediawiki-core/tree/master/languages/i18n
+- message files from jquery.uls project 
https://github.com/wikimedia/jquery.uls/blob/master/i18n
+
+Single message file for all languages
+-------------------------------------
+There are some alternate message file format supported for different use 
cases. If your application is not big, and want all the translation in a single 
file, you can have it as shown in the below example:
+
+```json
+{
+       "@metadata": {
+               "authors": [
+                       "Alice",
+                       "David",
+                       "Santhosh"
+               ],
+               "last-updated": "2012-09-21",
+               "locale": "en",
+               "message-documentation": "qqq",
+               "AnotherMetadata": "AnotherMedatadataValue"
+       },
+       "en": {
+               "appname-title": "Example Application",
+               "appname-sub-title": "An example application with jquery.i18n",
+               "appname-header-introduction": "Introduction",
+               "appname-about": "About this application",
+               "appname-footer": "Footer text"
+               },
+       "ml": {
+               "appname-title": "അപ്ലിക്കേഷന്‍ ഉദാഹരണം",
+               "appname-sub-title": "jquery.i18n ഉപയോഗിച്ചുള്ള അപ്ലിക്കേഷന്‍ 
ഉദാഹരണം",
+               "appname-header-introduction": "ആമുഖം",
+               "appname-about": "ഈ അപ്ലിക്കേഷനെപ്പറ്റി",
+               "appname-footer": "അടിക്കുറിപ്പു്"
+       }
+}
+```
+
+Here the json file contains language code as key value and messagekey-message 
pairs as the value for all language pairs. You can choose this format or 
per-language file formats depending on your use case. Per-language files are 
more convenient for collaboration, version controlling, scalability, etc.
+
+In this approach, it is also possible to give a file name as the value of 
language code.
+
+```json
+{
+       "@metadata": {
+               "authors": [
+                       "Alice",
+                       "David",
+                       "Santhosh"
+               ],
+               "last-updated": "2012-09-21",
+               "locale": "en",
+               "message-documentation": "qqq",
+               "AnotherMetadata": "AnotherMedatadataValue"
+       },
+       "en": {
+               "appname-title": "Example Application",
+               "appname-sub-title": "An example application with jquery.i18n",
+               "appname-header-introduction": "Introduction",
+               "appname-about": "About this application",
+               "appname-footer": "Footer text"
+               },
+       "ml": "path/to/ml.json"
+}
+```
+
+Translation
+===========
+To translate the jquery.i18n application, depending on the expertise of the 
translator, there are multiple ways.
+
+* Editing the json files directly - Suitable for translators with technical 
background. Also suitable if your application is small and you want to work 
with only a small number of languages
+* Providing a translation interface along with your application: Suitable for 
proprietary or private applications with significant amount of translators
+* Using open source translation platforms like translatewiki.net. The 
MediaWiki and jquery.uls from previous examples use translatewiki.net for 
crowdsourced message translation. Translatewiki.net can update your code repo 
in regular intervals with updated translations. Highly recommended if your 
application is opensource and want localized to as many as languages possible 
with maximum number of translators.
+
+Usage
+=====
+
+## Switching locale
+
+While initializing the `jquery.i18n`, the locale for the page can be given 
using the `locale` option. For example
+
+```javascript
+$.i18n( {
+    locale: 'he' // Locale is Hebrew
+} );
+```
+
+In case locale option is not given, `jquery.i18n` plugin will use the language 
attribute given for the html tag. For example
+
+```html
+<html lang="he" dir="rtl">
+```
+
+In this case the locale will be he(Hebrew). If that `lang` attribute is also 
missing, it will try to use the locale specified by the browser.
+
+It is possible to switch to another locale after plugin is initialized. See 
below example:
+
+```javascript
+$.i18n({
+    locale: 'he' // Locale is Hebrew
+});
+$.i18n( 'message_hello' ); // This will give the Hebrew translation of message 
key `message_hello`.
+$.i18n().locale = 'ml'; // Now onwards locale is 'Malayalam'
+$.i18n( 'message_hello' ); // This will give the Malayalam translation of 
message key `message_hello`.
+```
+
+## Message Loading
+
+JSON formatted messages can be loaded to the plugin using multiple ways.
+
+### Dynamic loading using `load` method.
+
+Following example shows loading messages for two locales- localex, and 
localey. Here localex and localey are just examples. They should be valid IS0 
639 language codes(eg: en, ml, hi, fr, ta etc)
+
+```javascript
+$.i18n().load( {
+       'localex' : {
+               'message-key1' : 'message1' // Message for localex.
+       },
+       'localey' : {
+               'message-key1' : 'message1'
+       }
+} );
+```
+
+If we want to load the messages for a specific locale, it can be done like 
this:
+
+```javascript
+$.i18n().load({
+    'message-hello': 'Hello World',
+    'message-welcome': 'Welcome'
+}, 'en');
+```
+
+Note the second argument for the `load` method. It should be a valid language 
code.
+
+It is also possible to refer messages from an external URL. See below example
+
+```javascript
+$.i18n().load( {
+       en: {
+               message_hello: 'Hello World',
+       message_welcome: 'Welcome'
+       },
+       hi: 'i18n/messages-hi.json', // Messages for Hindi
+       de: 'i18n/messages-de.json'
+} );
+```
+
+Messages for a locale can be also loaded in parts. Example
+
+```javascript
+$.i18n().load( {
+       en: {
+               message_hello: 'Hello World',
+       message_welcome: 'Welcome'
+       }
+} );
+
+$.i18n().load( {
+       // This does not remove the previous messages.
+       en: {
+               'message_header' : 'Header',
+               'message_footer' : 'Footer',
+               // This will overwrite message_welcome message
+               'message_welcome' : 'Welcome back'
+       }
+} );
+```
+
+Since it is desirable to render interface messages instantly and not after a 
delay of loading the message files from a server, make sure that the messages 
are present at client side before using jQuery.i18n.
+
+The library should expose an API to load an object containing key-value pair 
of messages. Example: ```$.i18n.load(data)```. This will return a 
```jQuery.Promise```.
+
+jquery.i18n plugin
+=========================
+
+The jQuery plugin defines ```$.i18n()``` and ```$.fn.i18n()```
+
+```javascript
+$.i18n( 'message-key-sample1' );
+$.i18n( 'message-key-sample1' );
+$.i18n( 'Found $1 {{plural:$1|result|results}}', 10 ); // Message key itself 
is message text
+$.i18n( 'Showing $1 out of $2 {{plural:$2|result|results}}', 5,100 );
+$.i18n(' User X updated {{gender|his|her}} profile', 'male' );
+
+$( '#foo' ).i18n(); // to translate the element matching jquery selector based 
on data-i18n key
+```
+
+Data API
+--------
+
+It is possible to display localized messages without any custom JavaScript. 
For the HTML tags, add an attribute data-i18n with value as the message key. 
Example:
+```html
+<li data-i18n="message-key"></li>.
+```
+
+It is also possible to have the above li node with fallback text already in 
place.
+```html
+<li data-i18n="message-key">Fallback text</li>
+```
+
+The framework will place the localized message corresponding to message-key as 
the text value of the node. Similar to $('selector').i18n( ... ).
+This will not work for dynamically created elements.
+
+Note that if data-i18n contains html markup, that html will not be used as the 
element content, instead, the text version will be used. $.fn.i18n is always 
about replacing text of the element. If you want to change the html of the 
element, you may want to use: ```$(selector).html($.i18n(messagekey))```
+
+Examples
+========
+
+See http://thottingal.in/projects/js/jquery.i18n/demo/
+
+Message format
+==============
+
+## Placeholders
+
+Messages take parameters. They are represented by $1, $2, $3, … in the message 
texts, and replaced at run time. Typical parameter values are numbers (Example: 
"Delete 3 versions?"), or user names (Example: "Page last edited by $1"), page 
names, links, and so on, or sometimes other messages.
+
+```javascript
+var message = "Welcome, $1";
+$.i18n(message, 'Alice'); // This gives "Welcome, Alice"
+```
+
+
+## Plurals
+
+To make the syntax of sentence correct, plural forms are required. jquery.i18n 
support plural forms in the message using the syntax 
`{{PLURAL:$1|pluralform1|pluralform2|...}}`
+
+For example:
+
+```javascript
+var message = "Found $1 {{PLURAL:$1|result|results}}";
+$.i18n(message, 1); // This gives "Found 1 result"
+$.i18n(message, 4); // This gives "Found 4 results"
+```
+Note that {{PLURAL:...}} is not case sensitive. It can be {{plural:...}} too.
+
+In case of English, there are only 2 plural forms, but many languages use more 
than 2 plural forms. All the plural forms can be given in the above syntax, 
separated by pipe(|)
+
+## Gender
+Similar to plural, depending on gender of placeholders, mostly user names, the 
syntax changes dynamically. An example in English is "Alice changed her profile 
picture" and "Bob changed his profile picture". To support this {{GENDER...}} 
syntax can be used as show in example
+
+```javascript
+var message = "$1 changed {{GENDER:$2|his|her}} profile picture";
+$.i18n(message, 'Alice', 'female' ); // This gives "Alice changed her profile 
picture"
+$.i18n(message, 'Bob', 'male' ); // This gives "Bob changed his profile 
picture"
+```
+
+Note that {{GENDER:...}} is not case sensitive. It can be {{gender:...}} too.
+
+## Grammar
+
+
+```javascript
+$.i18n( { locale: 'fi' } );
+
+var message = "{{grammar:genitive|$1}}";
+
+$.i18n(message, 'talo' ); // This gives "talon"
+
+$.i18n().locale = 'hy'; // Switch to locale Armenian
+$.i18n(message, 'Մաունա'); // This gives "Մաունայի"
+```
+
+## Directionality-safe isolation
+
+To avoid BIDI corruption that looks like "(Foo_(Bar", which happens when a 
string is inserted into a context with the reverse directionality, you can use 
`{{bidi:…}}`. Directionality-neutral characters at the edge of the string can 
get wrongly interpreted by the BIDI algorithm. This would let you embed your 
substituted string into a new BIDI context, //e.g.//:
+
+   "`Shalom, {{bidi:$1}}, hi!`"
+
+The embedded context's directionality is determined by looking at the argument 
for `$1`, and then explicitly inserted into the Unicode text, ensuring correct 
rendering (because then the bidi algorithm "knows" the argument text is a 
separate context).
+
+
+Fallback
+========
+
+The plugin takes an option 'fallback' with the default value 'en'. The library 
reuses the fallback data available in MediaWiki for calculating the language 
fallbacks. Fallbacks are used when a message key is not found in a locale. 
Example fallbacks: sa->hi->en or tt->tt-cyrl->ru.
+
+See jquery.i18n.fallbacks.js in the source.
+
+Magic word support
+===================
+* For plural, gender and grammar support, MediaWiki template-like syntax - 
{{...}} will be used.
+* There will be a default implementation for all these in 
$.i18n.language['default']
+* The plural, gender and grammar methods in ```$.i18n.language[ 'default' ]``` 
can be overridden or extended in ```$.i18n.language['languageCode']```.
+* Language-specific rules about Gender and Grammar can be written in 
languages/langXYZ.js files
+* Plural forms will be dynamically calculated using the CLDR plural parser.
+
+Extending the parser
+--------------------
+Following example illustrates extending the parser to support more magic words
+
+```javascript
+$.extend( $.i18n.parser.emitter, {
+       // Handle SITENAME keywords
+       sitename: function () {
+               return 'Wikipedia';
+       },
+       // Handle LINK keywords
+       link: function ( nodes ) {
+               return '<a href="' + nodes[1] + '">' + nodes[0] + '</a>';
+       }
+} );
+```
+
+This will parse the message
+```javascript
+$.i18n( '{{link:{{SITENAME}}|http://en.wikipedia.org}}' );
+```
+
+to
+
+```html
+<a href="http://en.wikipedia.org";>Wikipedia</a>
+```
+
+Message documentation
+=====================
+
+The message keys and messages won't give a enough context about the message 
being translated to the translator. Whenever a developer adds a new message, it 
is a usual practice to document the message to a file named qqq.json
+with same message key.
+
+Example qqq.json:
+```json
+{
+       "@metadata": {
+               "authors": [
+                       "Developer Name"
+               ]
+       },
+       "appname-title": "Application name. Transliteration is recommended",
+       "appname-sub-title": "Brief explanation of the application",
+       "appname-header-introduction": "Text for the introduction header",
+       "appname-about": "About this application text",
+       "appname-footer": "Footer text"
+}
+
+```
+
+In MediaWiki and its hundreds of extensions, message documentation is a 
strictly followed practice. There is a grunt task to check whether all messages 
are documented or not. See https://www.npmjs.org/package/grunt-banana-checker
diff --git 
a/lib/jquery.i18n/libs/CLDRPluralRuleParser/src/CLDRPluralRuleParser.js 
b/lib/jquery.i18n/libs/CLDRPluralRuleParser/src/CLDRPluralRuleParser.js
index 441bc91..1491e3d 100644
--- a/lib/jquery.i18n/libs/CLDRPluralRuleParser/src/CLDRPluralRuleParser.js
+++ b/lib/jquery.i18n/libs/CLDRPluralRuleParser/src/CLDRPluralRuleParser.js
@@ -1,77 +1,122 @@
-/* This is cldrpluralparser 1.0, ported to MediaWiki ResourceLoader */
-
 /**
-* 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 <santhosh.thottin...@gmail.com>
-* @author Timo Tijhof
-* @author Amir Aharoni
-*/
+ * cldrpluralparser.js
+ * A parser engine for CLDR plural rules.
+ *
+ * Copyright 2012-2014 Santhosh Thottingal and other contributors
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ * @version 0.1.0
+ * @source https://github.com/santhoshtr/CLDRPluralRuleParser
+ * @author Santhosh Thottingal <santhosh.thottin...@gmail.com>
+ * @author Timo Tijhof
+ * @author Amir Aharoni
+ */
 
 /**
  * Evaluates a plural rule in CLDR syntax for a number
- * @param rule
- * @param number
- * @return true|false|null
+ * @param {string} rule
+ * @param {integer} number
+ * @return {boolean} true if evaluation passed, false if evaluation failed.
  */
-( function( mw ) {
+
+// UMD returnExports https://github.com/umdjs/umd/blob/master/returnExports.js
+(function(root, factory) {
+       if (typeof define === 'function' && define.amd) {
+               // AMD. Register as an anonymous module.
+               define(factory);
+       } else if (typeof exports === 'object') {
+               // Node. Does not work with strict CommonJS, but
+               // only CommonJS-like environments that support module.exports,
+               // like Node.
+               module.exports = factory();
+       } else {
+               // Browser globals (root is window)
+               root.pluralRuleParser = factory();
+       }
+}(this, function() {
 
 function pluralRuleParser(rule, number) {
+       'use strict';
+
        /*
        Syntax: see http://unicode.org/reports/tr35/#Language_Plural_Rules
        -----------------------------------------------------------------
-
        condition     = and_condition ('or' and_condition)*
+               ('@integer' samples)?
+               ('@decimal' samples)?
        and_condition = relation ('and' relation)*
-       relation      = is_relation | in_relation | within_relation | 'n' <EOL>
+       relation      = is_relation | in_relation | within_relation
        is_relation   = expr 'is' ('not')? value
-       in_relation   = expr ('not')? 'in' range_list
+       in_relation   = expr (('not')? 'in' | '=' | '!=') range_list
        within_relation = expr ('not')? 'within' range_list
-       expr          = 'n' ('mod' value)?
+       expr          = operand (('mod' | '%') value)?
+       operand       = 'n' | 'i' | 'f' | 't' | 'v' | 'w'
        range_list    = (range | value) (',' range_list)*
        value         = digit+
        digit         = 0|1|2|3|4|5|6|7|8|9
        range         = value'..'value
-
+       samples       = sampleRange (',' sampleRange)* (',' ('…'|'...'))?
+       sampleRange   = decimalValue '~' decimalValue
+       decimalValue  = value ('.' value)?
        */
-       // Indicates current position in the rule as we parse through it.
+
+       // We don't evaluate the samples section of the rule. Ignore it.
+       rule = rule.split('@')[0].replace(/^\s*/, '').replace(/\s*$/, '');
+
+       if (!rule.length) {
+               // Empty rule or 'other' rule.
+               return true;
+       }
+
+       // Indicates the 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');
+       var pos = 0,
+               operand,
+               expression,
+               relation,
+               result,
+               whitespace = makeRegexParser(/^\s+/),
+               value = makeRegexParser(/^\d+/),
+               _n_ = makeStringParser('n'),
+               _i_ = makeStringParser('i'),
+               _f_ = makeStringParser('f'),
+               _t_ = makeStringParser('t'),
+               _v_ = makeStringParser('v'),
+               _w_ = makeStringParser('w'),
+               _is_ = makeStringParser('is'),
+               _isnot_ = makeStringParser('is not'),
+               _isnot_sign_ = makeStringParser('!='),
+               _equal_ = makeStringParser('='),
+               _mod_ = makeStringParser('mod'),
+               _percent_ = makeStringParser('%'),
+               _not_ = makeStringParser('not'),
+               _in_ = makeStringParser('in'),
+               _within_ = makeStringParser('within'),
+               _range_ = makeStringParser('..'),
+               _comma_ = makeStringParser(','),
+               _or_ = makeStringParser('or'),
+               _and_ = makeStringParser('and');
 
        function debug() {
-               /* console.log.apply(console, arguments);*/
+               // 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]();
+               return function() {
+                       var i, result;
+
+                       for (i = 0; i < parserSyntax.length; i++) {
+                               result = parserSyntax[i]();
+
                                if (result !== null) {
                                        return result;
                                }
                        }
+
                        return null;
                };
        }
@@ -80,236 +125,484 @@
        // 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) {
+               var i, parserRes,
+                       originalPos = pos,
+                       result = [];
+
+               for (i = 0; i < parserSyntax.length; i++) {
+                       parserRes = parserSyntax[i]();
+
+                       if (parserRes === null) {
                                pos = originalPos;
+
                                return null;
                        }
-                       result.push(res);
+
+                       result.push(parserRes);
                }
+
                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();
+               return function() {
+                       var originalPos = pos,
+                               result = [],
+                               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
-
+       // Helpers - just make parserSyntax out of simpler JS builtin types
        function makeStringParser(s) {
                var len = s.length;
-               return function () {
+
+               return function() {
                        var result = null;
+
                        if (rule.substr(pos, len) === s) {
                                result = s;
                                pos += len;
                        }
+
                        return result;
                };
        }
 
        function makeRegexParser(regex) {
-               return function () {
+               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_();
+       /**
+        * Integer digits of n.
+        */
+       function i() {
+               var result = _i_();
+
                if (result === null) {
-                       debug(" -- failed n");
+                       debug(' -- failed i', parseInt(number, 10));
+
                        return result;
                }
+
                result = parseInt(number, 10);
-               debug(" -- passed n ", result);
+               debug(' -- passed i ', result);
+
                return result;
        }
 
-       var expression = choice([mod, n]);
+       /**
+        * Absolute value of the source number (integer and decimals).
+        */
+       function n() {
+               var result = _n_();
+
+               if (result === null) {
+                       debug(' -- failed n ', number);
+
+                       return result;
+               }
+
+               result = parseFloat(number, 10);
+               debug(' -- passed n ', result);
+
+               return result;
+       }
+
+       /**
+        * Visible fractional digits in n, with trailing zeros.
+        */
+       function f() {
+               var result = _f_();
+
+               if (result === null) {
+                       debug(' -- failed f ', number);
+
+                       return result;
+               }
+
+               result = (number + '.').split('.')[1] || 0;
+               debug(' -- passed f ', result);
+
+               return result;
+       }
+
+       /**
+        * Visible fractional digits in n, without trailing zeros.
+        */
+       function t() {
+               var result = _t_();
+
+               if (result === null) {
+                       debug(' -- failed t ', number);
+
+                       return result;
+               }
+
+               result = (number + '.').split('.')[1].replace(/0$/, '') || 0;
+               debug(' -- passed t ', result);
+
+               return result;
+       }
+
+       /**
+        * Number of visible fraction digits in n, with trailing zeros.
+        */
+       function v() {
+               var result = _v_();
+
+               if (result === null) {
+                       debug(' -- failed v ', number);
+
+                       return result;
+               }
+
+               result = (number + '.').split('.')[1].length || 0;
+               debug(' -- passed v ', result);
+
+               return result;
+       }
+
+       /**
+        * Number of visible fraction digits in n, without trailing zeros.
+        */
+       function w() {
+               var result = _w_();
+
+               if (result === null) {
+                       debug(' -- failed w ', number);
+
+                       return result;
+               }
+
+               result = (number + '.').split('.')[1].replace(/0$/, '').length 
|| 0;
+               debug(' -- passed w ', result);
+
+               return result;
+       }
+
+       // operand       = 'n' | 'i' | 'f' | 't' | 'v' | 'w'
+       operand = choice([n, i, f, t, v, w]);
+
+       // expr          = operand (('mod' | '%') value)?
+       expression = choice([mod, operand]);
 
        function mod() {
-               var result = sequence([n, whitespace, _mod_, whitespace, 
digits]);
+               var result = sequence(
+                       [operand, whitespace, choice([_mod_, _percent_]), 
whitespace, value]
+               );
+
                if (result === null) {
-                       debug(" -- failed mod");
+                       debug(' -- failed mod');
+
                        return null;
                }
-               debug(" -- passed mod");
+
+               debug(' -- passed ' + parseInt(result[0], 10) + ' ' + result[2] 
+ ' ' + parseInt(result[4], 10));
+
                return parseInt(result[0], 10) % parseInt(result[4], 10);
        }
 
        function not() {
                var result = sequence([whitespace, _not_]);
+
                if (result === null) {
-                       debug(" -- failed not");
+                       debug(' -- failed not');
+
                        return null;
-               } else {
-                       return result[1];
                }
+
+               return result[1];
        }
 
+       // is_relation   = expr 'is' ('not')? value
        function is() {
-               var result = sequence([expression, whitespace, _is_, nOrMore(0, 
not), whitespace, digits]);
+               var result = sequence([expression, whitespace, choice([_is_]), 
whitespace, value]);
+
                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(' -- passed is : ' + result[0] + ' == ' + 
parseInt(result[4], 10));
+
+                       return result[0] === parseInt(result[4], 10);
                }
-               debug(" -- failed is");
+
+               debug(' -- failed is');
+
                return null;
        }
 
-       function rangeList() {
-               // range_list    = (range | value) (',' range_list)*
-               var result = sequence([choice([range, digits]), nOrMore(0, 
rangeTail)]);
-               var resultList = [];
+       // is_relation   = expr 'is' ('not')? value
+       function isnot() {
+               var result = sequence(
+                       [expression, whitespace, choice([_isnot_, 
_isnot_sign_]), whitespace, value]
+               );
+
                if (result !== null) {
-                       resultList = resultList.concat(result[0], result[1][0]);
+                       debug(' -- passed isnot: ' + result[0] + ' != ' + 
parseInt(result[4], 10));
+
+                       return result[0] !== parseInt(result[4], 10);
+               }
+
+               debug(' -- failed isnot');
+
+               return null;
+       }
+
+       function not_in() {
+               var i, range_list,
+                       result = sequence([expression, whitespace, 
_isnot_sign_, whitespace, rangeList]);
+
+               if (result !== null) {
+                       debug(' -- passed not_in: ' + result[0] + ' != ' + 
result[4]);
+                       range_list = result[4];
+
+                       for (i = 0; i < range_list.length; i++) {
+                               if (parseInt(range_list[i], 10) === 
parseInt(result[0], 10)) {
+                                       return false;
+                               }
+                       }
+
+                       return true;
+               }
+
+               debug(' -- failed not_in');
+
+               return null;
+       }
+
+       // range_list    = (range | value) (',' range_list)*
+       function rangeList() {
+               var result = sequence([choice([range, value]), nOrMore(0, 
rangeTail)]),
+                       resultList = [];
+
+               if (result !== null) {
+                       resultList = resultList.concat(result[0]);
+
+                       if (result[1][0]) {
+                               resultList = resultList.concat(result[1][0]);
+                       }
+
                        return resultList;
                }
-               debug(" -- failed rangeList");
+
+               debug(' -- failed rangeList');
+
                return null;
        }
 
        function rangeTail() {
                // ',' range_list
                var result = sequence([_comma_, rangeList]);
+
                if (result !== null) {
                        return result[1];
                }
-               debug(" -- failed rangeTail");
+
+               debug(' -- failed rangeTail');
+
                return null;
        }
 
+       // range         = value'..'value
        function range() {
-               var i;
-               var result = sequence([digits, _range_, digits]);
+               var i, array, left, right,
+                       result = sequence([value, _range_, value]);
+
                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++) {
+                       debug(' -- passed range');
+
+                       array = [];
+                       left = parseInt(result[0], 10);
+                       right = parseInt(result[2], 10);
+
+                       for (i = left; i <= right; i++) {
                                array.push(i);
                        }
+
                        return array;
                }
-               debug(" -- failed range");
+
+               debug(' -- failed range');
+
                return null;
        }
 
        function _in() {
+               var result, range_list, i;
+
                // in_relation   = expr ('not')? 'in' range_list
-               var result = sequence([expression, nOrMore(0, not), whitespace, 
_in_, whitespace, rangeList]);
+               result = sequence(
+                       [expression, nOrMore(0, not), whitespace, choice([_in_, 
_equal_]), 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]) {
+                       debug(' -- passed _in:' + result);
+
+                       range_list = result[5];
+
+                       for (i = 0; i < range_list.length; i++) {
+                               if (parseInt(range_list[i], 10) === 
parseInt(result[0], 10)) {
                                        return (result[1][0] !== 'not');
                                }
                        }
+
                        return (result[1][0] === 'not');
                }
-               debug(" -- failed _in ");
+
+               debug(' -- failed _in ');
+
                return null;
        }
 
+       /**
+        * The difference between "in" and "within" is that
+        * "in" only includes integers in the specified range,
+        * while "within" includes all values.
+        */
        function within() {
-               var result = sequence([expression, whitespace, _within_, 
whitespace, rangeList]);
+               var range_list, result;
+
+               // within_relation = expr ('not')? 'within' range_list
+               result = sequence(
+                       [expression, nOrMore(0, not), 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(' -- passed within');
+
+                       range_list = result[5];
+
+                       if ((result[0] >= parseInt(range_list[0], 10)) &&
+                               (result[0] < 
parseInt(range_list[range_list.length - 1], 10))) {
+
+                               return (result[1][0] !== 'not');
+                       }
+
+                       return (result[1][0] === 'not');
                }
-               debug(" -- failed within ");
+
+               debug(' -- failed within ');
+
                return null;
        }
 
+       // relation      = is_relation | in_relation | within_relation
+       relation = choice([is, not_in, isnot, _in, within]);
 
-       var relation = choice([is, _in, within]);
-
+       // and_condition = relation ('and' relation)*
        function and() {
-               var result = sequence([relation, whitespace, _and_, whitespace, 
condition]);
+               var i,
+                       result = sequence([relation, nOrMore(0, andTail)]);
+
                if (result) {
-                       debug(" -- passed and");
-                       return result[0] && result[4];
+                       if (!result[0]) {
+                               return false;
+                       }
+
+                       for (i = 0; i < result[1].length; i++) {
+                               if (!result[1][i]) {
+                                       return false;
+                               }
+                       }
+
+                       return true;
                }
-               debug(" -- failed and");
+
+               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];
+       // ('and' relation)*
+       function andTail() {
+               var result = sequence([whitespace, _and_, whitespace, 
relation]);
+
+               if (result !== null) {
+                       debug(' -- passed andTail' + result);
+
+                       return result[3];
                }
-               debug(" -- failed or");
+
+               debug(' -- failed andTail');
+
+               return null;
+
+       }
+       //  ('or' and_condition)*
+       function orTail() {
+               var result = sequence([whitespace, _or_, whitespace, and]);
+
+               if (result !== null) {
+                       debug(' -- passed orTail: ' + result[3]);
+
+                       return result[3];
+               }
+
+               debug(' -- failed orTail');
+
                return null;
        }
 
-       var condition = choice([and, or, relation]);
+       // condition     = and_condition ('or' and_condition)*
+       function condition() {
+               var i,
+                       result = sequence([and, nOrMore(0, orTail)]);
 
-       function isInt(n) {
-               return parseFloat(n) % 1 === 0;
-       }
+               if (result) {
+                       for (i = 0; i < result[1].length; i++) {
+                               if (result[1][i]) {
+                                       return true;
+                               }
+                       }
 
-
-       function start() {
-               if (!isInt(number)) {
-                       return false;
+                       return result[0];
                }
-               var result = condition();
-               return result;
+
+               return false;
        }
 
+       result = condition();
 
-       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.
+        * 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);
+       if (result === null) {
+               throw new Error('Parse error at position ' + pos.toString() + ' 
for rule: ' + rule);
+       }
+
+       if (pos !== rule.length) {
+               debug('Warning: Rule not parsed completely. Parser stopped at ' 
+ rule.substr(0, pos) + ' for rule: ' + rule);
        }
 
        return result;
 }
 
-/* For module loaders, e.g. NodeJS, NPM */
-if (typeof module !== 'undefined' && module.exports) {
-       module.exports = pluralRuleParser;
-}
+return pluralRuleParser;
 
-/* pluralRuleParser ends here */
-mw.libs.pluralRuleParser = pluralRuleParser;
-
-} )( mediaWiki );
+}));
diff --git a/lib/jquery.i18n/package.json b/lib/jquery.i18n/package.json
index 109b4c8..0dded29 100644
--- a/lib/jquery.i18n/package.json
+++ b/lib/jquery.i18n/package.json
@@ -1,6 +1,6 @@
 {
   "name": "jquery.i18n",
-  "version": "1.0.2",
+  "version": "1.0.3",
   "description": "jQuery based internationalization library",
   "homepage": "https://github.com/wikimedia/jquery.i18n";,
   "keywords": [
@@ -11,42 +11,36 @@
     "l10n"
   ],
   "author": {
-        "name": "Santhosh Thottingal",
-        "email": "santhosh.thottin...@gmail.com"
+    "name": "Santhosh Thottingal",
+    "email": "santhosh.thottin...@gmail.com"
   },
   "contributors": [
     "Amir Aharoni <amir.ahar...@mail.huji.ac.il>",
     "Niklas Laxström <nlaxst...@wikimedia.org>",
-    "Neil Kandalgaonkar <ne...@brevity.org>"
+    "Neil Kandalgaonkar <ne...@brevity.org>",
+    "David Chan <da...@troi.org>"
   ],
   "devDependencies": {
-   "qunit": "~0.5.16",
-    "grunt": "~0.4.1",
-    "grunt-cli": "~0.1",
-    "grunt-contrib-jshint": "~0.5.4",
-    "grunt-contrib-qunit": "~0.2.1",
-    "grunt-contrib-watch": "~0.4.3"
+    "qunit": "0.7.6",
+    "grunt": "0.4.5",
+    "grunt-cli": "0.1.13",
+    "grunt-contrib-jshint": "0.11.2",
+    "grunt-contrib-connect": "0.10.1",
+    "grunt-contrib-qunit": "0.7.0",
+    "grunt-contrib-watch": "0.6.1",
+    "grunt-jscs": "1.8.0"
   },
   "repository": {
     "type": "git",
     "url": "git://github.com/wikimedia/jquery.i18n.git"
   },
   "bugs": {
-           "url" : "http://github.com/wikimedia/jquery.i18n/issues";
-         },
+      "url" : "http://github.com/wikimedia/jquery.i18n/issues";
+  },
   "engine": {
     "node": ">=0.8.x"
   },
-  "licenses": [
-    {
-      "type": "GPL",
-      "url": "https://github.com/wikimedia/jquery.i18n/blob/master/GPL-LICENSE";
-    },
-    {
-      "type": "MIT",
-      "url": "https://github.com/wikimedia/jquery.i18n/blob/master/MIT-LICENSE";
-    }
-  ],
+  "license": "(MIT OR GPL-2.0)",
   "scripts": {
     "test": "grunt test --verbose"
   }
diff --git a/lib/jquery.i18n/src/jquery.i18n.emitter.bidi.js 
b/lib/jquery.i18n/src/jquery.i18n.emitter.bidi.js
new file mode 100644
index 0000000..3a5b625
--- /dev/null
+++ b/lib/jquery.i18n/src/jquery.i18n.emitter.bidi.js
@@ -0,0 +1,88 @@
+/**
+ * BIDI embedding support for jQuery.i18n
+ *
+ * Copyright (C) 2015, David Chan
+ *
+ * This code 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 this code
+ * 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 strongDirRegExp;
+
+       /**
+        * Matches the first strong directionality codepoint:
+        * - in group 1 if it is LTR
+        * - in group 2 if it is RTL
+        * Does not match if there is no strong directionality codepoint.
+        *
+        * Generated by UnicodeJS (see tools/strongDir) from the UCD; see
+        * https://git.wikimedia.org/summary/unicodejs.git .
+        */
+       strongDirRegExp = new RegExp(
+               '(?:' +
+                       '(' +
+                               
'[\u0041-\u005a\u0061-\u007a\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02b8\u02bb-\u02c1\u02d0\u02d1\u02e0-\u02e4\u02ee\u0370-\u0373\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0482\u048a-\u052f\u0531-\u0556\u0559-\u055f\u0561-\u0587\u0589\u0903-\u0939\u093b\u093d-\u0940\u0949-\u094c\u094e-\u0950\u0958-\u0961\u0964-\u0980\u0982\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd-\u09c0\u09c7\u09c8\u09cb\u09cc\u09ce\u09d7\u09dc\u09dd\u09df-\u09e1\u09e6-\u09f1\u09f4-\u09fa\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3e-\u0a40\u0a59-\u0a5c\u0a5e\u0a66-\u0a6f\u0a72-\u0a74\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd-\u0ac0\u0ac9\u0acb\u0acc\u0ad0\u0ae0\u0ae1\u0ae6-\u0af0\u0af9\u0b02\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b3e\u0b40\u0b47\u0b48\u0b4b\u0b4c\u0b57\u0b5c\u0b5d\u0b5f-\u0b61\u0b66-\u0b77\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe\u0bbf\u0bc1\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcc\u0bd0\u0bd7\u0be6-\u0bf2\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c41-\u0c44\u0c58-\u0c5a\u0c60\u0c61\u0c66-\u0c6f\u0c7f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd-\u0cc4\u0cc6-\u0cc8\u0cca\u0ccb\u0cd5\u0cd6\u0cde\u0ce0\u0ce1\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d40\u0d46-\u0d48\u0d4a-\u0d4c\u0d4e\u0d57\u0d5f-\u0d61\u0d66-\u0d75\u0d79-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dcf-\u0dd1\u0dd8-\u0ddf\u0de6-\u0def\u0df2-\u0df4\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e4f-\u0e5b\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0ed0-\u0ed9\u0edc-\u0edf\u0f00-\u0f17\u0f1a-\u0f34\u0f36\u0f38\u0f3e-\u0f47\u0f49-\u0f6c\u0f7f\u0f85\u0f88-\u0f8c\u0fbe-\u0fc5\u0fc7-\u0fcc\u0fce-\u0fda\u1000-\u102c\u1031\u1038\u103b\u103c\u103f-\u1057\u105a-\u105d\u1061-\u1070\u1075-\u1081\u1083\u1084\u1087-\u108c\u108e-\u109c\u109e-\u10c5\u10c7\u10cd\u10d0-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1360-\u137c\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u167f\u1681-\u169a\u16a0-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1735\u1736\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17b6\u17be-\u17c5\u17c7\u17c8\u17d4-\u17da\u17dc\u17e0-\u17e9\u1810-\u1819\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1923-\u1926\u1929-\u192b\u1930\u1931\u1933-\u1938\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19da\u1a00-\u1a16\u1a19\u1a1a\u1a1e-\u1a55\u1a57\u1a61\u1a63\u1a64\u1a6d-\u1a72\u1a80-\u1a89\u1a90-\u1a99\u1aa0-\u1aad\u1b04-\u1b33\u1b35\u1b3b\u1b3d-\u1b41\u1b43-\u1b4b\u1b50-\u1b6a\u1b74-\u1b7c\u1b82-\u1ba1\u1ba6\u1ba7\u1baa\u1bae-\u1be5\u1be7\u1bea-\u1bec\u1bee\u1bf2\u1bf3\u1bfc-\u1c2b\u1c34\u1c35\u1c3b-\u1c49\u1c4d-\u1c7f\u1cc0-\u1cc7\u1cd3\u1ce1\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200e\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u214f\u2160-\u2188\u2336-\u237a\u2395\u249c-\u24e9\u26ac\u2800-\u28ff\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d70\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u302e\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u3190-\u31ba\u31f0-\u321c\u3220-\u324f\u3260-\u327b\u327f-\u32b0\u32c0-\u32cb\u32d0-\u32fe\u3300-\u3376\u337b-\u33dd\u33e0-\u33fe\u3400-\u4db5\u4e00-\u9fd5\ua000-\ua48c\ua4d0-\ua60c\ua610-\ua62b\ua640-\ua66e\ua680-\ua69d\ua6a0-\ua6ef\ua6f2-\ua6f7\ua722-\ua787\ua789-\ua7ad\ua7b0-\ua7b7\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua824\ua827\ua830-\ua837\ua840-\ua873\ua880-\ua8c3\ua8ce-\ua8d9\ua8f2-\ua8fd\ua900-\ua925\ua92e-\ua946\ua952\ua953\ua95f-\ua97c\ua983-\ua9b2\ua9b4\ua9b5\ua9ba\ua9bb\ua9bd-\ua9cd\ua9cf-\ua9d9\ua9de-\ua9e4\ua9e6-\ua9fe\uaa00-\uaa28\uaa2f\uaa30\uaa33\uaa34\uaa40-\uaa42\uaa44-\uaa4b\uaa4d\uaa50-\uaa59\uaa5c-\uaa7b\uaa7d-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaaeb\uaaee-\uaaf5\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab65\uab70-\uabe4\uabe6\uabe7\uabe9-\uabec\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\ue000-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b]|\ud800[\udc0d-\udc26]|\ud800[\udc28-\udc3a]|\ud800\udc3c|\ud800\udc3d|\ud800[\udc3f-\udc4d]|\ud800[\udc50-\udc5d]|\ud800[\udc80-\udcfa]|\ud800\udd00|\ud800\udd02|\ud800[\udd07-\udd33]|\ud800[\udd37-\udd3f]|\ud800[\uddd0-\uddfc]|\ud800[\ude80-\ude9c]|\ud800[\udea0-\uded0]|\ud800[\udf00-\udf23]|\ud800[\udf30-\udf4a]|\ud800[\udf50-\udf75]|\ud800[\udf80-\udf9d]|\ud800[\udf9f-\udfc3]|\ud800[\udfc8-\udfd5]|\ud801[\udc00-\udc9d]|\ud801[\udca0-\udca9]|\ud801[\udd00-\udd27]|\ud801[\udd30-\udd63]|\ud801\udd6f|\ud801[\ude00-\udf36]|\ud801[\udf40-\udf55]|\ud801[\udf60-\udf67]|\ud804\udc00|\ud804[\udc02-\udc37]|\ud804[\udc47-\udc4d]|\ud804[\udc66-\udc6f]|\ud804[\udc82-\udcb2]|\ud804\udcb7|\ud804\udcb8|\ud804[\udcbb-\udcc1]|\ud804[\udcd0-\udce8]|\ud804[\udcf0-\udcf9]|\ud804[\udd03-\udd26]|\ud804\udd2c|\ud804[\udd36-\udd43]|\ud804[\udd50-\udd72]|\ud804[\udd74-\udd76]|\ud804[\udd82-\uddb5]|\ud804[\uddbf-\uddc9]|\ud804\uddcd|\ud804[\uddd0-\udddf]|\ud804[\udde1-\uddf4]|\ud804[\ude00-\ude11]|\ud804[\ude13-\ude2e]|\ud804\ude32|\ud804\ude33|\ud804\ude35|\ud804[\ude38-\ude3d]|\ud804[\ude80-\ude86]|\ud804\ude88|\ud804[\ude8a-\ude8d]|\ud804[\ude8f-\ude9d]|\ud804[\ude9f-\udea9]|\ud804[\udeb0-\udede]|\ud804[\udee0-\udee2]|\ud804[\udef0-\udef9]|\ud804\udf02|\ud804\udf03|\ud804[\udf05-\udf0c]|\ud804\udf0f|\ud804\udf10|\ud804[\udf13-\udf28]|\ud804[\udf2a-\udf30]|\ud804\udf32|\ud804\udf33|\ud804[\udf35-\udf39]|\ud804[\udf3d-\udf3f]|\ud804[\udf41-\udf44]|\ud804\udf47|\ud804\udf48|\ud804[\udf4b-\udf4d]|\ud804\udf50|\ud804\udf57|\ud804[\udf5d-\udf63]|\ud805[\udc80-\udcb2]|\ud805\udcb9|\ud805[\udcbb-\udcbe]|\ud805\udcc1|\ud805[\udcc4-\udcc7]|\ud805[\udcd0-\udcd9]|\ud805[\udd80-\uddb1]|\ud805[\uddb8-\uddbb]|\ud805\uddbe|\ud805[\uddc1-\udddb]|\ud805[\ude00-\ude32]|\ud805\ude3b|\ud805\ude3c|\ud805\ude3e|\ud805[\ude41-\ude44]|\ud805[\ude50-\ude59]|\ud805[\ude80-\udeaa]|\ud805\udeac|\ud805\udeae|\ud805\udeaf|\ud805\udeb6|\ud805[\udec0-\udec9]|\ud805[\udf00-\udf19]|\ud805\udf20|\ud805\udf21|\ud805\udf26|\ud805[\udf30-\udf3f]|\ud806[\udca0-\udcf2]|\ud806\udcff|\ud806[\udec0-\udef8]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e]|\ud809[\udc70-\udc74]|\ud809[\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38]|\ud81a[\ude40-\ude5e]|\ud81a[\ude60-\ude69]|\ud81a\ude6e|\ud81a\ude6f|\ud81a[\uded0-\udeed]|\ud81a\udef5|\ud81a[\udf00-\udf2f]|\ud81a[\udf37-\udf45]|\ud81a[\udf50-\udf59]|\ud81a[\udf5b-\udf61]|\ud81a[\udf63-\udf77]|\ud81a[\udf7d-\udf8f]|\ud81b[\udf00-\udf44]|\ud81b[\udf50-\udf7e]|\ud81b[\udf93-\udf9f]|\ud82c\udc00|\ud82c\udc01|\ud82f[\udc00-\udc6a]|\ud82f[\udc70-\udc7c]|\ud82f[\udc80-\udc88]|\ud82f[\udc90-\udc99]|\ud82f\udc9c|\ud82f\udc9f|\ud834[\udc00-\udcf5]|\ud834[\udd00-\udd26]|\ud834[\udd29-\udd66]|\ud834[\udd6a-\udd72]|\ud834\udd83|\ud834\udd84|\ud834[\udd8c-\udda9]|\ud834[\uddae-\udde8]|\ud834[\udf60-\udf71]|\ud835[\udc00-\udc54]|\ud835[\udc56-\udc9c]|\ud835\udc9e|\ud835\udc9f|\ud835\udca2|\ud835\udca5|\ud835\udca6|\ud835[\udca9-\udcac]|\ud835[\udcae-\udcb9]|\ud835\udcbb|\ud835[\udcbd-\udcc3]|\ud835[\udcc5-\udd05]|\ud835[\udd07-\udd0a]|\ud835[\udd0d-\udd14]|\ud835[\udd16-\udd1c]|\ud835[\udd1e-\udd39]|\ud835[\udd3b-\udd3e]|\ud835[\udd40-\udd44]|\ud835\udd46|\ud835[\udd4a-\udd50]|\ud835[\udd52-\udea5]|\ud835[\udea8-\udeda]|\ud835[\udedc-\udf14]|\ud835[\udf16-\udf4e]|\ud835[\udf50-\udf88]|\ud835[\udf8a-\udfc2]|\ud835[\udfc4-\udfcb]|\ud836[\udc00-\uddff]|\ud836[\ude37-\ude3a]|\ud836[\ude6d-\ude74]|\ud836[\ude76-\ude83]|\ud836[\ude85-\ude8b]|\ud83c[\udd10-\udd2e]|\ud83c[\udd30-\udd69]|\ud83c[\udd70-\udd9a]|\ud83c[\udde6-\ude02]|\ud83c[\ude10-\ude3a]|\ud83c[\ude40-\ude48]|\ud83c\ude50|\ud83c\ude51|[\ud840-\ud868][\udc00-\udfff]|\ud869[\udc00-\uded6]|\ud869[\udf00-\udfff]|[\ud86a-\ud86c][\udc00-\udfff]|\ud86d[\udc00-\udf34]|\ud86d[\udf40-\udfff]|\ud86e[\udc00-\udc1d]|\ud86e[\udc20-\udfff]|[\ud86f-\ud872][\udc00-\udfff]|\ud873[\udc00-\udea1]|\ud87e[\udc00-\ude1d]|[\udb80-\udbbe][\udc00-\udfff]|\udbbf[\udc00-\udffd]|[\udbc0-\udbfe][\udc00-\udfff]|\udbff[\udc00-\udffd]'
 +
+                       ')|(' +
+                               
'[\u0590\u05be\u05c0\u05c3\u05c6\u05c8-\u05ff\u07c0-\u07ea\u07f4\u07f5\u07fa-\u0815\u081a\u0824\u0828\u082e-\u0858\u085c-\u089f\u200f\ufb1d\ufb1f-\ufb28\ufb2a-\ufb4f\u0608\u060b\u060d\u061b-\u064a\u066d-\u066f\u0671-\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u0710\u0712-\u072f\u074b-\u07a5\u07b1-\u07bf\u08a0-\u08e2\ufb50-\ufd3d\ufd40-\ufdcf\ufdf0-\ufdfc\ufdfe\ufdff\ufe70-\ufefe]|\ud802[\udc00-\udd1e]|\ud802[\udd20-\ude00]|\ud802\ude04|\ud802[\ude07-\ude0b]|\ud802[\ude10-\ude37]|\ud802[\ude3b-\ude3e]|\ud802[\ude40-\udee4]|\ud802[\udee7-\udf38]|\ud802[\udf40-\udfff]|\ud803[\udc00-\ude5f]|\ud803[\ude7f-\udfff]|\ud83a[\udc00-\udccf]|\ud83a[\udcd7-\udfff]|\ud83b[\udc00-\uddff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\ude00-\udeef]|\ud83b[\udef2-\udeff]'
 +
+                       ')' +
+               ')'
+       );
+
+       /**
+        * Gets directionality of the first strongly directional codepoint
+        *
+        * This is the rule the BIDI algorithm uses to determine the 
directionality of
+        * paragraphs ( http://unicode.org/reports/tr9/#The_Paragraph_Level ) 
and
+        * FSI isolates ( 
http://unicode.org/reports/tr9/#Explicit_Directional_Isolates ).
+        *
+        * TODO: Does not handle BIDI control characters inside the text.
+        * TODO: Does not handle unallocated characters.
+        */
+       function strongDirFromContent( text ) {
+               var m = text.match( strongDirRegExp );
+               if ( !m ) {
+                       return null;
+               }
+               if ( m[2] === undefined ) {
+                       return 'ltr';
+               }
+               return 'rtl';
+       }
+
+       $.extend( $.i18n.parser.emitter, {
+               /**
+                * Wraps argument with unicode control characters for 
directionality safety
+                *
+                * This solves the problem where directionality-neutral 
characters at the edge of
+                * the argument string get interpreted with the wrong 
directionality from the
+                * enclosing context, giving renderings that look corrupted 
like "(Ben_(WMF".
+                *
+                * The wrapping is LRE...PDF or RLE...PDF, depending on the 
detected
+                * directionality of the argument string, using the BIDI 
algorithm's own "First
+                * strong directional codepoint" rule. Essentially, this works 
round the fact that
+                * there is no embedding equivalent of U+2068 FSI (isolation 
with heuristic
+                * direction inference). The latter is cleaner but still not 
widely supported.
+                */
+               bidi: function ( nodes ) {
+                       var dir = strongDirFromContent( nodes[0] );
+                       if ( dir === 'ltr' ) {
+                               // Wrap in LEFT-TO-RIGHT EMBEDDING ... POP 
DIRECTIONAL FORMATTING
+                               return '\u202A' + nodes[0] + '\u202C';
+                       }
+                       if ( dir === 'rtl' ) {
+                               // Wrap in RIGHT-TO-LEFT EMBEDDING ... POP 
DIRECTIONAL FORMATTING
+                               return '\u202B' + nodes[0] + '\u202C';
+                       }
+                       // No strong directionality: do not wrap
+                       return nodes[0];
+               }
+       } );
+}( jQuery ) );
diff --git a/lib/jquery.i18n/src/jquery.i18n.fallbacks.js 
b/lib/jquery.i18n/src/jquery.i18n.fallbacks.js
index 0b2eafb..4584c5f 100644
--- a/lib/jquery.i18n/src/jquery.i18n.fallbacks.js
+++ b/lib/jquery.i18n/src/jquery.i18n.fallbacks.js
@@ -16,171 +16,171 @@
 
        $.i18n = $.i18n || {};
        $.extend( $.i18n.fallbacks, {
-               'ab': ['ru'],
-               'ace': ['id'],
-               'aln': ['sq'],
+               ab: [ 'ru' ],
+               ace: [ 'id' ],
+               aln: [ 'sq' ],
                // Not so standard - als is supposed to be Tosk Albanian,
                // but in Wikipedia it's used for a Germanic language.
-               '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']
+               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 ) );
diff --git a/lib/jquery.i18n/src/jquery.i18n.js 
b/lib/jquery.i18n/src/jquery.i18n.js
index 3f76549..9236e4e 100644
--- a/lib/jquery.i18n/src/jquery.i18n.js
+++ b/lib/jquery.i18n/src/jquery.i18n.js
@@ -96,18 +96,40 @@
 
                /**
                 * General message loading API This can take a URL string for
-                * the json formatted messages.
+                * the json formatted messages. Example:
                 * <code>load('path/to/all_localizations.json');</code>
                 *
-                * This can also load a localization file for a locale <code>
+                * To load a localization file for a locale:
+                * <code>
                 * load('path/to/de-messages.json', 'de' );
                 * </code>
+                *
+                * To load a localization file from a directory:
+                * <code>
+                * load('path/to/i18n/directory', 'de' );
+                * </code>
+                * The above method has the advantage of fallback resolution.
+                * ie, it will automatically load the fallback locales for de.
+                * For most usecases, this is the recommended method.
+                * It is optional to have trailing slash at end.
+                *
                 * A data object containing message key- message translation 
mappings
-                * can also be passed Eg:
+                * can also be passed. Example:
                 * <code>
                 * load( { 'hello' : 'Hello' }, optionalLocale );
-                * </code> If the data argument is
-                * null/undefined/false,
+                * </code>
+                *
+                * A source map containing key-value pair of languagename and 
locations
+                * can also be passed. Example:
+                * <code>
+                * load( {
+                * bn: 'i18n/bn.json',
+                * he: 'i18n/he.json',
+                * en: 'i18n/en.json'
+                * } )
+                * </code>
+                *
+                * If the data argument is null/undefined/false,
                 * all cached messages for the i18n instance will get reset.
                 *
                 * @param {String|Object} source
@@ -115,7 +137,27 @@
                 * @returns {jQuery.Promise}
                 */
                load: function ( source, locale ) {
-                       return this.messageStore.load( source, locale );
+                       var fallbackLocales, locIndex, fallbackLocale, 
sourceMap = {};
+                       if ( !source && !locale ) {
+                               source = 'i18n/' + $.i18n().locale + '.json';
+                               locale = $.i18n().locale;
+                       }
+                       if ( typeof source === 'string' &&
+                               source.split( '.' ).pop() !== 'json'
+                       ) {
+                               // Load specified locale then check for 
fallbacks when directory is specified in load()
+                               sourceMap[locale] = source + '/' + locale + 
'.json';
+                               fallbackLocales = ( $.i18n.fallbacks[locale] || 
[] )
+                                       .concat( this.options.fallbackLocale );
+                               for ( locIndex in fallbackLocales ) {
+                                       fallbackLocale = 
fallbackLocales[locIndex];
+                                       sourceMap[fallbackLocale] = source + 
'/' + fallbackLocale + '.json';
+                               }
+                               return this.load( sourceMap );
+                       } else {
+                               return this.messageStore.load( source, locale );
+                       }
+
                },
 
                /**
@@ -131,7 +173,7 @@
                        // should probably not change the 'this.parser' but just
                        // pass it to the parser.
                        this.parser.language = 
$.i18n.languages[$.i18n().locale] || $.i18n.languages['default'];
-                       if( message === '' ) {
+                       if ( message === '' ) {
                                message = key;
                        }
                        return this.parser.parse( message, parameters );
diff --git a/lib/jquery.i18n/src/jquery.i18n.language.js 
b/lib/jquery.i18n/src/jquery.i18n.language.js
index ee6eacb..3ce0a99 100644
--- a/lib/jquery.i18n/src/jquery.i18n.language.js
+++ b/lib/jquery.i18n/src/jquery.i18n.language.js
@@ -5,261 +5,262 @@
        var language = {
                // CLDR plural rules generated using
                // libs/CLDRPluralRuleParser/tools/PluralXML2JSON.html
-               'pluralRules': {
-                       'ak': {
-                               'one': 'n = 0..1'
+               pluralRules: {
+                       ak: {
+                               one: 'n = 0..1'
                        },
-                       'am': {
-                               'one': 'i = 0 or n = 1'
+                       am: {
+                               one: 'i = 0 or n = 1'
                        },
-                       'ar': {
-                               'zero': 'n = 0',
-                               'one': 'n = 1',
-                               'two': 'n = 2',
-                               'few': 'n % 100 = 3..10',
-                               'many': 'n % 100 = 11..99'
+                       ar: {
+                               zero: 'n = 0',
+                               one: 'n = 1',
+                               two: 'n = 2',
+                               few: 'n % 100 = 3..10',
+                               many: 'n % 100 = 11..99'
                        },
-                       'be': {
-                               'one': 'n % 10 = 1 and n % 100 != 11',
-                               'few': 'n % 10 = 2..4 and n % 100 != 12..14',
-                               'many': 'n % 10 = 0 or n % 10 = 5..9 or n % 100 
= 11..14'
+                       be: {
+                               one: 'n % 10 = 1 and n % 100 != 11',
+                               few: 'n % 10 = 2..4 and n % 100 != 12..14',
+                               many: 'n % 10 = 0 or n % 10 = 5..9 or n % 100 = 
11..14'
                        },
-                       'bh': {
-                               'one': 'n = 0..1'
+                       bh: {
+                               one: 'n = 0..1'
                        },
-                       'bn': {
-                               'one': 'i = 0 or n = 1'
+                       bn: {
+                               one: 'i = 0 or n = 1'
                        },
-                       'br': {
-                               'one': 'n % 10 = 1 and n % 100 != 11,71,91',
-                               'two': 'n % 10 = 2 and n % 100 != 12,72,92',
-                               'few': 'n % 10 = 3..4,9 and n % 100 != 
10..19,70..79,90..99',
-                               'many': 'n != 0 and n % 1000000 = 0'
+                       br: {
+                               one: 'n % 10 = 1 and n % 100 != 11,71,91',
+                               two: 'n % 10 = 2 and n % 100 != 12,72,92',
+                               few: 'n % 10 = 3..4,9 and n % 100 != 
10..19,70..79,90..99',
+                               many: 'n != 0 and n % 1000000 = 0'
                        },
-                       'bs': {
-                               'one': 'v = 0 and i % 10 = 1 and i % 100 != 11 
or f % 10 = 1 and f % 100 != 11',
-                               'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14 or f % 10 = 2..4 and f % 100 != 12..14'
+                       bs: {
+                               one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or 
f % 10 = 1 and f % 100 != 11',
+                               few: 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14 or f % 10 = 2..4 and f % 100 != 12..14'
                        },
-                       'cs': {
-                               'one': 'i = 1 and v = 0',
-                               'few': 'i = 2..4 and v = 0',
-                               'many': 'v != 0'
+                       cs: {
+                               one: 'i = 1 and v = 0',
+                               few: 'i = 2..4 and v = 0',
+                               many: 'v != 0'
                        },
-                       'cy': {
-                               'zero': 'n = 0',
-                               'one': 'n = 1',
-                               'two': 'n = 2',
-                               'few': 'n = 3',
-                               'many': 'n = 6'
+                       cy: {
+                               zero: 'n = 0',
+                               one: 'n = 1',
+                               two: 'n = 2',
+                               few: 'n = 3',
+                               many: 'n = 6'
                        },
-                       'da': {
-                               'one': 'n = 1 or t != 0 and i = 0,1'
+                       da: {
+                               one: 'n = 1 or t != 0 and i = 0,1'
                        },
-                       'fa': {
-                               'one': 'i = 0 or n = 1'
+                       fa: {
+                               one: 'i = 0 or n = 1'
                        },
-                       'ff': {
-                               'one': 'i = 0,1'
+                       ff: {
+                               one: 'i = 0,1'
                        },
-                       'fil': {
-                               'one': 'i = 0..1 and v = 0'
+                       fil: {
+                               one: 'i = 0..1 and v = 0'
                        },
-                       'fr': {
-                               'one': 'i = 0,1'
+                       fr: {
+                               one: 'i = 0,1'
                        },
-                       'ga': {
-                               'one': 'n = 1',
-                               'two': 'n = 2',
-                               'few': 'n = 3..6',
-                               'many': 'n = 7..10'
+                       ga: {
+                               one: 'n = 1',
+                               two: 'n = 2',
+                               few: 'n = 3..6',
+                               many: 'n = 7..10'
                        },
-                       'gd': {
-                               'one': 'n = 1,11',
-                               'two': 'n = 2,12',
-                               'few': 'n = 3..10,13..19'
+                       gd: {
+                               one: 'n = 1,11',
+                               two: 'n = 2,12',
+                               few: 'n = 3..10,13..19'
                        },
-                       'gu': {
-                               'one': 'i = 0 or n = 1'
+                       gu: {
+                               one: 'i = 0 or n = 1'
                        },
-                       'guw': {
-                               'one': 'n = 0..1'
+                       guw: {
+                               one: 'n = 0..1'
                        },
-                       'gv': {
-                               'one': 'n % 10 = 1',
-                               'two': 'n % 10 = 2',
-                               'few': 'n % 100 = 0,20,40,60'
+                       gv: {
+                               one: 'n % 10 = 1',
+                               two: 'n % 10 = 2',
+                               few: 'n % 100 = 0,20,40,60'
                        },
-                       'he': {
-                               'one': 'i = 1 and v = 0',
-                               'two': 'i = 2 and v = 0',
-                               'many': 'v = 0 and n != 0..10 and n % 10 = 0'
+                       he: {
+                               one: 'i = 1 and v = 0',
+                               two: 'i = 2 and v = 0',
+                               many: 'v = 0 and n != 0..10 and n % 10 = 0'
                        },
-                       'hi': {
-                               'one': 'i = 0 or n = 1'
+                       hi: {
+                               one: 'i = 0 or n = 1'
                        },
-                       'hr': {
-                               'one': 'v = 0 and i % 10 = 1 and i % 100 != 11 
or f % 10 = 1 and f % 100 != 11',
-                               'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14 or f % 10 = 2..4 and f % 100 != 12..14'
+                       hr: {
+                               one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or 
f % 10 = 1 and f % 100 != 11',
+                               few: 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14 or f % 10 = 2..4 and f % 100 != 12..14'
                        },
-                       'hy': {
-                               'one': 'i = 0,1'
+                       hy: {
+                               one: 'i = 0,1'
                        },
-                       'is': {
-                               'one': 't = 0 and i % 10 = 1 and i % 100 != 11 
or t != 0'
+                       is: {
+                               one: 't = 0 and i % 10 = 1 and i % 100 != 11 or 
t != 0'
                        },
-                       'iu': {
-                               'one': 'n = 1',
-                               'two': 'n = 2'
+                       iu: {
+                               one: 'n = 1',
+                               two: 'n = 2'
                        },
-                       'iw': {
-                               'one': 'i = 1 and v = 0',
-                               'two': 'i = 2 and v = 0',
-                               'many': 'v = 0 and n != 0..10 and n % 10 = 0'
+                       iw: {
+                               one: 'i = 1 and v = 0',
+                               two: 'i = 2 and v = 0',
+                               many: 'v = 0 and n != 0..10 and n % 10 = 0'
                        },
-                       'kab': {
-                               'one': 'i = 0,1'
+                       kab: {
+                               one: 'i = 0,1'
                        },
-                       'kn': {
-                               'one': 'i = 0 or n = 1'
+                       kn: {
+                               one: 'i = 0 or n = 1'
                        },
-                       'kw': {
-                               'one': 'n = 1',
-                               'two': 'n = 2'
+                       kw: {
+                               one: 'n = 1',
+                               two: 'n = 2'
                        },
-                       'lag': {
-                               'zero': 'n = 0',
-                               'one': 'i = 0,1 and n != 0'
+                       lag: {
+                               zero: 'n = 0',
+                               one: 'i = 0,1 and n != 0'
                        },
-                       'ln': {
-                               'one': 'n = 0..1'
+                       ln: {
+                               one: 'n = 0..1'
                        },
-                       'lt': {
-                               'one': 'n % 10 = 1 and n % 100 != 11..19',
-                               'few': 'n % 10 = 2..9 and n % 100 != 11..19',
-                               'many': 'f != 0'
+                       lt: {
+                               one: 'n % 10 = 1 and n % 100 != 11..19',
+                               few: 'n % 10 = 2..9 and n % 100 != 11..19',
+                               many: 'f != 0'
                        },
-                       'lv': {
-                               'zero': 'n % 10 = 0 or n % 100 = 11..19 or v = 
2 and f % 100 = 11..19',
-                               'one': 'n % 10 = 1 and n % 100 != 11 or v = 2 
and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1'
+                       lv: {
+                               zero: 'n % 10 = 0 or n % 100 = 11..19 or v = 2 
and f % 100 = 11..19',
+                               one: 'n % 10 = 1 and n % 100 != 11 or v = 2 and 
f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1'
                        },
-                       'mg': {
-                               'one': 'n = 0..1'
+                       mg: {
+                               one: 'n = 0..1'
                        },
-                       'mk': {
-                               'one': 'v = 0 and i % 10 = 1 or f % 10 = 1'
+                       mk: {
+                               one: 'v = 0 and i % 10 = 1 or f % 10 = 1'
                        },
-                       'mo': {
-                               'one': 'i = 1 and v = 0',
-                               'few': 'v != 0 or n = 0 or n != 1 and n % 100 = 
1..19'
+                       mo: {
+                               one: 'i = 1 and v = 0',
+                               few: 'v != 0 or n = 0 or n != 1 and n % 100 = 
1..19'
                        },
-                       'mr': {
-                               'one': 'i = 0 or n = 1'
+                       mr: {
+                               one: 'i = 0 or n = 1'
                        },
-                       'mt': {
-                               'one': 'n = 1',
-                               'few': 'n = 0 or n % 100 = 2..10',
-                               'many': 'n % 100 = 11..19'
+                       mt: {
+                               one: 'n = 1',
+                               few: 'n = 0 or n % 100 = 2..10',
+                               many: 'n % 100 = 11..19'
                        },
-                       'naq': {
-                               'one': 'n = 1',
-                               'two': 'n = 2'
+                       naq: {
+                               one: 'n = 1',
+                               two: 'n = 2'
                        },
-                       'nso': {
-                               'one': 'n = 0..1'
+                       nso: {
+                               one: 'n = 0..1'
                        },
-                       'pa': {
-                               'one': 'n = 0..1'
+                       pa: {
+                               one: 'n = 0..1'
                        },
-                       'pl': {
-                               'one': 'i = 1 and v = 0',
-                               'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14',
-                               'many': 'v = 0 and i != 1 and i % 10 = 0..1 or 
v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14'
+                       pl: {
+                               one: 'i = 1 and v = 0',
+                               few: 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14',
+                               many: 'v = 0 and i != 1 and i % 10 = 0..1 or v 
= 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14'
                        },
-                       'pt': {
-                               'one': 'i = 1 and v = 0 or i = 0 and t = 1'
+                       pt: {
+                               one: 'i = 1 and v = 0 or i = 0 and t = 1'
                        },
-                       'pt_PT': {
-                               'one': 'n = 1 and v = 0'
+                       // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
+                       pt_PT: {
+                               one: 'n = 1 and v = 0'
                        },
-                       'ro': {
-                               'one': 'i = 1 and v = 0',
-                               'few': 'v != 0 or n = 0 or n != 1 and n % 100 = 
1..19'
+                       // jscs:enable requireCamelCaseOrUpperCaseIdentifiers
+                       ro: {
+                               one: 'i = 1 and v = 0',
+                               few: 'v != 0 or n = 0 or n != 1 and n % 100 = 
1..19'
                        },
-                       'ru': {
-                               'one': 'v = 0 and i % 10 = 1 and i % 100 != 11',
-                               'many': 'v = 0 and i % 10 = 0 or v = 0 and i % 
10 = 5..9 or v = 0 and i % 100 = 11..14'
+                       ru: {
+                               one: 'v = 0 and i % 10 = 1 and i % 100 != 11',
+                               many: 'v = 0 and i % 10 = 0 or v = 0 and i % 10 
= 5..9 or v = 0 and i % 100 = 11..14'
                        },
-                       'se': {
-                               'one': 'n = 1',
-                               'two': 'n = 2'
+                       se: {
+                               one: 'n = 1',
+                               two: 'n = 2'
                        },
-                       'sh': {
-                               'one': 'v = 0 and i % 10 = 1 and i % 100 != 11 
or f % 10 = 1 and f % 100 != 11',
-                               'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14 or f % 10 = 2..4 and f % 100 != 12..14'
+                       sh: {
+                               one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or 
f % 10 = 1 and f % 100 != 11',
+                               few: 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14 or f % 10 = 2..4 and f % 100 != 12..14'
                        },
-                       'shi': {
-                               'one': 'i = 0 or n = 1',
-                               'few': 'n = 2..10'
+                       shi: {
+                               one: 'i = 0 or n = 1',
+                               few: 'n = 2..10'
                        },
-                       'si': {
-                               'one': 'n = 0,1 or i = 0 and f = 1'
+                       si: {
+                               one: 'n = 0,1 or i = 0 and f = 1'
                        },
-                       'sk': {
-                               'one': 'i = 1 and v = 0',
-                               'few': 'i = 2..4 and v = 0',
-                               'many': 'v != 0'
+                       sk: {
+                               one: 'i = 1 and v = 0',
+                               few: 'i = 2..4 and v = 0',
+                               many: 'v != 0'
                        },
-                       'sl': {
-                               'one': 'v = 0 and i % 100 = 1',
-                               'two': 'v = 0 and i % 100 = 2',
-                               'few': 'v = 0 and i % 100 = 3..4 or v != 0'
+                       sl: {
+                               one: 'v = 0 and i % 100 = 1',
+                               two: 'v = 0 and i % 100 = 2',
+                               few: 'v = 0 and i % 100 = 3..4 or v != 0'
                        },
-                       'sma': {
-                               'one': 'n = 1',
-                               'two': 'n = 2'
+                       sma: {
+                               one: 'n = 1',
+                               two: 'n = 2'
                        },
-                       'smi': {
-                               'one': 'n = 1',
-                               'two': 'n = 2'
+                       smi: {
+                               one: 'n = 1',
+                               two: 'n = 2'
                        },
-                       'smj': {
-                               'one': 'n = 1',
-                               'two': 'n = 2'
+                       smj: {
+                               one: 'n = 1',
+                               two: 'n = 2'
                        },
-                       'smn': {
-                               'one': 'n = 1',
-                               'two': 'n = 2'
+                       smn: {
+                               one: 'n = 1',
+                               two: 'n = 2'
                        },
-                       'sms': {
-                               'one': 'n = 1',
-                               'two': 'n = 2'
+                       sms: {
+                               one: 'n = 1',
+                               two: 'n = 2'
                        },
-                       'sr': {
-                               'one': 'v = 0 and i % 10 = 1 and i % 100 != 11 
or f % 10 = 1 and f % 100 != 11',
-                               'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14 or f % 10 = 2..4 and f % 100 != 12..14'
+                       sr: {
+                               one: 'v = 0 and i % 10 = 1 and i % 100 != 11 or 
f % 10 = 1 and f % 100 != 11',
+                               few: 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14 or f % 10 = 2..4 and f % 100 != 12..14'
                        },
-                       'ti': {
-                               'one': 'n = 0..1'
+                       ti: {
+                               one: 'n = 0..1'
                        },
-                       'tl': {
-                               'one': 'i = 0..1 and v = 0'
+                       tl: {
+                               one: 'i = 0..1 and v = 0'
                        },
-                       'tzm': {
-                               'one': 'n = 0..1 or n = 11..99'
+                       tzm: {
+                               one: 'n = 0..1 or n = 11..99'
                        },
-                       'uk': {
-                               'one': 'v = 0 and i % 10 = 1 and i % 100 != 11',
-                               'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14',
-                               'many': 'v = 0 and i % 10 = 0 or v = 0 and i % 
10 = 5..9 or v = 0 and i % 100 = 11..14'
+                       uk: {
+                               one: 'v = 0 and i % 10 = 1 and i % 100 != 11',
+                               few: 'v = 0 and i % 10 = 2..4 and i % 100 != 
12..14',
+                               many: 'v = 0 and i % 10 = 0 or v = 0 and i % 10 
= 5..9 or v = 0 and i % 100 = 11..14'
                        },
-                       'wa': {
-                               'one': 'n = 0..1'
+                       wa: {
+                               one: 'n = 0..1'
                        },
-                       'zu': {
-                               'one': 'i = 0 or n = 1'
+                       zu: {
+                               one: 'i = 0 or n = 1'
                        }
                },
-
 
                /**
                 * Plural form transformations, needed for some languages.
@@ -274,7 +275,7 @@
                        var pluralRules,
                                pluralFormIndex,
                                index,
-                               explicitPluralPattern = new RegExp('\\d+=', 
'i'),
+                               explicitPluralPattern = new RegExp( '\\d+=', 
'i' ),
                                formCount,
                                form;
 
@@ -344,13 +345,13 @@
                 * @param {number} num Value to be converted
                 * @param {boolean} integer Convert the return value to an 
integer
                 */
-               'convertNumber': function ( num, integer ) {
+               convertNumber: function ( num, integer ) {
                        var tmp, item, i,
                                transformTable, numberString, convertedNumber;
 
                        // Set the target Transform table:
                        transformTable = this.digitTransformTable( 
$.i18n().locale );
-                       numberString = '' + num;
+                       numberString = String( num );
                        convertedNumber = '';
 
                        if ( !transformTable ) {
@@ -411,7 +412,7 @@
                 *
                 * @return string
                 */
-               'gender': function ( gender, forms ) {
+               gender: function ( gender, forms ) {
                        if ( !forms || forms.length === 0 ) {
                                return '';
                        }
@@ -453,8 +454,8 @@
                                my: '၀၁၂၃၄၅၆၇၈၉',
                                ta: '௦௧௨௩௪௫௬௭௮௯',
                                te: '౦౧౨౩౪౫౬౭౮౯',
-                               th: '๐๑๒๓๔๕๖๗๘๙', //FIXME use iso 639 codes
-                               bo: '༠༡༢༣༤༥༦༧༨༩' //FIXME use iso 639 codes
+                               th: '๐๑๒๓๔๕๖๗๘๙', // FIXME use iso 639 codes
+                               bo: '༠༡༢༣༤༥༦༧༨༩' // FIXME use iso 639 codes
                        };
 
                        if ( !tables[language] ) {
@@ -466,6 +467,6 @@
        };
 
        $.extend( $.i18n.languages, {
-               'default': language
+               default: language
        } );
 }( jQuery ) );
diff --git a/lib/jquery.i18n/src/jquery.i18n.messagestore.js 
b/lib/jquery.i18n/src/jquery.i18n.messagestore.js
index 4a28f0e..759295c 100644
--- a/lib/jquery.i18n/src/jquery.i18n.messagestore.js
+++ b/lib/jquery.i18n/src/jquery.i18n.messagestore.js
@@ -88,7 +88,7 @@
                 * @param locale
                 * @param messages
                 */
-               set: function( locale, messages ) {
+               set: function ( locale, messages ) {
                        if ( !this.messages[locale] ) {
                                this.messages[locale] = messages;
                        } else {
@@ -108,9 +108,17 @@
        };
 
        function jsonMessageLoader( url ) {
-               return $.getJSON( url ).fail( function ( jqxhr, settings, 
exception ) {
-                       $.i18n.log( 'Error in loading messages from ' + url + ' 
Exception: ' + exception );
-               } );
+               var deferred = $.Deferred();
+
+               $.getJSON( url )
+                       .done( deferred.resolve )
+                       .fail( function ( jqxhr, settings, exception ) {
+                               $.i18n.log( 'Error in loading messages from ' + 
url + ' Exception: ' + exception );
+                               // Ignore 404 exception, because we are 
handling fallabacks explicitly
+                               deferred.resolve();
+                       } );
+
+               return deferred.promise();
        }
 
        $.extend( $.i18n.messageStore, new MessageStore() );
diff --git a/lib/jquery.i18n/src/jquery.i18n.parser.js 
b/lib/jquery.i18n/src/jquery.i18n.parser.js
index 7dc26e6..3dea284 100644
--- a/lib/jquery.i18n/src/jquery.i18n.parser.js
+++ b/lib/jquery.i18n/src/jquery.i18n.parser.js
@@ -53,7 +53,7 @@
                                pos = 0;
 
                        // Try parsers until one works, if none work return null
-                       function choice ( parserSyntax ) {
+                       function choice( parserSyntax ) {
                                return function () {
                                        var i, result;
 
@@ -72,7 +72,7 @@
                        // Try several parserSyntax-es in a row.
                        // All must succeed; otherwise, return null.
                        // This is the only eager one.
-                       function sequence ( parserSyntax ) {
+                       function sequence( parserSyntax ) {
                                var i, res,
                                        originalPos = pos,
                                        result = [];
@@ -94,7 +94,7 @@
 
                        // Run the same parser over and over until it fails.
                        // Must succeed a minimum of n times; otherwise, return 
null.
-                       function nOrMore ( n, p ) {
+                       function nOrMore( n, p ) {
                                return function () {
                                        var originalPos = pos,
                                                result = [],
@@ -117,7 +117,7 @@
 
                        // Helpers -- just make parserSyntax out of simpler JS 
builtin types
 
-                       function makeStringParser ( s ) {
+                       function makeStringParser( s ) {
                                var len = s.length;
 
                                return function () {
@@ -132,7 +132,7 @@
                                };
                        }
 
-                       function makeRegexParser ( regex ) {
+                       function makeRegexParser( regex ) {
                                return function () {
                                        var matches = message.substr( pos 
).match( regex );
 
@@ -163,7 +163,7 @@
                        // But using this as a combinator seems to cause 
problems
                        // when combined with nOrMore().
                        // May be some scoping issue.
-                       function transform ( p, fn ) {
+                       function transform( p, fn ) {
                                return function () {
                                        var result = p();
 
@@ -174,19 +174,19 @@
                        // 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 () {
+                       function literalWithoutBar() {
                                var result = nOrMore( 1, 
escapedOrLiteralWithoutBar )();
 
                                return result === null ? null : result.join( '' 
);
                        }
 
-                       function literal () {
+                       function literal() {
                                var result = nOrMore( 1, 
escapedOrRegularLiteral )();
 
                                return result === null ? null : result.join( '' 
);
                        }
 
-                       function escapedLiteral () {
+                       function escapedLiteral() {
                                var result = sequence( [ backslash, 
anyCharacter ] );
 
                                return result === null ? null : result[1];
@@ -196,7 +196,7 @@
                        escapedOrLiteralWithoutBar = choice( [ escapedLiteral, 
regularLiteralWithoutBar ] );
                        escapedOrRegularLiteral = choice( [ escapedLiteral, 
regularLiteral ] );
 
-                       function replacement () {
+                       function replacement() {
                                var result = sequence( [ dollar, digits ] );
 
                                if ( result === null ) {
@@ -216,7 +216,7 @@
                                }
                        );
 
-                       function templateParam () {
+                       function templateParam() {
                                var expr,
                                        result = sequence( [ pipe, nOrMore( 0, 
paramExpression ) ] );
 
@@ -231,13 +231,13 @@
                                return expr.length > 1 ? [ 'CONCAT' ].concat( 
expr ) : expr[0];
                        }
 
-                       function templateWithReplacement () {
+                       function templateWithReplacement() {
                                var result = sequence( [ templateName, colon, 
replacement ] );
 
                                return result === null ? null : [ result[0], 
result[2] ];
                        }
 
-                       function templateWithOutReplacement () {
+                       function templateWithOutReplacement() {
                                var result = sequence( [ templateName, colon, 
paramExpression ] );
 
                                return result === null ? null : [ result[0], 
result[2] ];
@@ -270,7 +270,7 @@
                        openTemplate = makeStringParser( '{{' );
                        closeTemplate = makeStringParser( '}}' );
 
-                       function template () {
+                       function template() {
                                var result = sequence( [ openTemplate, 
templateContents, closeTemplate ] );
 
                                return result === null ? null : result[1];
@@ -279,7 +279,7 @@
                        expression = choice( [ template, replacement, literal ] 
);
                        paramExpression = choice( [ template, replacement, 
literalWithoutBar ] );
 
-                       function start () {
+                       function start() {
                                var result = nOrMore( 0, expression )();
 
                                if ( result === null ) {
diff --git a/lib/jquery.i18n/src/languages/os.js 
b/lib/jquery.i18n/src/languages/os.js
index 67dccfb..4744367 100644
--- a/lib/jquery.i18n/src/languages/os.js
+++ b/lib/jquery.i18n/src/languages/os.js
@@ -20,22 +20,20 @@
                        // Variable for ending
                        ending = '';
 
-                       // Checking if the $word is in plural form
                        if ( word.match( /тæ$/i ) ) {
+                               // Checking if the $word is in plural form
                                word = word.substring( 0, word.length - 1 );
                                endAllative = 'æм';
-                       }
-                       // Works if word is in singular form.
-                       // Checking if word ends on one of the vowels: е, ё, и, 
о, ы, э, ю,
-                       // я.
-                       else if ( word.match( /[аæеёиоыэюя]$/i ) ) {
+                       } else if ( word.match( /[аæеёиоыэюя]$/i ) ) {
+                               // Works if word is in singular form.
+                               // Checking if word ends on one of the vowels: 
е, ё, и, о, ы, э, ю,
+                               // я.
                                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 ) ) {
+                       } else if ( word.match( /у$/i ) ) {
+                               // Checking if word ends on 'у'. 'У' can be 
either consonant 'W' or
+                               // vowel 'U' in cyrillic Ossetic.
+                               // Examples: {{grammar:genitive|аунеу}} = 
аунеуы,
+                               // {{grammar:genitive|лæппу}} = лæппуйы.
                                if ( !word.substring( word.length - 2, 
word.length - 1 )
                                                .match( /[аæеёиоыэюя]$/i ) ) {
                                        jot = 'й';

-- 
To view, visit https://gerrit.wikimedia.org/r/223045
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I531f49bd8c2f60bd569c411d7d7025f91d1551e9
Gerrit-PatchSet: 1
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Jforrester <jforres...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to