Files
ihasmail/web/src/locales/__tests__/uk-plurals.test.ts
T
jcoffey-dev 5737362621 Ukrainian, and not the Russian one with a different name on it
781 of 796 strings. Generated by AI, unreviewed, marked Beta.

The thing this catalogue had to avoid is the reason it took the work it did.
Ukrainian and Russian share a script and share a plural rule -- one, few,
many, with 11 counting as many and 21 counting as one -- and share almost
nothing else that matters in a mail client. «Вхідні» is not «Входящие»,
«Кошик» is not «Корзина», «Листування» is not «Цепочка». A Ukrainian
catalogue produced by adapting the Russian one would pass every structural
check in this repo and still be the wrong language, and a Ukrainian reader
would notice in the first sentence and would be right to resent it. The
vocabulary here was chosen against what Ukrainian software says, not against
the neighbouring file.

Two words worth naming: «тека» rather than «папка» for folder, which is the
form Ukrainian software settled on; and «мітка» for label rather than
Russian's «ярлык», which in Ukrainian means a shortcut and would be a small
false friend on every screen.

Plurals tested against the shipped catalogue at 1, 2, 4, 5, 11, 21 and 0, plus
the check that every counted string carries all four categories. A missing
"few" falls back to "other" silently and is grammatical often enough to go
unnoticed.

The cross-check that Ukrainian and Russian are actually different files is
worth having but cannot live here: Russian is still an open pull request, so
ru.ts does not exist on this branch. It belongs in a follow-up once both have
landed.
2026-08-31 12:44:53 -07:00

37 lines
1.6 KiB
TypeScript

import { describe, expect, it, afterEach } from "vitest";
import { plural, setCatalog } from "@/lib/i18n";
import { catalog as uk } from "@/locales/uk";
/**
* Ukrainian needs `few` and `many` where English has one plural, and the rule
* is not "n === 1": 11 is `many` despite ending in 1, and 21 is `one` again.
* English's two-form assumption would render "5 лист", which is the kind of
* wrong that makes a translation read as machine output whatever the
* vocabulary is.
*/
const FORMS = { one: "{n} message", other: "{n} messages" };
afterEach(() => setCatalog("en", { strings: {}, plurals: {} }));
describe("Ukrainian plurals", () => {
it("picks one, few and many by the language's own rule", () => {
setCatalog("uk", uk);
expect(plural(1, FORMS)).toBe("1 лист");
expect(plural(2, FORMS)).toBe("2 листи");
expect(plural(4, FORMS)).toBe("4 листи");
expect(plural(5, FORMS)).toBe("5 листів");
expect(plural(11, FORMS)).toBe("11 листів"); // many, despite ending in 1
expect(plural(21, FORMS)).toBe("21 лист"); // one again
expect(plural(0, FORMS)).toBe("0 листів");
});
it("carries every form for each counted string it ships", () => {
// A missing `few` falls back to `other` silently, and is grammatical often
// enough to go unnoticed while being wrong the rest of the time.
for (const [key, forms] of Object.entries(uk.plurals)) {
for (const cat of ["one", "few", "many", "other"] as const) {
expect(forms[cat], `${key} is missing "${cat}"`).toBeTruthy();
}
}
});
});