2b1135a786e2c53c020e0d9b828dd9a5e9b9fa377d010a7358c347e57bd2368636a12957fccb09f7d5ae4660c03b6cfcc2e91db91419097bb0aa07b30ae667 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import isArray from '../utils/is-array';
  2. import isUndefined from '../utils/is-undefined';
  3. import { deprecateSimple } from '../utils/deprecate';
  4. import { mergeConfigs } from './set';
  5. import { Locale } from './constructor';
  6. import keys from '../utils/keys';
  7. import { baseConfig } from './base-config';
  8. // internal storage for locale config files
  9. var locales = {},
  10. localeFamilies = {},
  11. globalLocale;
  12. function commonPrefix(arr1, arr2) {
  13. var i,
  14. minl = Math.min(arr1.length, arr2.length);
  15. for (i = 0; i < minl; i += 1) {
  16. if (arr1[i] !== arr2[i]) {
  17. return i;
  18. }
  19. }
  20. return minl;
  21. }
  22. function normalizeLocale(key) {
  23. return key ? key.toLowerCase().replace('_', '-') : key;
  24. }
  25. // pick the locale from the array
  26. // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
  27. // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
  28. function chooseLocale(names) {
  29. var i = 0,
  30. j,
  31. next,
  32. locale,
  33. split;
  34. while (i < names.length) {
  35. split = normalizeLocale(names[i]).split('-');
  36. j = split.length;
  37. next = normalizeLocale(names[i + 1]);
  38. next = next ? next.split('-') : null;
  39. while (j > 0) {
  40. locale = loadLocale(split.slice(0, j).join('-'));
  41. if (locale) {
  42. return locale;
  43. }
  44. if (
  45. next &&
  46. next.length >= j &&
  47. commonPrefix(split, next) >= j - 1
  48. ) {
  49. //the next array item is better than a shallower substring of this one
  50. break;
  51. }
  52. j--;
  53. }
  54. i++;
  55. }
  56. return globalLocale;
  57. }
  58. function isLocaleNameSane(name) {
  59. // Prevent names that look like filesystem paths, i.e contain '/' or '\'
  60. // Ensure name is available and function returns boolean
  61. return !!(name && name.match('^[^/\\\\]*$'));
  62. }
  63. function loadLocale(name) {
  64. var oldLocale = null,
  65. aliasedRequire;
  66. // TODO: Find a better way to register and load all the locales in Node
  67. if (
  68. locales[name] === undefined &&
  69. typeof module !== 'undefined' &&
  70. module &&
  71. module.exports &&
  72. isLocaleNameSane(name)
  73. ) {
  74. try {
  75. oldLocale = globalLocale._abbr;
  76. aliasedRequire = require;
  77. aliasedRequire('./locale/' + name);
  78. getSetGlobalLocale(oldLocale);
  79. } catch (e) {
  80. // mark as not found to avoid repeating expensive file require call causing high CPU
  81. // when trying to find en-US, en_US, en-us for every format call
  82. locales[name] = null; // null means not found
  83. }
  84. }
  85. return locales[name];
  86. }
  87. // This function will load locale and then set the global locale. If
  88. // no arguments are passed in, it will simply return the current global
  89. // locale key.
  90. export function getSetGlobalLocale(key, values) {
  91. var data;
  92. if (key) {
  93. if (isUndefined(values)) {
  94. data = getLocale(key);
  95. } else {
  96. data = defineLocale(key, values);
  97. }
  98. if (data) {
  99. // moment.duration._locale = moment._locale = data;
  100. globalLocale = data;
  101. } else {
  102. if (typeof console !== 'undefined' && console.warn) {
  103. //warn user if arguments are passed but the locale could not be set
  104. console.warn(
  105. 'Locale ' + key + ' not found. Did you forget to load it?'
  106. );
  107. }
  108. }
  109. }
  110. return globalLocale._abbr;
  111. }
  112. export function defineLocale(name, config) {
  113. if (config !== null) {
  114. var locale,
  115. parentConfig = baseConfig;
  116. config.abbr = name;
  117. if (locales[name] != null) {
  118. deprecateSimple(
  119. 'defineLocaleOverride',
  120. 'use moment.updateLocale(localeName, config) to change ' +
  121. 'an existing locale. moment.defineLocale(localeName, ' +
  122. 'config) should only be used for creating a new locale ' +
  123. 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'
  124. );
  125. parentConfig = locales[name]._config;
  126. } else if (config.parentLocale != null) {
  127. if (locales[config.parentLocale] != null) {
  128. parentConfig = locales[config.parentLocale]._config;
  129. } else {
  130. locale = loadLocale(config.parentLocale);
  131. if (locale != null) {
  132. parentConfig = locale._config;
  133. } else {
  134. if (!localeFamilies[config.parentLocale]) {
  135. localeFamilies[config.parentLocale] = [];
  136. }
  137. localeFamilies[config.parentLocale].push({
  138. name: name,
  139. config: config,
  140. });
  141. return null;
  142. }
  143. }
  144. }
  145. locales[name] = new Locale(mergeConfigs(parentConfig, config));
  146. if (localeFamilies[name]) {
  147. localeFamilies[name].forEach(function (x) {
  148. defineLocale(x.name, x.config);
  149. });
  150. }
  151. // backwards compat for now: also set the locale
  152. // make sure we set the locale AFTER all child locales have been
  153. // created, so we won't end up with the child locale set.
  154. getSetGlobalLocale(name);
  155. return locales[name];
  156. } else {
  157. // useful for testing
  158. delete locales[name];
  159. return null;
  160. }
  161. }
  162. export function updateLocale(name, config) {
  163. if (config != null) {
  164. var locale,
  165. tmpLocale,
  166. parentConfig = baseConfig;
  167. if (locales[name] != null && locales[name].parentLocale != null) {
  168. // Update existing child locale in-place to avoid memory-leaks
  169. locales[name].set(mergeConfigs(locales[name]._config, config));
  170. } else {
  171. // MERGE
  172. tmpLocale = loadLocale(name);
  173. if (tmpLocale != null) {
  174. parentConfig = tmpLocale._config;
  175. }
  176. config = mergeConfigs(parentConfig, config);
  177. if (tmpLocale == null) {
  178. // updateLocale is called for creating a new locale
  179. // Set abbr so it will have a name (getters return
  180. // undefined otherwise).
  181. config.abbr = name;
  182. }
  183. locale = new Locale(config);
  184. locale.parentLocale = locales[name];
  185. locales[name] = locale;
  186. }
  187. // backwards compat for now: also set the locale
  188. getSetGlobalLocale(name);
  189. } else {
  190. // pass null for config to unupdate, useful for tests
  191. if (locales[name] != null) {
  192. if (locales[name].parentLocale != null) {
  193. locales[name] = locales[name].parentLocale;
  194. if (name === getSetGlobalLocale()) {
  195. getSetGlobalLocale(name);
  196. }
  197. } else if (locales[name] != null) {
  198. delete locales[name];
  199. }
  200. }
  201. }
  202. return locales[name];
  203. }
  204. // returns locale data
  205. export function getLocale(key) {
  206. var locale;
  207. if (key && key._locale && key._locale._abbr) {
  208. key = key._locale._abbr;
  209. }
  210. if (!key) {
  211. return globalLocale;
  212. }
  213. if (!isArray(key)) {
  214. //short-circuit everything else
  215. locale = loadLocale(key);
  216. if (locale) {
  217. return locale;
  218. }
  219. key = [key];
  220. }
  221. return chooseLocale(key);
  222. }
  223. export function listLocales() {
  224. return keys(locales);
  225. }