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.
37 lines
1.6 KiB
TypeScript
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();
|
|
}
|
|
}
|
|
});
|
|
});
|