Polyfill for Intl.PluralRules providing locale-sensitive plural rule selection
npx @tessl/cli install tessl/npm-formatjs--intl-pluralrules@5.4.0A spec-compliant polyfill for the Intl.PluralRules API that provides locale-sensitive plural rule selection for internationalization applications. This polyfill offers full ECMAScript Conformance test suite compatibility, supports dynamic locale data loading for optimal bundle size, and provides flexible integration options including conditional polyfilling based on runtime capability detection.
npm install @formatjs/intl-pluralrulesimport { PluralRules } from "@formatjs/intl-pluralrules";For CommonJS:
const { PluralRules } = require("@formatjs/intl-pluralrules");For polyfill usage:
import "@formatjs/intl-pluralrules/polyfill";
// Or to force polyfill
import "@formatjs/intl-pluralrules/polyfill-force";Utility imports:
import { shouldPolyfill } from "@formatjs/intl-pluralrules/should-polyfill";
import { supportedLocales } from "@formatjs/intl-pluralrules/supported-locales.generated";import { PluralRules } from "@formatjs/intl-pluralrules";
import "@formatjs/intl-pluralrules/locale-data/en";
// Create PluralRules instance
const pr = new PluralRules("en");
// Get plural rule for numbers
console.log(pr.select(0)); // "other"
console.log(pr.select(1)); // "one"
console.log(pr.select(2)); // "other"
// Use with ordinal numbers
const ordinalPr = new PluralRules("en", { type: "ordinal" });
console.log(ordinalPr.select(1)); // "one" (1st)
console.log(ordinalPr.select(2)); // "two" (2nd)
console.log(ordinalPr.select(3)); // "few" (3rd)
console.log(ordinalPr.select(4)); // "other" (4th)
// Check resolved options
console.log(pr.resolvedOptions());
// {
// locale: "en",
// type: "cardinal",
// minimumIntegerDigits: 1,
// minimumFractionDigits: 0,
// maximumFractionDigits: 3,
// pluralCategories: ["one", "other"]
// }The polyfill is built around several key components:
Creates a new PluralRules instance with locale and formatting options.
/**
* Creates a new PluralRules instance
* @param locales - Locale identifier(s) or array of locale identifiers
* @param options - Configuration options for plural rules
*/
class PluralRules implements Intl.PluralRules {
constructor(locales?: string | string[], options?: Intl.PluralRulesOptions);
}Returns the plural rule category for a given number.
/**
* Returns the plural rule for the given number
* @param val - Number to get plural rule for
* @returns LDML plural rule category
*/
select(val: number): LDMLPluralRule;Returns the resolved options used by the PluralRules instance.
/**
* Returns the resolved options object
* @returns Object containing resolved locale and formatting options
*/
resolvedOptions(): Intl.ResolvedPluralRulesOptions;Returns the string representation of the PluralRules instance.
/**
* Returns string representation of the PluralRules instance
* @returns Always returns '[object Intl.PluralRules]'
*/
toString(): string;Checks which locales are supported by the implementation.
/**
* Returns an array of supported locales from the provided list
* @param locales - Locale identifier(s) to check
* @param options - Options for locale matching
* @returns Array of supported locale strings
*/
static supportedLocalesOf(
locales?: string | string[],
options?: Pick<Intl.PluralRulesOptions, 'localeMatcher'>
): string[];Adds locale data to the polyfill for specific locales.
/**
* Adds locale data to the polyfill
* @param data - Locale data objects to add
*/
static __addLocaleData(...data: PluralRulesLocaleData[]): void;Additional static properties available on the PluralRules class.
/**
* Set of available locales supported by the polyfill
*/
static availableLocales: Set<string>;
/**
* Default locale used when none is specified
*/
static getDefaultLocale(): string;
/**
* Flag indicating this is a polyfilled implementation
*/
static polyfilled: boolean;
/**
* Array of relevant Unicode extension keys (empty for PluralRules)
*/
static relevantExtensionKeys: never[];
/**
* Internal locale data storage
*/
static localeData: Record<string, PluralRulesData>;Determines if the polyfill is needed for the current environment.
/**
* Determines if polyfill is needed for the given locale
* @param locale - Locale to check (defaults to 'en')
* @returns Locale string if polyfill needed, undefined if native support exists
*/
function shouldPolyfill(locale?: string): string | undefined;Array of all locale codes supported by the polyfill.
/**
* Array of supported locale codes
*/
const supportedLocales: string[];/**
* LDML plural rule categories
*/
type LDMLPluralRule = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';
/**
* Internal slots interface for PluralRules instances
* Note: NumberFormatDigitInternalSlots is from @formatjs/ecma402-abstract
*/
interface PluralRulesInternal extends NumberFormatDigitInternalSlots {
initializedPluralRules: boolean;
locale: string;
type: 'cardinal' | 'ordinal';
}
/**
* Structure for locale-specific plural rules data
*/
interface PluralRulesLocaleData {
data: PluralRulesData;
locale: string;
}
/**
* Plural rules data containing categories and evaluation function
*/
interface PluralRulesData {
categories: {
cardinal: LDMLPluralRule[];
ordinal: LDMLPluralRule[];
};
fn: (n: string, ord?: boolean) => LDMLPluralRule;
}
/**
* Operands record for plural rule calculations
* Note: Decimal is from the decimal.js package
*/
interface OperandsRecord {
Number: Decimal;
IntegerDigits: number;
NumberOfFractionDigits: number;
NumberOfFractionDigitsWithoutTrailing: number;
FractionDigits: number;
FractionDigitsWithoutTrailing: number;
}// Only polyfills if native support is missing or insufficient
import "@formatjs/intl-pluralrules/polyfill";
// Now Intl.PluralRules is available globally
const pr = new Intl.PluralRules("en");// Always uses the polyfill implementation
import "@formatjs/intl-pluralrules/polyfill-force";
// Intl.PluralRules is replaced with polyfill
const pr = new Intl.PluralRules("en");import { shouldPolyfill } from "@formatjs/intl-pluralrules/should-polyfill";
if (shouldPolyfill()) {
// Load and apply polyfill
import("@formatjs/intl-pluralrules/polyfill");
}The polyfill throws appropriate errors following the ECMAScript specification:
newExample error handling:
try {
// This will throw TypeError
const pr = PluralRules("en"); // Missing 'new'
} catch (error) {
console.error(error.message); // "Intl.PluralRules must be called with 'new'"
}
try {
const pr = new PluralRules("en");
// This will throw TypeError
pr.select.call({}, 1);
} catch (error) {
console.error(error.message); // Method called on incompatible receiver
}import { PluralRules } from "@formatjs/intl-pluralrules";
// Define custom locale data
const customLocaleData = {
data: {
categories: {
cardinal: ["one", "other"],
ordinal: ["other"]
},
fn: (n: string, ord?: boolean) => {
return parseInt(n) === 1 && !ord ? "one" : "other";
}
},
locale: "custom-locale"
};
// Add the custom locale data
PluralRules.__addLocaleData(customLocaleData);
const pr = new PluralRules("custom-locale");
console.log(pr.select(1)); // "one"
console.log(pr.select(2)); // "other"const pr = new PluralRules("en", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
// Fraction digits affect plural rule calculation
console.log(pr.select(1.00)); // Considers formatting options// The polyfill handles locale fallback automatically
const pr = new PluralRules("en-US-variant-something");
console.log(pr.resolvedOptions().locale); // Will fallback to "en" if full locale not available