PluralRules.js (11790B)
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 * PluralRules internal properties. 7 * 8 * 9.1 Internal slots of Service Constructors 9 * 16.2.3 Properties of the Intl.PluralRules Constructor, Internal slots 10 * 11 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6 12 */ 13 var pluralRulesInternalProperties = { 14 localeData: pluralRulesLocaleData, 15 relevantExtensionKeys: [], 16 }; 17 18 function pluralRulesLocaleData() { 19 // PluralRules don't support any extension keys. 20 return {}; 21 } 22 23 /** 24 * 16.1.2 InitializePluralRules ( pluralRules, locales, options ) 25 * 26 * Compute an internal properties object from |lazyPluralRulesData|. 27 * 28 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6 29 */ 30 function resolvePluralRulesInternals(lazyPluralRulesData) { 31 assert(IsObject(lazyPluralRulesData), "lazy data not an object?"); 32 33 var internalProps = std_Object_create(null); 34 35 var PluralRules = pluralRulesInternalProperties; 36 37 // Compute effective locale. 38 39 // Step 9. 40 var localeData = PluralRules.localeData; 41 42 // Step 10. 43 var r = ResolveLocale( 44 "PluralRules", 45 lazyPluralRulesData.requestedLocales, 46 lazyPluralRulesData.opt, 47 PluralRules.relevantExtensionKeys, 48 localeData 49 ); 50 51 // Step 11. 52 internalProps.locale = r.locale; 53 54 // Step 7. 55 internalProps.type = lazyPluralRulesData.type; 56 57 // Step 8. SetNumberFormatDigitOptions, step 6. 58 internalProps.minimumIntegerDigits = lazyPluralRulesData.minimumIntegerDigits; 59 60 // Step 8. SetNumberFormatDigitOptions, step 14. 61 internalProps.roundingIncrement = lazyPluralRulesData.roundingIncrement; 62 63 // Step 8. SetNumberFormatDigitOptions, step 15. 64 internalProps.roundingMode = lazyPluralRulesData.roundingMode; 65 66 // Step 8. SetNumberFormatDigitOptions, step 16. 67 internalProps.trailingZeroDisplay = lazyPluralRulesData.trailingZeroDisplay; 68 69 // Step 8. SetNumberFormatDigitOptions, steps 25-26. 70 if ("minimumFractionDigits" in lazyPluralRulesData) { 71 assert( 72 "maximumFractionDigits" in lazyPluralRulesData, 73 "min/max frac digits mismatch" 74 ); 75 internalProps.minimumFractionDigits = 76 lazyPluralRulesData.minimumFractionDigits; 77 internalProps.maximumFractionDigits = 78 lazyPluralRulesData.maximumFractionDigits; 79 } 80 81 // Step 8. SetNumberFormatDigitOptions, steps 24 and 26. 82 if ("minimumSignificantDigits" in lazyPluralRulesData) { 83 assert( 84 "maximumSignificantDigits" in lazyPluralRulesData, 85 "min/max sig digits mismatch" 86 ); 87 internalProps.minimumSignificantDigits = 88 lazyPluralRulesData.minimumSignificantDigits; 89 internalProps.maximumSignificantDigits = 90 lazyPluralRulesData.maximumSignificantDigits; 91 } 92 93 // Step 8. SetNumberFormatDigitOptions, steps 26-30. 94 internalProps.roundingPriority = lazyPluralRulesData.roundingPriority; 95 96 // `pluralCategories` is lazily computed on first access. 97 internalProps.pluralCategories = null; 98 99 return internalProps; 100 } 101 102 /** 103 * Returns an object containing the PluralRules internal properties of |obj|. 104 */ 105 function getPluralRulesInternals(obj) { 106 assert(IsObject(obj), "getPluralRulesInternals called with non-object"); 107 assert( 108 intl_GuardToPluralRules(obj) !== null, 109 "getPluralRulesInternals called with non-PluralRules" 110 ); 111 112 var internals = getIntlObjectInternals(obj); 113 assert( 114 internals.type === "PluralRules", 115 "bad type escaped getIntlObjectInternals" 116 ); 117 118 var internalProps = maybeInternalProperties(internals); 119 if (internalProps) { 120 return internalProps; 121 } 122 123 internalProps = resolvePluralRulesInternals(internals.lazyData); 124 setInternalProperties(internals, internalProps); 125 return internalProps; 126 } 127 128 /** 129 * 16.1.2 InitializePluralRules ( pluralRules, locales, options ) 130 * 131 * Initializes an object as a PluralRules. 132 * 133 * This method is complicated a moderate bit by its implementing initialization 134 * as a *lazy* concept. Everything that must happen now, does -- but we defer 135 * all the work we can until the object is actually used as a PluralRules. 136 * This later work occurs in |resolvePluralRulesInternals|; steps not noted 137 * here occur there. 138 * 139 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6 140 */ 141 function InitializePluralRules(pluralRules, locales, options) { 142 assert(IsObject(pluralRules), "InitializePluralRules called with non-object"); 143 assert( 144 intl_GuardToPluralRules(pluralRules) !== null, 145 "InitializePluralRules called with non-PluralRules" 146 ); 147 148 // Lazy PluralRules data has the following structure: 149 // 150 // { 151 // requestedLocales: List of locales, 152 // type: "cardinal" / "ordinal", 153 // 154 // opt: // opt object computer in InitializePluralRules 155 // { 156 // localeMatcher: "lookup" / "best fit", 157 // } 158 // 159 // minimumIntegerDigits: integer ∈ [1, 21], 160 // 161 // // optional, mutually exclusive with the significant-digits option 162 // minimumFractionDigits: integer ∈ [0, 100], 163 // maximumFractionDigits: integer ∈ [0, 100], 164 // 165 // // optional, mutually exclusive with the fraction-digits option 166 // minimumSignificantDigits: integer ∈ [1, 21], 167 // maximumSignificantDigits: integer ∈ [1, 21], 168 // 169 // roundingPriority: "auto" / "lessPrecision" / "morePrecision", 170 // 171 // trailingZeroDisplay: "auto" / "stripIfInteger", 172 // 173 // roundingIncrement: integer ∈ (1, 2, 5, 174 // 10, 20, 25, 50, 175 // 100, 200, 250, 500, 176 // 1000, 2000, 2500, 5000), 177 // 178 // roundingMode: "ceil" / "floor" / "expand" / "trunc" / 179 // "halfCeil" / "halfFloor" / "halfExpand" / "halfTrunc" / "halfEven", 180 // } 181 // 182 // Note that lazy data is only installed as a final step of initialization, 183 // so every PluralRules lazy data object has *all* these properties, never a 184 // subset of them. 185 var lazyPluralRulesData = std_Object_create(null); 186 187 // Step 1. 188 var requestedLocales = CanonicalizeLocaleList(locales); 189 lazyPluralRulesData.requestedLocales = requestedLocales; 190 191 // Step 2. (Inlined call to CoerceOptionsToObject.) 192 if (options === undefined) { 193 options = std_Object_create(null); 194 } else { 195 options = ToObject(options); 196 } 197 198 // Step 3. 199 var opt = NEW_RECORD(); 200 lazyPluralRulesData.opt = opt; 201 202 // Steps 4-5. 203 var matcher = GetOption( 204 options, 205 "localeMatcher", 206 "string", 207 ["lookup", "best fit"], 208 "best fit" 209 ); 210 opt.localeMatcher = matcher; 211 212 // Steps 6-7. 213 var type = GetOption( 214 options, 215 "type", 216 "string", 217 ["cardinal", "ordinal"], 218 "cardinal" 219 ); 220 lazyPluralRulesData.type = type; 221 222 // Step 8. 223 SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0, 3, "standard"); 224 225 // Step 12. 226 // 227 // We've done everything that must be done now: mark the lazy data as fully 228 // computed and install it. 229 initializeIntlObject(pluralRules, "PluralRules", lazyPluralRulesData); 230 } 231 232 /** 233 * 16.3.3 Intl.PluralRules.prototype.select ( value ) 234 * 235 * Returns a String value representing the plural category matching 236 * the number passed as value according to the 237 * effective locale and the formatting options of this PluralRules. 238 * 239 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6 240 */ 241 function Intl_PluralRules_select(value) { 242 // Step 1. 243 var pluralRules = this; 244 245 // Step 2. 246 if ( 247 !IsObject(pluralRules) || 248 (pluralRules = intl_GuardToPluralRules(pluralRules)) === null 249 ) { 250 return callFunction( 251 intl_CallPluralRulesMethodIfWrapped, 252 this, 253 value, 254 "Intl_PluralRules_select" 255 ); 256 } 257 258 // Step 3. 259 var n = TO_NUMBER(value); 260 261 // Ensure the PluralRules internals are resolved. 262 getPluralRulesInternals(pluralRules); 263 264 // Step 4. 265 return intl_SelectPluralRule(pluralRules, n); 266 } 267 268 /** 269 * 16.3.4 Intl.PluralRules.prototype.selectRange ( start, end ) 270 * 271 * Returns a String value representing the plural category matching the input 272 * number range according to the effective locale and the formatting options 273 * of this PluralRules. 274 * 275 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6 276 */ 277 function Intl_PluralRules_selectRange(start, end) { 278 // Step 1. 279 var pluralRules = this; 280 281 // Step 2. 282 if ( 283 !IsObject(pluralRules) || 284 (pluralRules = intl_GuardToPluralRules(pluralRules)) === null 285 ) { 286 return callFunction( 287 intl_CallPluralRulesMethodIfWrapped, 288 this, 289 start, 290 end, 291 "Intl_PluralRules_selectRange" 292 ); 293 } 294 295 // Step 3. 296 if (start === undefined || end === undefined) { 297 ThrowTypeError( 298 JSMSG_UNDEFINED_NUMBER, 299 start === undefined ? "start" : "end", 300 "PluralRules", 301 "selectRange" 302 ); 303 } 304 305 // Step 4. 306 var x = TO_NUMBER(start); 307 308 // Step 5. 309 var y = TO_NUMBER(end); 310 311 // Step 6. 312 return intl_SelectPluralRuleRange(pluralRules, x, y); 313 } 314 315 /** 316 * 16.3.5 Intl.PluralRules.prototype.resolvedOptions ( ) 317 * 318 * Returns the resolved options for a PluralRules object. 319 * 320 * ES2024 Intl draft rev a1db4567870dbe505121a4255f1210338757190a 321 */ 322 function Intl_PluralRules_resolvedOptions() { 323 // Step 1. 324 var pluralRules = this; 325 326 // Step 2. 327 if ( 328 !IsObject(pluralRules) || 329 (pluralRules = intl_GuardToPluralRules(pluralRules)) === null 330 ) { 331 return callFunction( 332 intl_CallPluralRulesMethodIfWrapped, 333 this, 334 "Intl_PluralRules_resolvedOptions" 335 ); 336 } 337 338 var internals = getPluralRulesInternals(pluralRules); 339 340 // Step 4. 341 var internalsPluralCategories = internals.pluralCategories; 342 if (internalsPluralCategories === null) { 343 internalsPluralCategories = intl_GetPluralCategories(pluralRules); 344 internals.pluralCategories = internalsPluralCategories; 345 } 346 347 // Step 5.b. 348 var pluralCategories = []; 349 for (var i = 0; i < internalsPluralCategories.length; i++) { 350 DefineDataProperty(pluralCategories, i, internalsPluralCategories[i]); 351 } 352 353 // Steps 3 and 5. 354 var result = { 355 locale: internals.locale, 356 type: internals.type, 357 minimumIntegerDigits: internals.minimumIntegerDigits, 358 }; 359 360 // Min/Max fraction digits are either both present or not present at all. 361 assert( 362 hasOwn("minimumFractionDigits", internals) === 363 hasOwn("maximumFractionDigits", internals), 364 "minimumFractionDigits is present iff maximumFractionDigits is present" 365 ); 366 367 if (hasOwn("minimumFractionDigits", internals)) { 368 DefineDataProperty( 369 result, 370 "minimumFractionDigits", 371 internals.minimumFractionDigits 372 ); 373 DefineDataProperty( 374 result, 375 "maximumFractionDigits", 376 internals.maximumFractionDigits 377 ); 378 } 379 380 // Min/Max significant digits are either both present or not present at all. 381 assert( 382 hasOwn("minimumSignificantDigits", internals) === 383 hasOwn("maximumSignificantDigits", internals), 384 "minimumSignificantDigits is present iff maximumSignificantDigits is present" 385 ); 386 387 if (hasOwn("minimumSignificantDigits", internals)) { 388 DefineDataProperty( 389 result, 390 "minimumSignificantDigits", 391 internals.minimumSignificantDigits 392 ); 393 DefineDataProperty( 394 result, 395 "maximumSignificantDigits", 396 internals.maximumSignificantDigits 397 ); 398 } 399 400 DefineDataProperty(result, "pluralCategories", pluralCategories); 401 DefineDataProperty(result, "roundingIncrement", internals.roundingIncrement); 402 DefineDataProperty(result, "roundingMode", internals.roundingMode); 403 DefineDataProperty(result, "roundingPriority", internals.roundingPriority); 404 DefineDataProperty( 405 result, 406 "trailingZeroDisplay", 407 internals.trailingZeroDisplay 408 ); 409 410 // Step 6. 411 return result; 412 }