feat(i18n): the server's closed English vocabularies named in the reader's language (E30 S2)

- lib/vocab + the `vocab` locale area: TMDB crew jobs and departments, auto-tags (D1 A: the data
  stays English, the catalog is named at its one source, useTags), built-in download profiles,
  YouTube topics; held key-for-key to the backend's own lists by backendIds.contract.
- Countries by ISO code: TMDB parse stores {code, name}; rows stored before are coded from TMDB's
  own country list in one request (run_enrich); Intl names them, historic codes (YU, SU, XI…)
  from the catalog.
- Player tracks named from the language tag, forced/SDH flags and codec tail, not Plex's
  server-language displayTitle; no invented "Audio N" on the server.
- Download lifecycle notices (shared / expiring / expired / evicted) named from data; the share
  notice carries the title.
- Scheduler: the job result rides along as data and is named per key (dashboard + run notice).
- Invite status named; a render crash's notice no longer shows the engine's English message.
- Fix: YouTube links …/wiki/Humour, the tagger looked for "Humor" — Comedy never came from a topic.
This commit is contained in:
2026-09-29 21:21:19 +02:00
parent f6298d29af
commit f3a85916ec
50 changed files with 1475 additions and 94 deletions
@@ -64,7 +64,11 @@ const detail: PlexItemDetail = {
{ name: "Sam Writer", job: "Writer" },
],
studios: ["Studio A", "Studio B"],
production_countries: ["United States"],
// One coded (named in the reader's language), one from a row enriched before codes were stored.
production_countries: [
{ code: "HU", name: "Hungary" },
{ code: null, name: "United States" },
],
recommendations: [],
backdrops: [],
},
@@ -93,7 +97,8 @@ describe("PlexInfo enriched detail (page)", () => {
expect(screen.getByText("$160M")).toBeTruthy();
expect(screen.getByText("$830M")).toBeTruthy();
expect(screen.getByText("Studio A")).toBeTruthy(); // tmdb.studios preferred over the fallback
expect(screen.getByText("United States")).toBeTruthy();
// E30: a coded country is named through the vocab (key-echo i18n here), an uncoded one as stored.
expect(screen.getByText("vocab.region.HU, United States")).toBeTruthy();
});
it("keyword chips filter the library on click", () => {
@@ -127,6 +132,8 @@ describe("PlexInfo enriched detail (page)", () => {
const onOpenPerson = vi.fn();
wrap(<PlexInfo onPlayItem={noop} detail={detail} variant="page" onOpenPerson={onOpenPerson} />);
expect(screen.getByText("plex.info.crew")).toBeTruthy();
// E30: the job is TMDB's English word, named through the vocab (key-echo i18n here).
expect(screen.getByText("vocab.crewJob.Director")).toBeTruthy();
fireEvent.click(screen.getByText("Jane Director"));
expect(onOpenPerson).toHaveBeenCalledWith("Jane Director");
});