]> git.parisson.com Git - pdf.js.git/commitdiff
Missing webL10n library files
authorYury Delendik <ydelendik@mozilla.com>
Mon, 30 Apr 2012 20:33:37 +0000 (15:33 -0500)
committerYury Delendik <ydelendik@mozilla.com>
Mon, 30 Apr 2012 20:33:37 +0000 (15:33 -0500)
external/webL10n/README.md [new file with mode: 0644]
external/webL10n/l10n.js [new file with mode: 0644]

diff --git a/external/webL10n/README.md b/external/webL10n/README.md
new file mode 100644 (file)
index 0000000..5299552
--- /dev/null
@@ -0,0 +1,3 @@
+The source code for the library can be found at
+
+  https://github.com/fabi1cazenave/webL10n
diff --git a/external/webL10n/l10n.js b/external/webL10n/l10n.js
new file mode 100644 (file)
index 0000000..fedf70c
--- /dev/null
@@ -0,0 +1,304 @@
+/*  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.
+  */
+
+'use strict';
+
+(function(window) {
+  var gL10nData = {};
+  var gTextData = '';
+  var gLanguage = '';
+
+  // parser
+
+  function evalString(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, "'");
+  }
+
+  function parseProperties(text, lang) {
+    var reBlank = /^\s*|\s*$/;
+    var reComment = /^\s*#|^\s*$/;
+    var reSection = /^\s*\[(.*)\]\s*$/;
+    var reImport = /^\s*@import\s+url\((.*)\)\s*$/i;
+
+    // parse the *.properties file into an associative array
+    var currentLang = '*';
+    var supportedLang = [];
+    var skipLang = false;
+    var data = [];
+    var match = '';
+    var entries = text.replace(reBlank, '').split(/[\r\n]+/);
+    for (var i = 0; i < entries.length; i++) {
+      var line = entries[i];
+
+      // comment or blank line?
+      if (reComment.test(line))
+        continue;
+
+      // section start?
+      if (reSection.test(line)) {
+        match = reSection.exec(line);
+        currentLang = match[1];
+        skipLang = (currentLang != lang) && (currentLang != '*');
+        continue;
+      } else if (skipLang) {
+        continue;
+      }
+
+      // @import rule?
+      if (reImport.test(line)) {
+        match = reImport.exec(line);
+      }
+
+      // key-value pair
+      var tmp = line.split('=');
+      if (tmp.length > 1)
+        data[tmp[0]] = evalString(tmp[1]);
+    }
+
+    // find the attribute descriptions, if any
+    for (var key in data) {
+      var id, prop, index = key.lastIndexOf('.');
+      if (index > 0) { // attribute
+        id = key.substring(0, index);
+        prop = key.substr(index + 1);
+      } else { // textContent, could be innerHTML as well
+        id = key;
+        prop = 'textContent';
+      }
+      if (!gL10nData[id])
+        gL10nData[id] = {};
+      gL10nData[id][prop] = data[key];
+    }
+  }
+
+  function parse(text, lang) {
+    gTextData += text;
+    // we only support *.properties files at the moment
+    return parseProperties(text, lang);
+  }
+
+  // load and parse the specified resource file
+  function loadResource(href, lang, onSuccess, onFailure) {
+    var xhr = new XMLHttpRequest();
+    xhr.open('GET', href, true);
+    xhr.overrideMimeType('text/plain; charset=utf-8');
+    xhr.onreadystatechange = function() {
+      if (xhr.readyState == 4) {
+        if (xhr.status == 200 || xhr.status == 0) {
+          parse(xhr.responseText, lang);
+          if (onSuccess)
+            onSuccess();
+        } else {
+          if (onFailure)
+            onFailure();
+        }
+      }
+    };
+    xhr.send(null);
+  }
+
+  // load and parse all resources for the specified locale
+  function loadLocale(lang, callback) {
+    clear();
+
+    // check all <link type="application/l10n" href="..." /> nodes
+    // and load the resource files
+    var langLinks = document.querySelectorAll('link[type="application/l10n"]');
+    var langCount = langLinks.length;
+
+    // start the callback when all resources are loaded
+    var onResourceLoaded = null;
+    var gResourceCount = 0;
+    onResourceLoaded = function() {
+      gResourceCount++;
+      if (gResourceCount >= langCount) {
+        // execute the [optional] callback
+        if (callback)
+          callback();
+        // fire a 'localized' DOM event
+        var evtObject = document.createEvent('Event');
+        evtObject.initEvent('localized', false, false);
+        evtObject.language = lang;
+        window.dispatchEvent(evtObject);
+      }
+    }
+
+    // load all resource files
+    function l10nResourceLink(link) {
+      var href = link.href;
+      var type = link.type;
+      this.load = function(lang, callback) {
+        var applied = lang;
+        loadResource(href, lang, callback, function() {
+          console.warn(href + ' not found.');
+          applied = '';
+        });
+        return applied; // return lang if found, an empty string if not found
+      };
+    }
+
+    gLanguage = lang;
+    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
+        gLanguage = '';
+    }
+  }
+
+  // fetch an l10n object, warn if not found
+  function getL10nData(key) {
+    var data = gL10nData[key];
+    if (!data)
+      console.warn('[l10n] #' + key + ' missing for [' + gLanguage + ']');
+    return data;
+  }
+
+  // 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].textContent;
+      } else {
+        console.warn('[l10n] 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 a string
+  function translateString(key, args) {
+    var data = getL10nData(key);
+    if (!data)
+      return '{{' + key + '}}';
+    return substArguments(data.textContent, args);
+  }
+
+  // translate an HTML element
+  function translateElement(element) {
+    if (!element || !element.dataset)
+      return;
+
+    // get the related l10n object
+    var key = element.dataset.l10nId;
+    var data = getL10nData(key);
+    if (!data)
+      return;
+
+    // get arguments (if any)
+    // TODO: more flexible parser?
+    var args;
+    if (element.dataset.l10nArgs) try {
+      args = JSON.parse(element.dataset.l10nArgs);
+    } catch (e) {
+      console.warn('[l10n] could not parse arguments for #' + key + '');
+    }
+
+    // translate element
+    // TODO: security check?
+    for (var k in data)
+      element[k] = substArguments(data[k], args);
+  }
+
+  // translate an HTML subtree
+  function translateFragment(element) {
+    element = element || document.querySelector('html');
+
+    // check all translatable children (= w/ a `data-l10n-id' attribute)
+    var children = element.querySelectorAll('*[data-l10n-id]');
+    var elementCount = children.length;
+    for (var i = 0; i < elementCount; i++)
+      translateElement(children[i]);
+
+    // translate element itself if necessary
+    if (element.dataset.l10nId)
+      translateElement(element);
+  }
+
+  // clear all l10n data
+  function clear() {
+    gL10nData = {};
+    gTextData = '';
+    gLanguage = '';
+  }
+
+  // load the default locale on startup
+  window.addEventListener('DOMContentLoaded', function() {
+    var lang = navigator.language;
+    if (navigator.mozSettings) {
+      var req = navigator.mozSettings.getLock().get('language.current');
+      req.onsuccess = function() {
+        loadLocale(req.result['language.current'] || lang, translateFragment);
+      };
+      req.onerror = function() {
+        loadLocale(lang, translateFragment);
+      };
+    } else {
+      loadLocale(lang, translateFragment);
+    }
+  });
+
+  // Public API
+  document.mozL10n = {
+    // get a localized string
+    get: translateString,
+
+    // get|set the document language and direction
+    get language() {
+      return {
+        // get|set the document language (ISO-639-1)
+        get code() { return gLanguage; },
+        set code(lang) { loadLocale(lang, translateFragment); },
+
+        // get the direction (ltr|rtl) of the current language
+        get direction() {
+          // 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';
+        }
+      };
+    }
+  };
+})(this);