DisplayNames.js (9422B)
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 5 /** 6 * Intl.DisplayNames internal properties. 7 */ 8 function displayNamesLocaleData() { 9 // Intl.DisplayNames doesn't support any extension keys. 10 return {}; 11 } 12 var displayNamesInternalProperties = { 13 localeData: displayNamesLocaleData, 14 relevantExtensionKeys: [], 15 }; 16 17 function mozDisplayNamesLocaleData() { 18 return { 19 ca: intl_availableCalendars, 20 default: { 21 ca: intl_defaultCalendar, 22 }, 23 }; 24 } 25 var mozDisplayNamesInternalProperties = { 26 localeData: mozDisplayNamesLocaleData, 27 relevantExtensionKeys: ["ca"], 28 }; 29 30 /** 31 * Intl.DisplayNames ( [ locales [ , options ] ] ) 32 * 33 * Compute an internal properties object from |lazyDisplayNamesData|. 34 */ 35 function resolveDisplayNamesInternals(lazyDisplayNamesData) { 36 assert(IsObject(lazyDisplayNamesData), "lazy data not an object?"); 37 38 var internalProps = std_Object_create(null); 39 40 var mozExtensions = lazyDisplayNamesData.mozExtensions; 41 42 var DisplayNames = mozExtensions 43 ? mozDisplayNamesInternalProperties 44 : displayNamesInternalProperties; 45 46 // Compute effective locale. 47 48 // Step 7. 49 var localeData = DisplayNames.localeData; 50 51 // Step 10. 52 var r = ResolveLocale( 53 "DisplayNames", 54 lazyDisplayNamesData.requestedLocales, 55 lazyDisplayNamesData.opt, 56 DisplayNames.relevantExtensionKeys, 57 localeData 58 ); 59 60 // Step 12. 61 internalProps.style = lazyDisplayNamesData.style; 62 63 // Step 14. 64 var type = lazyDisplayNamesData.type; 65 internalProps.type = type; 66 67 // Step 16. 68 internalProps.fallback = lazyDisplayNamesData.fallback; 69 70 // Step 17. 71 internalProps.locale = r.locale; 72 73 // Step 25. 74 if (type === "language") { 75 internalProps.languageDisplay = lazyDisplayNamesData.languageDisplay; 76 } 77 78 if (mozExtensions) { 79 // Note that special casing applies in DateTimeFormat. 80 internalProps.calendar = r.ca; 81 } 82 83 // The caller is responsible for associating |internalProps| with the right 84 // object using |setInternalProperties|. 85 return internalProps; 86 } 87 88 /** 89 * Returns an object containing the DisplayNames internal properties of |obj|. 90 */ 91 function getDisplayNamesInternals(obj) { 92 assert(IsObject(obj), "getDisplayNamesInternals called with non-object"); 93 assert( 94 intl_GuardToDisplayNames(obj) !== null, 95 "getDisplayNamesInternals called with non-DisplayNames" 96 ); 97 98 var internals = getIntlObjectInternals(obj); 99 assert( 100 internals.type === "DisplayNames", 101 "bad type escaped getIntlObjectInternals" 102 ); 103 104 // If internal properties have already been computed, use them. 105 var internalProps = maybeInternalProperties(internals); 106 if (internalProps) { 107 return internalProps; 108 } 109 110 // Otherwise it's time to fully create them. 111 internalProps = resolveDisplayNamesInternals(internals.lazyData); 112 setInternalProperties(internals, internalProps); 113 return internalProps; 114 } 115 116 /** 117 * Intl.DisplayNames ( [ locales [ , options ] ] ) 118 * 119 * Initializes an object as a DisplayNames. 120 * 121 * This method is complicated a moderate bit by its implementing initialization 122 * as a *lazy* concept. Everything that must happen now, does -- but we defer 123 * all the work we can until the object is actually used as a DisplayNames. 124 * This later work occurs in |resolveDisplayNamesInternals|; steps not noted 125 * here occur there. 126 */ 127 function InitializeDisplayNames(displayNames, locales, options, mozExtensions) { 128 assert( 129 IsObject(displayNames), 130 "InitializeDisplayNames called with non-object" 131 ); 132 assert( 133 intl_GuardToDisplayNames(displayNames) !== null, 134 "InitializeDisplayNames called with non-DisplayNames" 135 ); 136 137 // Lazy DisplayNames data has the following structure: 138 // 139 // { 140 // requestedLocales: List of locales, 141 // 142 // opt: // opt object computed in InitializeDisplayNames 143 // { 144 // localeMatcher: "lookup" / "best fit", 145 // 146 // ca: string matching a Unicode extension type, // optional 147 // } 148 // 149 // localeMatcher: "lookup" / "best fit", 150 // 151 // style: "narrow" / "short" / "abbreviated" / "long", 152 // 153 // type: "language" / "region" / "script" / "currency" / "weekday" / 154 // "month" / "quarter" / "dayPeriod" / "dateTimeField" 155 // 156 // fallback: "code" / "none", 157 // 158 // // field present only if type === "language": 159 // languageDisplay: "dialect" / "standard", 160 // 161 // mozExtensions: true / false, 162 // } 163 // 164 // Note that lazy data is only installed as a final step of initialization, 165 // so every DisplayNames lazy data object has *all* these properties, never a 166 // subset of them. 167 var lazyDisplayNamesData = std_Object_create(null); 168 169 // Step 3. 170 var requestedLocales = CanonicalizeLocaleList(locales); 171 lazyDisplayNamesData.requestedLocales = requestedLocales; 172 173 // Step 4. 174 if (!IsObject(options)) { 175 ThrowTypeError( 176 JSMSG_OBJECT_REQUIRED, 177 options === null ? "null" : typeof options 178 ); 179 } 180 181 // Step 5. 182 var opt = NEW_RECORD(); 183 lazyDisplayNamesData.opt = opt; 184 lazyDisplayNamesData.mozExtensions = mozExtensions; 185 186 // Steps 7-8. 187 var matcher = GetOption( 188 options, 189 "localeMatcher", 190 "string", 191 ["lookup", "best fit"], 192 "best fit" 193 ); 194 opt.localeMatcher = matcher; 195 196 if (mozExtensions) { 197 var calendar = GetOption( 198 options, 199 "calendar", 200 "string", 201 undefined, 202 undefined 203 ); 204 205 if (calendar !== undefined) { 206 calendar = intl_ValidateAndCanonicalizeUnicodeExtensionType( 207 calendar, 208 "calendar", 209 "ca" 210 ); 211 } 212 213 opt.ca = calendar; 214 } 215 216 // Step 10. 217 var style; 218 if (mozExtensions) { 219 style = GetOption( 220 options, 221 "style", 222 "string", 223 ["narrow", "short", "abbreviated", "long"], 224 "long" 225 ); 226 } else { 227 style = GetOption( 228 options, 229 "style", 230 "string", 231 ["narrow", "short", "long"], 232 "long" 233 ); 234 } 235 236 // Step 11. 237 lazyDisplayNamesData.style = style; 238 239 // Step 12. 240 var type; 241 if (mozExtensions) { 242 type = GetOption( 243 options, 244 "type", 245 "string", 246 [ 247 "language", 248 "region", 249 "script", 250 "currency", 251 "calendar", 252 "dateTimeField", 253 "weekday", 254 "month", 255 "quarter", 256 "dayPeriod", 257 ], 258 undefined 259 ); 260 } else { 261 type = GetOption( 262 options, 263 "type", 264 "string", 265 ["language", "region", "script", "currency", "calendar", "dateTimeField"], 266 undefined 267 ); 268 } 269 270 // Step 13. 271 if (type === undefined) { 272 ThrowTypeError(JSMSG_UNDEFINED_TYPE); 273 } 274 275 // Step 14. 276 lazyDisplayNamesData.type = type; 277 278 // Step 15. 279 var fallback = GetOption( 280 options, 281 "fallback", 282 "string", 283 ["code", "none"], 284 "code" 285 ); 286 287 // Step 16. 288 lazyDisplayNamesData.fallback = fallback; 289 290 // Step 24. 291 var languageDisplay = GetOption( 292 options, 293 "languageDisplay", 294 "string", 295 ["dialect", "standard"], 296 "dialect" 297 ); 298 299 // Step 25. 300 if (type === "language") { 301 lazyDisplayNamesData.languageDisplay = languageDisplay; 302 } 303 304 // We've done everything that must be done now: mark the lazy data as fully 305 // computed and install it. 306 initializeIntlObject(displayNames, "DisplayNames", lazyDisplayNamesData); 307 } 308 309 /** 310 * Returns the resolved options for a DisplayNames object. 311 */ 312 function Intl_DisplayNames_of(code) { 313 // Step 1. 314 var displayNames = this; 315 316 // Steps 2-3. 317 if ( 318 !IsObject(displayNames) || 319 (displayNames = intl_GuardToDisplayNames(displayNames)) === null 320 ) { 321 return callFunction( 322 intl_CallDisplayNamesMethodIfWrapped, 323 this, 324 "Intl_DisplayNames_of" 325 ); 326 } 327 328 code = ToString(code); 329 330 var internals = getDisplayNamesInternals(displayNames); 331 332 // Unpack the internals object to avoid a slow runtime to selfhosted JS call 333 // in |intl_ComputeDisplayName()|. 334 var { 335 locale, 336 calendar = "", 337 style, 338 type, 339 languageDisplay = "", 340 fallback, 341 } = internals; 342 343 // Steps 5-10. 344 return intl_ComputeDisplayName( 345 displayNames, 346 locale, 347 calendar, 348 style, 349 languageDisplay, 350 fallback, 351 type, 352 code 353 ); 354 } 355 356 /** 357 * Returns the resolved options for a DisplayNames object. 358 */ 359 function Intl_DisplayNames_resolvedOptions() { 360 // Step 1. 361 var displayNames = this; 362 363 // Steps 2-3. 364 if ( 365 !IsObject(displayNames) || 366 (displayNames = intl_GuardToDisplayNames(displayNames)) === null 367 ) { 368 return callFunction( 369 intl_CallDisplayNamesMethodIfWrapped, 370 this, 371 "Intl_DisplayNames_resolvedOptions" 372 ); 373 } 374 375 var internals = getDisplayNamesInternals(displayNames); 376 377 // Steps 4-5. 378 var options = { 379 locale: internals.locale, 380 style: internals.style, 381 type: internals.type, 382 fallback: internals.fallback, 383 }; 384 385 // languageDisplay is only present for language display names. 386 assert( 387 hasOwn("languageDisplay", internals) === (internals.type === "language"), 388 "languageDisplay is present iff type is 'language'" 389 ); 390 391 if (hasOwn("languageDisplay", internals)) { 392 DefineDataProperty(options, "languageDisplay", internals.languageDisplay); 393 } 394 395 if (hasOwn("calendar", internals)) { 396 DefineDataProperty(options, "calendar", internals.calendar); 397 } 398 399 // Step 6. 400 return options; 401 }