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