mirror of
https://github.com/rxliuli/apps.apple.com.git
synced 2025-11-09 20:00:34 +00:00
init commit
This commit is contained in:
458
shared/localization/node_modules/make-plural/cardinals.mjs
generated
vendored
Normal file
458
shared/localization/node_modules/make-plural/cardinals.mjs
generated
vendored
Normal file
@@ -0,0 +1,458 @@
|
||||
function a(n) {
|
||||
return n == 1 ? 'one' : 'other';
|
||||
}
|
||||
function b(n) {
|
||||
return (n == 0 || n == 1) ? 'one' : 'other';
|
||||
}
|
||||
function c(n) {
|
||||
return n >= 0 && n <= 1 ? 'one' : 'other';
|
||||
}
|
||||
function d(n) {
|
||||
var s = String(n).split('.'), v0 = !s[1];
|
||||
return n == 1 && v0 ? 'one' : 'other';
|
||||
}
|
||||
function e(n) {
|
||||
return 'other';
|
||||
}
|
||||
function f(n) {
|
||||
return n == 1 ? 'one'
|
||||
: n == 2 ? 'two'
|
||||
: 'other';
|
||||
}
|
||||
|
||||
export const _in = e;
|
||||
export const af = a;
|
||||
export const ak = b;
|
||||
export const am = c;
|
||||
export const an = a;
|
||||
export function ar(n) {
|
||||
var s = String(n).split('.'), t0 = Number(s[0]) == n, n100 = t0 && s[0].slice(-2);
|
||||
return n == 0 ? 'zero'
|
||||
: n == 1 ? 'one'
|
||||
: n == 2 ? 'two'
|
||||
: (n100 >= 3 && n100 <= 10) ? 'few'
|
||||
: (n100 >= 11 && n100 <= 99) ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export function ars(n) {
|
||||
var s = String(n).split('.'), t0 = Number(s[0]) == n, n100 = t0 && s[0].slice(-2);
|
||||
return n == 0 ? 'zero'
|
||||
: n == 1 ? 'one'
|
||||
: n == 2 ? 'two'
|
||||
: (n100 >= 3 && n100 <= 10) ? 'few'
|
||||
: (n100 >= 11 && n100 <= 99) ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export const as = c;
|
||||
export const asa = a;
|
||||
export const ast = d;
|
||||
export const az = a;
|
||||
export function be(n) {
|
||||
var s = String(n).split('.'), t0 = Number(s[0]) == n, n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2);
|
||||
return n10 == 1 && n100 != 11 ? 'one'
|
||||
: (n10 >= 2 && n10 <= 4) && (n100 < 12 || n100 > 14) ? 'few'
|
||||
: t0 && n10 == 0 || (n10 >= 5 && n10 <= 9) || (n100 >= 11 && n100 <= 14) ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export const bem = a;
|
||||
export const bez = a;
|
||||
export const bg = a;
|
||||
export const bho = b;
|
||||
export const bm = e;
|
||||
export const bn = c;
|
||||
export const bo = e;
|
||||
export function br(n) {
|
||||
var s = String(n).split('.'), t0 = Number(s[0]) == n, n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2), n1000000 = t0 && s[0].slice(-6);
|
||||
return n10 == 1 && n100 != 11 && n100 != 71 && n100 != 91 ? 'one'
|
||||
: n10 == 2 && n100 != 12 && n100 != 72 && n100 != 92 ? 'two'
|
||||
: ((n10 == 3 || n10 == 4) || n10 == 9) && (n100 < 10 || n100 > 19) && (n100 < 70 || n100 > 79) && (n100 < 90 || n100 > 99) ? 'few'
|
||||
: n != 0 && t0 && n1000000 == 0 ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export const brx = a;
|
||||
export function bs(n) {
|
||||
var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1], i10 = i.slice(-1), i100 = i.slice(-2), f10 = f.slice(-1), f100 = f.slice(-2);
|
||||
return v0 && i10 == 1 && i100 != 11 || f10 == 1 && f100 != 11 ? 'one'
|
||||
: v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 || i100 > 14) || (f10 >= 2 && f10 <= 4) && (f100 < 12 || f100 > 14) ? 'few'
|
||||
: 'other';
|
||||
}
|
||||
export const ca = d;
|
||||
export const ce = a;
|
||||
export function ceb(n) {
|
||||
var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1], i10 = i.slice(-1), f10 = f.slice(-1);
|
||||
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 cgg = a;
|
||||
export const chr = a;
|
||||
export const ckb = a;
|
||||
export function cs(n) {
|
||||
var s = String(n).split('.'), i = s[0], v0 = !s[1];
|
||||
return n == 1 && v0 ? 'one'
|
||||
: (i >= 2 && i <= 4) && v0 ? 'few'
|
||||
: !v0 ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export function cy(n) {
|
||||
return n == 0 ? 'zero'
|
||||
: n == 1 ? 'one'
|
||||
: n == 2 ? 'two'
|
||||
: n == 3 ? 'few'
|
||||
: n == 6 ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export function da(n) {
|
||||
var s = String(n).split('.'), i = s[0], t0 = Number(s[0]) == n;
|
||||
return n == 1 || !t0 && (i == 0 || i == 1) ? 'one' : 'other';
|
||||
}
|
||||
export const de = d;
|
||||
export function dsb(n) {
|
||||
var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1], i100 = i.slice(-2), f100 = f.slice(-2);
|
||||
return v0 && i100 == 1 || f100 == 1 ? 'one'
|
||||
: v0 && i100 == 2 || f100 == 2 ? 'two'
|
||||
: v0 && (i100 == 3 || i100 == 4) || (f100 == 3 || f100 == 4) ? 'few'
|
||||
: 'other';
|
||||
}
|
||||
export const dv = a;
|
||||
export const dz = e;
|
||||
export const ee = a;
|
||||
export const el = a;
|
||||
export const en = d;
|
||||
export const eo = a;
|
||||
export const es = a;
|
||||
export const et = d;
|
||||
export const eu = a;
|
||||
export const fa = c;
|
||||
export function ff(n) {
|
||||
return n >= 0 && n < 2 ? 'one' : 'other';
|
||||
}
|
||||
export const fi = d;
|
||||
export function fil(n) {
|
||||
var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1], i10 = i.slice(-1), f10 = f.slice(-1);
|
||||
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 function fr(n) {
|
||||
return n >= 0 && n < 2 ? 'one' : 'other';
|
||||
}
|
||||
export const fur = a;
|
||||
export const fy = d;
|
||||
export function ga(n) {
|
||||
var s = String(n).split('.'), t0 = Number(s[0]) == n;
|
||||
return n == 1 ? 'one'
|
||||
: n == 2 ? 'two'
|
||||
: (t0 && n >= 3 && n <= 6) ? 'few'
|
||||
: (t0 && n >= 7 && n <= 10) ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export function gd(n) {
|
||||
var s = String(n).split('.'), t0 = Number(s[0]) == n;
|
||||
return (n == 1 || n == 11) ? 'one'
|
||||
: (n == 2 || n == 12) ? 'two'
|
||||
: ((t0 && n >= 3 && n <= 10) || (t0 && n >= 13 && n <= 19)) ? 'few'
|
||||
: 'other';
|
||||
}
|
||||
export const gl = d;
|
||||
export const gsw = a;
|
||||
export const gu = c;
|
||||
export const guw = b;
|
||||
export function gv(n) {
|
||||
var s = String(n).split('.'), i = s[0], v0 = !s[1], i10 = i.slice(-1), i100 = i.slice(-2);
|
||||
return v0 && i10 == 1 ? 'one'
|
||||
: v0 && i10 == 2 ? 'two'
|
||||
: v0 && (i100 == 0 || i100 == 20 || i100 == 40 || i100 == 60 || i100 == 80) ? 'few'
|
||||
: !v0 ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export const ha = a;
|
||||
export const haw = a;
|
||||
export function he(n) {
|
||||
var s = String(n).split('.'), i = s[0], v0 = !s[1], t0 = Number(s[0]) == n, n10 = t0 && s[0].slice(-1);
|
||||
return n == 1 && v0 ? 'one'
|
||||
: i == 2 && v0 ? 'two'
|
||||
: v0 && (n < 0 || n > 10) && t0 && n10 == 0 ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export const hi = c;
|
||||
export function hr(n) {
|
||||
var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1], i10 = i.slice(-1), i100 = i.slice(-2), f10 = f.slice(-1), f100 = f.slice(-2);
|
||||
return v0 && i10 == 1 && i100 != 11 || f10 == 1 && f100 != 11 ? 'one'
|
||||
: v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 || i100 > 14) || (f10 >= 2 && f10 <= 4) && (f100 < 12 || f100 > 14) ? 'few'
|
||||
: 'other';
|
||||
}
|
||||
export function hsb(n) {
|
||||
var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1], i100 = i.slice(-2), f100 = f.slice(-2);
|
||||
return v0 && i100 == 1 || f100 == 1 ? 'one'
|
||||
: v0 && i100 == 2 || f100 == 2 ? 'two'
|
||||
: v0 && (i100 == 3 || i100 == 4) || (f100 == 3 || f100 == 4) ? 'few'
|
||||
: 'other';
|
||||
}
|
||||
export const hu = a;
|
||||
export function hy(n) {
|
||||
return n >= 0 && n < 2 ? 'one' : 'other';
|
||||
}
|
||||
export const ia = d;
|
||||
export const id = e;
|
||||
export const ig = e;
|
||||
export const ii = e;
|
||||
export const io = d;
|
||||
export function is(n) {
|
||||
var s = String(n).split('.'), i = s[0], t0 = Number(s[0]) == n, i10 = i.slice(-1), i100 = i.slice(-2);
|
||||
return t0 && i10 == 1 && i100 != 11 || !t0 ? 'one' : 'other';
|
||||
}
|
||||
export const it = d;
|
||||
export const iu = f;
|
||||
export function iw(n) {
|
||||
var s = String(n).split('.'), i = s[0], v0 = !s[1], t0 = Number(s[0]) == n, n10 = t0 && s[0].slice(-1);
|
||||
return n == 1 && v0 ? 'one'
|
||||
: i == 2 && v0 ? 'two'
|
||||
: v0 && (n < 0 || n > 10) && t0 && n10 == 0 ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export const ja = e;
|
||||
export const jbo = e;
|
||||
export const jgo = a;
|
||||
export const ji = d;
|
||||
export const jmc = a;
|
||||
export const jv = e;
|
||||
export const jw = e;
|
||||
export const ka = a;
|
||||
export function kab(n) {
|
||||
return n >= 0 && n < 2 ? 'one' : 'other';
|
||||
}
|
||||
export const kaj = a;
|
||||
export const kcg = a;
|
||||
export const kde = e;
|
||||
export const kea = e;
|
||||
export const kk = a;
|
||||
export const kkj = a;
|
||||
export const kl = a;
|
||||
export const km = e;
|
||||
export const kn = c;
|
||||
export const ko = e;
|
||||
export const ks = a;
|
||||
export const ksb = a;
|
||||
export function ksh(n) {
|
||||
return n == 0 ? 'zero'
|
||||
: n == 1 ? 'one'
|
||||
: 'other';
|
||||
}
|
||||
export const ku = a;
|
||||
export function kw(n) {
|
||||
var s = String(n).split('.'), t0 = Number(s[0]) == n, n100 = t0 && s[0].slice(-2), n1000 = t0 && s[0].slice(-3), n100000 = t0 && s[0].slice(-5), n1000000 = t0 && s[0].slice(-6);
|
||||
return n == 0 ? 'zero'
|
||||
: n == 1 ? 'one'
|
||||
: (n100 == 2 || n100 == 22 || n100 == 42 || n100 == 62 || n100 == 82) || t0 && n1000 == 0 && ((n100000 >= 1000 && n100000 <= 20000) || n100000 == 40000 || n100000 == 60000 || n100000 == 80000) || n != 0 && n1000000 == 100000 ? 'two'
|
||||
: (n100 == 3 || n100 == 23 || n100 == 43 || n100 == 63 || n100 == 83) ? 'few'
|
||||
: n != 1 && (n100 == 1 || n100 == 21 || n100 == 41 || n100 == 61 || n100 == 81) ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export const ky = a;
|
||||
export function lag(n) {
|
||||
var s = String(n).split('.'), i = s[0];
|
||||
return n == 0 ? 'zero'
|
||||
: (i == 0 || i == 1) && n != 0 ? 'one'
|
||||
: 'other';
|
||||
}
|
||||
export const lb = a;
|
||||
export const lg = a;
|
||||
export const lkt = e;
|
||||
export const ln = b;
|
||||
export const lo = e;
|
||||
export function lt(n) {
|
||||
var s = String(n).split('.'), f = s[1] || '', t0 = Number(s[0]) == n, n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2);
|
||||
return n10 == 1 && (n100 < 11 || n100 > 19) ? 'one'
|
||||
: (n10 >= 2 && n10 <= 9) && (n100 < 11 || n100 > 19) ? 'few'
|
||||
: f != 0 ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export function lv(n) {
|
||||
var s = String(n).split('.'), f = s[1] || '', v = f.length, t0 = Number(s[0]) == n, n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2), f100 = f.slice(-2), f10 = f.slice(-1);
|
||||
return t0 && n10 == 0 || (n100 >= 11 && n100 <= 19) || v == 2 && (f100 >= 11 && f100 <= 19) ? 'zero'
|
||||
: n10 == 1 && n100 != 11 || v == 2 && f10 == 1 && f100 != 11 || v != 2 && f10 == 1 ? 'one'
|
||||
: 'other';
|
||||
}
|
||||
export const mas = a;
|
||||
export const mg = b;
|
||||
export const mgo = a;
|
||||
export function mk(n) {
|
||||
var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1], i10 = i.slice(-1), i100 = i.slice(-2), f10 = f.slice(-1), f100 = f.slice(-2);
|
||||
return v0 && i10 == 1 && i100 != 11 || f10 == 1 && f100 != 11 ? 'one' : 'other';
|
||||
}
|
||||
export const ml = a;
|
||||
export const mn = a;
|
||||
export function mo(n) {
|
||||
var s = String(n).split('.'), v0 = !s[1], t0 = Number(s[0]) == n, n100 = t0 && s[0].slice(-2);
|
||||
return n == 1 && v0 ? 'one'
|
||||
: !v0 || n == 0 || (n100 >= 2 && n100 <= 19) ? 'few'
|
||||
: 'other';
|
||||
}
|
||||
export const mr = a;
|
||||
export const ms = e;
|
||||
export function mt(n) {
|
||||
var s = String(n).split('.'), t0 = Number(s[0]) == n, n100 = t0 && s[0].slice(-2);
|
||||
return n == 1 ? 'one'
|
||||
: n == 0 || (n100 >= 2 && n100 <= 10) ? 'few'
|
||||
: (n100 >= 11 && n100 <= 19) ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export const my = e;
|
||||
export const nah = a;
|
||||
export const naq = f;
|
||||
export const nb = a;
|
||||
export const nd = a;
|
||||
export const ne = a;
|
||||
export const nl = d;
|
||||
export const nn = a;
|
||||
export const nnh = a;
|
||||
export const no = a;
|
||||
export const nqo = e;
|
||||
export const nr = a;
|
||||
export const nso = b;
|
||||
export const ny = a;
|
||||
export const nyn = a;
|
||||
export const om = a;
|
||||
export const or = a;
|
||||
export const os = a;
|
||||
export const osa = e;
|
||||
export const pa = b;
|
||||
export const pap = a;
|
||||
export function pl(n) {
|
||||
var s = String(n).split('.'), i = s[0], v0 = !s[1], i10 = i.slice(-1), i100 = i.slice(-2);
|
||||
return n == 1 && v0 ? 'one'
|
||||
: v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 || i100 > 14) ? 'few'
|
||||
: v0 && i != 1 && (i10 == 0 || i10 == 1) || v0 && (i10 >= 5 && i10 <= 9) || v0 && (i100 >= 12 && i100 <= 14) ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export function prg(n) {
|
||||
var s = String(n).split('.'), f = s[1] || '', v = f.length, t0 = Number(s[0]) == n, n10 = t0 && s[0].slice(-1), n100 = t0 && s[0].slice(-2), f100 = f.slice(-2), f10 = f.slice(-1);
|
||||
return t0 && n10 == 0 || (n100 >= 11 && n100 <= 19) || v == 2 && (f100 >= 11 && f100 <= 19) ? 'zero'
|
||||
: n10 == 1 && n100 != 11 || v == 2 && f10 == 1 && f100 != 11 || v != 2 && f10 == 1 ? 'one'
|
||||
: 'other';
|
||||
}
|
||||
export const ps = a;
|
||||
export function pt(n) {
|
||||
var s = String(n).split('.'), i = s[0];
|
||||
return (i == 0 || i == 1) ? 'one' : 'other';
|
||||
}
|
||||
export const pt_PT = d;
|
||||
export const rm = a;
|
||||
export function ro(n) {
|
||||
var s = String(n).split('.'), v0 = !s[1], t0 = Number(s[0]) == n, n100 = t0 && s[0].slice(-2);
|
||||
return n == 1 && v0 ? 'one'
|
||||
: !v0 || n == 0 || (n100 >= 2 && n100 <= 19) ? 'few'
|
||||
: 'other';
|
||||
}
|
||||
export const rof = a;
|
||||
export const root = e;
|
||||
export function ru(n) {
|
||||
var s = String(n).split('.'), i = s[0], v0 = !s[1], i10 = i.slice(-1), i100 = i.slice(-2);
|
||||
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';
|
||||
}
|
||||
export const rwk = a;
|
||||
export const sah = e;
|
||||
export const saq = a;
|
||||
export const sc = d;
|
||||
export const scn = d;
|
||||
export const sd = a;
|
||||
export const sdh = a;
|
||||
export const se = f;
|
||||
export const seh = a;
|
||||
export const ses = e;
|
||||
export const sg = e;
|
||||
export function sh(n) {
|
||||
var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1], i10 = i.slice(-1), i100 = i.slice(-2), f10 = f.slice(-1), f100 = f.slice(-2);
|
||||
return v0 && i10 == 1 && i100 != 11 || f10 == 1 && f100 != 11 ? 'one'
|
||||
: v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 || i100 > 14) || (f10 >= 2 && f10 <= 4) && (f100 < 12 || f100 > 14) ? 'few'
|
||||
: 'other';
|
||||
}
|
||||
export function shi(n) {
|
||||
var s = String(n).split('.'), t0 = Number(s[0]) == n;
|
||||
return n >= 0 && n <= 1 ? 'one'
|
||||
: (t0 && n >= 2 && n <= 10) ? 'few'
|
||||
: 'other';
|
||||
}
|
||||
export function si(n) {
|
||||
var s = String(n).split('.'), i = s[0], f = s[1] || '';
|
||||
return (n == 0 || n == 1) || i == 0 && f == 1 ? 'one' : 'other';
|
||||
}
|
||||
export function sk(n) {
|
||||
var s = String(n).split('.'), i = s[0], v0 = !s[1];
|
||||
return n == 1 && v0 ? 'one'
|
||||
: (i >= 2 && i <= 4) && v0 ? 'few'
|
||||
: !v0 ? 'many'
|
||||
: 'other';
|
||||
}
|
||||
export function sl(n) {
|
||||
var s = String(n).split('.'), i = s[0], v0 = !s[1], i100 = i.slice(-2);
|
||||
return v0 && i100 == 1 ? 'one'
|
||||
: v0 && i100 == 2 ? 'two'
|
||||
: v0 && (i100 == 3 || i100 == 4) || !v0 ? 'few'
|
||||
: 'other';
|
||||
}
|
||||
export const sma = f;
|
||||
export const smi = f;
|
||||
export const smj = f;
|
||||
export const smn = f;
|
||||
export const sms = f;
|
||||
export const sn = a;
|
||||
export const so = a;
|
||||
export const sq = a;
|
||||
export function sr(n) {
|
||||
var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1], i10 = i.slice(-1), i100 = i.slice(-2), f10 = f.slice(-1), f100 = f.slice(-2);
|
||||
return v0 && i10 == 1 && i100 != 11 || f10 == 1 && f100 != 11 ? 'one'
|
||||
: v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 || i100 > 14) || (f10 >= 2 && f10 <= 4) && (f100 < 12 || f100 > 14) ? 'few'
|
||||
: 'other';
|
||||
}
|
||||
export const ss = a;
|
||||
export const ssy = a;
|
||||
export const st = a;
|
||||
export const su = e;
|
||||
export const sv = d;
|
||||
export const sw = d;
|
||||
export const syr = a;
|
||||
export const ta = a;
|
||||
export const te = a;
|
||||
export const teo = a;
|
||||
export const th = e;
|
||||
export const ti = b;
|
||||
export const tig = a;
|
||||
export const tk = a;
|
||||
export function tl(n) {
|
||||
var s = String(n).split('.'), i = s[0], f = s[1] || '', v0 = !s[1], i10 = i.slice(-1), f10 = f.slice(-1);
|
||||
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 tn = a;
|
||||
export const to = e;
|
||||
export const tr = a;
|
||||
export const ts = a;
|
||||
export function tzm(n) {
|
||||
var s = String(n).split('.'), t0 = Number(s[0]) == n;
|
||||
return (n == 0 || n == 1) || (t0 && n >= 11 && n <= 99) ? 'one' : 'other';
|
||||
}
|
||||
export const ug = a;
|
||||
export function uk(n) {
|
||||
var s = String(n).split('.'), i = s[0], v0 = !s[1], i10 = i.slice(-1), i100 = i.slice(-2);
|
||||
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';
|
||||
}
|
||||
export const ur = d;
|
||||
export const uz = a;
|
||||
export const ve = a;
|
||||
export const vi = e;
|
||||
export const vo = a;
|
||||
export const vun = a;
|
||||
export const wa = b;
|
||||
export const wae = a;
|
||||
export const wo = e;
|
||||
export const xh = a;
|
||||
export const xog = a;
|
||||
export const yi = d;
|
||||
export const yo = e;
|
||||
export const yue = e;
|
||||
export const zh = e;
|
||||
export const zu = c;
|
||||
78
shared/localization/src/getLocAttributes.ts
Normal file
78
shared/localization/src/getLocAttributes.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { getPageDir } from './getPageDir';
|
||||
|
||||
/**
|
||||
* Checks if a string contains language script
|
||||
* ex. "zh-Hant-HK", "zh-Hant-TW", "zh-Hans-CN"
|
||||
* @param {string} locale
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const hasSupportedLanguageScript = (locale: string): boolean => {
|
||||
const SUPPORTED_SCRIPTS = ['-hans-', '-hant-'];
|
||||
|
||||
const formattedLocale = locale.toLowerCase();
|
||||
return SUPPORTED_SCRIPTS.some((item) => formattedLocale.includes(item));
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* BCP47 https://www.w3.org/International/articles/language-tags/
|
||||
*
|
||||
* @param {string} language https://en.wikipedia.org/wiki/ISO_639
|
||||
* @param {string} region https://en.wikipedia.org/wiki/ISO_3166-1
|
||||
* @param {string} script https://en.wikipedia.org/wiki/ISO_15924
|
||||
|
||||
*/
|
||||
const buildBcp47String = (
|
||||
language: string,
|
||||
region: string,
|
||||
script?: string,
|
||||
): string => {
|
||||
let capitalizeScript: string | null = null;
|
||||
if (script) {
|
||||
capitalizeScript =
|
||||
script[0].toUpperCase() + script.substring(1).toLowerCase();
|
||||
}
|
||||
let bcp47Arr = [
|
||||
language.toLowerCase(),
|
||||
capitalizeScript,
|
||||
region.toUpperCase(),
|
||||
];
|
||||
|
||||
return bcp47Arr.filter((item) => item !== null).join('-');
|
||||
};
|
||||
|
||||
/**
|
||||
* @description
|
||||
* get values to be used in <html> tag lang and dir attributes.
|
||||
*
|
||||
* @param {string} locale
|
||||
* @returns { { dir: 'rtl' | 'ltr', lang: string }} HTML dir + lang values
|
||||
*/
|
||||
|
||||
export function getLocAttributes(locale: string): {
|
||||
dir: 'rtl' | 'ltr';
|
||||
lang: string;
|
||||
} {
|
||||
const pageDir = getPageDir(locale);
|
||||
let bcp47 = locale;
|
||||
|
||||
const localeStrings = locale.split('-');
|
||||
|
||||
// region index in array
|
||||
const regionIndex = hasSupportedLanguageScript(locale) ? 2 : 1;
|
||||
|
||||
const language = localeStrings[0];
|
||||
const script = hasSupportedLanguageScript(locale)
|
||||
? localeStrings[1]
|
||||
: undefined;
|
||||
const region = localeStrings[regionIndex];
|
||||
|
||||
if (language && region) {
|
||||
bcp47 = buildBcp47String(language, region, script);
|
||||
}
|
||||
|
||||
return {
|
||||
dir: pageDir,
|
||||
lang: bcp47,
|
||||
};
|
||||
}
|
||||
40
shared/localization/src/getPageDir.ts
Normal file
40
shared/localization/src/getPageDir.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* TODO: rdar://73010072 (Make localization utils its own package)
|
||||
* Copied from:
|
||||
* https://github.pie.apple.com/amp-ui/desktop-music-app/blob/main/app/utils/page-dir.js
|
||||
*/
|
||||
|
||||
// these overrides were determined to always show page in RTL, even if the global elements dont contain
|
||||
// an he_il entry
|
||||
// <rdar://problem/49297213> LOC: IW-IL: RTL: Web Preview Pages: The Preview Pages are not RTL.
|
||||
const RTL_LANG_CODES_OVERRIDE = [
|
||||
'he', // hebrew
|
||||
];
|
||||
|
||||
const RTL_LANG_CODES = [
|
||||
'ar', // arabic
|
||||
'he', // hebrew
|
||||
'ku', // kurdish
|
||||
'ur', // urdu
|
||||
'ps', // pashto
|
||||
'yi', // yiddish
|
||||
];
|
||||
|
||||
/**
|
||||
* Determine the page-direction for a given locale
|
||||
*
|
||||
* @param {String} localeCode - A string containing a language code and region code separated by a hyphen.
|
||||
* @param {String|undefined|null} langParam - A language code passed from the `l=` query param.
|
||||
*/
|
||||
export function getPageDir(
|
||||
localeCode: string,
|
||||
langParam: string | undefined | null = null,
|
||||
) {
|
||||
const twoLettersLangCode = localeCode.split('-')[0];
|
||||
const isRTLLang = RTL_LANG_CODES.includes(twoLettersLangCode);
|
||||
const isRTLLangOverride =
|
||||
typeof langParam === 'string' &&
|
||||
RTL_LANG_CODES_OVERRIDE.includes(langParam);
|
||||
|
||||
return isRTLLang || isRTLLangOverride ? 'rtl' : 'ltr';
|
||||
}
|
||||
104
shared/localization/src/i18n.ts
Normal file
104
shared/localization/src/i18n.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import Translator from './translator';
|
||||
import type {
|
||||
Locale,
|
||||
InterpolationOptions,
|
||||
ILocaleJSON,
|
||||
ITranslator,
|
||||
} from './types';
|
||||
import type { Logger } from '@amp/web-apps-logger';
|
||||
|
||||
/** @internal */
|
||||
const formatOptions = (
|
||||
options: InterpolationOptions | number,
|
||||
): InterpolationOptions =>
|
||||
typeof options === 'number' ? { count: options } : options;
|
||||
|
||||
/**
|
||||
*
|
||||
* Adapter class to expose expected LOC interface
|
||||
* @category Localization
|
||||
*/
|
||||
export class I18N {
|
||||
private readonly log: Logger;
|
||||
private readonly locale: Locale;
|
||||
private readonly translator: ITranslator;
|
||||
private readonly keys: ILocaleJSON;
|
||||
private readonly alwaysShowScreamers: boolean;
|
||||
|
||||
/**
|
||||
* builds a new I18N class
|
||||
* @param locale - the locale to use default:`'en-us'`
|
||||
* @param translation - translation object default: `{}`
|
||||
* @param alwaysShowScreamers - optional boolean that is set upstream
|
||||
* by a FeatureKit feature flag. This makes it so the LOC keys themselves are
|
||||
* printed to the DOM, rather than their translations, which is helpful for QA testing
|
||||
*/
|
||||
constructor(
|
||||
log: Logger,
|
||||
locale: Locale = 'en-us',
|
||||
translation: ILocaleJSON = {},
|
||||
alwaysShowScreamers: boolean = false,
|
||||
) {
|
||||
this.log = log;
|
||||
this.locale = locale;
|
||||
this.translator = new Translator(locale, translation, {
|
||||
onMissingKeyFn: (key: string): string => {
|
||||
log.warn('key missing:', key);
|
||||
return `**${key}**`;
|
||||
},
|
||||
onMissingInterpolationFn: (key: string, interpolation: string) => {
|
||||
log.warn(`key ${key} missing interpolation:`, interpolation);
|
||||
},
|
||||
});
|
||||
this.keys = translation;
|
||||
this.alwaysShowScreamers = alwaysShowScreamers;
|
||||
}
|
||||
|
||||
get currentLocale(): Locale {
|
||||
return this.locale;
|
||||
}
|
||||
|
||||
get currentKeys(): ILocaleJSON {
|
||||
return this.keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets non-interpolated string.
|
||||
* @category Localization
|
||||
* @param key key to lookup in the translation.json
|
||||
* @returns an uninterpolated string value
|
||||
*/
|
||||
getUninterpolatedString(key: string): string {
|
||||
if (this.alwaysShowScreamers) {
|
||||
return key;
|
||||
} else {
|
||||
return this.translator.getUninterpolatedString(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for fetching translation based on key.
|
||||
*
|
||||
* If alwaysShowScreamers is true, return the key itself for QA testing purposes
|
||||
* (our app tends to call into this method within Svelte templates)
|
||||
*
|
||||
* @category Localization
|
||||
* @param key key to lookup in the translation.json
|
||||
* @param options options for translations
|
||||
* @returns interpolated string
|
||||
*/
|
||||
t = (key: string, options: number | InterpolationOptions = {}): string => {
|
||||
if (this.alwaysShowScreamers) {
|
||||
return key;
|
||||
}
|
||||
|
||||
let internalOptions: InterpolationOptions = formatOptions(options);
|
||||
if (typeof key !== 'string') {
|
||||
this.log.warn('received non-string key:', key);
|
||||
return '';
|
||||
}
|
||||
return this.translator.translate(key, internalOptions);
|
||||
};
|
||||
}
|
||||
|
||||
export default I18N;
|
||||
15
shared/localization/src/setHTMLAttributes.ts
Normal file
15
shared/localization/src/setHTMLAttributes.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { getLocAttributes } from './getLocAttributes';
|
||||
|
||||
/**
|
||||
* sets Language attributes to HTML tag.
|
||||
* @param {string} language
|
||||
* @returns {void}
|
||||
*/
|
||||
export function setHTMLAttributes(language: string): void {
|
||||
if (typeof window === 'undefined') return;
|
||||
const attributes = getLocAttributes(language);
|
||||
|
||||
for (let [attribute, value] of Object.entries(attributes)) {
|
||||
window.document.documentElement.setAttribute(attribute, value);
|
||||
}
|
||||
}
|
||||
174
shared/localization/src/translator.ts
Normal file
174
shared/localization/src/translator.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
//TODO: rdar://73157363 (Limit loc plural functions to only use supported locales)
|
||||
import * as cardinals from 'make-plural/cardinals';
|
||||
import type {
|
||||
Locale,
|
||||
ILocaleJSON,
|
||||
InterpolationOptions,
|
||||
TranslatorOptions,
|
||||
ImissingInterpolationFn,
|
||||
ImissingKeyFn,
|
||||
ITranslator,
|
||||
} from './types';
|
||||
|
||||
const DEFAULT_MISSING_FN: ImissingKeyFn = (key: string): string => `**${key}**`;
|
||||
const DEFAULT_INTERPOLATION_REGEX: RegExp = /@@(.*?)@@/g;
|
||||
|
||||
/**
|
||||
* Interpolates string and returns result.
|
||||
* @category Localization
|
||||
* @param phrase phrase to be interpolated ex. ```"hello my name is @@name@@" ```
|
||||
* @param options object containing values to subsitute ex. ``` { name: "Joe" } ```
|
||||
* @param onMissingInterpolationFn callback to be called if options object does not contain a value for the interpolation schema
|
||||
*
|
||||
* @returns translated string ex ``` "hello my name is Joe" ```
|
||||
*/
|
||||
export function interpolateString(
|
||||
key: string,
|
||||
phrase: string,
|
||||
options: InterpolationOptions,
|
||||
onMissingInterpolationFn: ImissingInterpolationFn | null,
|
||||
locale: Locale,
|
||||
): string {
|
||||
const result = phrase.replace(
|
||||
DEFAULT_INTERPOLATION_REGEX,
|
||||
function (expression: string, argument: string) {
|
||||
const optionHasProperty = options.hasOwnProperty(argument);
|
||||
const optionType = typeof options[argument];
|
||||
const argumentIsUndefined = optionType === 'undefined';
|
||||
const argumentIsValid =
|
||||
optionType === 'string' || optionType === 'number';
|
||||
let value: string = expression;
|
||||
if (optionHasProperty && argumentIsValid) {
|
||||
let validValue: string | number = options[argument];
|
||||
if (
|
||||
optionType === 'number' &&
|
||||
options.hasOwnProperty('count')
|
||||
) {
|
||||
validValue = (validValue as number).toLocaleString([
|
||||
locale,
|
||||
'en-US',
|
||||
]);
|
||||
}
|
||||
value = validValue as string;
|
||||
} else if (onMissingInterpolationFn && argumentIsUndefined) {
|
||||
onMissingInterpolationFn(key, value);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
type Cardinal = (n: number | string) => cardinals.PluralCategory;
|
||||
|
||||
function getCardinal(selectedLang: string): Cardinal | undefined {
|
||||
// @ts-ignore-error TypeScript does not allow us to index into a namespace dynamically
|
||||
return cardinals[selectedLang];
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: rdar://73157363 (Limit loc plural functions to only use supported locales)
|
||||
* Used to select the locale specific cardinal plural form key.
|
||||
* @category Localization
|
||||
* @param count number to determine the cardinal value
|
||||
* @param key base key
|
||||
* @param locale to lookup plural
|
||||
*
|
||||
* Reference:
|
||||
* https://confluence.sd.apple.com/pages/viewpage.action?spaceKey=ASL&title=Pluralization+Rules
|
||||
*
|
||||
* @returns key + correct plural ex. ```[key].[ 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'] ```
|
||||
*/
|
||||
|
||||
export const getPlural = (
|
||||
count: number,
|
||||
key: string,
|
||||
locale: Locale,
|
||||
): string => {
|
||||
const lang = locale.split('-')[0];
|
||||
|
||||
// use english plural for dev strings
|
||||
const selectedLang = lang === 'dev' ? 'en' : lang;
|
||||
const cardinal = getCardinal(selectedLang);
|
||||
|
||||
let plural: cardinals.PluralCategory | null = null;
|
||||
if (cardinal) {
|
||||
plural = cardinal(count);
|
||||
// TODO: rdar://93665757 (JMOTW: investigate where to use 'few' and 'many' loc keys)
|
||||
if (plural === 'few' || plural === 'many') plural = 'other';
|
||||
}
|
||||
return plural ? `${key}.${plural}` : key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class that manages translations, plural rules,
|
||||
* and interpolation for a single locale.
|
||||
* @category Localization
|
||||
*/
|
||||
class Translator implements ITranslator {
|
||||
private translationMap: Map<string, string>;
|
||||
private locale: Locale;
|
||||
private onMissingKeyFn: ImissingKeyFn;
|
||||
private onMissingInterpolationFn: ImissingInterpolationFn | null;
|
||||
constructor(
|
||||
locale: Locale,
|
||||
phrases: ILocaleJSON,
|
||||
options: TranslatorOptions = {},
|
||||
) {
|
||||
const {
|
||||
onMissingKeyFn = DEFAULT_MISSING_FN,
|
||||
onMissingInterpolationFn = null,
|
||||
} = options;
|
||||
this.locale = locale;
|
||||
this.translationMap = new Map(Object.entries(phrases));
|
||||
this.onMissingKeyFn = onMissingKeyFn;
|
||||
this.onMissingInterpolationFn = onMissingInterpolationFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the correct value from the translation map.
|
||||
* @category Localization
|
||||
* @param key used to look up the value
|
||||
*/
|
||||
private getValue(key: string): string | null {
|
||||
return this.translationMap.get(key) || null;
|
||||
}
|
||||
/**
|
||||
* Gets an uniterpolated value of key.
|
||||
* @category Localization
|
||||
* @param key used to look up the value
|
||||
*/
|
||||
getUninterpolatedString(key: string) {
|
||||
const keyValue = this.getValue(key);
|
||||
return keyValue ? keyValue : this.onMissingKeyFn(key);
|
||||
}
|
||||
/**
|
||||
* Translate string based on translation map, plural rules interpolates values.
|
||||
* @category Localization
|
||||
* @param key used to look up the value
|
||||
* @param options used for interpolation
|
||||
* @returns translated string
|
||||
*/
|
||||
translate(key: string, options: InterpolationOptions = {}): string {
|
||||
let internalKey = key;
|
||||
const { count } = options;
|
||||
|
||||
if (count && !isNaN(count)) {
|
||||
internalKey = getPlural(count, key, this.locale);
|
||||
}
|
||||
|
||||
const keyValue = this.getValue(internalKey);
|
||||
return keyValue
|
||||
? interpolateString(
|
||||
internalKey,
|
||||
keyValue,
|
||||
options,
|
||||
this.onMissingInterpolationFn,
|
||||
this.locale,
|
||||
)
|
||||
: this.onMissingKeyFn(internalKey);
|
||||
}
|
||||
}
|
||||
|
||||
export default Translator;
|
||||
Reference in New Issue
Block a user