UNPKG

@formatjs/intl-pluralrules

Version:
76 lines (75 loc) 3.21 kB
import { ComputeExponentForMagnitude, FormatNumericToString, invariant, Type } from "@formatjs/ecma402-abstract"; import Decimal from "decimal.js"; import { GetOperands } from "./GetOperands.js"; /** * ResolvePluralInternal ( pluralRules, n ) * * Internal version of ResolvePlural that returns both the formatted string and plural category. * This is needed for selectRange, which must compare formatted strings to determine if the * start and end values are identical. * * The formatted string is obtained by applying the number formatting options (digit options) * from the PluralRules object to the input number. This ensures that formatting-sensitive * plural rules work correctly (e.g., rules that depend on visible fraction digits). * * @param pl - An initialized PluralRules object * @param n - Mathematical value to resolve * @returns Record containing the formatted string and plural category */ export function ResolvePluralInternal(pl, n, { getInternalSlots, PluralRuleSelect }) { const internalSlots = getInternalSlots(pl); invariant(Type(internalSlots) === "Object", "pl has to be an object"); invariant("initializedPluralRules" in internalSlots, "pluralrules must be initialized"); // Handle non-finite values (Infinity, -Infinity, NaN) if (!n.isFinite()) { return { formattedString: String(n), pluralCategory: "other" }; } const { locale, type, notation } = internalSlots; // ECMA-402 Spec: Format the number according to digit options const res = FormatNumericToString(internalSlots, n); const s = res.formattedString; // Extension: Calculate compact exponent if using compact notation // This enables CLDR c/e operands for proper plural selection with compact numbers let exponent = 0; if (notation === "compact" && !n.isZero()) { // Implementation: Only calculate exponent if NumberFormat locale data is available (soft dependency) if (internalSlots.dataLocaleData?.numbers) { try { // Calculate magnitude (floor of log10 of absolute value) const magnitudeNum = Math.floor(Math.log10(Math.abs(n.toNumber()))); const magnitude = new Decimal(magnitudeNum); // Use ComputeExponentForMagnitude from ecma402-abstract // This determines which compact notation pattern to use (K, M, B, etc.) // Cast to any since it expects NumberFormatInternal exponent = ComputeExponentForMagnitude(internalSlots, magnitude); } catch { // Gracefully fall back to 0 if exponent calculation fails exponent = 0; } } } // ECMA-402 Spec: Extract CLDR operands from the formatted string // Extension: Pass exponent for c/e operands const operands = GetOperands(s, exponent); // ECMA-402 Spec: Select the appropriate plural category using the locale's plural rules const pluralCategory = PluralRuleSelect(locale, type, n, operands); return { formattedString: s, pluralCategory }; } /** * http://ecma-international.org/ecma-402/7.0/index.html#sec-resolveplural * @param pl * @param n * @param PluralRuleSelect Has to pass in bc it's implementation-specific */ export function ResolvePlural(pl, n, { getInternalSlots, PluralRuleSelect }) { return ResolvePluralInternal(pl, n, { getInternalSlots, PluralRuleSelect }).pluralCategory; }