Files
siftlode/frontend/src/lib/vocab.ts
T
peter 4aaaaa7860 chore(comments): drop epic, sprint and review-round ids from the code
The code cited its plan instead of saying why: `E28 review J4`, `(R6 S3b)`, `S1 justified…`,
`deep-review round 5`. That plan lives in the private ops repo, so for every reader of this one the
citation was noise, and a sentence whose subject was the plan step ("round 2 showed…") hid its
lesson behind an id. Each one is gone and the "why" kept: a bare citation is deleted, a sentence
carrying a lesson is reworded around it ("a later attempt showed…").

Measured (`scripts/audit_metrics.py`, product code): 652 lines in 270 files carried an epic code by
the definition the next commit holds (507 / 241 by the old, two-digit-only one) -> 0. Across all
code, tests, scripts and configs: ~1100 epic-code lines plus ~320 sprint/round ids.

Behaviour unchanged, checked mechanically: every changed .py file has the same AST once docstrings
are blanked; css/sh/toml/yml/json files are identical once comments are stripped; the only
non-comment changes in TS/JS are test titles, one description string in a contract table, and five
ESLint messages, whose text is all the gates snapshot diff holds. Not touched: Markdown (docs and
the changelog keep their history), Alembic migrations, TV episode notation (`S2 · E5`), DESIGN.md
ids (`C1`, `B4`, `S3h`), tracker ids (`SEC-16`, `DX-17`).

One stale claim fixed on the way: test_hls_reaper's docstring said the player checkpoints "every
10 s whether or not it is playing", which plex/stream.py calls wrong; it now says what the code does.
2026-10-05 00:19:39 +02:00

92 lines
4.3 KiB
TypeScript

import type { TFunction } from "i18next";
import type { PlexTrackNaming } from "./api";
// The closed vocabularies the server stores in ENGLISH — a TMDB crew job or department, an
// auto-tag, a built-in download profile, a YouTube topic, a Plex certificate's word ("Not Rated"; a
// code such as "PG-13" reads the same everywhere and falls through as stored) — named in the reader's language. The data
// stays language-neutral (it is filtered and matched on); only the NAME is translated, here, from the
// `vocab` locale area. A word the area does not hold shows as stored, so a new server value is never
// hidden — and the backend's own lists are held key-for-key to the area by backendIds.contract.
type Group = "crewJob" | "department" | "autoTag" | "profile" | "topic" | "certificate";
/** A vocabulary word's key: its letters and digits ("Health & Fitness" → "HealthFitness"). */
export function vocabKey(word: string): string {
return word.replace(/[^A-Za-z0-9]/g, "");
}
/** The i18n key of one word of `group` ("" when the word has no letters or digits) — for a caller
* whose `t` is not i18next's own (a pure builder handed a narrow `t`). */
export function vocabI18nKey(group: Group, word: string): string {
const key = vocabKey(word);
return key ? `vocab.${group}.${key}` : "";
}
/** One stored English word of `group`, in the reader's language (`fallback`, by default the word
* itself, when the area does not hold it). */
export function vocab(t: TFunction, group: Group, word: string, fallback = word): string {
const key = vocabI18nKey(group, word);
return key ? t(key, { defaultValue: fallback }) : fallback;
}
/** A download profile's name: a built-in one is seeded under an English name and named here; a user's
* own is theirs, as typed. */
export function profileName(t: TFunction, p: { name: string; is_builtin: boolean }): string {
return p.is_builtin ? vocab(t, "profile", p.name) : p.name;
}
function intlName(code: string, lang: string, type: "region" | "language"): string | undefined {
try {
return new Intl.DisplayNames([lang], { type, fallback: "none" }).of(code);
} catch {
return undefined; // not a well-formed code
}
}
/** A country from its ISO 3166 code. A code no longer in use (Yugoslavia, the Soviet Union) is
* named from `vocab.region` — Intl renames those to today's state (YU → Serbia) or cannot name them
* (XI, TMDB's Northern Ireland); `fallback` (the stored English name) when neither can. */
export function regionName(
t: TFunction,
code: string | null | undefined,
lang: string,
fallback = "",
): string {
if (!code) return fallback;
const up = code.toUpperCase();
return (
t(`vocab.region.${up}`, { defaultValue: "" }) || intlName(up, lang, "region") || fallback || up
);
}
/** A language from its BCP 47 tag ("en-US" → "American English"), capitalized to stand alone as a
* label ("magyar" → "Magyar"); `fallback` for a tag Intl cannot name ("und", a typo). */
export function languageName(tag: string | null | undefined, lang: string, fallback = ""): string {
const name = tag ? intlName(tag.replace(/_/g, "-"), lang, "language") : undefined;
if (!name || name === tag) return fallback;
return name.charAt(0).toLocaleUpperCase(lang) + name.slice(1);
}
/** An audio or subtitle track's name in the reader's language: "Angol (AC3 5.1)", "Magyar,
* kényszerített". Built from the language tag; a track without one keeps Plex's own title, and a
* track with neither is numbered. */
export function trackLabel(
t: TFunction,
track: PlexTrackNaming,
kind: "audio" | "subtitle",
lang: string,
): string {
// The language alone: Plex tags one track "en-US" and the next "en", and both are just English here.
const name = languageName(track.lang_tag?.split(/[-_]/)[0], lang);
if (!name) return track.label || t(`vocab.track.${kind}N`, { n: track.ord + 1 });
const flags = [
track.forced ? t("vocab.track.forced") : null,
track.hearing_impaired ? t("vocab.track.sdh") : null,
].filter(Boolean);
// The codec tail is technical and language-neutral but for its channel word ("AC3 Stereo").
const detail = track.detail
?.replace(/\bStereo\b/i, t("vocab.track.stereo"))
.replace(/\bMono\b/i, t("vocab.track.mono"));
return [name, ...flags].join(", ") + (detail ? ` (${detail})` : "");
}