make-plural
Advanced tools
Comparing version 1.2.0 to 1.3.0
@@ -89,16 +89,26 @@ /** | ||
function test(fn, tests, opt) { | ||
function test(lc, fn, tests, opt) { | ||
var ok = true, | ||
_e = function(t) { | ||
ok = false; | ||
if (!opt['quiet']) console.error( | ||
'ERROR: plural rule self-test failed for v = ' | ||
+ (typeof t == 'string' ? '"' + t + '"' : t) | ||
+ ' (was "' + fn(t) + '", expected "' + k + '")'); | ||
_test = function(k, x, ord) { | ||
try { var r = fn(x, ord); } | ||
catch (e) { r = e.toString(); } | ||
if (r != k) { | ||
ok = false; | ||
if (!opt['quiet']) console.error( | ||
'Locale "' + lc + '" ' + (ord ? 'ordinal' : 'cardinal') | ||
+ ' rule self-test failed for v = ' | ||
+ (typeof x == 'string' ? '"' + x + '"' : x) | ||
+ ' (was "' + r + '", expected "' + k + '")' | ||
); | ||
return false; | ||
} | ||
return true; | ||
}; | ||
for (var k in tests) { | ||
tests[k].forEach(function(v) { | ||
if (fn(v) != k) _e(v); | ||
else if (!/\.0+$/.test(v) && fn(Number(v)) != k) _e(Number(v)); | ||
}); | ||
for (var t in tests) { | ||
var ord = (t == 'ordinal'); | ||
for (var k in tests[t]) { | ||
tests[t][k].forEach(function(v) { | ||
if (_test(k, v, ord) && !/\.0+$/.test(v)) _test(k, Number(v), ord); | ||
}); | ||
} | ||
} | ||
@@ -112,3 +122,3 @@ return ok; | ||
var _require = (typeof require == 'function') ? require : function(url) { | ||
if (!cldr && Plurals.src_url) url = Plurals.src_url.replace(/[^\/]*$/, url); | ||
if (Plurals.src_url && (url[0] == '.')) url = Plurals.src_url.replace(/[^\/]*$/, url); | ||
var xhr = new XMLHttpRequest(); | ||
@@ -119,25 +129,64 @@ xhr.open('get', url, false); | ||
}; | ||
if (!cldr) cldr = _require('./data/unicode-cldr-plural-rules.json'); | ||
else if (typeof cldr == 'string') cldr = _require(cldr); | ||
Plurals.rules = cldr && cldr['supplemental']['plurals-type-cardinal']; | ||
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']; | ||
} | ||
} | ||
}; | ||
Plurals.build = function(lc, opt) { | ||
var fn, fn_str, lines = [], symbols = {}, tests = {}; | ||
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 (!Plurals.rules) Plurals.set_rules(); | ||
if (!Plurals.rules[lc]) { if (!opt['quiet']) console.error('ERROR: locale "' + lc + '" not found'); return null; } | ||
for (var r in Plurals.rules[lc]) { | ||
var key = r.replace('pluralRule-count-', ''), | ||
parts = Plurals.rules[lc][r].split(/@\w*/), | ||
cond = parts.shift().trim(); | ||
if (cond) lines.push('if (' + parse(cond, symbols) + ') return \'' + key + '\';'); | ||
tests[key] = test_values(parts.join(' ')); | ||
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) + ';')); | ||
} | ||
} | ||
lines.unshift(vars(symbols)); | ||
lines.push('return \'other\';'); | ||
fn = new Function('n', lines.join('\n').trim()); | ||
if (!opt['no_tests'] && !test(fn, tests, opt)) return null; | ||
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 = 'function(n) {\n ' + lines.join('\n ').trim() + '\n}'; | ||
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 '); | ||
@@ -144,0 +193,0 @@ return fn_str; |
{ | ||
"name": "make-plural", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "Translates Unicode CLDR pluralization rules to executable JavaScript", | ||
@@ -5,0 +5,0 @@ "keywords": ["unicode", "cldr", "i18n", "internationalization", "pluralization"], |
115
README.md
@@ -25,18 +25,12 @@ make-plural | ||
> console.log( Plurals.build('fr') ) | ||
function(n) { | ||
if (n >= 0 && n < 2) return 'one'; | ||
return 'other'; | ||
} | ||
> console.log( Plurals.build('sk') ) | ||
function(n) { | ||
> console.log(Plurals.build('sk')) // Slovak | ||
function(n,ord) { | ||
var s = String(n).split('.'), i = s[0], v0 = !s[1]; | ||
if (n == 1 && v0) return 'one'; | ||
if ((i >= 2 && i <= 4) && v0) return 'few'; | ||
if (!v0) return 'many'; | ||
return 'other'; | ||
return (n == 1 && v0) ? 'one' | ||
: ((i >= 2 && i <= 4) && v0) ? 'few' | ||
: (!v0) ? 'many' | ||
: 'other'; | ||
} | ||
> sk = Plurals.build('sk', {'return_function':1}) | ||
> sk = Plurals.build('sk', { 'return_function':1 }) | ||
[Function] | ||
@@ -55,2 +49,22 @@ | ||
'other' | ||
> console.log(Plurals.build('en', { 'ordinals':1 })) // English | ||
function(n,ord) { | ||
var s = String(n).split('.'), v0 = !s[1], t0 = Number(s[0]) == n, | ||
n10 = t0 && s[0].substr(-1), n100 = t0 && s[0].substr(-2); | ||
if (ord) return (n10 == 1 && n100 != 11) ? 'one' | ||
: (n10 == 2 && n100 != 12) ? 'two' | ||
: (n10 == 3 && n100 != 13) ? 'few' | ||
: 'other'; | ||
return (n == 1 && v0) ? 'one' : 'other'; | ||
} | ||
> en = Plurals.build('en', { 'ordinals':1, 'return_function':1 }) | ||
[Function] | ||
> en(2) | ||
'other' | ||
> en(2, true) | ||
'two' | ||
``` | ||
@@ -63,6 +77,6 @@ | ||
<script> | ||
console.log( Plurals.build('sk') ); | ||
var sk = Plurals.build('sk', {'return_function':1}); | ||
console.log( '1: ' + sk(1) + ', 3.0: ' + sk(3.0) + | ||
', "1.0": ' + sk('1.0') + ', "0": ' + sk('0') ); | ||
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')); | ||
</script> | ||
@@ -72,8 +86,9 @@ ``` | ||
``` | ||
function(n) { | ||
function(n,ord) { | ||
var s = String(n).split('.'), i = s[0], v0 = !s[1]; | ||
if (n == 1 && v0) return 'one'; | ||
if ((i >= 2 && i <= 4) && v0) return 'few'; | ||
if (!v0) return 'many'; | ||
return 'other'; | ||
if (ord) return 'other'; | ||
return (n == 1 && v0) ? 'one' | ||
: ((i >= 2 && i <= 4) && v0) ? 'few' | ||
: (!v0) ? 'many' | ||
: 'other'; | ||
} | ||
@@ -84,8 +99,34 @@ | ||
If `request()` isn't available, the CLDR rules are fetched automatically by the | ||
first call to `Plurals.build()` using a synchronous `XMLHttpRequest` for the | ||
JSON file at the location `data/unicode-cldr-plural-rules.json` relative to the | ||
given `path/to/make-plural.js`. If that doesn't work for you, you should call | ||
If `request()` isn't available, the CLDR rules are fetched automatically when | ||
required using synchronous `XMLHttpRequest` calls for the JSON files at the | ||
default locations. If that doesn't work for you, you should call | ||
`Plurals.set_rules(cldr)` before calling `Plurals.build()`. | ||
## Usage: CLI | ||
```sh | ||
$ ./bin/make-plural | ||
Locales verified ok: | ||
af ak am ar asa ast az be bem bez bg bh bm bn bo br brx bs ca cgg chr ckb | ||
cs cy da de dsb dv dz ee el en eo es et eu fa ff fi fil fo fr fur fy ga | ||
gd gl gsw gu guw gv ha haw he hi hr hsb hu hy id ig ii in is it iu iw ja | ||
jbo jgo ji jmc jv jw ka kab kaj kcg kde kea kk kkj kl km kn ko ks ksb ksh | ||
ku kw ky lag lb lg lkt ln lo lt lv mas mg mgo mk ml mn mo mr ms mt my nah | ||
naq nb nd ne nl nn nnh no nqo nr nso ny nyn om or os pa pap pl prg ps pt | ||
pt-PT rm ro rof root ru rwk sah saq se seh ses sg sh shi si sk sl sma smi | ||
smj smn sms sn so sq sr ss ssy st sv sw syr ta te teo th ti tig tk tl tn | ||
to tr ts tzm ug uk ur uz ve vi vo vun wa wae wo xh xog yi yo zh zu | ||
$ ./bin/make-plural fr | ||
function(n,ord) { | ||
if (ord) return (n == 1) ? 'one' : 'other'; | ||
return (n >= 0 && n < 2) ? 'one' : 'other'; | ||
} | ||
$ ./bin/make-plural fr 1.5 | ||
one | ||
``` | ||
## Methods | ||
@@ -95,8 +136,12 @@ | ||
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`. The | ||
optional `opt` object may contain the following members, each of which is | ||
assumed as false by default: | ||
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 | ||
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.) | ||
are not included | ||
* `no_tests` — if true, the generated function is not verified by testing it | ||
with each of the example values included in the CLDR rules | ||
* `ordinals` — if true, rules for ordinal values (1st, 2nd, etc.) are included | ||
* `quiet` — if true, no output is reported to `console.error` on error | ||
@@ -106,7 +151,10 @@ * `return_function` — if true, `build` returns an executable function of `n` | ||
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 | ||
will return the ordinal rather than cardinal category applicable to `n` in | ||
locale `lc`. | ||
### 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/25/supplemental/plurals.json). | ||
By default, the included rules in `data/unicode-cldr-plural-rules.json` are | ||
used. | ||
file formatted like [this](http://www.unicode.org/repos/cldr-aux/json/26/supplemental/plurals.json). | ||
@@ -117,2 +165,5 @@ If called within a context where `request()` isn't available and `cldr` is a | ||
By default, `build()` will call `set_rules(cldr)` when required, using the | ||
rules included in `data/`: `unicode-cldr-plural-rules.json` and | ||
`unicode-cldr-ordinal-rules.json`. | ||
@@ -119,0 +170,0 @@ ## Dependencies |
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
91987
6
1340
167