aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.7/packages/addon-kit/lib/l10n.js
blob: 198f711b132efc6ac9811e42542139db8e2dbb37 (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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* 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";

const { Cc, Ci } = require("chrome");
const { getPreferedLocales, findClosestLocale } = require("api-utils/l10n/locale");
const { getRulesForLocale } = require("api-utils/l10n/plural-rules");

// Get URI for the addon root folder:
const { rootURI } = require("@packaging");

let globalHash = {};
let pluralMappingFunction = getRulesForLocale("en");

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];

    // First handle simple universal forms that may not be mandatory
    // for each language, (i.e. not different than 'other' form,
    // but still usefull for better phrasing)
    // For example 0 in english is the same form than 'other'
    // but we accept 'zero' form if specified in localization file
    if (n === 0 && "zero" in localized)
      localized = localized["zero"];
    else if (n === 1 && "one" in localized)
      localized = localized["one"];
    else if (n === 2 && "two" in localized)
      localized = localized["two"];
    else {
      let pluralForm = pluralMappingFunction(n);
      if (pluralForm in localized)
        localized = localized[pluralForm];
      else // Fallback in case of error: missing plural form
        localized = localized["other"];
    }

    // 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;
}

function readJsonUri(uri) {
  try {
    return JSON.parse(readURI(uri));
  }
  catch(e) {
    console.error("Error while reading locale file:\n" + uri + "\n" + e);
  }
  return {};
}

// Returns the array stored in `locales.json` manifest that list available
// locales files
function getAvailableLocales() {
  let uri = rootURI + "locales.json";
  let manifest = readJsonUri(uri);

  return "locales" in manifest && Array.isArray(manifest.locales) ?
         manifest.locales : [];
}

// Returns URI of the best locales file to use from the XPI
function getBestLocaleFile() {

  // Read localization manifest file that contains list of available languages
  let availableLocales = getAvailableLocales();

  // Retrieve list of prefered locales to use
  let preferedLocales = getPreferedLocales();

  // Compute the most preferable locale to use by using these two lists
  let bestMatchingLocale = findClosestLocale(availableLocales, preferedLocales);

  // It may be null if the addon doesn't have any locale file
  if (!bestMatchingLocale)
    return null;

  // Retrieve the related plural mapping function
  let shortLocaleCode = bestMatchingLocale.split("-")[0].toLowerCase();
  pluralMappingFunction = getRulesForLocale(shortLocaleCode);

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

function init() {
  // First, search for a locale file:
  let localeURI = getBestLocaleFile();
  if (!localeURI)
    return;

  // 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 = readJsonUri(localeURI);
}
init();