locale.js 4.4 KB

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