|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 "use strict"; |
|
5 |
|
6 module.metadata = { |
|
7 "stability": "unstable" |
|
8 }; |
|
9 |
|
10 const prefs = require("../preferences/service"); |
|
11 const { Cu, Cc, Ci } = require("chrome"); |
|
12 const { Services } = Cu.import("resource://gre/modules/Services.jsm"); |
|
13 |
|
14 /** |
|
15 * Gets the currently selected locale for display. |
|
16 * Gets all usable locale that we can use sorted by priority of relevance |
|
17 * @return Array of locales, begins with highest priority |
|
18 */ |
|
19 const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; |
|
20 const PREF_SELECTED_LOCALE = "general.useragent.locale"; |
|
21 const PREF_ACCEPT_LANGUAGES = "intl.accept_languages"; |
|
22 |
|
23 function getPreferedLocales(caseSensitve) { |
|
24 let locales = []; |
|
25 function addLocale(locale) { |
|
26 locale = locale.trim(); |
|
27 if (!caseSensitve) |
|
28 locale = locale.toLowerCase(); |
|
29 if (locales.indexOf(locale) === -1) |
|
30 locales.push(locale); |
|
31 } |
|
32 |
|
33 // Most important locale is OS one. But we use it, only if |
|
34 // "intl.locale.matchOS" pref is set to `true`. |
|
35 // Currently only used for multi-locales mobile builds. |
|
36 // http://mxr.mozilla.org/mozilla-central/source/mobile/android/installer/Makefile.in#46 |
|
37 if (prefs.get(PREF_MATCH_OS_LOCALE, false)) { |
|
38 let localeService = Cc["@mozilla.org/intl/nslocaleservice;1"]. |
|
39 getService(Ci.nsILocaleService); |
|
40 let osLocale = localeService.getLocaleComponentForUserAgent(); |
|
41 addLocale(osLocale); |
|
42 } |
|
43 |
|
44 // In some cases, mainly on Fennec and on Linux version, |
|
45 // `general.useragent.locale` is a special 'localized' value, like: |
|
46 // "chrome://global/locale/intl.properties" |
|
47 let browserUiLocale = prefs.getLocalized(PREF_SELECTED_LOCALE, "") || |
|
48 prefs.get(PREF_SELECTED_LOCALE, ""); |
|
49 if (browserUiLocale) |
|
50 addLocale(browserUiLocale); |
|
51 |
|
52 // Third priority is the list of locales used for web content |
|
53 let contentLocales = prefs.get(PREF_ACCEPT_LANGUAGES, ""); |
|
54 if (contentLocales) { |
|
55 // This list is a string of locales seperated by commas. |
|
56 // There is spaces after commas, so strip each item |
|
57 for each(let locale in contentLocales.split(",")) |
|
58 addLocale(locale.replace(/(^\s+)|(\s+$)/g, "")); |
|
59 } |
|
60 |
|
61 // Finally, we ensure that en-US is the final fallback if it wasn't added |
|
62 addLocale("en-US"); |
|
63 |
|
64 return locales; |
|
65 } |
|
66 exports.getPreferedLocales = getPreferedLocales; |
|
67 |
|
68 /** |
|
69 * Selects the closest matching locale from a list of locales. |
|
70 * |
|
71 * @param aLocales |
|
72 * An array of available locales |
|
73 * @param aMatchLocales |
|
74 * An array of prefered locales, ordered by priority. Most wanted first. |
|
75 * Locales have to be in lowercase. |
|
76 * If null, uses getPreferedLocales() results |
|
77 * @return the best match for the currently selected locale |
|
78 * |
|
79 * Stolen from http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/internal/XPIProvider.jsm |
|
80 */ |
|
81 exports.findClosestLocale = function findClosestLocale(aLocales, aMatchLocales) { |
|
82 aMatchLocales = aMatchLocales || getPreferedLocales(); |
|
83 |
|
84 // Holds the best matching localized resource |
|
85 let bestmatch = null; |
|
86 // The number of locale parts it matched with |
|
87 let bestmatchcount = 0; |
|
88 // The number of locale parts in the match |
|
89 let bestpartcount = 0; |
|
90 |
|
91 for each (let locale in aMatchLocales) { |
|
92 let lparts = locale.split("-"); |
|
93 for each (let localized in aLocales) { |
|
94 let found = localized.toLowerCase(); |
|
95 // Exact match is returned immediately |
|
96 if (locale == found) |
|
97 return localized; |
|
98 |
|
99 let fparts = found.split("-"); |
|
100 /* If we have found a possible match and this one isn't any longer |
|
101 then we dont need to check further. */ |
|
102 if (bestmatch && fparts.length < bestmatchcount) |
|
103 continue; |
|
104 |
|
105 // Count the number of parts that match |
|
106 let maxmatchcount = Math.min(fparts.length, lparts.length); |
|
107 let matchcount = 0; |
|
108 while (matchcount < maxmatchcount && |
|
109 fparts[matchcount] == lparts[matchcount]) |
|
110 matchcount++; |
|
111 |
|
112 /* If we matched more than the last best match or matched the same and |
|
113 this locale is less specific than the last best match. */ |
|
114 if (matchcount > bestmatchcount || |
|
115 (matchcount == bestmatchcount && fparts.length < bestpartcount)) { |
|
116 bestmatch = localized; |
|
117 bestmatchcount = matchcount; |
|
118 bestpartcount = fparts.length; |
|
119 } |
|
120 } |
|
121 // If we found a valid match for this locale return it |
|
122 if (bestmatch) |
|
123 return bestmatch; |
|
124 } |
|
125 return null; |
|
126 } |