aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.5/packages/addon-kit/lib/l10n.js
blob: 1bd7e5761614aa0356fcdf2f55769035699cb9ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

let prefs = require("preferences-service");
let { Cc, Ci } = require("chrome");

let globalHash = {};

exports.get = function get(k) {

  // For now, we only accept a "string" as first argument
  // TODO: handle plural forms in gettext pattern
  if (typeof k !== "string")
    throw new Error("First argument of localization method should be a string");

  // Get translation from big hashmap or default to hard coded string:
  let localized = globalHash[k] || k;

  // # Simplest usecase:
  //   // String hard coded in source code:
  //   _("Hello world")
  //   // Identifier of a key stored in properties file
  //   _("helloString")
  if (arguments.length <= 1)
    return localized;

  let args = arguments;

  if (typeof localized == "object" && "other" in localized) {
    // # Plural form:
    //   // Strings hard coded in source code:
    //   _(["One download", "%d downloads"], 10);
    //   // Identifier of a key stored in properties file
    //   _("downloadNumber", 0);
    let n = arguments[1];
    let pluralForm = "other";
    // TODO: Make this rule specific to each language
    if (n <= 1)
      pluralForm = "one";
    localized = localized[pluralForm];
    // Simulate a string with one placeholder:
    args = [null, n];
  }

  // # String with placeholders:
  //   // Strings hard coded in source code:
  //   _("Hello %s", username)
  //   // Identifier of a key stored in properties file
  //   _("helloString", username)
  // * We supports `%1s`, `%2s`, ... pattern in order to change arguments order
  // in translation.
  // * In case of plural form, we has `%d` instead of `%s`.
  let offset = 1;
  localized = localized.replace(/%(\d*)(s|d)/g, function (v, n) {
      let rv = args[n != "" ? n : offset];
      offset++;
      return rv;
    });

  return localized;
}

function readURI(uri) {
  let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
                createInstance(Ci.nsIXMLHttpRequest);
  request.open('GET', uri, false);
  request.overrideMimeType('text/plain');
  request.send();
  return request.responseText;
}

// Returns URI of the best locales file to use from the XPI
//   Reproduce platform algorithm, see `LanguagesMatch` and
//   `nsChromeRegistryChrome::nsProviderArray::GetProvider` functions:
//   http://mxr.mozilla.org/mozilla-central/source/chrome/src/nsChromeRegistryChrome.cpp#93
// TODO: Implement a better matching algorithm using "intl.accept_languages"
// and following: http://tools.ietf.org/html/rfc4647#page-14
function searchAddonLocaleFile(preferred) {
  // Get URI for the addon root folder:
  let rootURI = require("@packaging").rootURI;

  // Read localization manifest file that contains list of available languages
  let localesManifest = JSON.parse(readURI(rootURI + "locales.json"));
  let locales = localesManifest.locales;

  let localeFile = null;
  // Select exact matching first
  if (locales.indexOf(preferred) != -1) {
    localeFile = preferred;
  }
  // Then ignore yy in "xx-yy" pattern.
  // Ex: accept "fr-FR", if `preferred` is "fr"
  else {
    let prefix = preferred.replace(/-.*$/, "");
    for each(let filename in locales) {
      if (filename.indexOf(prefix + "-") == 0) {
        localeFile = filename;
        break;
      }
    }
  }
  if (!localeFile)
    return null;

  return rootURI + "locale/" + localeFile + ".json";
}

function init() {
  // First, search for a locale file:
  let preferred = prefs.get("general.useragent.locale", "en-US");
  let localeURI = searchAddonLocaleFile(preferred);
  if (!localeURI)
    return;

  let manifestJSON = readURI(localeURI);
  let manifest = JSON.parse(manifestJSON);

  // Locale files only contains one big JSON object that is used as
  // an hashtable of: "key to translate" => "translated key"
  // TODO: We are likely to change this in order to be able to overload
  //       a specific key translation. For a specific package, module or line?
  globalHash = manifest;
}
init();