summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/static/js/html10n.js825
-rw-r--r--src/static/js/l10n.js1028
-rw-r--r--src/templates/index.html14
-rw-r--r--src/templates/pad.html10
-rw-r--r--src/templates/timeslider.html13
5 files changed, 835 insertions, 1055 deletions
diff --git a/src/static/js/html10n.js b/src/static/js/html10n.js
new file mode 100644
index 00000000..c36dcc9f
--- /dev/null
+++ b/src/static/js/html10n.js
@@ -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) \ No newline at end of file
diff --git a/src/static/js/l10n.js b/src/static/js/l10n.js
deleted file mode 100644
index ef8218d3..00000000
--- a/src/static/js/l10n.js
+++ /dev/null
@@ -1,1028 +0,0 @@
-/** 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.
- */
-/*jshint browser: true, devel: true, es5: true, globalstrict: true */
-'use strict';
-
-document.webL10n = (function(window, document, undefined) {
- var gL10nData = {};
- var gTextData = '';
- var gTextProp = 'textContent';
- var gLanguage = '';
- var gMacros = {};
- var gReadyState = 'loading';
-
- // read-only setting -- we recommend to load l10n resources synchronously
- var gAsyncResourceLoading = true;
-
- // debug helpers
- var gDEBUG = false;
- function consoleLog(message) {
- if (gDEBUG)
- console.log('[l10n] ' + message);
- };
- function consoleWarn(message) {
- if (gDEBUG)
- console.warn('[l10n] ' + message);
- };
-
- /**
- * DOM helpers for the so-called "HTML API".
- *
- * These functions are written for modern browsers. For old versions of IE,
- * they're overridden in the 'startup' section at the end of this file.
- */
-
- function getL10nResourceLinks() {
- return document.querySelectorAll('link[type="application/l10n"]');
- }
-
- function getTranslatableChildren(element) {
- return element ? element.querySelectorAll('*[data-l10n-id]') : [];
- }
-
- function getL10nAttributes(element) {
- if (!element)
- return {};
-
- var l10nId = element.getAttribute('data-l10n-id');
- var l10nArgs = element.getAttribute('data-l10n-args');
- var args = {};
- if (l10nArgs) {
- try {
- args = JSON.parse(l10nArgs);
- } catch (e) {
- consoleWarn('could not parse arguments for #' + l10nId);
- }
- }
- return { id: l10nId, args: args };
- }
-
- function fireL10nReadyEvent(lang) {
- var evtObject = document.createEvent('Event');
- evtObject.initEvent('localized', false, false);
- evtObject.language = lang;
- window.dispatchEvent(evtObject);
- }
-
-
- /**
- * l10n resource parser:
- * - reads (async XHR) the l10n resource matching `lang';
- * - imports linked resources (synchronously) when specified;
- * - parses the text data (fills `gL10nData' and `gTextData');
- * - triggers success/failure callbacks when done.
- *
- * @param {string} href
- * URL of the l10n resource to parse.
- *
- * @param {string} lang
- * locale (language) to parse.
- *
- * @param {Function} successCallback
- * triggered when the l10n resource has been successully parsed.
- *
- * @param {Function} failureCallback
- * triggered when the an error has occured.
- *
- * @return {void}
- * uses the following global variables: gL10nData, gTextData, gTextProp.
- */
-
- function parseResource(href, lang, successCallback, failureCallback) {
- var baseURL = href.replace(/\/[^\/]*$/, '/');
-
- // handle escaped characters (backslashes) in a string
- function evalString(text) {
- if (text.lastIndexOf('\\') < 0)
- return text;
- return text.replace(/\\\\/g, '\\')
- .replace(/\\n/g, '\n')
- .replace(/\\r/g, '\r')
- .replace(/\\t/g, '\t')
- .replace(/\\b/g, '\b')
- .replace(/\\f/g, '\f')
- .replace(/\\{/g, '{')
- .replace(/\\}/g, '}')
- .replace(/\\"/g, '"')
- .replace(/\\'/g, "'");
- }
-
- // parse *.properties text data into an l10n dictionary
- function parseProperties(text) {
- var dictionary = {};
-
- // token expressions
- var reBlank = /^\s*|\s*$/;
- var reComment = /^\s*;|^\s*$/;// Use ; for comments!
- var reSection = /^\s*\[(.*)\]\s*$/;
- var reImport = /^\s*@import\s+url\((.*)\)\s*$/i;
- var reSplit = /^([^=\s]*)\s*=\s*(.+)$/; // TODO: escape EOLs with '\'
-
- // parse the *.properties file into an associative array
- function parseRawLines(rawText, extendedSyntax) {
- var entries = rawText.replace(reBlank, '').split(/[\r\n]+/);
- var currentLang = '*';
- var genericLang = lang.replace(/-[a-z]+$/i, '');
- var skipLang = false;
- var match = '';
-
- for (var i = 0; i < entries.length; i++) {
- var line = entries[i];
-
- // comment or blank line?
- if (reComment.test(line))
- continue;
-
- // the extended syntax supports [lang] sections and @import rules
- if (extendedSyntax) {
- if (reSection.test(line)) { // section start?
- match = reSection.exec(line);
- currentLang = match[1];
- skipLang = (currentLang !== '*') &&
- (currentLang !== lang) && (currentLang !== genericLang);
- continue;
- } else if (skipLang) {
- continue;
- }
- if (reImport.test(line)) { // @import rule?
- match = reImport.exec(line);
- loadImport(baseURL + match[1]); // load the resource synchronously
- }
- }
-
- // key-value pair
- consoleLog(tmp)
- var tmp = line.match(reSplit);
- if (tmp && tmp.length == 3)
- dictionary[tmp[1]] = evalString(tmp[2]);
- }
- }
-
- // import another *.properties file
- function loadImport(url) {
- loadResource(url, function(content) {
- parseRawLines(content, false); // don't allow recursive imports
- }, false, false); // load synchronously
- }
-
- // fill the dictionary
- parseRawLines(text, true);
- return dictionary;
- }
-
- // load the specified resource file
- function loadResource(url, onSuccess, onFailure, asynchronous) {
- var xhr = new XMLHttpRequest();
- xhr.open('GET', url, asynchronous);
- if (xhr.overrideMimeType) {
- xhr.overrideMimeType('text/plain; charset=utf-8');
- }
- xhr.onreadystatechange = function() {
- if (xhr.readyState == 4) {
- if (xhr.status == 200 || xhr.status === 0) {
- if (onSuccess)
- onSuccess(xhr.responseText);
- } else {
- if (onFailure)
- onFailure();
- }
- }
- };
- xhr.send(null);
- }
-
- // load and parse l10n data (warning: global variables are used here)
- loadResource(href, function(response) {
- gTextData += response; // mostly for debug
-
- // parse *.properties text data into an l10n dictionary
- var data = parseProperties(response);
-
- // allowed attributes
- var attrList =
- { "title": 1
- , "innerHTML": 1
- , "alt": 1
- , "textContent": 1
- }
-
- // find attribute descriptions, if any
- for (var key in data) {
- var id, prop, index = key.lastIndexOf('.');
- if (index > 0 && key.substr(index + 1) in attrList) { // an attribute has been specified
- id = key.substring(0, index);
- prop = key.substr(index + 1);
- } else { // no attribute: assuming text content by default
- id = key;
- prop = gTextProp;
- }
- if (!gL10nData[id]) {
- gL10nData[id] = {};
- }
- gL10nData[id][prop] = data[key];
- }
-
- // trigger callback
- if (successCallback)
- successCallback();
- }, failureCallback, gAsyncResourceLoading);
- };
-
- // load and parse all resources for the specified locale
- function loadLocale(lang, callback) {
- clear();
- gLanguage = lang;
-
- // check all <link type="application/l10n" href="..." /> nodes
- // and load the resource files
- var langLinks = getL10nResourceLinks();
- var langCount = langLinks.length;
- if (langCount == 0) {
- consoleLog('no resource to load, early way out');
- fireL10nReadyEvent(lang);
- gReadyState = 'complete';
- return;
- }
-
- // start the callback when all resources are loaded
- var onResourceLoaded = null;
- var gResourceCount = 0;
- onResourceLoaded = function() {
- gResourceCount++;
- if (gResourceCount >= langCount) {
- if (callback) // execute the [optional] callback
- callback();
- fireL10nReadyEvent(lang);
- gReadyState = 'complete';
- }
- };
-
- // load all resource files
- function l10nResourceLink(link) {
- var href = link.href;
- var type = link.type;
- this.load = function(lang, callback) {
- var applied = lang;
- parseResource(href, lang, callback, function() {
- consoleWarn(href + ' not found.');
- applied = '';
- });
- return applied; // return lang if found, an empty string if not found
- };
- }
-
- for (var i = 0; i < langCount; i++) {
- var resource = new l10nResourceLink(langLinks[i]);
- var rv = resource.load(lang, onResourceLoaded);
- if (rv != lang) { // lang not found, used default resource instead
- consoleWarn('"' + lang + '" resource not found');
- gLanguage = '';
- }
- }
- }
-
- // clear all l10n data
- function clear() {
- gL10nData = {};
- gTextData = '';
- gLanguage = '';
- // TODO: clear all non predefined macros.
- // There's no such macro /yet/ but we're planning to have some...
- }
-
-
- /**
- * 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
- gMacros.plural = function(str, param, key, prop) {
- var n = parseFloat(param);
- if (isNaN(n))
- return str;
-
- // TODO: support other properties (l20n still doesn't...)
- if (prop != gTextProp)
- return str;
-
- // initialize _pluralRules
- if (!gMacros._pluralRules)
- gMacros._pluralRules = getPluralRules(gLanguage);
- var index = '[' + gMacros._pluralRules(n) + ']';
-
- // try to find a [zero|one|two] key if it's defined
- if (n === 0 && (key + '[zero]') in gL10nData) {
- str = gL10nData[key + '[zero]'][prop];
- } else if (n == 1 && (key + '[one]') in gL10nData) {
- str = gL10nData[key + '[one]'][prop];
- } else if (n == 2 && (key + '[two]') in gL10nData) {
- str = gL10nData[key + '[two]'][prop];
- } else if ((key + index) in gL10nData) {
- str = gL10nData[key + index][prop];
- }
-
- return str;
- };
-
-
- /**
- * l10n dictionary functions
- */
-
- // fetch an l10n object, warn if not found, apply `args' if possible
- function getL10nData(key, args) {
- var data = gL10nData[key];
- if (!data) {
- consoleWarn('#' + key + ' missing for [' + gLanguage + ']');
- }
-
- /** This is where l10n expressions should be processed.
- * The plan is to support C-style expressions from the l20n project;
- * until then, only two kinds of simple expressions are supported:
- * {[ index ]} and {{ arguments }}.
- */
- var rv = {};
- for (var prop in data) {
- var str = data[prop];
- str = substIndexes(str, args, key, prop);
- str = substArguments(str, args);
- rv[prop] = str;
- }
- return rv;
- }
-
- // replace {[macros]} with their values
- function substIndexes(str, args, key, prop) {
- var reIndex = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)\s*\]\}/;
- var reMatch = reIndex.exec(str);
- if (!reMatch || !reMatch.length)
- return str;
-
- // an index/macro has been found
- // Note: at the moment, only one parameter is supported
- var macroName = reMatch[1];
- var paramName = reMatch[2];
- var param;
- if (args && paramName in args) {
- param = args[paramName];
- } else if (paramName in gL10nData) {
- param = gL10nData[paramName];
- }
-
- // there's no macro parser yet: it has to be defined in gMacros
- if (macroName in gMacros) {
- var macro = gMacros[macroName];
- str = macro(str, param, key, prop);
- }
- return str;
- }
-
- // replace {{arguments}} with their values
- function substArguments(str, args) {
- var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/;
- var match = reArgs.exec(str);
- while (match) {
- if (!match || match.length < 2)
- return str; // argument key not found
-
- var arg = match[1];
- var sub = '';
- if (arg in args) {
- sub = args[arg];
- } else if (arg in gL10nData) {
- sub = gL10nData[arg][gTextProp];
- } else {
- consoleWarn('could not find argument {{' + arg + '}}');
- return str;
- }
-
- str = str.substring(0, match.index) + sub +
- str.substr(match.index + match[0].length);
- match = reArgs.exec(str);
- }
- return str;
- }
-
- // translate an HTML element
- function translateElement(element) {
- var l10n = getL10nAttributes(element);
- if (!l10n.id)
- return;
-
- // get the related l10n object
- var data = getL10nData(l10n.id, l10n.args);
- if (!data) {
- consoleWarn('#' + l10n.id + ' missing for [' + gLanguage + ']');
- return;
- }
-
- // translate element (TODO: security checks?)
- // for the node content, replace the content of the first child textNode
- // and clear other child textNodes
- if (data[gTextProp]) { // XXX
- if (element.children.length === 0) {
- element[gTextProp] = data[gTextProp];
- } else {
- var children = element.childNodes,
- found = false;
- for (var i = 0, l = children.length; i < l; i++) {
- if (children[i].nodeType === 3 &&
- /\S/.test(children[i].textContent)) { // XXX
- // using nodeValue seems cross-browser
- if (found) {
- children[i].nodeValue = '';
- } else {
- children[i].nodeValue = data[gTextProp];
- found = true;
- }
- }
- }
- if (!found) {
- consoleWarn('unexpected error, could not translate element content');
- }
- }
- delete data[gTextProp];
- }
-
- for (var k in data) {
- element[k] = data[k];
- }
- }
-
- // translate an HTML subtree
- function translateFragment(element) {
- element = element || document.documentElement;
-
- // check all translatable children (= w/ a `data-l10n-id' attribute)
- var children = getTranslatableChildren(element);
- var elementCount = children.length;
- for (var i = 0; i < elementCount; i++) {
- translateElement(children[i]);
- }
-
- // translate element itself if necessary
- translateElement(element);
- }
-
-
- /**
- * Startup & Public API
- *
- * Warning: this part of the code contains browser-specific chunks --
- * that's where obsolete browsers, namely IE8 and earlier, are handled.
- *
- * Unlike the rest of the lib, this section is not shared with FirefoxOS/Gaia.
- */
-
- // browser-specific startup
- if (document.addEventListener) { // modern browsers and IE9+
- document.addEventListener('DOMContentLoaded', function() {
- var lang = document.documentElement.lang || navigator.language || navigator.userLanguage || 'en';
- loadLocale(lang, translateFragment);
- }, false);
- } else if (window.attachEvent) { // IE8 and before (= oldIE)
- // TODO: check if jQuery is loaded (CSS selector + JSON + events)
-
- // dummy `console.log' and `console.warn' functions
- if (!window.console) {
- consoleLog = function(message) {}; // just ignore console.log calls
- consoleWarn = function(message) {
- if (gDEBUG)
- alert('[l10n] ' + message); // vintage debugging, baby!
- };
- }
-
- // worst hack ever for IE6 and IE7
- if (!window.JSON) {
- consoleWarn('[l10n] no JSON support');
-
- getL10nAttributes = function(element) {
- if (!element)
- return {};
- var l10nId = element.getAttribute('data-l10n-id'),
- l10nArgs = element.getAttribute('data-l10n-args'),
- args = {};
- if (l10nArgs) try {
- args = eval(l10nArgs); // XXX yeah, I know...
- } catch (e) {
- consoleWarn('[l10n] could not parse arguments for #' + l10nId);
- }
- return { id: l10nId, args: args };
- };
- }
-
- // override `getTranslatableChildren' and `getL10nResourceLinks'
- if (!document.querySelectorAll) {
- consoleWarn('[l10n] no "querySelectorAll" support');
-
- getTranslatableChildren = function(element) {
- if (!element)
- return [];
- var nodes = element.getElementsByTagName('*'),
- l10nElements = [],
- n = nodes.length;
- for (var i = 0; i < n; i++) {
- if (nodes[i].getAttribute('data-l10n-id'))
- l10nElements.push(nodes[i]);
- }
- return l10nElements;
- };
-
- getL10nResourceLinks = function() {
- var links = document.getElementsByTagName('link'),
- l10nLinks = [],
- n = links.length;
- for (var i = 0; i < n; i++) {
- if (links[i].type == 'application/l10n')
- l10nLinks.push(links[i]);
- }
- return l10nLinks;
- };
- }
-
- // fire non-standard `localized' DOM events
- if (document.createEventObject && !document.createEvent) {
- fireL10nReadyEvent = function(lang) {
- // hack to simulate a custom event in IE:
- // to catch this event, add an event handler to `onpropertychange'
- document.documentElement.localized = 1;
- };
- }
-
- // startup for IE<9
- window.attachEvent('onload', function() {
- gTextProp = document.body.textContent ? 'textContent' : 'innerText';
- var lang = document.documentElement.lang || navigator.language || navigator.userLanguage || 'en';
- loadLocale(lang, translateFragment);
- });
- }
-
- // cross-browser API (sorry, oldIE doesn't support getters & setters)
- return {
- // get a localized string
- get: function(key, args, fallback) {
- var data = getL10nData(key, args) || fallback;
- if (data) { // XXX double-check this
- return 'textContent' in data ? data.textContent : '';
- }
- return '{{' + key + '}}';
- },
-
- // debug
- getData: function() { return gL10nData; },
- getText: function() { return gTextData; },
-
- // get|set the document language
- getLanguage: function() { return gLanguage; },
- setLanguage: function(lang) { loadLocale(lang, translateFragment); },
-
- // get the direction (ltr|rtl) of the current language
- getDirection: function() {
- // http://www.w3.org/International/questions/qa-scripts
- // Arabic, Hebrew, Farsi, Pashto, Urdu
- var rtlList = ['ar', 'he', 'fa', 'ps', 'ur'];
- return (rtlList.indexOf(gLanguage) >= 0) ? 'rtl' : 'ltr';
- },
-
- // translate an element or document fragment
- translate: translateFragment,
-
- // this can be used to prevent race conditions
- getReadyState: function() { return gReadyState; }
- };
-
-}) (window, document);
-
-// gettext-like shortcut for navigator.webL10n.get
-if (window._ === undefined)
- var _ = document.webL10n.get;
-
-// CommonJS
-try {
- exports = document.webL10n;
-}catch(e){} \ No newline at end of file
diff --git a/src/templates/index.html b/src/templates/index.html
index 961c6012..222b9f29 100644
--- a/src/templates/index.html
+++ b/src/templates/index.html
@@ -31,17 +31,11 @@
<meta charset="utf-8">
<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%>">
-
- <script type="text/javascript">
- (function(document) {
- // Set language for l10n
- 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>
+
+ <link rel="localizations" type="application/l10n+json" href="../locales.ini" />
+ <script type="text/javascript" src="../static/js/html10n.js"></script>
+ <script type="text/javascript" src="../static/js/l10n.js"></script>
<style>
html, body {
diff --git a/src/templates/pad.html b/src/templates/pad.html
index 804c2e80..caeb4553 100644
--- a/src/templates/pad.html
+++ b/src/templates/pad.html
@@ -34,14 +34,8 @@
<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="resource" type="application/l10n" href="../locales.ini" />
- <script type="text/javascript">
- (function() {
- // Set language for l10n
- var language = document.cookie.match(/language=(\w{2})/);
- if(language) document.documentElement.lang = language[1];
- })();
- </script>
+ <link rel="localizations" type="application/l10n+json" href="../locales.ini" />
+ <script type="text/javascript" src="../static/js/html10n.js"></script>
<script type="text/javascript" src="../static/js/l10n.js"></script>
<% e.begin_block("styles"); %>
diff --git a/src/templates/timeslider.html b/src/templates/timeslider.html
index a3e94118..6f64ae82 100644
--- a/src/templates/timeslider.html
+++ b/src/templates/timeslider.html
@@ -32,16 +32,11 @@
<meta charset="utf-8">
<meta name="robots" content="noindex, nofollow">
<link rel="shortcut icon" href="<%=settings.faviconTimeslider%>">
- <link rel="resource" type="application/l10n" href="../../locales.ini" />
-
- <script type="text/javascript">
- (function() {
- // Set language for l10n
- var language = document.cookie.match(/language=(\w{2})/);
- if(language) document.documentElement.lang = language[1];
- })();
- </script>
+
+ <link rel="localizations" type="application/l10n+json" href="../../locales.ini" />
+ <script type="text/javascript" src="../../static/js/html10n.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/timeslider.css">
<link rel="stylesheet" href="../../static/custom/timeslider.css">