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