feat(tmdb): 多语言保留规则——译名 / 别名只留简繁中文、英文与原语言

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-06 18:14:54 +08:00
1 parent 69d820a6bf
commit 9c5f04b02f
8 files changed
+423 -29

No files matched your search

+16 -12
View File
@@ -9,6 +9,7 @@
*/
import { redactUrl } from "@umt/contracts";
import { RequestLimiter, TtlLruCache } from "./cache.ts";
import { allowedLanguage, altTitleLanguage, isAllowedLanguage } from "./language-policy.ts";
export type TmdbErrorKind = "auth" | "network" | "timeout" | "notfound" | "rate" | "protocol";
@@ -27,7 +28,7 @@ export type MediaEntityType = "movie" | "tv";
export interface CandidateTitle {
text: string;
/** BCP47,如 zh-CN;原名为原始语言;别名为 null */
/** BCP47,如 zh-CN;原名为原始语言;别名为按地区 + 文字推断的语言(language-policy altTitleLanguage) */
lang: string | null;
kind: "localized" | "original" | "alias" | "translation";
/** 别名/翻译的地区(iso_3166_1) */
@@ -395,9 +396,13 @@ export class TmdbClient {
),
);
const c = this.fromBrief(type, first!.j, first!.lang);
// 多语言保留规则(language-policy.ts):本地化名 / 译名只留简中 / 繁中 / 英文 / 原语言;别名按地区 + 文字推断语言,
// 推断不出的不要。MP __get_names 把全部别名与译名都计入可匹配名称,这里收窄(用户规则 2026-10-06)
const work = { originalLanguage: c.originalLanguage, originCountry: c.originCountry };
c.titles = c.titles.filter((t) => t.kind !== "localized" || isAllowedLanguage(t.lang, work));
for (const { lang, j } of rest) {
const t = s(type === "movie" ? j.title : j.name);
if (t) TmdbClient.mergeTitle(c, { text: t, lang, kind: "localized" });
if (t && isAllowedLanguage(lang, work)) TmdbClient.mergeTitle(c, { text: t, lang, kind: "localized" });
}
const alt = first!.j.alternative_titles as Json | undefined;
const altList = ((type === "movie" ? alt?.titles : alt?.results) ?? []) as Json[];
@@ -405,25 +410,24 @@ export class TmdbClient {
for (const a of altList) {
const text = s(a.title);
if (!text) continue;
aliases.add(text);
const region = s(a.iso_3166_1);
c.titles.push({ text, lang: null, kind: "alias", ...(region ? { region } : {}) });
const lang = altTitleLanguage({ title: text, region, type: s(a.type) }, work);
if (!lang) continue;
aliases.add(text);
c.titles.push({ text, lang, kind: "alias", ...(region ? { region } : {}) });
}
// translations:MP __get_names 同样把各语言译名计入可匹配名称
// translations:MP __get_names 同样把各语言译名计入可匹配名称(这里只留保留语言)
const trs = (((first!.j.translations as Json | undefined)?.translations ?? []) as Json[]).filter(Boolean);
for (const tr of trs) {
const data = tr.data as Json | undefined;
const text = s(type === "movie" ? data?.title : data?.name);
if (!text) continue;
aliases.add(text);
const l = s(tr.iso_639_1);
const region = s(tr.iso_3166_1);
TmdbClient.mergeTitle(c, {
text,
lang: l ? (region ? `${l}-${region}` : l) : null,
kind: "translation",
...(region ? { region } : {}),
});
const lang = allowedLanguage(l ? (region ? `${l}-${region}` : l) : null, work);
if (!lang) continue;
aliases.add(text);
TmdbClient.mergeTitle(c, { text, lang, kind: "translation", ...(region ? { region } : {}) });
}
c.aliases = [...aliases];
const ext = first!.j.external_ids as Json | undefined;