feat(i18n): every server refusal is said by a record the contract holds
A refusal code or reason with no sentence fell through to the generic "couldn't carry out that action" unnoticed: the parity test is green when EN and HU miss the same key. One did (an edit refused for a file that is not video or audio). - lib/api/refusalCodes.ts: which sentence answers each code, as data; localizeDetail says a refusal through it, unchanged for every reader. - backendIds contract: every detail code the backend sends is in exactly one record, every reason it can carry has its sentence in EN and HU, and no family names a reason the backend cannot send. - The reasons that travel in a variable are Literal types (edit, move, cookies, quota), so the type checker refuses an unknown one too. - downloads.errors.edit.not_media in EN and HU.
This commit is contained in:
@@ -3,6 +3,8 @@ import { join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { REFUSAL_FAMILIES, type RefusalFamily } from "../lib/api/refusalCodes";
|
||||
|
||||
// Every locale key is used by the app. The parity test proves EN and HU agree; nothing proved either
|
||||
// one is still READ — 75 keys had outlived the code that showed them (a whole sidebar sort vocabulary,
|
||||
// the old login page, a pager the DataTable replaced), each a string a translator would keep in step
|
||||
@@ -49,9 +51,14 @@ const code = sources(SRC).join("\n");
|
||||
const literals = new Set([...code.matchAll(/["'`]([\w.-]+)["'`]/g)].map((m) => m[1]!));
|
||||
// Each dynamic key as a SHAPE, every interpolation a `*`: `plex.sort.*`, `editor.*.hint`,
|
||||
// `config.choices.*.*` — from a template literal that opens with a key head, or a head concatenated.
|
||||
// A refusal's sentence is built from its record (`lib/api/refusalCodes`) rather than a template the
|
||||
// scan could read, so each family's prefix is a shape of its own.
|
||||
const shapes = [
|
||||
...[...code.matchAll(/`([\w.]+\.(?:\$\{[^}`]*\}|[\w.-])*)`/g)].map((m) => m[1]!),
|
||||
...[...code.matchAll(/["'`]([\w.]+\.)["'`]\s*\+/g)].map((m) => `${m[1]!}\${}`),
|
||||
...Object.values(REFUSAL_FAMILIES).map(
|
||||
(f: RefusalFamily) => `${f.prefix}.\${reason}${f.suffix ?? ""}`,
|
||||
),
|
||||
]
|
||||
.filter((s) => s.includes("${"))
|
||||
.map((s) => s.replace(/\$\{[^}]*\}/g, "*"));
|
||||
|
||||
Reference in New Issue
Block a user