src/PluralResolver.js
import baseLogger from './logger.js';
import { getCleanedCode } from './utils.js'
const suffixesOrder = {
zero: 0,
one: 1,
two: 2,
few: 3,
many: 4,
other: 5,
};
const dummyRule = {
select: (count) => count === 1 ? 'one' : 'other',
resolvedOptions: () => ({
pluralCategories: ['one', 'other']
})
};
class PluralResolver {
constructor(languageUtils, options = {}) {
this.languageUtils = languageUtils;
this.options = options;
this.logger = baseLogger.create('pluralResolver');
// Cache calls to Intl.PluralRules, since repeated calls can be slow in runtimes like React Native
// and the memory usage difference is negligible
this.pluralRulesCache = {};
}
addRule(lng, obj) {
this.rules[lng] = obj;
}
clearCache() {
this.pluralRulesCache = {};
}
getRule(code, options = {}) {
const cleanedCode = getCleanedCode(code === 'dev' ? 'en' : code);
const type = options.ordinal ? 'ordinal' : 'cardinal';
const cacheKey = JSON.stringify({ cleanedCode, type });
if (cacheKey in this.pluralRulesCache) {
return this.pluralRulesCache[cacheKey];
}
let rule;
try {
rule = new Intl.PluralRules(cleanedCode, { type });
} catch (err) {
if (!Intl) {
this.logger.error('No Intl support, please use an Intl polyfill!');
return dummyRule;
}
if (!code.match(/-|_/)) return dummyRule;
const lngPart = this.languageUtils.getLanguagePartFromCode(code);
rule = this.getRule(lngPart, options);
}
this.pluralRulesCache[cacheKey] = rule;
return rule;
}
needsPlural(code, options = {}) {
let rule = this.getRule(code, options);
if (!rule) rule = this.getRule('dev', options);
return rule?.resolvedOptions().pluralCategories.length > 1;
}
getPluralFormsOfKey(code, key, options = {}) {
return this.getSuffixes(code, options).map((suffix) => `${key}${suffix}`);
}
getSuffixes(code, options = {}) {
let rule = this.getRule(code, options);
if (!rule) rule = this.getRule('dev', options);
if (!rule) return [];
return rule.resolvedOptions().pluralCategories
.sort((pluralCategory1, pluralCategory2) => suffixesOrder[pluralCategory1] - suffixesOrder[pluralCategory2])
.map(pluralCategory => `${this.options.prepend}${options.ordinal ? `ordinal${this.options.prepend}` : ''}${pluralCategory}`);
}
getSuffix(code, count, options = {}) {
const rule = this.getRule(code, options);
if (rule) {
return `${this.options.prepend}${options.ordinal ? `ordinal${this.options.prepend}` : ''}${rule.select(count)}`;
}
this.logger.warn(`no plural rule found for: ${code}`);
return this.getSuffix('dev', count, options);
}
}
export default PluralResolver;