Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions packages/compiler/src/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export class Compiler {
.replace(/^[ ,]+|[ ,…]+$/g, '')
.replace(/(0\.[0-9])~(1\.[1-9])/g, '$1 1.0 $2')
.split(/[ ,~…]+/)
.filter(n => !n.includes('c'))
}

constructor(lc, { cardinals, ordinals } = Compiler) {
Expand Down Expand Up @@ -66,9 +65,11 @@ export class Compiler {
)
.replace(/{\s*return\s+([^{}]*);\s*}$/, '$1')
this.test = () => {
const { cardinals, ordinals } = this.types
const ordArg = Boolean(ordinals && cardinals)
for (const type of ['cardinal', 'ordinal'])
for (const [cat, values] of Object.entries(this.examples[type]))
testCat(this.lc, type, cat, values, this.fn)
testCat(this.lc, this.fn, ordArg, type, cat, values)
}
}
return this.fn
Expand Down Expand Up @@ -111,10 +112,10 @@ export class Compiler {
body = ` return ${this.buildBody(pt, true)};`
}

const args = this.parser.args(ordinals && cardinals)
const vars = this.parser.vars()
if (vars) body = ` ${vars};\n${body}`

const args = ordinals && cardinals ? 'n, ord' : 'n'
return new Function(args, body) // eslint-disable-line no-new-func
}
}
18 changes: 12 additions & 6 deletions packages/compiler/src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ export class Parser {
}
return cond
.replace(/([^=\s])([!=%]+)([^=\s])/g, '$1 $2 $3')
.replace(
/[ce] (!?)= ([0-9]+)(?:\.\.[0-9]+)?/g,
// assume c & e always have the value 0
(m, noteq, x0) => (!noteq === (x0 === '0') ? 'true' : 'false')
)
.replace(/^true and |^false or | and true$| or false$/g, '')
.replace(/[ce] (!?)= 0(?:\.\.([0-9]+))?/g, (m, noteq, max) => {
this.c = 1
if (max) return noteq ? `c > ${max}` : `c <= ${max}`
else return noteq ? 'c' : '!c'
})
.replace(/([tv]) (!?)= 0/g, (m, sym, noteq) => {
const sn = sym + '0'
this[sn] = 1
Expand Down Expand Up @@ -52,6 +51,13 @@ export class Parser {
.replace(/ = /g, ' == ')
}

args(ord) {
let args = ['n']
if (ord) args.push('ord')
if (this.c) args.push('c')
return args.join(', ')
}

vars() {
let vars = []
if (this.i) vars.push('i = s[0]')
Expand Down
33 changes: 22 additions & 11 deletions packages/compiler/src/tests.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
function errorMsg(lc, n, type, msg) {
const val = JSON.stringify(n)
return `Locale ${lc} ${type} rule self-test failed for ${val} (${msg})`
}
const errorMsg = (lc, n, type, fn, args, msg) => `\
Locale ${lc} ${type} rule self-test failed for ${typeof n} ${n} (${msg})
Function: ${fn.toString()}
Arguments: ${JSON.stringify(args)}`

function testCond(lc, n, type, expResult, fn) {
function testCond(lc, fn, ordArg, n, type, expResult) {
const compact = typeof n === 'string' && n.match(/(.*)c(\d+)$/)
let args = [n]
if (ordArg) args.push(type === 'ordinal')
if (compact) {
const c = Number(compact[2])
args[0] = Number(compact[1]) * Math.pow(10, c)
args.push(c)
}
try {
var r = fn(n, type === 'ordinal')
var r = fn(...args)
} catch (error) {
/* istanbul ignore next: should not happen unless CLDR data is broken */
throw new Error(errorMsg(lc, n, type, error))
throw new Error(errorMsg(lc, n, type, fn, args, error))
}
if (r !== expResult) {
const res = JSON.stringify(r)
const exp = JSON.stringify(expResult)
throw new Error(errorMsg(lc, n, type, `was ${res}, expected ${exp}`))
throw new Error(
errorMsg(lc, n, type, fn, args, `was ${res}, expected ${exp}`)
)
}
}

export function testCat(lc, type, cat, values, fn) {
export function testCat(lc, fn, ordArg, type, cat, values) {
for (const n of values) {
testCond(lc, n, type, cat, fn)
if (!/\.0+$/.test(n)) testCond(lc, Number(n), type, cat, fn)
testCond(lc, fn, ordArg, n, type, cat)
if (!n.includes('c') && !/\.0+$/.test(n))
testCond(lc, fn, ordArg, Number(n), type, cat)
}
}
2 changes: 2 additions & 0 deletions packages/plurals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Each of the endpoints is available as ES modules only.
returning the pluralization category for the input (either a number or a string representation of a number).
`Plurals` functions also accept a second boolean parameter to return
the ordinal (`true`) rather than cardinal (`false`, default) plural category.
For some locales, an additional argument sets the exponent used in compact notation;
currently, this is only used in Romance languages.
Note that `Ordinals` includes a slightly smaller subset of locales than `Cardinals` and `Plurals`,
due to a lack of data in the CLDR.
- `PluralRanges` provides a set of functions similarly keyed by locale code,
Expand Down
16 changes: 8 additions & 8 deletions packages/plurals/cardinals.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ const d = (n) => {
return n == 1 && v0 ? 'one' : 'other';
};
const e = (n) => 'other';
const f = (n) => {
const f = (n, c) => {
const s = String(n).split('.'), i = s[0], v0 = !s[1], i1000000 = i.slice(-6);
return n == 1 && v0 ? 'one'
: i != 0 && i1000000 == 0 && v0 ? 'many'
: !c && i != 0 && i1000000 == 0 && v0 || c > 5 ? 'many'
: 'other';
};
const g = (n) => n == 1 ? 'one'
Expand Down Expand Up @@ -120,10 +120,10 @@ export const ee = a;
export const el = a;
export const en = d;
export const eo = a;
export const es = (n) => {
export const es = (n, c) => {
const s = String(n).split('.'), i = s[0], v0 = !s[1], i1000000 = i.slice(-6);
return n == 1 ? 'one'
: i != 0 && i1000000 == 0 && v0 ? 'many'
: !c && i != 0 && i1000000 == 0 && v0 || c > 5 ? 'many'
: 'other';
};
export const et = d;
Expand All @@ -136,10 +136,10 @@ export const fil = (n) => {
return v0 && (i == 1 || i == 2 || i == 3) || v0 && i10 != 4 && i10 != 6 && i10 != 9 || !v0 && f10 != 4 && f10 != 6 && f10 != 9 ? 'one' : 'other';
};
export const fo = a;
export const fr = (n) => {
export const fr = (n, c) => {
const s = String(n).split('.'), i = s[0], v0 = !s[1], i1000000 = i.slice(-6);
return n >= 0 && n < 2 ? 'one'
: i != 0 && i1000000 == 0 && v0 ? 'many'
: !c && i != 0 && i1000000 == 0 && v0 || c > 5 ? 'many'
: 'other';
};
export const fur = a;
Expand Down Expand Up @@ -331,10 +331,10 @@ export const prg = (n) => {
: 'other';
};
export const ps = a;
export const pt = (n) => {
export const pt = (n, c) => {
const s = String(n).split('.'), i = s[0], v0 = !s[1], i1000000 = i.slice(-6);
return (i == 0 || i == 1) ? 'one'
: i != 0 && i1000000 == 0 && v0 ? 'many'
: !c && i != 0 && i1000000 == 0 && v0 || c > 5 ? 'many'
: 'other';
};
export const pt_PT = f;
Expand Down
Loading