summaryrefslogtreecommitdiffstats
path: root/toolkit/jetpack/sdk/l10n/locale.js
blob: 950b33b20efaf19c15f2cb3c25fd3c3e7cf1341b (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
/* 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";

module.metadata = {
  "stability": "unstable"
};

const prefs = require("../preferences/service");
const { Cu, Cc, Ci } = require("chrome");
const { Services } = Cu.import("resource://gre/modules/Services.jsm");

/**
 * Gets the currently selected locale for display.
 * Gets all usable locale that we can use sorted by priority of relevance
 * @return  Array of locales, begins with highest priority
 */
const PREF_MATCH_OS_LOCALE  = "intl.locale.matchOS";
const PREF_SELECTED_LOCALE  = "general.useragent.locale";
const PREF_ACCEPT_LANGUAGES = "intl.accept_languages";

function getPreferedLocales(caseSensitve) {
  let locales = [];
  function addLocale(locale) {
    locale = locale.trim();
    if (!caseSensitve)
      locale = locale.toLowerCase();
    if (locales.indexOf(locale) === -1)
      locales.push(locale);
  }

  // Most important locale is OS one. But we use it, only if
  // "intl.locale.matchOS" pref is set to `true`.
  // Currently only used for multi-locales mobile builds.
  // http://mxr.mozilla.org/mozilla-central/source/mobile/android/installer/Makefile.in#46
  if (prefs.get(PREF_MATCH_OS_LOCALE, false)) {
    let localeService = Cc["@mozilla.org/intl/nslocaleservice;1"].
                        getService(Ci.nsILocaleService);
    let osLocale = localeService.getLocaleComponentForUserAgent();
    addLocale(osLocale);
  }

  // In some cases, mainly on Fennec and on Linux version,
  // `general.useragent.locale` is a special 'localized' value, like:
  // "chrome://global/locale/intl.properties"
  let browserUiLocale = prefs.getLocalized(PREF_SELECTED_LOCALE, "") ||
                        prefs.get(PREF_SELECTED_LOCALE, "");
  if (browserUiLocale)
    addLocale(browserUiLocale);

  // Third priority is the list of locales used for web content
  let contentLocales = prefs.getLocalized(PREF_ACCEPT_LANGUAGES, "") ||
                       prefs.get(PREF_ACCEPT_LANGUAGES, "");
  if (contentLocales) {
    // This list is a string of locales seperated by commas.
    // There is spaces after commas, so strip each item
    for (let locale of contentLocales.split(","))
      addLocale(locale.replace(/(^\s+)|(\s+$)/g, ""));
  }

  // Finally, we ensure that en-US is the final fallback if it wasn't added
  addLocale("en-US");

  return locales;
}
exports.getPreferedLocales = getPreferedLocales;

/**
 * Selects the closest matching locale from a list of locales.
 *
 * @param  aLocales
 *         An array of available locales
 * @param  aMatchLocales
 *         An array of prefered locales, ordered by priority. Most wanted first.
 *         Locales have to be in lowercase.
 *         If null, uses getPreferedLocales() results
 * @return the best match for the currently selected locale
 *
 * Stolen from http://dxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/internal/XPIProvider.jsm
 */
exports.findClosestLocale = function findClosestLocale(aLocales, aMatchLocales) {
  aMatchLocales = aMatchLocales || getPreferedLocales();

  // Holds the best matching localized resource
  let bestmatch = null;
  // The number of locale parts it matched with
  let bestmatchcount = 0;
  // The number of locale parts in the match
  let bestpartcount = 0;

  for (let locale of aMatchLocales) {
    let lparts = locale.split("-");
    for (let localized of aLocales) {
      let found = localized.toLowerCase();
      // Exact match is returned immediately
      if (locale == found)
        return localized;

      let fparts = found.split("-");
      /* If we have found a possible match and this one isn't any longer
         then we dont need to check further. */
      if (bestmatch && fparts.length < bestmatchcount)
        continue;

      // Count the number of parts that match
      let maxmatchcount = Math.min(fparts.length, lparts.length);
      let matchcount = 0;
      while (matchcount < maxmatchcount &&
             fparts[matchcount] == lparts[matchcount])
        matchcount++;

      /* If we matched more than the last best match or matched the same and
         this locale is less specific than the last best match. */
      if (matchcount > bestmatchcount ||
         (matchcount == bestmatchcount && fparts.length < bestpartcount)) {
        bestmatch = localized;
        bestmatchcount = matchcount;
        bestpartcount = fparts.length;
      }
    }
    // If we found a valid match for this locale return it
    if (bestmatch)
      return bestmatch;
  }
  return null;
}