fix(i18n): count plural forms only large round numbers use

This commit is contained in:
Tautvydas Derzinskas committed 2026-10-20 08:07:07 +02:00
1 parent 96300510b4
commit 993c75c828
3 files changed
+25 -7

No files matched your search

+2 -7
View File
@@ -1,6 +1,7 @@
// Lists each locale's keys missing from, or no longer in, en.json: `npm run i18n:check [code]`.
// A plural key (key_one, key_other, ...) needs every form the locale uses for whole-number counts.
import { readdirSync, readFileSync } from "node:fs";
import { pluralForms } from "./plural-forms.mjs";
const dir = new URL("../src/i18n/locales/", import.meta.url);
const PLURAL = /_(zero|one|two|few|many|other)$/;
@@ -13,12 +14,6 @@ function keys(node, prefix = "") {
const read = (code) => keys(JSON.parse(readFileSync(new URL(`${code}.json`, dir), "utf8")));
// Fractional-only forms (Lithuanian `many`) are left out: counts here are whole numbers.
function pluralForms(code) {
const rules = new Intl.PluralRules(code);
return new Set(Array.from({ length: 1000 }, (_, n) => rules.select(n)));
}
const plurals = new Set();
const plain = [];
for (const key of read("en")) {
@@ -40,7 +35,7 @@ if (only && codes.length === 0) {
for (const code of codes) {
const forms = [...pluralForms(code)];
const en = new Set([...plain, ...[...plurals].flatMap((base) => forms.map((form) => `${base}_${form}`))]);
// Extra plural forms a translator added (such as `_many`) aren't stale, just unused for whole numbers.
// Extra plural forms a translator added (Lithuanian `_many`) aren't stale, just unused by this locale's whole numbers.
const locale = new Set(read(code).filter((key) => en.has(key) || !plurals.has(key.replace(PLURAL, ""))));
const missing = [...en].filter((key) => !locale.has(key));
const stale = [...locale].filter((key) => !en.has(key));
+9
View File
@@ -0,0 +1,9 @@
// The plural forms a locale uses for whole-number counts: 0-999 plus powers of ten, so forms only large
// round numbers take (French and Spanish `many` for 1,000,000) count too. Fractional-only forms (Lithuanian
// `many`) are left out.
const SAMPLES = [...Array.from({ length: 1000 }, (_, n) => n), ...Array.from({ length: 10 }, (_, k) => 10 ** (k + 3))];
export function pluralForms(code) {
const rules = new Intl.PluralRules(code);
return new Set(SAMPLES.map((n) => rules.select(n)));
}
+14
View File
@@ -0,0 +1,14 @@
import { describe, expect, it } from "vitest";
import { pluralForms } from "./plural-forms.mjs";
describe("pluralForms", () => {
it("includes forms only large round counts use", () => {
expect([...pluralForms("fr")].toSorted()).toEqual(["many", "one", "other"]);
expect([...pluralForms("es")].toSorted()).toEqual(["many", "one", "other"]);
});
it("leaves out fractional-only forms", () => {
expect([...pluralForms("lt")].toSorted()).toEqual(["few", "one", "other"]);
expect([...pluralForms("en")].toSorted()).toEqual(["one", "other"]);
});
});