mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-20 06:29:53 +01:00
Replace webL10n with html10n.js
This commit is contained in:
parent
4534d20c80
commit
53521c8732
5 changed files with 835 additions and 1055 deletions
825
src/static/js/html10n.js
Normal file
825
src/static/js/html10n.js
Normal file
|
@ -0,0 +1,825 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2012 Marcel Klehr
|
||||||
|
* Copyright (c) 2011-2012 Fabien Cazenave, Mozilla
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
window.html10n = (function(window, document, undefined) {
|
||||||
|
var consoleLog = console? console.log : function() {}
|
||||||
|
, consoleWarn = console? console.warn : function() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MicroEvent - to make any js object an event emitter (server or browser)
|
||||||
|
*/
|
||||||
|
|
||||||
|
var MicroEvent = function(){}
|
||||||
|
MicroEvent.prototype = {
|
||||||
|
bind : function(event, fct){
|
||||||
|
this._events = this._events || {};
|
||||||
|
this._events[event] = this._events[event] || [];
|
||||||
|
this._events[event].push(fct);
|
||||||
|
},
|
||||||
|
unbind : function(event, fct){
|
||||||
|
this._events = this._events || {};
|
||||||
|
if( event in this._events === false ) return;
|
||||||
|
this._events[event].splice(this._events[event].indexOf(fct), 1);
|
||||||
|
},
|
||||||
|
trigger : function(event /* , args... */){
|
||||||
|
this._events = this._events || {};
|
||||||
|
if( event in this._events === false ) return;
|
||||||
|
for(var i = 0; i < this._events[event].length; i++){
|
||||||
|
this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* mixin will delegate all MicroEvent.js function in the destination object
|
||||||
|
* @param {Object} the object which will support MicroEvent
|
||||||
|
*/
|
||||||
|
MicroEvent.mixin = function(destObject){
|
||||||
|
var props = ['bind', 'unbind', 'trigger'];
|
||||||
|
if(!destObject.prototype) destObject.prototype = {};
|
||||||
|
for(var i = 0; i < props.length; i ++){
|
||||||
|
destObject.prototype[props[i]] = MicroEvent.prototype[props[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loader
|
||||||
|
* The loader is responsible for loading
|
||||||
|
* and caching all necessary resources
|
||||||
|
*/
|
||||||
|
function Loader(resources) {
|
||||||
|
this.resources = resources
|
||||||
|
this.cache = {} // file => contents
|
||||||
|
this.langs = {} // lang => strings
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader.prototype.load = function(lang, cb) {
|
||||||
|
if(this.langs[lang]) return cb()
|
||||||
|
|
||||||
|
if (this.resources.length > 0) {
|
||||||
|
var reqs = 0;
|
||||||
|
for (var i=0, n=this.resources.length; i < n; i++) {
|
||||||
|
this.fetch(this.resources[i], lang, function(e) {
|
||||||
|
reqs++;
|
||||||
|
if(e) setTimeout(function(){ throw e }, 0)
|
||||||
|
|
||||||
|
if (reqs < n) return;// Call back once all reqs are completed
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader.prototype.fetch = function(href, lang, cb) {
|
||||||
|
var that = this
|
||||||
|
|
||||||
|
if (this.cache[href]) {
|
||||||
|
this.parse(this.cache[href])
|
||||||
|
cb()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest()
|
||||||
|
xhr.open('GET', href, /*async: */true)
|
||||||
|
if (xhr.overrideMimeType) {
|
||||||
|
xhr.overrideMimeType('application/json; charset=utf-8');
|
||||||
|
}
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if (xhr.readyState == 4) {
|
||||||
|
if (xhr.status == 200 || xhr.status === 0) {
|
||||||
|
var data = JSON.parse(xhr.responseText)
|
||||||
|
that.cache[href] = data
|
||||||
|
// Pass on the contents for parsing
|
||||||
|
this.parse(lang, data, cb)
|
||||||
|
} else {
|
||||||
|
cb(new Error('Failed to load '+href))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader.prototype.parse = function(lang, data, cb) {
|
||||||
|
if ('object' != typeof data) {
|
||||||
|
cb(new Error('A file couldn\'t be parsed as json.'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data[lang]) {
|
||||||
|
cb(new Error('Couldn\'t find translations for '+lang))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('string' == typeof data[lang]) {
|
||||||
|
// Import rule
|
||||||
|
this.fetch(data[lang], lang, cb)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('object' != typeof data[lang]) {
|
||||||
|
cb(new Error('Translations should be specified as JSON objects!'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.langs[lang] = data[lang]
|
||||||
|
// TODO: Also store accompanying langs
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The html10n object
|
||||||
|
*/
|
||||||
|
var html10n =
|
||||||
|
{ language: null
|
||||||
|
}
|
||||||
|
MicroEvent.mixin(html10n)
|
||||||
|
|
||||||
|
html10n.macros = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get rules for plural forms (shared with JetPack), see:
|
||||||
|
* http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
|
||||||
|
* https://github.com/mozilla/addon-sdk/blob/master/python-lib/plural-rules-generator.p
|
||||||
|
*
|
||||||
|
* @param {string} lang
|
||||||
|
* locale (language) used.
|
||||||
|
*
|
||||||
|
* @return {Function}
|
||||||
|
* returns a function that gives the plural form name for a given integer:
|
||||||
|
* var fun = getPluralRules('en');
|
||||||
|
* fun(1) -> 'one'
|
||||||
|
* fun(0) -> 'other'
|
||||||
|
* fun(1000) -> 'other'.
|
||||||
|
*/
|
||||||
|
function getPluralRules(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 start <= n && n <= end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// list of all plural rules methods:
|
||||||
|
// map an integer to the plural form name to use
|
||||||
|
var pluralRules = {
|
||||||
|
'0': function(n) {
|
||||||
|
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)) {
|
||||||
|
consoleWarn('plural form unknown for [' + lang + ']');
|
||||||
|
return function() { return 'other'; };
|
||||||
|
}
|
||||||
|
return pluralRules[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pre-defined 'plural' macro
|
||||||
|
*/
|
||||||
|
html10n.macros.plural = function(translations, key, str, param) {
|
||||||
|
var n = parseFloat(param);
|
||||||
|
if (isNaN(n))
|
||||||
|
return str;
|
||||||
|
|
||||||
|
// initialize _pluralRules
|
||||||
|
if (!this._pluralRules)
|
||||||
|
this._pluralRules = getPluralRules(html10n.language);
|
||||||
|
var index = '[' + this._pluralRules(n) + ']';
|
||||||
|
|
||||||
|
// try to find a [zero|one|two] key if it's defined
|
||||||
|
if (n === 0 && (key + '[zero]') in translations) {
|
||||||
|
str = translations[key + '[zero]'];
|
||||||
|
} else if (n == 1 && (key + '[one]') in translations) {
|
||||||
|
str = translations[key + '[one]'];
|
||||||
|
} else if (n == 2 && (key + '[two]') in translations) {
|
||||||
|
str = translations[key + '[two]'];
|
||||||
|
} else if ((key + index) in translations) {
|
||||||
|
str = translations[key + index][prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localize a document
|
||||||
|
* @param langs An array of lang codes defining fallbacks
|
||||||
|
*/
|
||||||
|
html10n.localize = function(langs) {
|
||||||
|
// if only one string => create an array
|
||||||
|
if ('string' == typeof langs) langs = [langs]
|
||||||
|
|
||||||
|
this.build(langs, function(er, translations) {
|
||||||
|
html10n.translations = translations
|
||||||
|
html10n.translateElement(translations)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers the translation process
|
||||||
|
* for an element
|
||||||
|
* @param translations A hash of all translation strings
|
||||||
|
* @param element A DOM element, if omitted, the document element will be used
|
||||||
|
*/
|
||||||
|
html10n.translateElement = function(translations, element) {
|
||||||
|
element = element || document.documentElement
|
||||||
|
|
||||||
|
var children = element? getTranslatableChildren(element) : document.childNodes;
|
||||||
|
for (var i=0, n=children.length; i < n; i++) {
|
||||||
|
translateNode(translations, children[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate element itself if necessary
|
||||||
|
translateNode(translations, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
function asyncForEach(list, iterator, cb) {
|
||||||
|
var i = 0
|
||||||
|
, n = list.length
|
||||||
|
iterator(list[i], i, function each(err) {
|
||||||
|
consoleLog(err)
|
||||||
|
i++
|
||||||
|
if (i < n) return iterator(list[i],i, each);
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTranslatableChildren(element) {
|
||||||
|
if(!document.querySelectorAll) {
|
||||||
|
if (!element) return []
|
||||||
|
var nodes = element.getElementsByTagName('*')
|
||||||
|
, l10nElements = []
|
||||||
|
for (var i=0, n=nodes.length; i < n; i++) {
|
||||||
|
if (nodes[i].getAttribute('data-l10n-id'))
|
||||||
|
l10nElements.push(nodes[i]);
|
||||||
|
}
|
||||||
|
return l10nElements
|
||||||
|
}
|
||||||
|
return element.querySelectorAll('*[data-l10n-id]')
|
||||||
|
}
|
||||||
|
|
||||||
|
html10n.get = function(id, args) {
|
||||||
|
var translations = html10n.translations
|
||||||
|
if(!translations[id]) return consoleWarn('Could not find string '+id)
|
||||||
|
|
||||||
|
// apply args
|
||||||
|
str = substArguments(translations[id], args)
|
||||||
|
|
||||||
|
// apply macros
|
||||||
|
return substMacros(translations, id, str, args)
|
||||||
|
|
||||||
|
// replace {{arguments}} with their values or the
|
||||||
|
// associated translation string (based on its key)
|
||||||
|
function substArguments(str, args) {
|
||||||
|
var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/
|
||||||
|
, match
|
||||||
|
|
||||||
|
while (match = reArgs.exec(str)) {
|
||||||
|
if (!match || match.length < 2)
|
||||||
|
return str // argument key not found
|
||||||
|
|
||||||
|
var arg = match[1]
|
||||||
|
, sub = ''
|
||||||
|
if (arg in args) {
|
||||||
|
sub = args[arg]
|
||||||
|
} else if (arg in translations) {
|
||||||
|
sub = translations[arg]
|
||||||
|
} else {
|
||||||
|
consoleWarn('Could not find argument {{' + arg + '}}')
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str.substring(0, match.index) + sub + str.substr(match.index + match[0].length)
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace {[macros]} with their values
|
||||||
|
function substMacros(key, str, args) {
|
||||||
|
var regex = /\{\[\s*([a-zA-Z]+):([a-zA-Z]+)\s*\]\}/
|
||||||
|
, match = regex.exec(str);
|
||||||
|
if (!match || !match.length)
|
||||||
|
return str;
|
||||||
|
|
||||||
|
// a macro has been found
|
||||||
|
// Note: at the moment, only one parameter is supported
|
||||||
|
var macroName = reMatch[1]
|
||||||
|
, paramName = reMatch[2]
|
||||||
|
|
||||||
|
if (!(macroName in gMacros)) return str
|
||||||
|
|
||||||
|
var param
|
||||||
|
if (args && paramName in args) {
|
||||||
|
param = args[paramName]
|
||||||
|
} else if (paramName in translations) {
|
||||||
|
param = translations[paramName]
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's no macro parser yet: it has to be defined in gMacros
|
||||||
|
var macro = html10n.macros[macroName]
|
||||||
|
str = macro(translations, key, str, param)
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies translations to a DOM node (recursive)
|
||||||
|
*/
|
||||||
|
html10n.translateNode = function(translations, node) {
|
||||||
|
var str = {}
|
||||||
|
|
||||||
|
// get id
|
||||||
|
str.id = node.getAttribute('data-l10n-id')
|
||||||
|
if (!str.id) return
|
||||||
|
if(!translations[str.id]) return
|
||||||
|
|
||||||
|
// get args
|
||||||
|
if(window.JSON) {
|
||||||
|
str.args = JSON.parse(node.getAttribute('data-l10n-args'))
|
||||||
|
}else{
|
||||||
|
try{
|
||||||
|
str.args = eval(node.getAttribute('data-l10n-args'))
|
||||||
|
}catch(e) {
|
||||||
|
consoleWarn('Couldn\'t parse args for '+str.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str.str = html10n.get(str.id, str.args)
|
||||||
|
|
||||||
|
var prop
|
||||||
|
, index = str.id.lastIndexOf('.')
|
||||||
|
, attrList = // allowed attributes
|
||||||
|
{ "title": 1
|
||||||
|
, "innerHTML": 1
|
||||||
|
, "alt": 1
|
||||||
|
, "textContent": 1
|
||||||
|
}
|
||||||
|
// get attribute name to apply str to
|
||||||
|
if (index > 0 && str.id.substr(index + 1) in attrList) { // an attribute has been specified
|
||||||
|
prop = str.id.substr(index + 1)
|
||||||
|
} else { // no attribute: assuming text content by default
|
||||||
|
prop = document.body.textContent ? 'textContent' : 'innerText'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply translation
|
||||||
|
if (node.children.length === 0) {
|
||||||
|
node[prop] = str.str
|
||||||
|
} else {
|
||||||
|
var children = element.childNodes,
|
||||||
|
found = false
|
||||||
|
for (var i=0, n=children.length; i < n; i++) {
|
||||||
|
if (children[i].nodeType === 3 && /\S/.test(children[i].textContent)) {
|
||||||
|
if (!found) {
|
||||||
|
children[i].nodeValue = str.str
|
||||||
|
found = true
|
||||||
|
} else {
|
||||||
|
children[i].nodeValue = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
consoleWarn('Unexpected error: could not translate element content')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a translation object from a list of langs (loads the necessary translations)
|
||||||
|
* @param langs Array - a list of langs sorted by priority (default langs should go last)
|
||||||
|
*/
|
||||||
|
html10n.build = function(langs, cb) {
|
||||||
|
var build = {}
|
||||||
|
|
||||||
|
asyncForEach(langs, function (lang, i, next) {
|
||||||
|
html10n.loader.load(lang, next)
|
||||||
|
}, function() {
|
||||||
|
|
||||||
|
langs.reverse()
|
||||||
|
for (var i=0, n=langs.length; i < n; i++) {
|
||||||
|
// apply all strings of the current lang in the list
|
||||||
|
// to our build object
|
||||||
|
for (var string in this.loader.langs[lang]) {
|
||||||
|
build[string] = this.loader.langs[lang][string]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb(null, build)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index all <link>s
|
||||||
|
*/
|
||||||
|
html10n.index = function () {
|
||||||
|
// Find all <link>s
|
||||||
|
var links = document.getElementsByTagName('link')
|
||||||
|
for (var i=0, n=links.length; i < n; i++) {
|
||||||
|
if (links[i].type != 'application/l10n+json')
|
||||||
|
continue;
|
||||||
|
resources.push(links[i].href)
|
||||||
|
}
|
||||||
|
this.loader = new Loader(resources)
|
||||||
|
this.trigger('indexed')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.addEventListener) // modern browsers and IE9+
|
||||||
|
document.addEventListener('DOMContentLoaded', html10n.index, false)
|
||||||
|
else if (window.attachEvent)
|
||||||
|
document.attachEvent('onload', html10n.index, false)
|
||||||
|
|
||||||
|
// gettext-like shortcut
|
||||||
|
if (window._ === undefined)
|
||||||
|
var _ = html10n.get;
|
||||||
|
|
||||||
|
})(window, document)
|
File diff suppressed because it is too large
Load diff
|
@ -31,17 +31,11 @@
|
||||||
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
||||||
<link rel="resource" type="application/l10n" href="locales.ini" />
|
|
||||||
<link rel="shortcut icon" href="<%=settings.favicon%>">
|
<link rel="shortcut icon" href="<%=settings.favicon%>">
|
||||||
|
|
||||||
<script type="text/javascript">
|
<link rel="localizations" type="application/l10n+json" href="../locales.ini" />
|
||||||
(function(document) {
|
<script type="text/javascript" src="../static/js/html10n.js"></script>
|
||||||
// Set language for l10n
|
<script type="text/javascript" src="../static/js/l10n.js"></script>
|
||||||
var language = document.cookie.match(/language=(\w{2})/);
|
|
||||||
if(language) document.documentElement.lang = language[1];
|
|
||||||
})(document)
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript" src="static/js/l10n.js"></script>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
html, body {
|
html, body {
|
||||||
|
|
|
@ -34,14 +34,8 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
||||||
<link rel="shortcut icon" href="<%=settings.faviconPad%>">
|
<link rel="shortcut icon" href="<%=settings.faviconPad%>">
|
||||||
|
|
||||||
<link rel="resource" type="application/l10n" href="../locales.ini" />
|
<link rel="localizations" type="application/l10n+json" href="../locales.ini" />
|
||||||
<script type="text/javascript">
|
<script type="text/javascript" src="../static/js/html10n.js"></script>
|
||||||
(function() {
|
|
||||||
// Set language for l10n
|
|
||||||
var language = document.cookie.match(/language=(\w{2})/);
|
|
||||||
if(language) document.documentElement.lang = language[1];
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript" src="../static/js/l10n.js"></script>
|
<script type="text/javascript" src="../static/js/l10n.js"></script>
|
||||||
|
|
||||||
<% e.begin_block("styles"); %>
|
<% e.begin_block("styles"); %>
|
||||||
|
|
|
@ -32,16 +32,11 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="robots" content="noindex, nofollow">
|
<meta name="robots" content="noindex, nofollow">
|
||||||
<link rel="shortcut icon" href="<%=settings.faviconTimeslider%>">
|
<link rel="shortcut icon" href="<%=settings.faviconTimeslider%>">
|
||||||
<link rel="resource" type="application/l10n" href="../../locales.ini" />
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<link rel="localizations" type="application/l10n+json" href="../../locales.ini" />
|
||||||
(function() {
|
<script type="text/javascript" src="../../static/js/html10n.js"></script>
|
||||||
// Set language for l10n
|
|
||||||
var language = document.cookie.match(/language=(\w{2})/);
|
|
||||||
if(language) document.documentElement.lang = language[1];
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript" src="../../static/js/l10n.js"></script>
|
<script type="text/javascript" src="../../static/js/l10n.js"></script>
|
||||||
|
|
||||||
<link rel="stylesheet" href="../../static/css/pad.css">
|
<link rel="stylesheet" href="../../static/css/pad.css">
|
||||||
<link rel="stylesheet" href="../../static/css/timeslider.css">
|
<link rel="stylesheet" href="../../static/css/timeslider.css">
|
||||||
<link rel="stylesheet" href="../../static/custom/timeslider.css">
|
<link rel="stylesheet" href="../../static/custom/timeslider.css">
|
||||||
|
|
Loading…
Reference in a new issue