feat(i18n): every server A→Z list in the reader's alphabet (E30 S3)

The Postgres image is Alpine, whose en_US.utf8 is byte order: "Életrajzi" and
"Ötödik" sorted after "Zorró" in the feed, the channel lists, the Plex grid,
its genres and collections. `app/collation.py` names the ICU collation of the
reader's language (hu-x-icu puts "Ö" after "O" and "Cs" after "Cu"), wraps
every text ORDER BY with it - the feed's keyset cursor compares with the same
expression, so paging walks the order it sorted in - and orders the lists built
in Python (genre and studio facets, collection members, the discovery re-sort)
through the same collation. Plex title tiebreaks use the shown (HU) title.
This commit is contained in:
2026-09-29 22:39:57 +02:00
parent 584d082b6b
commit 67c9f10153
15 changed files with 328 additions and 38 deletions
@@ -0,0 +1,32 @@
import { describe, expect, it } from "vitest";
import { eslint } from "./lintProbe.testkit";
// `local/date-format` (eslint.config.js): docs/DESIGN.md "Copy" — a date is shown in the forms
// lib/format.ts builds, in the UI language. This pins that the rule fires on the ways a date used to
// be formatted by hand, and where it stands down.
const RULE = "local/date-format";
async function hits(source: string, filePath = "src/components/__probe.tsx"): Promise<number> {
const [result] = await eslint.lintText(source, { filePath });
return (result?.messages ?? []).filter((m) => m.ruleId === RULE).length;
}
describe("a hand-formatted date", () => {
it("is refused as toLocale*String or an Intl date formatter", async () => {
expect(await hits("export const a = (d: Date) => d.toLocaleString('en');\n")).toBe(1);
expect(await hits("export const b = (d: Date) => d.toLocaleDateString();\n")).toBe(1);
expect(await hits("export const c = (d: Date) => d.toLocaleTimeString();\n")).toBe(1);
expect(
await hits("export const e = new Intl.DateTimeFormat('en', { month: 'short' });\n"),
).toBe(1);
expect(await hits("export const f = new Intl.RelativeTimeFormat('en');\n")).toBe(1);
});
it("is left alone in lib/format.ts, in a test and for a number format", async () => {
const src = "export const g = (d: Date) => d.toLocaleString('en');\n";
expect(await hits(src, "src/lib/format.ts")).toBe(0);
expect(await hits(src, "src/lib/format.test.ts")).toBe(0);
expect(await hits("export const h = new Intl.NumberFormat('en');\n")).toBe(0);
});
});