intl-pluralrules
Advanced tools
Comparing version 1.0.3 to 1.1.0
{ | ||
"name": "intl-pluralrules", | ||
"version": "1.0.3", | ||
"version": "1.1.0", | ||
"description": "Intl.PluralRules polyfill", | ||
@@ -17,15 +17,21 @@ "keywords": [ | ||
"files": [ | ||
"plural-rules.js", | ||
"polyfill.js" | ||
"factory.*", | ||
"plural-rules.*", | ||
"pseudo-number-format.*", | ||
"polyfill.*" | ||
], | ||
"main": "polyfill.js", | ||
"babel": { | ||
"presets": [ | ||
[ | ||
"@babel/preset-env", | ||
{ | ||
"targets": "> 0.25%, not dead" | ||
} | ||
] | ||
] | ||
"main": "polyfill", | ||
"jest": { | ||
"moduleFileExtensions": [ | ||
"mjs", | ||
"js", | ||
"json" | ||
], | ||
"testMatch": [ | ||
"**/*.test.mjs" | ||
], | ||
"transform": { | ||
"\\.mjs$": "babel-jest" | ||
}, | ||
"transformIgnorePatterns": [] | ||
}, | ||
@@ -37,3 +43,3 @@ "prettier": { | ||
"dependencies": { | ||
"make-plural": "^4.3.0" | ||
"make-plural": "^6.0.0" | ||
}, | ||
@@ -44,6 +50,9 @@ "devDependencies": { | ||
"@babel/preset-env": "^7.4.5", | ||
"jest": "^24.8.0" | ||
"babel-jest": "^24.9.0", | ||
"jest": "^24.9.0" | ||
}, | ||
"scripts": { | ||
"build": "babel -d . src/ --ignore '**/*.test.js'", | ||
"build:cjs": "TARGET=cjs babel -d . src/ --ignore '**/*.test.mjs'", | ||
"build:mjs": "babel -d . src/ --ignore '**/*.test.mjs' --keep-file-extension", | ||
"build": "npm run build:mjs && npm run build:cjs", | ||
"prepublishOnly": "npm test && npm run build", | ||
@@ -50,0 +59,0 @@ "test": "jest" |
@@ -8,186 +8,30 @@ "use strict"; | ||
var _makePlural = _interopRequireDefault(require("make-plural")); | ||
var Plurals = _interopRequireDefault(require("make-plural/plurals")); | ||
var _pluralCategories = _interopRequireDefault(require("make-plural/umd/pluralCategories")); | ||
var Categories = _interopRequireDefault(require("make-plural/pluralCategories")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var _factory = _interopRequireDefault(require("./factory")); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
var _pseudoNumberFormat = _interopRequireDefault(require("./pseudo-number-format")); | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
// does not check for duplicate subtags | ||
var isStructurallyValidLanguageTag = function isStructurallyValidLanguageTag(locale) { | ||
return locale.split('-').every(function (subtag) { | ||
return /[a-z0-9]+/i.test(subtag); | ||
}); | ||
}; | ||
var NumberFormat = (typeof Intl === "undefined" ? "undefined" : _typeof(Intl)) === 'object' && Intl.NumberFormat || _pseudoNumberFormat.default; // make-plural exports are cast with safe-identifier to be valid JS identifiers | ||
var canonicalizeLocaleList = function canonicalizeLocaleList(locales) { | ||
if (!locales) return []; | ||
if (!Array.isArray(locales)) locales = [locales]; | ||
var res = {}; | ||
for (var i = 0; i < locales.length; ++i) { | ||
var tag = locales[i]; | ||
if (tag && _typeof(tag) === 'object') tag = String(tag); | ||
if (typeof tag !== 'string') { | ||
// Requiring tag to be a String or Object means that the Number value | ||
// NaN will not be interpreted as the language tag "nan", which stands | ||
// for Min Nan Chinese. | ||
var msg = "Locales should be strings, ".concat(JSON.stringify(tag), " isn't."); | ||
throw new TypeError(msg); | ||
} | ||
if (tag[0] === '*') continue; | ||
if (!isStructurallyValidLanguageTag(tag)) { | ||
var strTag = JSON.stringify(tag); | ||
var _msg = "The locale ".concat(strTag, " is not a structurally valid BCP 47 language tag."); | ||
throw new RangeError(_msg); | ||
} | ||
res[tag] = true; | ||
} | ||
return Object.keys(res); | ||
var id = function id(lc) { | ||
return lc === 'in' ? '_in' : lc === 'pt-PT' ? 'pt_PT' : lc; | ||
}; | ||
var defaultLocale = function defaultLocale() { | ||
return typeof navigator !== 'undefined' && navigator && (navigator.userLanguage || navigator.language) || 'en-US'; | ||
var getSelector = function getSelector(lc) { | ||
return Plurals[id(lc)]; | ||
}; | ||
var findLocale = function findLocale(locale) { | ||
do { | ||
if (_makePlural.default[locale]) return locale; | ||
locale = locale.replace(/-?[^-]*$/, ''); | ||
} while (locale); | ||
return null; | ||
var getCategories = function getCategories(lc, ord) { | ||
return Categories[id(lc)][ord ? 'ordinal' : 'cardinal']; | ||
}; | ||
var resolveLocale = function resolveLocale(locales) { | ||
var canonicalLocales = canonicalizeLocaleList(locales); | ||
for (var i = 0; i < canonicalLocales.length; ++i) { | ||
var lc = findLocale(canonicalLocales[i]); | ||
if (lc) return lc; | ||
} | ||
return findLocale(defaultLocale()); | ||
}; | ||
var getType = function getType(type) { | ||
if (!type) return 'cardinal'; | ||
if (type === 'cardinal' || type === 'ordinal') return type; | ||
throw new RangeError('Not a valid plural type: ' + JSON.stringify(type)); | ||
}; | ||
var PluralRules = | ||
/*#__PURE__*/ | ||
function () { | ||
_createClass(PluralRules, null, [{ | ||
key: "supportedLocalesOf", | ||
value: function supportedLocalesOf(locales) { | ||
return canonicalizeLocaleList(locales).filter(findLocale); | ||
} | ||
}]); | ||
function PluralRules(locales) { | ||
var opt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
_classCallCheck(this, PluralRules); | ||
this._locale = resolveLocale(locales); | ||
this._type = getType(opt.type); | ||
if ((typeof Intl === "undefined" ? "undefined" : _typeof(Intl)) === 'object' && Intl.NumberFormat) { | ||
// make-plural expects latin digits with . decimal separator | ||
this._nf = new Intl.NumberFormat('en', opt); | ||
} else { | ||
var minID = opt.minimumIntegerDigits, | ||
minFD = opt.minimumFractionDigits, | ||
maxFD = opt.maximumFractionDigits, | ||
minSD = opt.minimumSignificantDigits, | ||
maxSD = opt.maximumSignificantDigits; | ||
this._minID = typeof minID === 'number' ? minID : 1; | ||
this._minFD = typeof minFD === 'number' ? minFD : 0; | ||
this._maxFD = typeof maxFD === 'number' ? maxFD : Math.max(this._minFD, 3); | ||
if (typeof minSD === 'number' || typeof maxSD === 'number') { | ||
this._minSD = typeof minSD === 'number' ? minSD : 1; | ||
this._maxSD = typeof maxSD === 'number' ? maxSD : 21; | ||
} | ||
} | ||
} | ||
_createClass(PluralRules, [{ | ||
key: "resolvedOptions", | ||
value: function resolvedOptions() { | ||
var nfOpt = this._nf && this._nf.resolvedOptions(); | ||
var opt = { | ||
locale: this._locale, | ||
minimumIntegerDigits: nfOpt ? nfOpt.minimumIntegerDigits : this._minID, | ||
minimumFractionDigits: nfOpt ? nfOpt.minimumFractionDigits : this._minFD, | ||
maximumFractionDigits: nfOpt ? nfOpt.maximumFractionDigits : this._maxFD, | ||
pluralCategories: _pluralCategories.default[this._locale][this._type], | ||
type: this._type | ||
}; | ||
if (nfOpt && typeof nfOpt.minimumSignificantDigits === 'number') { | ||
opt.minimumSignificantDigits = nfOpt.minimumSignificantDigits; | ||
opt.maximumSignificantDigits = nfOpt.maximumSignificantDigits; | ||
} else if (typeof this._minSD === 'number') { | ||
opt.minimumSignificantDigits = this._minSD; | ||
opt.maximumSignificantDigits = this._maxSD; | ||
} | ||
return opt; | ||
} | ||
}, { | ||
key: "_format", | ||
value: function _format(n) { | ||
if (this._nf) return this._nf.format(n); | ||
if (this._minSD) { | ||
var raw = String(n); | ||
var prec = 0; | ||
for (var i = 0; i < raw.length; ++i) { | ||
var c = raw[i]; | ||
if (c >= '0' && c <= '9') ++prec; | ||
} | ||
if (prec < this._minSD) return n.toPrecision(this._minSD); | ||
if (prec > this._maxSD) return n.toPrecision(this._maxSD); | ||
return raw; | ||
} | ||
if (this._minFD > 0) return n.toFixed(this._minFD); | ||
if (this._maxFD === 0) return n.toFixed(0); | ||
return String(n); | ||
} | ||
}, { | ||
key: "select", | ||
value: function select(number) { | ||
if (typeof number !== 'number') number = Number(number); | ||
if (!isFinite(number)) return 'other'; | ||
var fmt = this._format(Math.abs(number)); | ||
return _makePlural.default[this._locale](fmt, this._type === 'ordinal'); | ||
} | ||
}]); | ||
return PluralRules; | ||
}(); | ||
exports.default = PluralRules; | ||
var PluralRules = (0, _factory.default)(NumberFormat, getSelector, getCategories); | ||
var _default = PluralRules; | ||
exports.default = _default; |
# intl-pluralrules | ||
A spec-compliant polyfill for [Intl.PluralRules]. Particularly useful if you | ||
need proper support for [`minimumFractionDigits`]. | ||
A spec-compliant implementation & polyfill for [Intl.PluralRules]. Particularly | ||
useful if you need proper support for [`minimumFractionDigits`], which are only | ||
supported in Chrome 77 & later. | ||
@@ -15,6 +16,6 @@ [intl.pluralrules]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/PluralRules | ||
## Usage | ||
## Polyfill | ||
To use the polyfill, just import it to make sure that a fully functional | ||
`Intl.PluralRules` is available in your environment: | ||
To use as a polyfill, just import it to ensure that `Intl.PluralRules` is | ||
available in your environment: | ||
@@ -27,5 +28,7 @@ ```js | ||
[multiple locales](https://nodejs.org/api/intl.html), the polyfill will not be | ||
loaded. | ||
loaded. Full support for `minimumFractionDigits` is not checked. | ||
The implementation itself is available as | ||
## Ponyfill | ||
A complete implementation of PluralRules is available as | ||
`intl-pluralrules/plural-rules`, if you'd prefer using it without modifying your | ||
@@ -40,1 +43,51 @@ `Intl` object, or if you wish to use it rather than your environment's own: | ||
``` | ||
## Factory | ||
In order to support all available locales, their data needs to be included in | ||
the package. This means that when minified and gzipped, the above-documented | ||
usage adds about 7kB to your application's production size. If this is a | ||
concern, you can use `intl-pluralrules/factory` and [make-plural] to build a | ||
PluralRules class with locale support limited to only what you actually use. | ||
[make-plural]: https://www.npmjs.com/package/make-plural | ||
Thanks to tree-shaking, this example that only supports English and French | ||
minifies & gzips to 1619 bytes. Do note that this size difference is only | ||
apparent with minified production builds. | ||
```js | ||
import getPluralRules from 'intl-pluralrules/factory' | ||
import { en, fr } from 'make-plural/plurals' | ||
import { en as enCat, fr as frCat } from 'make-plural/pluralCategories' | ||
const sel = { en, fr } | ||
const getSelector = lc => sel[lc] | ||
const cat = { en: enCat, fr: frCat } | ||
const getCategories = (lc, ord) => cat[lc][ord ? 'ordinal' : 'cardinal'] | ||
const PluralRules = getPluralRules( | ||
Intl.NumberFormat, // Not available in IE 10 | ||
getSelector, | ||
getCategories | ||
) | ||
export default PluralRules | ||
``` | ||
All arguments of `getPluralRules(NumberFormat, getSelector, getCategories)` are | ||
required. | ||
- `NumberFormat` should be `Intl.NumberFormat`, or a minimal implementation | ||
such as the one available at `intl-pluralrules/pseudo-number-format`. It | ||
should at least support the `"en"` locale and all of the min/max digit count | ||
options. | ||
- `getSelector(lc)` should return a `function(n, ord)` returning the plural | ||
category of `n`, using cardinal plural rules (by default), or ordinal rules if | ||
`ord` is true. `n` may be a number, or the formatted string representation of | ||
a number. This may be called with any user-provided string `lc`, and should | ||
return `undefined` for invalid or unsupported locales. | ||
- `getCategories(lc, ord)` should return the set of available plural categories | ||
for the locale, either for cardinals (by default), or ordinals if `ord` is | ||
true. This function will be called only with values for which `getSelector` | ||
returns a function. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
26097
12
445
91
5
1
+ Addedmake-plural@6.2.2(transitive)
- Removedmake-plural@4.3.0(transitive)
- Removedminimist@1.2.8(transitive)
Updatedmake-plural@^6.0.0