make-plural
Advanced tools
Comparing version 1.3.0 to 2.0.0
@@ -62,10 +62,2 @@ /** | ||
function test_values(str) { | ||
return str | ||
.replace(/decimal|integer/g, '') | ||
.replace(/^[ ,]+|[ ,…]+$/g, '') | ||
.replace(/(0\.[0-9])~(1\.[1-9])/g, '$1 1.0 $2') | ||
.split(/[ ,~…]+/); | ||
} | ||
function vars(symbols) { | ||
@@ -90,2 +82,51 @@ var vars = []; | ||
function build(lc, opt, tests) { | ||
var lines = [], symbols = {}, | ||
_fold = function(l) { return l.replace(/(.{1,72})( \|\| |$) ?/gm, '$1\n $2').replace(/\s+$/gm, ''); }, | ||
_compile = function(type, req) { | ||
var cases = []; | ||
if (!MakePlural.rules || !MakePlural.rules[type]) { | ||
MakePlural.load((type == 'ordinal') | ||
? './data/unicode-cldr-ordinal-rules.json' | ||
: './data/unicode-cldr-plural-rules.json'); | ||
} | ||
if (MakePlural.rules[type][lc]) { | ||
for (var r in MakePlural.rules[type][lc]) { | ||
var key = r.replace('pluralRule-count-', ''), | ||
parts = MakePlural.rules[type][lc][r].split(/@\w*/), | ||
cond = parts.shift().trim(); | ||
if (cond) cases.push([parse(cond, symbols), key]); | ||
tests[type][key] = parts.join(' ') | ||
.replace(/^[ ,]+|[ ,…]+$/g, '') | ||
.replace(/(0\.[0-9])~(1\.[1-9])/g, '$1 1.0 $2') | ||
.split(/[ ,~…]+/); | ||
} | ||
} else if (req) { | ||
if (!opt['quiet']) console.error('Locale "' + lc + '" ' + type + ' rules not found'); | ||
return false; | ||
} | ||
if (!cases.length) return "'other'"; | ||
if (cases.length == 1) return "(" + cases[0][0] + ") ? '" + cases[0][1] + "' : 'other'"; | ||
return cases.map(function(c) { return "(" + c[0] + ") ? '" + c[1] + "'"; }).concat("'other'").join('\n : '); | ||
}; | ||
if (opt['ordinals']) { | ||
if (opt['no_cardinals']) { | ||
var l = _compile('ordinal', true); | ||
if (!l) return null; | ||
lines.push(_fold(' return ' + l + ';')); | ||
} else { | ||
lines.push(_fold(' if (ord) return ' + _compile('ordinal', false) + ';')); | ||
} | ||
} | ||
if (!opt['no_cardinals']) { | ||
var l = _compile('cardinal', true); | ||
if (!l) return null; | ||
lines.push(_fold(' return ' + l + ';')); | ||
} | ||
var fn_vars = vars(symbols).replace(/(.{1,78})(,|$) ?/g, '\n $1$2').trim(); | ||
if (fn_vars) lines.unshift(' ' + fn_vars); | ||
return lines.join('\n'); | ||
} | ||
function test(lc, fn, tests, opt) { | ||
@@ -119,89 +160,57 @@ var ok = true, | ||
var Plurals = {}; | ||
function xhr_require(src, url) { | ||
if (src && (url[0] == '.')) url = src.replace(/[^\/]*$/, url); | ||
var xhr = new XMLHttpRequest(); | ||
xhr.open('get', url, false); | ||
xhr.send(); | ||
return (xhr.status == 200) && JSON.parse(xhr.responseText); | ||
} | ||
Plurals.set_rules = function(cldr) { | ||
var _require = (typeof require == 'function') ? require : function(url) { | ||
if (Plurals.src_url && (url[0] == '.')) url = Plurals.src_url.replace(/[^\/]*$/, url); | ||
var xhr = new XMLHttpRequest(); | ||
xhr.open('get', url, false); | ||
xhr.send(); | ||
return (xhr.status == 200) && JSON.parse(xhr.responseText); | ||
var MakePlural = function(lc, opt) { | ||
if (typeof lc == 'object') { opt = lc; lc = opt.lc; } | ||
else { | ||
if (!opt) opt = MakePlural.opt; | ||
if (!lc) lc = opt.lc; | ||
} | ||
var tests = { 'ordinal':{}, 'cardinal':{} }, | ||
fn_body = build(lc, opt, tests), | ||
fn = opt['ordinals'] && !opt['no_cardinals'] | ||
? new Function('n', 'ord', fn_body) | ||
: new Function('n', fn_body); | ||
fn.toString = function(name) { | ||
var s = Function.prototype.toString.call(fn); | ||
return s.replace(/^function( \w+)?/, name ? 'function ' + name : 'function'); | ||
}; | ||
if (typeof cldr == 'string') cldr = _require(cldr); | ||
if (cldr && cldr['supplemental']) { | ||
if (!Plurals.rules) Plurals.rules = {}; | ||
if ('plurals-type-cardinal' in cldr['supplemental']) { | ||
Plurals.rules['cardinal'] = cldr['supplemental']['plurals-type-cardinal']; | ||
} | ||
if ('plurals-type-ordinal' in cldr['supplemental']) { | ||
Plurals.rules['ordinal'] = cldr['supplemental']['plurals-type-ordinal']; | ||
} | ||
} | ||
return fn_body && (opt['no_tests'] || test(lc, fn, tests, opt)) ? fn : null; | ||
}; | ||
Plurals.build = function(lc, opt) { | ||
var fn, fn_str, fn_vars, lines = [], symbols = {}, | ||
tests = { 'ordinal':{}, 'cardinal':{} }, | ||
_compile = function(type, indent, req) { | ||
var cases = []; | ||
if (!Plurals.rules || !Plurals.rules[type]) { | ||
Plurals.set_rules((type == 'ordinal') | ||
? './data/unicode-cldr-ordinal-rules.json' | ||
: './data/unicode-cldr-plural-rules.json'); | ||
} | ||
if (Plurals.rules[type][lc]) { | ||
for (var r in Plurals.rules[type][lc]) { | ||
var key = r.replace('pluralRule-count-', ''), | ||
parts = Plurals.rules[type][lc][r].split(/@\w*/), | ||
cond = parts.shift().trim(); | ||
if (cond) cases.push([parse(cond, symbols), key]); | ||
tests[type][key] = test_values(parts.join(' ')); | ||
} | ||
} else if (req) { | ||
if (!opt['quiet']) console.error('Locale "' + lc + '" ' + type + ' rules not found'); | ||
return false; | ||
} | ||
if (!cases.length) return "'other'"; | ||
if (cases.length == 1) return "(" + cases[0][0] + ") ? '" + cases[0][1] + "' : 'other'"; | ||
return cases.map(function(c) { return "(" + c[0] + ") ? '" + c[1] + "'"; }).concat("'other'").join('\n : '); | ||
}, _fold = function(l) { | ||
return l.replace(/(.{1,72})( \|\| |$) ?/gm, '$1\n $2').replace(/\s+$/gm, ''); | ||
}; | ||
if (!opt) opt = {}; | ||
if (opt['ordinals']) { | ||
if (opt['no_cardinals']) { | ||
var l = _compile('ordinal', ' ', true); | ||
if (!l) return null; | ||
lines.push(_fold(' return ' + l + ';')); | ||
} else { | ||
lines.push(_fold(' if (ord) return ' + _compile('ordinal', ' ', false) + ';')); | ||
} | ||
MakePlural.opt = {}; | ||
MakePlural.rules = {}; | ||
MakePlural.load = function(/* arguments */) { | ||
var _require = (typeof require == 'function') ? require : function(url) { return xhr_require(MakePlural.src_url, url); }; | ||
if (!MakePlural.rules) MakePlural.rules = {}; | ||
for (var i = 0; i < arguments.length; ++i) { | ||
var cldr = (typeof arguments[i] == 'string') ? _require(arguments[i]) : arguments[i]; | ||
if (cldr && cldr['supplemental']) ['cardinal', 'ordinal'].forEach(function(type) { | ||
var set = cldr['supplemental']['plurals-type-' + type]; | ||
if (set) MakePlural.rules[type] = set; | ||
}); | ||
} | ||
if (!opt['no_cardinals']) { | ||
var l = _compile('cardinal', ' ', true); | ||
if (!l) return null; | ||
lines.push(_fold(' return ' + l + ';')); | ||
} | ||
fn_vars = vars(symbols).replace(/(.{1,78})(,|$) ?/g, '\n $1$2').trim(); | ||
if (fn_vars) lines.unshift(' ' + fn_vars); | ||
fn = new Function('n', 'ord', lines.join('\n').trim()); | ||
if (!opt['no_tests'] && !test(lc, fn, tests, opt)) return null; | ||
if (opt['return_function']) return fn; | ||
fn_str = (opt['ordinals'] && !opt['no_cardinals'] ? 'function(n,ord)' : 'function(n)') | ||
+ ' {\n' + lines.join('\n').replace(/{\s*(return [^;]+;)\s*}/, '$1') + '\n}'; | ||
if (opt['minify']) fn_str = fn_str.replace(/\s+/g, '').replace(/{var/, '{var '); | ||
return fn_str; | ||
return MakePlural; | ||
}; | ||
if ((typeof module !== 'undefined') && module.exports) { | ||
module.exports = Plurals; | ||
module.exports = MakePlural; | ||
} else if (typeof exports !== 'undefined') { | ||
// won't expose Plurals.rules | ||
for (var p in Plurals) exports[p] = Plurals[p]; | ||
for (var p in MakePlural) exports[p] = MakePlural[p]; | ||
exports.get = MakePlural; | ||
} else { | ||
try { Plurals.src_url = Array.prototype.slice.call(document.getElementsByTagName('script')).pop().src; } | ||
catch (e) { Plurals.src_url = ''; } | ||
global.Plurals = Plurals; | ||
try { MakePlural.src_url = Array.prototype.slice.call(document.getElementsByTagName('script')).pop().src; } | ||
catch (e) { MakePlural.src_url = ''; } | ||
global.MakePlural = MakePlural; | ||
} | ||
})(this); |
{ | ||
"name": "make-plural", | ||
"version": "1.3.0", | ||
"version": "2.0.0", | ||
"description": "Translates Unicode CLDR pluralization rules to executable JavaScript", | ||
@@ -5,0 +5,0 @@ "keywords": ["unicode", "cldr", "i18n", "internationalization", "pluralization"], |
@@ -6,3 +6,3 @@ make-plural | ||
[pluralization rules](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html) | ||
to executable JavaScript functions. | ||
to JavaScript functions. | ||
@@ -12,7 +12,7 @@ | ||
```sh | ||
``` | ||
npm install make-plural | ||
``` | ||
or | ||
```sh | ||
``` | ||
git clone https://github.com/eemeli/make-plural.js.git | ||
@@ -24,7 +24,7 @@ ``` | ||
```js | ||
> Plurals = require('make-plural') | ||
{ set_rules: [Function], build: [Function] } | ||
> MakePlural = require('make-plural') | ||
{ [Function] opt: {}, rules: {}, load: [Function] } | ||
> console.log(Plurals.build('sk')) // Slovak | ||
function(n,ord) { | ||
> console.log(MakePlural('sk').toString()) | ||
function(n) { | ||
var s = String(n).split('.'), i = s[0], v0 = !s[1]; | ||
@@ -37,4 +37,4 @@ return (n == 1 && v0) ? 'one' | ||
> sk = Plurals.build('sk', { 'return_function':1 }) | ||
[Function] | ||
> sk = MakePlural('sk') | ||
{ [Function] toString: [Function] } | ||
@@ -53,3 +53,3 @@ > sk(1) | ||
> console.log(Plurals.build('en', { 'ordinals':1 })) // English | ||
> console.log(MakePlural('en', {'ordinals':1}).toString()) | ||
function(n,ord) { | ||
@@ -65,4 +65,4 @@ var s = String(n).split('.'), v0 = !s[1], t0 = Number(s[0]) == n, | ||
> en = Plurals.build('en', { 'ordinals':1, 'return_function':1 }) | ||
[Function] | ||
> en = MakePlural('en', {'ordinals':1}) | ||
{ [Function] toString: [Function] } | ||
@@ -81,6 +81,6 @@ > en(2) | ||
<script> | ||
console.log(Plurals.build('sk', {'ordinals':1})); | ||
var sk = Plurals.build('sk', { 'ordinals':1, 'return_function':1 }); | ||
console.log('1: ' + sk(1) + ', 3.0: ' + sk(3.0) + | ||
', "1.0": ' + sk('1.0') + ', "0": ' + sk('0')); | ||
console.log(MakePlural('ru', {'ordinals':1}).toString()); | ||
var ru = MakePlural('ru', {'ordinals':1}); | ||
console.log('1: ' + ru(1) + ', 3.0: ' + ru(3.0) + | ||
', "1.0": ' + ru('1.0') + ', "0": ' + ru('0')); | ||
</script> | ||
@@ -91,11 +91,14 @@ ``` | ||
function(n,ord) { | ||
var s = String(n).split('.'), i = s[0], v0 = !s[1]; | ||
var s = String(n).split('.'), i = s[0], v0 = !s[1], i10 = i.substr(-1), | ||
i100 = i.substr(-2); | ||
if (ord) return 'other'; | ||
return (n == 1 && v0) ? 'one' | ||
: ((i >= 2 && i <= 4) && v0) ? 'few' | ||
: (!v0) ? 'many' | ||
return (v0 && i10 == 1 && i100 != 11) ? 'one' | ||
: (v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 | ||
|| i100 > 14)) ? 'few' | ||
: (v0 && i10 == 0 || v0 && (i10 >= 5 && i10 <= 9) | ||
|| v0 && (i100 >= 11 && i100 <= 14)) ? 'many' | ||
: 'other'; | ||
} | ||
1: one, 3.0: few, "1.0": many, "0": other | ||
1: one, 3.0: few, "1.0": other, "0": many | ||
``` | ||
@@ -106,3 +109,3 @@ | ||
default locations. If that doesn't work for you, you should call | ||
`Plurals.set_rules(cldr)` before calling `Plurals.build()`. | ||
`MakePlural.load(cldr)` before calling `MakePlural()`. | ||
@@ -126,3 +129,3 @@ | ||
$ ./bin/make-plural fr | ||
function(n,ord) { | ||
function fr(n,ord) { | ||
if (ord) return (n == 1) ? 'one' : 'other'; | ||
@@ -139,9 +142,12 @@ return (n >= 0 && n < 2) ? 'one' : 'other'; | ||
### build(lc, opt) | ||
By default, returns a string representation of a function that takes a single | ||
argument `n` and returns its plural category for the given locale `lc`. | ||
### MakePlural(lc, opt) | ||
Returns a function that takes a single argument `n` and returns its plural | ||
category for the given locale `lc`. | ||
The optional `opt` object may contain the following members, each of which is | ||
The returned function has an overloaded `toString(name)` method that may be | ||
used to generate a clean string representation of the function, with an | ||
optional name `name`. | ||
The optional `opt` parameter may contain the following members, each of which is | ||
assumed false by default: | ||
* `minify` — if true, the string output of `build` is minified | ||
* `no_cardinals` — if true, rules for cardinal values (1 day, 2 days, etc.) | ||
@@ -153,6 +159,4 @@ are not included | ||
* `quiet` — if true, no output is reported to `console.error` on error | ||
* `return_function` — if true, `build` returns an executable function of `n` | ||
rather than a string | ||
if `opt.ordinals` is true and `opt.no_cardinals` is not true, the returned | ||
If `opt.ordinals` is true and `opt.no_cardinals` is not true, the returned | ||
function takes a second parameter `ord`. Then, if `ord` is true, the function | ||
@@ -162,6 +166,15 @@ will return the ordinal rather than cardinal category applicable to `n` in | ||
### set_rules(cldr) | ||
Sets the used CLDR rules to `cldr`, which may be an object or the path to a JSON | ||
file formatted like [this](http://www.unicode.org/repos/cldr-aux/json/26/supplemental/plurals.json). | ||
If `opt` is not set, it takes the value of `MakePlural.opt`. If `lc` is not set, | ||
it takes the value of `opt.lc`. | ||
In a context where `module.exports` is not available but `exports` is, this | ||
function is exported as `MakePlural.get()`. | ||
### MakePlural.load(cldr, ...) | ||
Loads CLDR rules from one or more `cldr` variables, each of which may be an | ||
object or the path to a JSON file formatted like | ||
[this](http://www.unicode.org/repos/cldr-aux/json/26/supplemental/plurals.json). | ||
The stored rules are kept in `MakePlural.rules.cardinal` and | ||
`MakePlural.rules.ordinal`, which may also be directly accessed. | ||
If called within a context where `request()` isn't available and `cldr` is a | ||
@@ -171,6 +184,7 @@ string, it's taken as the URL of the JSON file that'll be fetched and parsed | ||
By default, `build()` will call `set_rules(cldr)` when required, using the | ||
rules included in `data/`: `unicode-cldr-plural-rules.json` and | ||
By default, `MakePlural()` will call `MakePlural.load(cldr)` when required, | ||
using the rules included in `data/`, `unicode-cldr-plural-rules.json` and | ||
`unicode-cldr-ordinal-rules.json`. | ||
## Dependencies | ||
@@ -181,1 +195,14 @@ | ||
as used in CLDR release 24 and later. | ||
Using `MakePlural.load()`, you may make use of external sources of CLDR data. | ||
For example, the following works when using together with | ||
[cldr-data](https://www.npmjs.org/package/cldr-data): | ||
```js | ||
> cldr = require('cldr-data'); | ||
> MakePlural = require('make-plural').load( | ||
cldr('supplemental/plurals'), | ||
cldr('supplemental/ordinals') | ||
); | ||
> MakePlural('ar')(3.14); | ||
'other' | ||
``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
93033
1345
194
2