You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by au...@apache.org on 2017/08/07 16:21:16 UTC
[2/4] cordova-plugin-globalization git commit: CB-12895 : added
eslint and removed jshint
http://git-wip-us.apache.org/repos/asf/cordova-plugin-globalization/blob/86e33d3f/www/firefoxos/l10n.js
----------------------------------------------------------------------
diff --git a/www/firefoxos/l10n.js b/www/firefoxos/l10n.js
index a2cd328..1253831 100644
--- a/www/firefoxos/l10n.js
+++ b/www/firefoxos/l10n.js
@@ -21,1597 +21,1568 @@
/* global unescape */
-(function(window, undefined) {
- 'use strict';
+(function (window, undefined) { // eslint-disable-line no-shadow-restricted-names
+ 'use strict';
+
+ /* jshint validthis:true */
+ function L10nError (message, id, loc) {
+ this.name = 'L10nError';
+ this.message = message;
+ this.id = id;
+ this.loc = loc;
+ }
+ L10nError.prototype = Object.create(Error.prototype);
+ L10nError.prototype.constructor = L10nError;
+
+ /* jshint browser:true */
+
+ var io = {
+ load: function load (url, callback, sync) {
+ var xhr = new XMLHttpRequest(); // eslint-disable-line no-undef
+
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType('text/plain');
+ }
+
+ xhr.open('GET', url, !sync);
+
+ xhr.addEventListener('load', function io_load (e) {
+ if (e.target.status === 200 || e.target.status === 0) {
+ callback(null, e.target.responseText);
+ } else {
+ callback(new L10nError('Not found: ' + url));
+ }
+ });
+ xhr.addEventListener('error', callback);
+ xhr.addEventListener('timeout', callback);
+
+ // the app: protocol throws on 404, see https://bugzil.la/827243
+ try {
+ xhr.send(null);
+ } catch (e) {
+ callback(new L10nError('Not found: ' + url));
+ }
+ },
+
+ loadJSON: function loadJSON (url, callback) {
+ var xhr = new XMLHttpRequest(); // eslint-disable-line no-undef
+
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType('application/json');
+ }
+
+ xhr.open('GET', url);
+
+ xhr.responseType = 'json';
+ xhr.addEventListener('load', function io_loadjson (e) {
+ if (e.target.status === 200 || e.target.status === 0) {
+ callback(null, e.target.response);
+ } else {
+ callback(new L10nError('Not found: ' + url));
+ }
+ });
+ xhr.addEventListener('error', callback);
+ xhr.addEventListener('timeout', callback);
+
+ // the app: protocol throws on 404, see https://bugzil.la/827243
+ try {
+ xhr.send(null);
+ } catch (e) {
+ callback(new L10nError('Not found: ' + url));
+ }
+ }
+ };
- /* jshint validthis:true */
- function L10nError(message, id, loc) {
- this.name = 'L10nError';
- this.message = message;
- this.id = id;
- this.loc = loc;
- }
- L10nError.prototype = Object.create(Error.prototype);
- L10nError.prototype.constructor = L10nError;
+ function EventEmitter () {}
+ EventEmitter.prototype.emit = function ee_emit () {
+ if (!this._listeners) {
+ return;
+ }
- /* jshint browser:true */
+ var args = Array.prototype.slice.call(arguments);
+ var type = args.shift();
+ if (!this._listeners[type]) {
+ return;
+ }
- var io = {
- load: function load(url, callback, sync) {
- var xhr = new XMLHttpRequest();
+ var typeListeners = this._listeners[type].slice();
+ for (var i = 0; i < typeListeners.length; i++) {
+ typeListeners[i].apply(this, args);
+ }
+ };
- if (xhr.overrideMimeType) {
- xhr.overrideMimeType('text/plain');
- }
+ EventEmitter.prototype.addEventListener = function ee_add (type, listener) {
+ if (!this._listeners) {
+ this._listeners = {};
+ }
+ if (!(type in this._listeners)) {
+ this._listeners[type] = [];
+ }
+ this._listeners[type].push(listener);
+ };
- xhr.open('GET', url, !sync);
+ EventEmitter.prototype.removeEventListener = function ee_rm (type, listener) {
+ if (!this._listeners) {
+ return;
+ }
- xhr.addEventListener('load', function io_load(e) {
- if (e.target.status === 200 || e.target.status === 0) {
- callback(null, e.target.responseText);
- } else {
- callback(new L10nError('Not found: ' + url));
+ var typeListeners = this._listeners[type];
+ var pos = typeListeners.indexOf(listener);
+ if (pos === -1) {
+ return;
}
- });
- xhr.addEventListener('error', callback);
- xhr.addEventListener('timeout', callback);
- // the app: protocol throws on 404, see https://bugzil.la/827243
- try {
- xhr.send(null);
- } catch (e) {
- callback(new L10nError('Not found: ' + url));
- }
- },
+ typeListeners.splice(pos, 1);
+ };
- loadJSON: function loadJSON(url, callback) {
- var xhr = new XMLHttpRequest();
+ function getPluralRule (lang) {
+ var locales2rules = {
+ 'af': 3,
+ 'ak': 4,
+ 'am': 4,
+ 'ar': 1,
+ 'asa': 3,
+ 'az': 0,
+ 'be': 11,
+ 'bem': 3,
+ 'bez': 3,
+ 'bg': 3,
+ 'bh': 4,
+ 'bm': 0,
+ 'bn': 3,
+ 'bo': 0,
+ 'br': 20,
+ 'brx': 3,
+ 'bs': 11,
+ 'ca': 3,
+ 'cgg': 3,
+ 'chr': 3,
+ 'cs': 12,
+ 'cy': 17,
+ 'da': 3,
+ 'de': 3,
+ 'dv': 3,
+ 'dz': 0,
+ 'ee': 3,
+ 'el': 3,
+ 'en': 3,
+ 'eo': 3,
+ 'es': 3,
+ 'et': 3,
+ 'eu': 3,
+ 'fa': 0,
+ 'ff': 5,
+ 'fi': 3,
+ 'fil': 4,
+ 'fo': 3,
+ 'fr': 5,
+ 'fur': 3,
+ 'fy': 3,
+ 'ga': 8,
+ 'gd': 24,
+ 'gl': 3,
+ 'gsw': 3,
+ 'gu': 3,
+ 'guw': 4,
+ 'gv': 23,
+ 'ha': 3,
+ 'haw': 3,
+ 'he': 2,
+ 'hi': 4,
+ 'hr': 11,
+ 'hu': 0,
+ 'id': 0,
+ 'ig': 0,
+ 'ii': 0,
+ 'is': 3,
+ 'it': 3,
+ 'iu': 7,
+ 'ja': 0,
+ 'jmc': 3,
+ 'jv': 0,
+ 'ka': 0,
+ 'kab': 5,
+ 'kaj': 3,
+ 'kcg': 3,
+ 'kde': 0,
+ 'kea': 0,
+ 'kk': 3,
+ 'kl': 3,
+ 'km': 0,
+ 'kn': 0,
+ 'ko': 0,
+ 'ksb': 3,
+ 'ksh': 21,
+ 'ku': 3,
+ 'kw': 7,
+ 'lag': 18,
+ 'lb': 3,
+ 'lg': 3,
+ 'ln': 4,
+ 'lo': 0,
+ 'lt': 10,
+ 'lv': 6,
+ 'mas': 3,
+ 'mg': 4,
+ 'mk': 16,
+ 'ml': 3,
+ 'mn': 3,
+ 'mo': 9,
+ 'mr': 3,
+ 'ms': 0,
+ 'mt': 15,
+ 'my': 0,
+ 'nah': 3,
+ 'naq': 7,
+ 'nb': 3,
+ 'nd': 3,
+ 'ne': 3,
+ 'nl': 3,
+ 'nn': 3,
+ 'no': 3,
+ 'nr': 3,
+ 'nso': 4,
+ 'ny': 3,
+ 'nyn': 3,
+ 'om': 3,
+ 'or': 3,
+ 'pa': 3,
+ 'pap': 3,
+ 'pl': 13,
+ 'ps': 3,
+ 'pt': 3,
+ 'rm': 3,
+ 'ro': 9,
+ 'rof': 3,
+ 'ru': 11,
+ 'rwk': 3,
+ 'sah': 0,
+ 'saq': 3,
+ 'se': 7,
+ 'seh': 3,
+ 'ses': 0,
+ 'sg': 0,
+ 'sh': 11,
+ 'shi': 19,
+ 'sk': 12,
+ 'sl': 14,
+ 'sma': 7,
+ 'smi': 7,
+ 'smj': 7,
+ 'smn': 7,
+ 'sms': 7,
+ 'sn': 3,
+ 'so': 3,
+ 'sq': 3,
+ 'sr': 11,
+ 'ss': 3,
+ 'ssy': 3,
+ 'st': 3,
+ 'sv': 3,
+ 'sw': 3,
+ 'syr': 3,
+ 'ta': 3,
+ 'te': 3,
+ 'teo': 3,
+ 'th': 0,
+ 'ti': 4,
+ 'tig': 3,
+ 'tk': 3,
+ 'tl': 4,
+ 'tn': 3,
+ 'to': 0,
+ 'tr': 0,
+ 'ts': 3,
+ 'tzm': 22,
+ 'uk': 11,
+ 'ur': 3,
+ 've': 3,
+ 'vi': 0,
+ 'vun': 3,
+ 'wa': 4,
+ 'wae': 3,
+ 'wo': 0,
+ 'xh': 3,
+ 'xog': 3,
+ 'yo': 0,
+ 'zh': 0,
+ 'zu': 3
+ };
+
+ // utility functions for plural rules methods
+ function isIn (n, list) {
+ return list.indexOf(n) !== -1;
+ }
+ function isBetween (n, start, end) {
+ return typeof n === typeof start && start <= n && n <= end;
+ }
+
+ // list of all plural rules methods:
+ // map an integer to the plural form name to use
+ var pluralRules = {
+ '0': function () {
+ return 'other';
+ },
+ '1': function (n) {
+ if ((isBetween((n % 100), 3, 10))) {
+ return 'few';
+ }
+ if (n === 0) {
+ return 'zero';
+ }
+ if ((isBetween((n % 100), 11, 99))) {
+ return 'many';
+ }
+ if (n === 2) {
+ return 'two';
+ }
+ if (n === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '2': function (n) {
+ if (n !== 0 && (n % 10) === 0) {
+ return 'many';
+ }
+ if (n === 2) {
+ return 'two';
+ }
+ if (n === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '3': function (n) {
+ if (n === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '4': function (n) {
+ if ((isBetween(n, 0, 1))) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '5': function (n) {
+ if ((isBetween(n, 0, 2)) && n !== 2) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '6': function (n) {
+ if (n === 0) {
+ return 'zero';
+ }
+ if ((n % 10) === 1 && (n % 100) !== 11) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '7': function (n) {
+ if (n === 2) {
+ return 'two';
+ }
+ if (n === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '8': function (n) {
+ if ((isBetween(n, 3, 6))) {
+ return 'few';
+ }
+ if ((isBetween(n, 7, 10))) {
+ return 'many';
+ }
+ if (n === 2) {
+ return 'two';
+ }
+ if (n === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '9': function (n) {
+ if ((n === 0 || n !== 1) && (isBetween((n % 100), 1, 19))) {
+ return 'few';
+ }
+ if (n === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '10': function (n) {
+ if ((isBetween((n % 10), 2, 9)) && !(isBetween((n % 100), 11, 19))) {
+ return 'few';
+ }
+ if ((n % 10) === 1 && !(isBetween((n % 100), 11, 19))) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '11': function (n) {
+ if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) {
+ return 'few';
+ }
+ if ((n % 10) === 0 ||
+ (isBetween((n % 10), 5, 9)) ||
+ (isBetween((n % 100), 11, 14))) {
+ return 'many';
+ }
+ if ((n % 10) === 1 && (n % 100) !== 11) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '12': function (n) {
+ if ((isBetween(n, 2, 4))) {
+ return 'few';
+ }
+ if (n === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '13': function (n) {
+ if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) {
+ return 'few';
+ }
+ if ((n !== 1 && (isBetween((n % 10), 0, 1))) ||
+ ((isBetween((n % 10), 5, 9))) ||
+ ((isBetween((n % 100), 12, 14)))) {
+ return 'many';
+ }
+ if (n === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '14': function (n) {
+ if ((isBetween((n % 100), 3, 4))) {
+ return 'few';
+ }
+ if ((n % 100) === 2) {
+ return 'two';
+ }
+ if ((n % 100) === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '15': function (n) {
+ if (n === 0 || (isBetween((n % 100), 2, 10))) {
+ return 'few';
+ }
+ if ((isBetween((n % 100), 11, 19))) {
+ return 'many';
+ }
+ if (n === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '16': function (n) {
+ if ((n % 10) === 1 && n !== 11) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '17': function (n) {
+ if (n === 3) {
+ return 'few';
+ }
+ if (n === 0) {
+ return 'zero';
+ }
+ if (n === 6) {
+ return 'many';
+ }
+ if (n === 2) {
+ return 'two';
+ }
+ if (n === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '18': function (n) {
+ if (n === 0) {
+ return 'zero';
+ }
+ if ((isBetween(n, 0, 2)) && n !== 0 && n !== 2) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '19': function (n) {
+ if ((isBetween(n, 2, 10))) {
+ return 'few';
+ }
+ if ((isBetween(n, 0, 1))) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '20': function (n) {
+ if ((isBetween((n % 10), 3, 4) || ((n % 10) === 9)) && !(
+ isBetween((n % 100), 10, 19) ||
+ isBetween((n % 100), 70, 79) ||
+ isBetween((n % 100), 90, 99)
+ )) {
+ return 'few';
+ }
+ if ((n % 1000000) === 0 && n !== 0) {
+ return 'many';
+ }
+ if ((n % 10) === 2 && !isIn((n % 100), [12, 72, 92])) {
+ return 'two';
+ }
+ if ((n % 10) === 1 && !isIn((n % 100), [11, 71, 91])) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '21': function (n) {
+ if (n === 0) {
+ return 'zero';
+ }
+ if (n === 1) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '22': function (n) {
+ if ((isBetween(n, 0, 1)) || (isBetween(n, 11, 99))) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '23': function (n) {
+ if ((isBetween((n % 10), 1, 2)) || (n % 20) === 0) {
+ return 'one';
+ }
+ return 'other';
+ },
+ '24': function (n) {
+ if ((isBetween(n, 3, 10) || isBetween(n, 13, 19))) {
+ return 'few';
+ }
+ if (isIn(n, [2, 12])) {
+ return 'two';
+ }
+ if (isIn(n, [1, 11])) {
+ return 'one';
+ }
+ return 'other';
+ }
+ };
+
+ // return a function that gives the plural form name for a given integer
+ var index = locales2rules[lang.replace(/-.*$/, '')];
+ if (!(index in pluralRules)) {
+ return function () { return 'other'; };
+ }
+ return pluralRules[index];
+ }
+
+ var parsePatterns;
+
+ function parse (ctx, source) {
+ var ast = {};
+ /* eslint-disable no-useless-escape */
+ if (!parsePatterns) {
+ parsePatterns = {
+ comment: /^\s*#|^\s*$/,
+ entity: /^([^=\s]+)\s*=\s*(.+)$/,
+ multiline: /[^\\]\\$/,
+ macro: /\{\[\s*(\w+)\(([^\)]*)\)\s*\]\}/i,
+ unicode: /\\u([0-9a-fA-F]{1,4})/g,
+ entries: /[\r\n]+/,
+ controlChars: /\\([\\\n\r\t\b\f\{\}\"\'])/g
+ };
+ }
+ /* eslint-enable no-useless-escape */
+ var entries = source.split(parsePatterns.entries);
+ for (var i = 0; i < entries.length; i++) {
+ var line = entries[i];
+
+ if (parsePatterns.comment.test(line)) {
+ continue;
+ }
+
+ while (parsePatterns.multiline.test(line) && i < entries.length) {
+ line = line.slice(0, -1) + entries[++i].trim();
+ }
+
+ var entityMatch = line.match(parsePatterns.entity);
+ if (entityMatch) {
+ try {
+ parseEntity(entityMatch[1], entityMatch[2], ast);
+ } catch (e) {
+ if (ctx) {
+ ctx._emitter.emit('error', e);
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+ return ast;
+ }
+
+ function setEntityValue (id, attr, key, value, ast) {
+ var obj = ast;
+ var prop = id;
+
+ if (attr) {
+ if (!(id in obj)) {
+ obj[id] = {};
+ }
+ if (typeof (obj[id]) === 'string') {
+ obj[id] = {'_': obj[id]};
+ }
+ obj = obj[id];
+ prop = attr;
+ }
+
+ if (!key) {
+ obj[prop] = value;
+ return;
+ }
+
+ if (!(prop in obj)) {
+ obj[prop] = {'_': {}};
+ } else if (typeof (obj[prop]) === 'string') {
+ obj[prop] = {'_index': parseMacro(obj[prop]), '_': {}};
+ }
+ obj[prop]._[key] = value;
+ }
+
+ function parseEntity (id, value, ast) {
+ var name, key;
+
+ var pos = id.indexOf('[');
+ if (pos !== -1) {
+ name = id.substr(0, pos);
+ key = id.substring(pos + 1, id.length - 1);
+ } else {
+ name = id;
+ key = null;
+ }
- if (xhr.overrideMimeType) {
- xhr.overrideMimeType('application/json');
- }
+ var nameElements = name.split('.');
- xhr.open('GET', url);
+ if (nameElements.length > 2) {
+ throw new Error('Error in ID: "' + name + '".' +
+ ' Nested attributes are not supported.');
+ }
- xhr.responseType = 'json';
- xhr.addEventListener('load', function io_loadjson(e) {
- if (e.target.status === 200 || e.target.status === 0) {
- callback(null, e.target.response);
+ var attr;
+ if (nameElements.length > 1) {
+ name = nameElements[0];
+ attr = nameElements[1];
} else {
- callback(new L10nError('Not found: ' + url));
- }
- });
- xhr.addEventListener('error', callback);
- xhr.addEventListener('timeout', callback);
-
- // the app: protocol throws on 404, see https://bugzil.la/827243
- try {
- xhr.send(null);
- } catch (e) {
- callback(new L10nError('Not found: ' + url));
- }
- }
- };
-
- function EventEmitter() {}
+ attr = null;
+ }
- EventEmitter.prototype.emit = function ee_emit() {
- if (!this._listeners) {
- return;
+ setEntityValue(name, attr, key, unescapeString(value), ast);
}
- var args = Array.prototype.slice.call(arguments);
- var type = args.shift();
- if (!this._listeners[type]) {
- return;
+ function unescapeControlCharacters (str) {
+ return str.replace(parsePatterns.controlChars, '$1');
}
- var typeListeners = this._listeners[type].slice();
- for (var i = 0; i < typeListeners.length; i++) {
- typeListeners[i].apply(this, args);
+ function unescapeUnicode (str) {
+ return str.replace(parsePatterns.unicode, function (match, token) {
+ return unescape('%u' + '0000'.slice(token.length) + token);
+ });
}
- };
- EventEmitter.prototype.addEventListener = function ee_add(type, listener) {
- if (!this._listeners) {
- this._listeners = {};
- }
- if (!(type in this._listeners)) {
- this._listeners[type] = [];
+ function unescapeString (str) {
+ if (str.lastIndexOf('\\') !== -1) {
+ str = unescapeControlCharacters(str);
+ }
+ return unescapeUnicode(str);
}
- this._listeners[type].push(listener);
- };
- EventEmitter.prototype.removeEventListener = function ee_rm(type, listener) {
- if (!this._listeners) {
- return;
+ function parseMacro (str) {
+ var match = str.match(parsePatterns.macro);
+ if (!match) {
+ throw new L10nError('Malformed macro');
+ }
+ return [match[1], match[2]];
}
- var typeListeners = this._listeners[type];
- var pos = typeListeners.indexOf(listener);
- if (pos === -1) {
- return;
- }
+ var MAX_PLACEABLE_LENGTH = 2500;
+ var MAX_PLACEABLES = 100;
+ var rePlaceables = /\{\{\s*(.+?)\s*\}\}/g;
- typeListeners.splice(pos, 1);
- };
-
-
- function getPluralRule(lang) {
- var locales2rules = {
- 'af': 3,
- 'ak': 4,
- 'am': 4,
- 'ar': 1,
- 'asa': 3,
- 'az': 0,
- 'be': 11,
- 'bem': 3,
- 'bez': 3,
- 'bg': 3,
- 'bh': 4,
- 'bm': 0,
- 'bn': 3,
- 'bo': 0,
- 'br': 20,
- 'brx': 3,
- 'bs': 11,
- 'ca': 3,
- 'cgg': 3,
- 'chr': 3,
- 'cs': 12,
- 'cy': 17,
- 'da': 3,
- 'de': 3,
- 'dv': 3,
- 'dz': 0,
- 'ee': 3,
- 'el': 3,
- 'en': 3,
- 'eo': 3,
- 'es': 3,
- 'et': 3,
- 'eu': 3,
- 'fa': 0,
- 'ff': 5,
- 'fi': 3,
- 'fil': 4,
- 'fo': 3,
- 'fr': 5,
- 'fur': 3,
- 'fy': 3,
- 'ga': 8,
- 'gd': 24,
- 'gl': 3,
- 'gsw': 3,
- 'gu': 3,
- 'guw': 4,
- 'gv': 23,
- 'ha': 3,
- 'haw': 3,
- 'he': 2,
- 'hi': 4,
- 'hr': 11,
- 'hu': 0,
- 'id': 0,
- 'ig': 0,
- 'ii': 0,
- 'is': 3,
- 'it': 3,
- 'iu': 7,
- 'ja': 0,
- 'jmc': 3,
- 'jv': 0,
- 'ka': 0,
- 'kab': 5,
- 'kaj': 3,
- 'kcg': 3,
- 'kde': 0,
- 'kea': 0,
- 'kk': 3,
- 'kl': 3,
- 'km': 0,
- 'kn': 0,
- 'ko': 0,
- 'ksb': 3,
- 'ksh': 21,
- 'ku': 3,
- 'kw': 7,
- 'lag': 18,
- 'lb': 3,
- 'lg': 3,
- 'ln': 4,
- 'lo': 0,
- 'lt': 10,
- 'lv': 6,
- 'mas': 3,
- 'mg': 4,
- 'mk': 16,
- 'ml': 3,
- 'mn': 3,
- 'mo': 9,
- 'mr': 3,
- 'ms': 0,
- 'mt': 15,
- 'my': 0,
- 'nah': 3,
- 'naq': 7,
- 'nb': 3,
- 'nd': 3,
- 'ne': 3,
- 'nl': 3,
- 'nn': 3,
- 'no': 3,
- 'nr': 3,
- 'nso': 4,
- 'ny': 3,
- 'nyn': 3,
- 'om': 3,
- 'or': 3,
- 'pa': 3,
- 'pap': 3,
- 'pl': 13,
- 'ps': 3,
- 'pt': 3,
- 'rm': 3,
- 'ro': 9,
- 'rof': 3,
- 'ru': 11,
- 'rwk': 3,
- 'sah': 0,
- 'saq': 3,
- 'se': 7,
- 'seh': 3,
- 'ses': 0,
- 'sg': 0,
- 'sh': 11,
- 'shi': 19,
- 'sk': 12,
- 'sl': 14,
- 'sma': 7,
- 'smi': 7,
- 'smj': 7,
- 'smn': 7,
- 'sms': 7,
- 'sn': 3,
- 'so': 3,
- 'sq': 3,
- 'sr': 11,
- 'ss': 3,
- 'ssy': 3,
- 'st': 3,
- 'sv': 3,
- 'sw': 3,
- 'syr': 3,
- 'ta': 3,
- 'te': 3,
- 'teo': 3,
- 'th': 0,
- 'ti': 4,
- 'tig': 3,
- 'tk': 3,
- 'tl': 4,
- 'tn': 3,
- 'to': 0,
- 'tr': 0,
- 'ts': 3,
- 'tzm': 22,
- 'uk': 11,
- 'ur': 3,
- 've': 3,
- 'vi': 0,
- 'vun': 3,
- 'wa': 4,
- 'wae': 3,
- 'wo': 0,
- 'xh': 3,
- 'xog': 3,
- 'yo': 0,
- 'zh': 0,
- 'zu': 3
+ function Entity (id, node, env) {
+ this.id = id;
+ this.env = env;
+ // the dirty guard prevents cyclic or recursive references from other
+ // Entities; see Entity.prototype.resolve
+ this.dirty = false;
+ if (typeof node === 'string') {
+ this.value = node;
+ } else {
+ // it's either a hash or it has attrs, or both
+ for (var key in node) {
+ if (node.hasOwnProperty(key) && key[0] !== '_') {
+ if (!this.attributes) {
+ this.attributes = {};
+ }
+ this.attributes[key] = new Entity(this.id + '.' + key, node[key],
+ env);
+ }
+ }
+ this.value = node._ || null;
+ this.index = node._index;
+ }
+ }
+
+ Entity.prototype.resolve = function E_resolve (ctxdata) {
+ if (this.dirty) {
+ return undefined;
+ }
+
+ this.dirty = true;
+ var val;
+ // if resolve fails, we want the exception to bubble up and stop the whole
+ // resolving process; however, we still need to clean up the dirty flag
+ try {
+ val = resolve(ctxdata, this.env, this.value, this.index);
+ } finally {
+ this.dirty = false;
+ }
+ return val;
};
- // utility functions for plural rules methods
- function isIn(n, list) {
- return list.indexOf(n) !== -1;
- }
- function isBetween(n, start, end) {
- return typeof n === typeof start && start <= n && n <= end;
- }
-
- // list of all plural rules methods:
- // map an integer to the plural form name to use
- var pluralRules = {
- '0': function() {
- return 'other';
- },
- '1': function(n) {
- if ((isBetween((n % 100), 3, 10))) {
- return 'few';
- }
- if (n === 0) {
- return 'zero';
- }
- if ((isBetween((n % 100), 11, 99))) {
- return 'many';
- }
- if (n === 2) {
- return 'two';
- }
- if (n === 1) {
- return 'one';
- }
- return 'other';
- },
- '2': function(n) {
- if (n !== 0 && (n % 10) === 0) {
- return 'many';
- }
- if (n === 2) {
- return 'two';
- }
- if (n === 1) {
- return 'one';
- }
- return 'other';
- },
- '3': function(n) {
- if (n === 1) {
- return 'one';
- }
- return 'other';
- },
- '4': function(n) {
- if ((isBetween(n, 0, 1))) {
- return 'one';
- }
- return 'other';
- },
- '5': function(n) {
- if ((isBetween(n, 0, 2)) && n !== 2) {
- return 'one';
- }
- return 'other';
- },
- '6': function(n) {
- if (n === 0) {
- return 'zero';
- }
- if ((n % 10) === 1 && (n % 100) !== 11) {
- return 'one';
- }
- return 'other';
- },
- '7': function(n) {
- if (n === 2) {
- return 'two';
- }
- if (n === 1) {
- return 'one';
- }
- return 'other';
- },
- '8': function(n) {
- if ((isBetween(n, 3, 6))) {
- return 'few';
- }
- if ((isBetween(n, 7, 10))) {
- return 'many';
- }
- if (n === 2) {
- return 'two';
- }
- if (n === 1) {
- return 'one';
- }
- return 'other';
- },
- '9': function(n) {
- if (n === 0 || n !== 1 && (isBetween((n % 100), 1, 19))) {
- return 'few';
- }
- if (n === 1) {
- return 'one';
- }
- return 'other';
- },
- '10': function(n) {
- if ((isBetween((n % 10), 2, 9)) && !(isBetween((n % 100), 11, 19))) {
- return 'few';
- }
- if ((n % 10) === 1 && !(isBetween((n % 100), 11, 19))) {
- return 'one';
- }
- return 'other';
- },
- '11': function(n) {
- if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) {
- return 'few';
- }
- if ((n % 10) === 0 ||
- (isBetween((n % 10), 5, 9)) ||
- (isBetween((n % 100), 11, 14))) {
- return 'many';
- }
- if ((n % 10) === 1 && (n % 100) !== 11) {
- return 'one';
- }
- return 'other';
- },
- '12': function(n) {
- if ((isBetween(n, 2, 4))) {
- return 'few';
- }
- if (n === 1) {
- return 'one';
- }
- return 'other';
- },
- '13': function(n) {
- if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) {
- return 'few';
+ Entity.prototype.toString = function E_toString (ctxdata) {
+ try {
+ return this.resolve(ctxdata);
+ } catch (e) {
+ return undefined;
}
- if (n !== 1 && (isBetween((n % 10), 0, 1)) ||
- (isBetween((n % 10), 5, 9)) ||
- (isBetween((n % 100), 12, 14))) {
- return 'many';
- }
- if (n === 1) {
- return 'one';
- }
- return 'other';
- },
- '14': function(n) {
- if ((isBetween((n % 100), 3, 4))) {
- return 'few';
- }
- if ((n % 100) === 2) {
- return 'two';
- }
- if ((n % 100) === 1) {
- return 'one';
- }
- return 'other';
- },
- '15': function(n) {
- if (n === 0 || (isBetween((n % 100), 2, 10))) {
- return 'few';
- }
- if ((isBetween((n % 100), 11, 19))) {
- return 'many';
- }
- if (n === 1) {
- return 'one';
- }
- return 'other';
- },
- '16': function(n) {
- if ((n % 10) === 1 && n !== 11) {
- return 'one';
- }
- return 'other';
- },
- '17': function(n) {
- if (n === 3) {
- return 'few';
- }
- if (n === 0) {
- return 'zero';
- }
- if (n === 6) {
- return 'many';
- }
- if (n === 2) {
- return 'two';
- }
- if (n === 1) {
- return 'one';
- }
- return 'other';
- },
- '18': function(n) {
- if (n === 0) {
- return 'zero';
- }
- if ((isBetween(n, 0, 2)) && n !== 0 && n !== 2) {
- return 'one';
- }
- return 'other';
- },
- '19': function(n) {
- if ((isBetween(n, 2, 10))) {
- return 'few';
- }
- if ((isBetween(n, 0, 1))) {
- return 'one';
- }
- return 'other';
- },
- '20': function(n) {
- if ((isBetween((n % 10), 3, 4) || ((n % 10) === 9)) && !(
- isBetween((n % 100), 10, 19) ||
- isBetween((n % 100), 70, 79) ||
- isBetween((n % 100), 90, 99)
- )) {
- return 'few';
- }
- if ((n % 1000000) === 0 && n !== 0) {
- return 'many';
- }
- if ((n % 10) === 2 && !isIn((n % 100), [12, 72, 92])) {
- return 'two';
- }
- if ((n % 10) === 1 && !isIn((n % 100), [11, 71, 91])) {
- return 'one';
- }
- return 'other';
- },
- '21': function(n) {
- if (n === 0) {
- return 'zero';
- }
- if (n === 1) {
- return 'one';
- }
- return 'other';
- },
- '22': function(n) {
- if ((isBetween(n, 0, 1)) || (isBetween(n, 11, 99))) {
- return 'one';
- }
- return 'other';
- },
- '23': function(n) {
- if ((isBetween((n % 10), 1, 2)) || (n % 20) === 0) {
- return 'one';
- }
- return 'other';
- },
- '24': function(n) {
- if ((isBetween(n, 3, 10) || isBetween(n, 13, 19))) {
- return 'few';
- }
- if (isIn(n, [2, 12])) {
- return 'two';
- }
- if (isIn(n, [1, 11])) {
- return 'one';
- }
- return 'other';
- }
};
- // return a function that gives the plural form name for a given integer
- var index = locales2rules[lang.replace(/-.*$/, '')];
- if (!(index in pluralRules)) {
- return function() { return 'other'; };
- }
- return pluralRules[index];
- }
-
-
-
-
- var parsePatterns;
-
- function parse(ctx, source) {
- var ast = {};
-
- if (!parsePatterns) {
- parsePatterns = {
- comment: /^\s*#|^\s*$/,
- entity: /^([^=\s]+)\s*=\s*(.+)$/,
- multiline: /[^\\]\\$/,
- macro: /\{\[\s*(\w+)\(([^\)]*)\)\s*\]\}/i,
- unicode: /\\u([0-9a-fA-F]{1,4})/g,
- entries: /[\r\n]+/,
- controlChars: /\\([\\\n\r\t\b\f\{\}\"\'])/g
- };
- }
+ Entity.prototype.valueOf = function E_valueOf (ctxdata) {
+ if (!this.attributes) {
+ return this.toString(ctxdata);
+ }
- var entries = source.split(parsePatterns.entries);
- for (var i = 0; i < entries.length; i++) {
- var line = entries[i];
+ var entity = {
+ value: this.toString(ctxdata),
+ attributes: {}
+ };
- if (parsePatterns.comment.test(line)) {
- continue;
- }
+ for (var key in this.attributes) {
+ if (this.attributes.hasOwnProperty(key)) {
+ entity.attributes[key] = this.attributes[key].toString(ctxdata);
+ }
+ }
- while (parsePatterns.multiline.test(line) && i < entries.length) {
- line = line.slice(0, -1) + entries[++i].trim();
- }
+ return entity;
+ };
- var entityMatch = line.match(parsePatterns.entity);
- if (entityMatch) {
- try {
- parseEntity(entityMatch[1], entityMatch[2], ast);
- } catch (e) {
- if (ctx) {
- ctx._emitter.emit('error', e);
- } else {
- throw e;
- }
+ function subPlaceable (ctxdata, env, match, id) {
+ if (ctxdata && ctxdata.hasOwnProperty(id) &&
+ (typeof ctxdata[id] === 'string' ||
+ (typeof ctxdata[id] === 'number' && !isNaN(ctxdata[id])))) {
+ return ctxdata[id];
+ }
+
+ if (env.hasOwnProperty(id)) {
+ if (!(env[id] instanceof Entity)) {
+ env[id] = new Entity(id, env[id], env);
+ }
+ var value = env[id].resolve(ctxdata);
+ if (typeof value === 'string') {
+ // prevent Billion Laughs attacks
+ if (value.length >= MAX_PLACEABLE_LENGTH) {
+ throw new L10nError('Too many characters in placeable (' +
+ value.length + ', max allowed is ' +
+ MAX_PLACEABLE_LENGTH + ')');
+ }
+ return value;
+ }
}
- }
- }
- return ast;
- }
-
- function setEntityValue(id, attr, key, value, ast) {
- var obj = ast;
- var prop = id;
-
- if (attr) {
- if (!(id in obj)) {
- obj[id] = {};
- }
- if (typeof(obj[id]) === 'string') {
- obj[id] = {'_': obj[id]};
- }
- obj = obj[id];
- prop = attr;
+ return match;
}
- if (!key) {
- obj[prop] = value;
- return;
+ function interpolate (ctxdata, env, str) {
+ var placeablesCount = 0;
+ var value = str.replace(rePlaceables, function (match, id) {
+ // prevent Quadratic Blowup attacks
+ if (placeablesCount++ >= MAX_PLACEABLES) {
+ throw new L10nError('Too many placeables (' + placeablesCount +
+ ', max allowed is ' + MAX_PLACEABLES + ')');
+ }
+ return subPlaceable(ctxdata, env, match, id);
+ });
+ placeablesCount = 0;
+ return value;
}
- if (!(prop in obj)) {
- obj[prop] = {'_': {}};
- } else if (typeof(obj[prop]) === 'string') {
- obj[prop] = {'_index': parseMacro(obj[prop]), '_': {}};
- }
- obj[prop]._[key] = value;
- }
-
- function parseEntity(id, value, ast) {
- var name, key;
-
- var pos = id.indexOf('[');
- if (pos !== -1) {
- name = id.substr(0, pos);
- key = id.substring(pos + 1, id.length - 1);
- } else {
- name = id;
- key = null;
- }
+ function resolve (ctxdata, env, expr, index) {
+ if (typeof expr === 'string') {
+ return interpolate(ctxdata, env, expr);
+ }
- var nameElements = name.split('.');
+ if (typeof expr === 'boolean' ||
+ typeof expr === 'number' ||
+ !expr) {
+ return expr;
+ }
- if (nameElements.length > 2) {
- throw new Error('Error in ID: "' + name + '".' +
- ' Nested attributes are not supported.');
- }
+ // otherwise, it's a dict
- var attr;
- if (nameElements.length > 1) {
- name = nameElements[0];
- attr = nameElements[1];
- } else {
- attr = null;
- }
+ if (index && ctxdata && ctxdata.hasOwnProperty(index[1])) {
+ var argValue = ctxdata[index[1]];
- setEntityValue(name, attr, key, unescapeString(value), ast);
- }
+ // special cases for zero, one, two if they are defined on the hash
+ if (argValue === 0 && 'zero' in expr) {
+ return resolve(ctxdata, env, expr.zero);
+ }
+ if (argValue === 1 && 'one' in expr) {
+ return resolve(ctxdata, env, expr.one);
+ }
+ if (argValue === 2 && 'two' in expr) {
+ return resolve(ctxdata, env, expr.two);
+ }
- function unescapeControlCharacters(str) {
- return str.replace(parsePatterns.controlChars, '$1');
- }
+ var selector = env.__plural(argValue);
+ if (expr.hasOwnProperty(selector)) {
+ return resolve(ctxdata, env, expr[selector]);
+ }
+ }
- function unescapeUnicode(str) {
- return str.replace(parsePatterns.unicode, function(match, token) {
- return unescape('%u' + '0000'.slice(token.length) + token);
- });
- }
+ // if there was no index or no selector was found, try 'other'
+ if ('other' in expr) {
+ return resolve(ctxdata, env, expr.other);
+ }
- function unescapeString(str) {
- if (str.lastIndexOf('\\') !== -1) {
- str = unescapeControlCharacters(str);
+ return undefined;
}
- return unescapeUnicode(str);
- }
- function parseMacro(str) {
- var match = str.match(parsePatterns.macro);
- if (!match) {
- throw new L10nError('Malformed macro');
- }
- return [match[1], match[2]];
- }
-
-
-
- var MAX_PLACEABLE_LENGTH = 2500;
- var MAX_PLACEABLES = 100;
- var rePlaceables = /\{\{\s*(.+?)\s*\}\}/g;
-
- function Entity(id, node, env) {
- this.id = id;
- this.env = env;
- // the dirty guard prevents cyclic or recursive references from other
- // Entities; see Entity.prototype.resolve
- this.dirty = false;
- if (typeof node === 'string') {
- this.value = node;
- } else {
- // it's either a hash or it has attrs, or both
- for (var key in node) {
- if (node.hasOwnProperty(key) && key[0] !== '_') {
- if (!this.attributes) {
- this.attributes = {};
- }
- this.attributes[key] = new Entity(this.id + '.' + key, node[key],
- env);
- }
- }
- this.value = node._ || null;
- this.index = node._index;
+ function compile (env, ast) {
+ env = env || {};
+ for (var id in ast) {
+ if (ast.hasOwnProperty(id)) {
+ env[id] = new Entity(id, ast[id], env);
+ }
+ }
+ return env;
}
- }
- Entity.prototype.resolve = function E_resolve(ctxdata) {
- if (this.dirty) {
- return undefined;
+ function Locale (id, ctx) {
+ this.id = id;
+ this.ctx = ctx;
+ this.isReady = false;
+ this.entries = {
+ __plural: getPluralRule(id)
+ };
}
- this.dirty = true;
- var val;
- // if resolve fails, we want the exception to bubble up and stop the whole
- // resolving process; however, we still need to clean up the dirty flag
- try {
- val = resolve(ctxdata, this.env, this.value, this.index);
- } finally {
- this.dirty = false;
- }
- return val;
- };
-
- Entity.prototype.toString = function E_toString(ctxdata) {
- try {
- return this.resolve(ctxdata);
- } catch (e) {
- return undefined;
- }
- };
+ Locale.prototype.getEntry = function L_getEntry (id) {
- Entity.prototype.valueOf = function E_valueOf(ctxdata) {
- if (!this.attributes) {
- return this.toString(ctxdata);
- }
+ var entries = this.entries;
- var entity = {
- value: this.toString(ctxdata),
- attributes: {}
- };
+ if (!entries.hasOwnProperty(id)) {
+ return undefined;
+ }
- for (var key in this.attributes) {
- if (this.attributes.hasOwnProperty(key)) {
- entity.attributes[key] = this.attributes[key].toString(ctxdata);
- }
- }
+ if (entries[id] instanceof Entity) {
+ return entries[id];
+ }
+
+ return entries[id] = new Entity(id, entries[id], entries); // eslint-disable-line no-return-assign
+ };
- return entity;
- };
+ Locale.prototype.build = function L_build (callback) {
+ var sync = !callback;
+ var ctx = this.ctx;
+ var self = this;
- function subPlaceable(ctxdata, env, match, id) {
- if (ctxdata && ctxdata.hasOwnProperty(id) &&
- (typeof ctxdata[id] === 'string' ||
- (typeof ctxdata[id] === 'number' && !isNaN(ctxdata[id])))) {
- return ctxdata[id];
- }
+ var l10nLoads = ctx.resLinks.length;
- if (env.hasOwnProperty(id)) {
- if (!(env[id] instanceof Entity)) {
- env[id] = new Entity(id, env[id], env);
- }
- var value = env[id].resolve(ctxdata);
- if (typeof value === 'string') {
- // prevent Billion Laughs attacks
- if (value.length >= MAX_PLACEABLE_LENGTH) {
- throw new L10nError('Too many characters in placeable (' +
- value.length + ', max allowed is ' +
- MAX_PLACEABLE_LENGTH + ')');
+ function onL10nLoaded (err) {
+ if (err) {
+ ctx._emitter.emit('error', err);
+ }
+ if (--l10nLoads <= 0) {
+ self.isReady = true;
+ if (callback) {
+ callback();
+ }
+ }
}
- return value;
- }
- }
- return match;
- }
-
- function interpolate(ctxdata, env, str) {
- var placeablesCount = 0;
- var value = str.replace(rePlaceables, function(match, id) {
- // prevent Quadratic Blowup attacks
- if (placeablesCount++ >= MAX_PLACEABLES) {
- throw new L10nError('Too many placeables (' + placeablesCount +
- ', max allowed is ' + MAX_PLACEABLES + ')');
- }
- return subPlaceable(ctxdata, env, match, id);
- });
- placeablesCount = 0;
- return value;
- }
-
- function resolve(ctxdata, env, expr, index) {
- if (typeof expr === 'string') {
- return interpolate(ctxdata, env, expr);
- }
- if (typeof expr === 'boolean' ||
- typeof expr === 'number' ||
- !expr) {
- return expr;
- }
+ if (l10nLoads === 0) {
+ onL10nLoaded();
+ return;
+ }
- // otherwise, it's a dict
-
- if (index && ctxdata && ctxdata.hasOwnProperty(index[1])) {
- var argValue = ctxdata[index[1]];
-
- // special cases for zero, one, two if they are defined on the hash
- if (argValue === 0 && 'zero' in expr) {
- return resolve(ctxdata, env, expr.zero);
- }
- if (argValue === 1 && 'one' in expr) {
- return resolve(ctxdata, env, expr.one);
- }
- if (argValue === 2 && 'two' in expr) {
- return resolve(ctxdata, env, expr.two);
- }
-
- var selector = env.__plural(argValue);
- if (expr.hasOwnProperty(selector)) {
- return resolve(ctxdata, env, expr[selector]);
- }
- }
+ function onJSONLoaded (err, json) {
+ if (!err && json) {
+ self.addAST(json);
+ }
+ onL10nLoaded(err);
+ }
- // if there was no index or no selector was found, try 'other'
- if ('other' in expr) {
- return resolve(ctxdata, env, expr.other);
- }
+ function onPropLoaded (err, source) {
+ if (!err && source) {
+ var ast = parse(ctx, source);
+ self.addAST(ast);
+ }
+ onL10nLoaded(err);
+ }
- return undefined;
- }
+ for (var i = 0; i < ctx.resLinks.length; i++) {
+ var path = ctx.resLinks[i].replace('{{locale}}', this.id);
+ var type = path.substr(path.lastIndexOf('.') + 1);
- function compile(env, ast) {
- env = env || {};
- for (var id in ast) {
- if (ast.hasOwnProperty(id)) {
- env[id] = new Entity(id, ast[id], env);
- }
- }
- return env;
- }
+ switch (type) {
+ case 'json':
+ io.loadJSON(path, onJSONLoaded, sync);
+ break;
+ case 'properties':
+ io.load(path, onPropLoaded, sync);
+ break;
+ }
+ }
+ };
+ Locale.prototype.addAST = function (ast) {
+ for (var id in ast) {
+ if (ast.hasOwnProperty(id)) {
+ this.entries[id] = ast[id];
+ }
+ }
+ };
+ Locale.prototype.getEntity = function (id, ctxdata) {
+ var entry = this.getEntry(id);
- function Locale(id, ctx) {
- this.id = id;
- this.ctx = ctx;
- this.isReady = false;
- this.entries = {
- __plural: getPluralRule(id)
+ if (!entry) {
+ return null;
+ }
+ return entry.valueOf(ctxdata);
};
- }
- Locale.prototype.getEntry = function L_getEntry(id) {
- /* jshint -W093 */
+ function Context (id) {
- var entries = this.entries;
+ this.id = id;
+ this.isReady = false;
+ this.isLoading = false;
- if (!entries.hasOwnProperty(id)) {
- return undefined;
- }
+ this.supportedLocales = [];
+ this.resLinks = [];
+ this.locales = {};
- if (entries[id] instanceof Entity) {
- return entries[id];
- }
+ this._emitter = new EventEmitter();
+
+ // Getting translations
- return entries[id] = new Entity(id, entries[id], entries);
- };
+ function getWithFallback (id) {
- Locale.prototype.build = function L_build(callback) {
- var sync = !callback;
- var ctx = this.ctx;
- var self = this;
+ if (!this.isReady) {
+ throw new L10nError('Context not ready');
+ }
- var l10nLoads = ctx.resLinks.length;
+ var cur = 0;
+ var loc;
+ var locale;
+ while (loc = this.supportedLocales[cur]) { // eslint-disable-line no-cond-assign
+ locale = this.getLocale(loc);
+ if (!locale.isReady) {
+ // build without callback, synchronously
+ locale.build(null);
+ }
+ var entry = locale.getEntry(id);
+ if (entry === undefined) {
+ cur++;
+ warning.call(this, new L10nError(id + ' not found in ' + loc, id,
+ loc));
+ continue;
+ }
+ return entry;
+ }
- function onL10nLoaded(err) {
- if (err) {
- ctx._emitter.emit('error', err);
- }
- if (--l10nLoads <= 0) {
- self.isReady = true;
- if (callback) {
- callback();
+ error.call(this, new L10nError(id + ' not found', id));
+ return null;
}
- }
- }
- if (l10nLoads === 0) {
- onL10nLoaded();
- return;
- }
+ this.get = function get (id, ctxdata) {
+ var entry = getWithFallback.call(this, id);
+ if (entry === null) {
+ return '';
+ }
- function onJSONLoaded(err, json) {
- if (!err && json) {
- self.addAST(json);
- }
- onL10nLoaded(err);
- }
+ return entry.toString(ctxdata) || '';
+ };
- function onPropLoaded(err, source) {
- if (!err && source) {
- var ast = parse(ctx, source);
- self.addAST(ast);
- }
- onL10nLoaded(err);
- }
+ this.getEntity = function getEntity (id, ctxdata) {
+ var entry = getWithFallback.call(this, id);
+ if (entry === null) {
+ return null;
+ }
+ return entry.valueOf(ctxdata);
+ };
- for (var i = 0; i < ctx.resLinks.length; i++) {
- var path = ctx.resLinks[i].replace('{{locale}}', this.id);
- var type = path.substr(path.lastIndexOf('.') + 1);
+ // Helpers
- switch (type) {
- case 'json':
- io.loadJSON(path, onJSONLoaded, sync);
- break;
- case 'properties':
- io.load(path, onPropLoaded, sync);
- break;
- }
- }
- };
+ this.getLocale = function getLocale (code) {
- Locale.prototype.addAST = function(ast) {
- for (var id in ast) {
- if (ast.hasOwnProperty(id)) {
- this.entries[id] = ast[id];
- }
- }
- };
+ var locales = this.locales;
+ if (locales[code]) {
+ return locales[code];
+ }
- Locale.prototype.getEntity = function(id, ctxdata) {
- var entry = this.getEntry(id);
+ return locales[code] = new Locale(code, this); // eslint-disable-line no-return-assign
+ };
- if (!entry) {
- return null;
- }
- return entry.valueOf(ctxdata);
- };
+ // Getting ready
+
+ function negotiate (available, requested, defaultLocale) {
+ if (available.indexOf(requested[0]) === -1 ||
+ requested[0] === defaultLocale) {
+ return [defaultLocale];
+ } else {
+ return [requested[0], defaultLocale];
+ }
+ }
+ function freeze (supported) {
+ var locale = this.getLocale(supported[0]);
+ if (locale.isReady) {
+ setReady.call(this, supported);
+ } else {
+ locale.build(setReady.bind(this, supported));
+ }
+ }
+ function setReady (supported) {
+ this.supportedLocales = supported;
+ this.isReady = true;
+ this._emitter.emit('ready');
+ }
- function Context(id) {
+ this.requestLocales = function requestLocales () {
+ if (this.isLoading && !this.isReady) {
+ throw new L10nError('Context not ready');
+ }
- this.id = id;
- this.isReady = false;
- this.isLoading = false;
+ this.isLoading = true;
+ var requested = Array.prototype.slice.call(arguments);
- this.supportedLocales = [];
- this.resLinks = [];
- this.locales = {};
+ var supported = negotiate(requested.concat('en-US'), requested, 'en-US');
+ freeze.call(this, supported);
+ };
- this._emitter = new EventEmitter();
+ // Events
+ this.addEventListener = function addEventListener (type, listener) {
+ this._emitter.addEventListener(type, listener);
+ };
- // Getting translations
+ this.removeEventListener = function removeEventListener (type, listener) {
+ this._emitter.removeEventListener(type, listener);
+ };
- function getWithFallback(id) {
- /* jshint -W084 */
+ this.ready = function ready (callback) {
+ if (this.isReady) {
+ setTimeout(callback);
+ }
+ this.addEventListener('ready', callback);
+ };
- if (!this.isReady) {
- throw new L10nError('Context not ready');
- }
+ this.once = function once (callback) {
+ if (this.isReady) {
+ setTimeout(callback);
+ return;
+ }
- var cur = 0;
- var loc;
- var locale;
- while (loc = this.supportedLocales[cur]) {
- locale = this.getLocale(loc);
- if (!locale.isReady) {
- // build without callback, synchronously
- locale.build(null);
- }
- var entry = locale.getEntry(id);
- if (entry === undefined) {
- cur++;
- warning.call(this, new L10nError(id + ' not found in ' + loc, id,
- loc));
- continue;
+ var callAndRemove = function () {
+ this.removeEventListener('ready', callAndRemove);
+ callback();
+ }.bind(this);
+ this.addEventListener('ready', callAndRemove);
+ };
+
+ // Errors
+
+ function warning (e) {
+ this._emitter.emit('warning', e);
+ return e;
}
- return entry;
- }
- error.call(this, new L10nError(id + ' not found', id));
- return null;
+ function error (e) {
+ this._emitter.emit('error', e);
+ return e;
+ }
}
- this.get = function get(id, ctxdata) {
- var entry = getWithFallback.call(this, id);
- if (entry === null) {
- return '';
- }
+ var DEBUG = false;
+ var isPretranslated = false;
+ var rtlList = ['ar', 'he', 'fa', 'ps', 'qps-plocm', 'ur'];
+ var nodeObserver = false;
- return entry.toString(ctxdata) || '';
+ var moConfig = {
+ attributes: true,
+ characterData: false,
+ childList: true,
+ subtree: true,
+ attributeFilter: ['data-l10n-id', 'data-l10n-args']
};
- this.getEntity = function getEntity(id, ctxdata) {
- var entry = getWithFallback.call(this, id);
- if (entry === null) {
- return null;
- }
-
- return entry.valueOf(ctxdata);
+ // Public API
+
+ navigator.mozL10n = {
+ ctx: new Context(),
+ get: function get (id, ctxdata) {
+ return navigator.mozL10n.ctx.get(id, ctxdata);
+ },
+ localize: function localize (element, id, args) {
+ return localizeElement.call(navigator.mozL10n, element, id, args);
+ },
+ translate: function () {
+ // XXX: Remove after removing obsolete calls. Bugs 992473 and 1020136
+ },
+ translateFragment: function (fragment) {
+ return translateFragment.call(navigator.mozL10n, fragment);
+ },
+ setAttributes: setL10nAttributes,
+ getAttributes: getL10nAttributes,
+ ready: function ready (callback) {
+ return navigator.mozL10n.ctx.ready(callback);
+ },
+ once: function once (callback) {
+ return navigator.mozL10n.ctx.once(callback);
+ },
+ get readyState () {
+ return navigator.mozL10n.ctx.isReady ? 'complete' : 'loading';
+ },
+ language: {
+ set code (lang) {
+ navigator.mozL10n.ctx.requestLocales(lang);
+ },
+ get code () {
+ return navigator.mozL10n.ctx.supportedLocales[0];
+ },
+ get direction () {
+ return getDirection(navigator.mozL10n.ctx.supportedLocales[0]);
+ }
+ },
+ _getInternalAPI: function () {
+ return {
+ Error: L10nError,
+ Context: Context,
+ Locale: Locale,
+ Entity: Entity,
+ getPluralRule: getPluralRule,
+ rePlaceables: rePlaceables,
+ getTranslatableChildren: getTranslatableChildren,
+ translateDocument: translateDocument,
+ loadINI: loadINI,
+ fireLocalizedEvent: fireLocalizedEvent,
+ parse: parse,
+ compile: compile
+ };
+ }
};
+ navigator.mozL10n.ctx.ready(onReady.bind(navigator.mozL10n));
- // Helpers
-
- this.getLocale = function getLocale(code) {
- /* jshint -W093 */
+ if (DEBUG) {
+ navigator.mozL10n.ctx.addEventListener('error', console.error);
+ navigator.mozL10n.ctx.addEventListener('warning', console.warn);
+ }
- var locales = this.locales;
- if (locales[code]) {
- return locales[code];
- }
+ function getDirection (lang) {
+ return (rtlList.indexOf(lang) >= 0) ? 'rtl' : 'ltr';
+ }
- return locales[code] = new Locale(code, this);
+ var readyStates = {
+ 'loading': 0,
+ 'interactive': 1,
+ 'complete': 2
};
+ function waitFor (state, callback) {
+ state = readyStates[state];
+ if (readyStates[document.readyState] >= state) {
+ callback();
+ return;
+ }
- // Getting ready
-
- function negotiate(available, requested, defaultLocale) {
- if (available.indexOf(requested[0]) === -1 ||
- requested[0] === defaultLocale) {
- return [defaultLocale];
- } else {
- return [requested[0], defaultLocale];
- }
- }
-
- function freeze(supported) {
- var locale = this.getLocale(supported[0]);
- if (locale.isReady) {
- setReady.call(this, supported);
- } else {
- locale.build(setReady.bind(this, supported));
- }
+ document.addEventListener('readystatechange', function l10n_onrsc () {
+ if (readyStates[document.readyState] >= state) {
+ document.removeEventListener('readystatechange', l10n_onrsc);
+ callback();
+ }
+ });
}
- function setReady(supported) {
- this.supportedLocales = supported;
- this.isReady = true;
- this._emitter.emit('ready');
- }
+ if (window.document) {
+ isPretranslated = (document.documentElement.lang === navigator.language);
- this.requestLocales = function requestLocales() {
- if (this.isLoading && !this.isReady) {
- throw new L10nError('Context not ready');
- }
+ // this is a special case for netError bug; see https://bugzil.la/444165
+ if (document.documentElement.dataset.noCompleteBug) {
+ pretranslate.call(navigator.mozL10n);
+ return;
+ }
- this.isLoading = true;
- var requested = Array.prototype.slice.call(arguments);
+ if (isPretranslated) {
+ waitFor('interactive', function () {
+ window.setTimeout(initResources.bind(navigator.mozL10n));
+ });
+ } else {
+ if (document.readyState === 'complete') {
+ window.setTimeout(initResources.bind(navigator.mozL10n));
+ } else {
+ waitFor('interactive', pretranslate.bind(navigator.mozL10n));
+ }
+ }
- var supported = negotiate(requested.concat('en-US'), requested, 'en-US');
- freeze.call(this, supported);
- };
+ }
+ function pretranslate () {
+ if (inlineLocalization.call(this)) {
+ waitFor('interactive', function () {
+ window.setTimeout(initResources.bind(this));
+ }.bind(this));
+ } else {
+ initResources.call(this);
+ }
+ }
- // Events
+ function inlineLocalization () {
+ var script = document.documentElement
+ .querySelector('script[type="application/l10n"]' +
+ '[lang="' + navigator.language + '"]');
+ if (!script) {
+ return false;
+ }
- this.addEventListener = function addEventListener(type, listener) {
- this._emitter.addEventListener(type, listener);
- };
+ var locale = this.ctx.getLocale(navigator.language);
+ // the inline localization is happenning very early, when the ctx is not
+ // yet ready and when the resources haven't been downloaded yet; add the
+ // inlined JSON directly to the current locale
+ locale.addAST(JSON.parse(script.innerHTML));
+ // localize the visible DOM
+ var l10n = {
+ ctx: locale,
+ language: {
+ code: locale.id,
+ direction: getDirection(locale.id)
+ }
+ };
+ translateDocument.call(l10n);
- this.removeEventListener = function removeEventListener(type, listener) {
- this._emitter.removeEventListener(type, listener);
- };
+ // the visible DOM is now pretranslated
+ isPretranslated = true;
+ return true;
+ }
- this.ready = function ready(callback) {
- if (this.isReady) {
- setTimeout(callback);
- }
- this.addEventListener('ready', callback);
- };
+ function initResources () {
+ var resLinks = document.head
+ .querySelectorAll('link[type="application/l10n"]');
+ var iniLinks = [];
- this.once = function once(callback) {
- /* jshint -W068 */
- if (this.isReady) {
- setTimeout(callback);
- return;
- }
-
- var callAndRemove = (function() {
- this.removeEventListener('ready', callAndRemove);
- callback();
- }).bind(this);
- this.addEventListener('ready', callAndRemove);
- };
+ for (var i = 0; i < resLinks.length; i++) {
+ var link = resLinks[i];
+ var url = link.getAttribute('href');
+ var type = url.substr(url.lastIndexOf('.') + 1);
+ if (type === 'ini') {
+ iniLinks.push(url);
+ }
+ this.ctx.resLinks.push(url);
+ }
+ var iniLoads = iniLinks.length;
+ if (iniLoads === 0) {
+ initLocale.call(this);
+ return;
+ }
- // Errors
+ function onIniLoaded (err) {
+ if (err) {
+ this.ctx._emitter.emit('error', err);
+ }
+ if (--iniLoads === 0) {
+ initLocale.call(this);
+ }
+ }
- function warning(e) {
- this._emitter.emit('warning', e);
- return e;
+ for (i = 0; i < iniLinks.length; i++) {
+ loadINI.call(this, iniLinks[i], onIniLoaded.bind(this));
+ }
}
- function error(e) {
- this._emitter.emit('error', e);
- return e;
- }
- }
-
-
-
- var DEBUG = false;
- var isPretranslated = false;
- var rtlList = ['ar', 'he', 'fa', 'ps', 'qps-plocm', 'ur'];
- var nodeObserver = false;
-
- var moConfig = {
- attributes: true,
- characterData: false,
- childList: true,
- subtree: true,
- attributeFilter: ['data-l10n-id', 'data-l10n-args']
- };
-
- // Public API
-
- navigator.mozL10n = {
- ctx: new Context(),
- get: function get(id, ctxdata) {
- return navigator.mozL10n.ctx.get(id, ctxdata);
- },
- localize: function localize(element, id, args) {
- return localizeElement.call(navigator.mozL10n, element, id, args);
- },
- translate: function () {
- // XXX: Remove after removing obsolete calls. Bugs 992473 and 1020136
- },
- translateFragment: function (fragment) {
- return translateFragment.call(navigator.mozL10n, fragment);
- },
- setAttributes: setL10nAttributes,
- getAttributes: getL10nAttributes,
- ready: function ready(callback) {
- return navigator.mozL10n.ctx.ready(callback);
- },
- once: function once(callback) {
- return navigator.mozL10n.ctx.once(callback);
- },
- get readyState() {
- return navigator.mozL10n.ctx.isReady ? 'complete' : 'loading';
- },
- language: {
- set code(lang) {
- navigator.mozL10n.ctx.requestLocales(lang);
- },
- get code() {
- return navigator.mozL10n.ctx.supportedLocales[0];
- },
- get direction() {
- return getDirection(navigator.mozL10n.ctx.supportedLocales[0]);
- }
- },
- _getInternalAPI: function() {
- return {
- Error: L10nError,
- Context: Context,
- Locale: Locale,
- Entity: Entity,
- getPluralRule: getPluralRule,
- rePlaceables: rePlaceables,
- getTranslatableChildren: getTranslatableChildren,
- translateDocument: translateDocument,
- loadINI: loadINI,
- fireLocalizedEvent: fireLocalizedEvent,
- parse: parse,
- compile: compile
- };
- }
- };
-
- navigator.mozL10n.ctx.ready(onReady.bind(navigator.mozL10n));
-
- if (DEBUG) {
- navigator.mozL10n.ctx.addEventListener('error', console.error);
- navigator.mozL10n.ctx.addEventListener('warning', console.warn);
- }
-
- function getDirection(lang) {
- return (rtlList.indexOf(lang) >= 0) ? 'rtl' : 'ltr';
- }
-
- var readyStates = {
- 'loading': 0,
- 'interactive': 1,
- 'complete': 2
- };
-
- function waitFor(state, callback) {
- state = readyStates[state];
- if (readyStates[document.readyState] >= state) {
- callback();
- return;
+ function initLocale () {
+ this.ctx.requestLocales(navigator.language);
+ window.addEventListener('languagechange', function l10n_langchange () {
+ navigator.mozL10n.language.code = navigator.language;
+ });
}
- document.addEventListener('readystatechange', function l10n_onrsc() {
- if (readyStates[document.readyState] >= state) {
- document.removeEventListener('readystatechange', l10n_onrsc);
- callback();
- }
- });
- }
-
- if (window.document) {
- isPretranslated = (document.documentElement.lang === navigator.language);
-
- // this is a special case for netError bug; see https://bugzil.la/444165
- if (document.documentElement.dataset.noCompleteBug) {
- pretranslate.call(navigator.mozL10n);
- return;
- }
+ function localizeMutations (mutations) {
+ var mutation;
+ for (var i = 0; i < mutations.length; i++) {
+ mutation = mutations[i];
+ if (mutation.type === 'childList') {
+ var addedNode;
- if (isPretranslated) {
- waitFor('interactive', function() {
- window.setTimeout(initResources.bind(navigator.mozL10n));
- });
- } else {
- if (document.readyState === 'complete') {
- window.setTimeout(initResources.bind(navigator.mozL10n));
- } else {
- waitFor('interactive', pretranslate.bind(navigator.mozL10n));
- }
- }
+ for (var j = 0; j < mutation.addedNodes.length; j++) {
+ addedNode = mutation.addedNodes[j];
- }
+ if (addedNode.nodeType !== Node.ELEMENT_NODE) { // eslint-disable-line no-undef
+ continue;
+ }
- function pretranslate() {
- /* jshint -W068 */
- if (inlineLocalization.call(this)) {
- waitFor('interactive', (function() {
- window.setTimeout(initResources.bind(this));
- }).bind(this));
- } else {
- initResources.call(this);
- }
- }
-
- function inlineLocalization() {
- var script = document.documentElement
- .querySelector('script[type="application/l10n"]' +
- '[lang="' + navigator.language + '"]');
- if (!script) {
- return false;
- }
+ if (addedNode.childElementCount) {
+ translateFragment.call(this, addedNode);
+ } else if (addedNode.hasAttribute('data-l10n-id')) {
+ translateElement.call(this, addedNode);
+ }
+ }
+ }
- var locale = this.ctx.getLocale(navigator.language);
- // the inline localization is happenning very early, when the ctx is not
- // yet ready and when the resources haven't been downloaded yet; add the
- // inlined JSON directly to the current locale
- locale.addAST(JSON.parse(script.innerHTML));
- // localize the visible DOM
- var l10n = {
- ctx: locale,
- language: {
- code: locale.id,
- direction: getDirection(locale.id)
- }
- };
- translateDocument.call(l10n);
-
- // the visible DOM is now pretranslated
- isPretranslated = true;
- return true;
- }
-
- function initResources() {
- var resLinks = document.head
- .querySelectorAll('link[type="application/l10n"]');
- var iniLinks = [];
-
- for (var i = 0; i < resLinks.length; i++) {
- var link = resLinks[i];
- var url = link.getAttribute('href');
- var type = url.substr(url.lastIndexOf('.') + 1);
- if (type === 'ini') {
- iniLinks.push(url);
- }
- this.ctx.resLinks.push(url);
+ if (mutation.type === 'attributes') {
+ translateElement.call(this, mutation.target);
+ }
+ }
}
- var iniLoads = iniLinks.length;
- if (iniLoads === 0) {
- initLocale.call(this);
- return;
+ function onMutations (mutations, self) {
+ self.disconnect();
+ localizeMutations.call(this, mutations);
+ self.observe(document, moConfig);
}
- function onIniLoaded(err) {
- if (err) {
- this.ctx._emitter.emit('error', err);
- }
- if (--iniLoads === 0) {
- initLocale.call(this);
- }
- }
+ function onReady () {
+ if (!isPretranslated) {
+ translateDocument.call(this);
+ }
+ isPretranslated = false;
+
+ if (!nodeObserver) {
+ nodeObserver = new MutationObserver(onMutations.bind(this)); // eslint-disable-line no-undef
+ nodeObserver.observe(document, moConfig);
+ }
- for (i = 0; i < iniLinks.length; i++) {
- loadINI.call(this, iniLinks[i], onIniLoaded.bind(this));
+ fireLocalizedEvent.call(this);
}
- }
- function initLocale() {
- this.ctx.requestLocales(navigator.language);
- window.addEventListener('languagechange', function l10n_langchange() {
- navigator.mozL10n.language.code = navigator.language;
- });
- }
+ function fireLocalizedEvent () {
+ var event = new CustomEvent('localized', { // eslint-disable-line no-undef
+ 'bubbles': false,
+ 'cancelable': false,
+ 'detail': {
+ 'language': this.ctx.supportedLocales[0]
+ }
+ });
+ window.dispatchEvent(event);
+ }
- function localizeMutations(mutations) {
- var mutation;
+ function loadINI (url, callback) {
+ var ctx = this.ctx;
+ io.load(url, function (err, source) {
+ var pos = ctx.resLinks.indexOf(url);
- for (var i = 0; i < mutations.length; i++) {
- mutation = mutations[i];
- if (mutation.type === 'childList') {
- var addedNode;
+ if (err) {
+ // remove the ini link from resLinks
+ ctx.resLinks.splice(pos, 1);
+ return callback(err);
+ }
- for (var j = 0; j < mutation.addedNodes.length; j++) {
- addedNode = mutation.addedNodes[j];
+ if (!source) {
+ ctx.resLinks.splice(pos, 1);
+ return callback(new Error('Empty file: ' + url));
+ }
- if (addedNode.nodeType !== Node.ELEMENT_NODE) {
- continue;
- }
+ var patterns = parseINI(source, url).resources.map(function (x) {
+ return x.replace('en-US', '{{locale}}');
+ });
+ ctx.resLinks.splice.apply(ctx.resLinks, [pos, 1].concat(patterns));
+ callback();
+ });
+ }
- if (addedNode.childElementCount) {
- translateFragment.call(this, addedNode);
- } else if (addedNode.hasAttribute('data-l10n-id')) {
- translateElement.call(this, addedNode);
- }
+ function relativePath (baseUrl, url) {
+ if (url[0] === '/') {
+ return url;
}
- }
- if (mutation.type === 'attributes') {
- translateElement.call(this, mutation.target);
- }
+ var dirs = baseUrl.split('/')
+ .slice(0, -1)
+ .concat(url.split('/'))
+ .filter(function (path) {
+ return path !== '.';
+ });
+
+ return dirs.join('/');
}
- }
- function onMutations(mutations, self) {
- self.disconnect();
- localizeMutations.call(this, mutations);
- self.observe(document, moConfig);
- }
+ var iniPatterns = {
+ 'section': /^\s*\[(.*)\]\s*$/,
+ 'import': /^\s*@import\s+url\((.*)\)\s*$/i,
+ 'entry': /[\r\n]+/
+ };
- function onReady() {
- if (!isPretranslated) {
- translateDocument.call(this);
- }
- isPretranslated = false;
+ function parseINI (source, iniPath) {
+ var entries = source.split(iniPatterns.entry);
+ var locales = ['en-US'];
+ var genericSection = true;
+ var uris = [];
+ var match;
- if (!nodeObserver) {
- nodeObserver = new MutationObserver(onMutations.bind(this));
- nodeObserver.observe(document, moConfig);
- }
+ for (var i = 0; i < entries.length; i++) {
+ var line = entries[i];
+ // we only care about en-US resources
+ if (genericSection && iniPatterns['import'].test(line)) {
+ match = iniPatterns['import'].exec(line);
+ var uri = relativePath(iniPath, match[1]);
+ uris.push(uri);
+ continue;
+ }
- fireLocalizedEvent.call(this);
- }
-
- function fireLocalizedEvent() {
- var event = new CustomEvent('localized', {
- 'bubbles': false,
- 'cancelable': false,
- 'detail': {
- 'language': this.ctx.supportedLocales[0]
- }
- });
- window.dispatchEvent(event);
- }
-
- /* jshint -W104 */
-
- function loadINI(url, callback) {
- var ctx = this.ctx;
- io.load(url, function(err, source) {
- var pos = ctx.resLinks.indexOf(url);
-
- if (err) {
- // remove the ini link from resLinks
- ctx.resLinks.splice(pos, 1);
- return callback(err);
- }
-
- if (!source) {
- ctx.resLinks.splice(pos, 1);
- return callback(new Error('Empty file: ' + url));
- }
-
- var patterns = parseINI(source, url).resources.map(function(x) {
- return x.replace('en-US', '{{locale}}');
- });
- ctx.resLinks.splice.apply(ctx.resLinks, [pos, 1].concat(patterns));
- callback();
- });
- }
-
- function relativePath(baseUrl, url) {
- if (url[0] === '/') {
- return url;
+ // but we need the list of all locales in the ini, too
+ if (iniPatterns.section.test(line)) {
+ genericSection = false;
+ match = iniPatterns.section.exec(line);
+ locales.push(match[1]);
+ }
+ }
+ return {
+ locales: locales,
+ resources: uris
+ };
}
- var dirs = baseUrl.split('/')
- .slice(0, -1)
- .concat(url.split('/'))
- .filter(function(path) {
- return path !== '.';
- });
-
- return dirs.join('/');
- }
-
- var iniPatterns = {
- 'section': /^\s*\[(.*)\]\s*$/,
- 'import': /^\s*@import\s+url\((.*)\)\s*$/i,
- 'entry': /[\r\n]+/
- };
-
- function parseINI(source, iniPath) {
- var entries = source.split(iniPatterns.entry);
- var locales = ['en-US'];
- var genericSection = true;
- var uris = [];
- var match;
-
- for (var i = 0; i < entries.length; i++) {
- var line = entries[i];
- // we only care about en-US resources
- if (genericSection && iniPatterns['import'].test(line)) {
- match = iniPatterns['import'].exec(line);
- var uri = relativePath(iniPath, match[1]);
- uris.push(uri);
- continue;
- }
-
- // but we need the list of all locales in the ini, too
- if (iniPatterns.section.test(line)) {
- genericSection = false;
- match = iniPatterns.section.exec(line);
- locales.push(match[1]);
- }
+ function translateDocument () {
+ document.documentElement.lang = this.language.code;
+ document.documentElement.dir = this.language.direction;
+ translateFragment.call(this, document.documentElement);
}
- return {
- locales: locales,
- resources: uris
- };
- }
-
- /* jshint -W104 */
- function translateDocument() {
- document.documentElement.lang = this.language.code;
- document.documentElement.dir = this.language.direction;
- translateFragment.call(this, document.documentElement);
- }
-
- function translateFragment(element) {
- if (element.hasAttribute('data-l10n-id')) {
- translateElement.call(this, element);
- }
+ function translateFragment (element) {
+ if (element.hasAttribute('data-l10n-id')) {
+ translateElement.call(this, element);
+ }
- var nodes = getTranslatableChildren(element);
- for (var i = 0; i < nodes.length; i++ ) {
- translateElement.call(this, nodes[i]);
+ var nodes = getTranslatableChildren(element);
+ for (var i = 0; i < nodes.length; i++) {
+ translateElement.call(this, nodes[i]);
+ }
}
- }
- function setL10nAttributes(element, id, args) {
- element.setAttribute('data-l10n-id', id);
- if (args) {
- element.setAttribute('data-l10n-args', JSON.stringify(args));
+ function setL10nAttributes (element, id, args) {
+ element.setAttribute('data-l10n-id', id);
+ if (args) {
+ element.setAttribute('data-l10n-args', JSON.stringify(args));
+ }
}
- }
- function getL10nAttributes(element) {
- return {
- id: element.getAttribute('data-l10n-id'),
- args: JSON.parse(element.getAttribute('data-l10n-args'))
- };
- }
-
- function getTranslatableChildren(element) {
- return element ? element.querySelectorAll('*[data-l10n-id]') : [];
- }
-
- function localizeElement(element, id, args) {
- if (!id) {
- element.removeAttribute('data-l10n-id');
- element.removeAttribute('data-l10n-args');
- setTextContent(element, '');
- return;
+ function getL10nAttributes (element) {
+ return {
+ id: element.getAttribute('data-l10n-id'),
+ args: JSON.parse(element.getAttribute('data-l10n-args'))
+ };
}
- element.setAttribute('data-l10n-id', id);
- if (args && typeof args === 'object') {
- element.setAttribute('data-l10n-args', JSON.stringify(args));
- } else {
- element.removeAttribute('data-l10n-args');
+ function getTranslatableChildren (element) {
+ return element ? element.querySelectorAll('*[data-l10n-id]') : [];
}
- }
- function translateElement(element) {
- var l10n = getL10nAttributes(element);
+ function localizeElement (element, id, args) {
+ if (!id) {
+ element.removeAttribute('data-l10n-id');
+ element.removeAttribute('data-l10n-args');
+ setTextContent(element, '');
+ return;
+ }
- if (!l10n.id) {
- return false;
+ element.setAttribute('data-l10n-id', id);
+ if (args && typeof args === 'object') {
+ element.setAttribute('data-l10n-args', JSON.stringify(args));
+ } else {
+ element.removeAttribute('data-l10n-args');
+ }
}
- var entity = this.ctx.getEntity(l10n.id, l10n.args);
+ function translateElement (element) {
+ var l10n = getL10nAttributes(element);
- if (!entity) {
- return false;
- }
+ if (!l10n.id) {
+ return false;
+ }
- if (typeof entity === 'string') {
- setTextContent(element, entity);
- return true;
- }
+ var entity = this.ctx.getEntity(l10n.id, l10n.args);
- if (entity.value) {
- setTextContent(element, entity.value);
- }
+ if (!entity) {
+ return false;
+ }
- for (var key in entity.attributes) {
- if (entity.attributes.hasOwnProperty(key)) {
- var attr = entity.attributes[key];
- if (key === 'ariaLabel') {
- element.setAttribute('aria-label', attr);
- } else if (key === 'innerHTML') {
- // XXX: to be removed once bug 994357 lands
- element.innerHTML = attr;
- } else {
- element.setAttribute(key, attr);
+ if (typeof entity === 'string') {
+ setTextContent(element, entity);
+ return true;
+ }
+
+ if (entity.value) {
+ setTextContent(element, entity.value);
}
- }
- }
- return true;
- }
+ for (var key in entity.attributes) {
+ if (entity.attributes.hasOwnProperty(key)) {
+ var attr = entity.attributes[key];
+ if (key === 'ariaLabel') {
+ element.setAttribute('aria-label', attr);
+ } else if (key === 'innerHTML') {
+ // XXX: to be removed once bug 994357 lands
+ element.innerHTML = attr;
+ } else {
+ element.setAttribute(key, attr);
+ }
+ }
+ }
- function setTextContent(element, text) {
- // standard case: no element children
- if (!element.firstElementChild) {
- element.textContent = text;
- return;
+ return true;
}
- // this element has element children: replace the content of the first
- // (non-blank) child textNode and clear other child textNodes
- var found = false;
- var reNotBlank = /\S/;
- for (var child = element.firstChild; child; child = child.nextSibling) {
- if (child.nodeType === Node.TEXT_NODE &&
+ function setTextContent (element, text) {
+ // standard case: no element children
+ if (!element.firstElementChild) {
+ element.textContent = text;
+ return;
+ }
+
+ // this element has element children: replace the content of the first
+ // (non-blank) child textNode and clear other child textNodes
+ var found = false;
+ var reNotBlank = /\S/;
+ for (var child = element.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType === Node.TEXT_NODE && // eslint-disable-line no-undef
reNotBlank.test(child.nodeValue)) {
- if (found) {
- child.nodeValue = '';
- } else {
- child.nodeValue = text;
- found = true;
+ if (found) {
+ child.nodeValue = '';
+ } else {
+ child.nodeValue = text;
+ found = true;
+ }
+ }
+ }
+ // if no (non-empty) textNode is found, insert a textNode before the
+ // element's first child.
+ if (!found) {
+ element.insertBefore(document.createTextNode(text), element.firstChild);
}
- }
- }
- // if no (non-empty) textNode is found, insert a textNode before the
- // element's first child.
- if (!found) {
- element.insertBefore(document.createTextNode(text), element.firstChild);
}
- }
})(this);
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org