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();
|