Files
jellybit/openspec/changes/archive/2026-08-07-tvdb-title-locale/specs/metadata-match/spec.md
T
av fdbc781197 metadata: TVDB отдаёт локализованное название и оригинал
- локаль из [general].language применяется при разборе ответа /search, а в
  запрос не уходит: параметр language у TVDB — фильтр выдачи, а не селектор
  перевода (ADR-2026-08-07)
- Title берётся из блока translations с тотальным фолбэком на primary name,
  OriginalTitle — из primary name; форма ответа сверена по документации и
  живым прогоном не подтверждена (docs/research)
- неожиданная форма ответа даёт WARN: признак — отсутствие во всей выдаче
  ключей языка ожидаемого вида, а не неудача разбора блока
2026-08-07 15:17:05 +03:00

9.4 KiB
Raw Blame History

ADDED Requirements

Requirement: Локализованное название кандидата TVDB

Кандидат TVDB SHALL нести локализованное название в поле Title и название на языке оригинала в поле OriginalTitle. Язык локализации задаёт та же глобальная настройка language, что и для TMDB, — правило единственного источника языка живёт в требовании «Локаль запроса к TMDB» и здесь не переписывается.

Источник локализованного названия — блок переводов в ответе поиска TVDB (карта «код языка → название»). OriginalTitle SHALL брать primary name записи — это название на языке оригинала и ось сравнения при матче.

Фолбэк SHALL быть тотальным: если перевода на нужный язык нет, его значение пусто после обрезки пробелов или блока переводов нет вовсе, Title SHALL быть равен primary name. Пустого Title при непустом primary name быть SHALL NOT. В Title SHALL попадать значение перевода после обрезки пробелов.

Ключ языка SHALL искаться регистронезависимо: молчаливый фолбэк из-за регистра ключа неотличим от отсутствия перевода и в эксплуатации не диагностируется. Если условию отвечает несколько ключей, выбор SHALL быть детерминированным — один и тот же ответ провайдера обязан давать один и тот же Title.

Локаль TVDB SHALL влиять только на разбор ответа и SHALL NOT сужать выдачу поиска: параметр языка в запрос поиска не передаётся. Причина — в docs/research/tvdb-search-translations.md; здесь заказано поведение, а не устройство чужого API. Тем самым разница с TMDB намеренна: у TMDB локаль едет в запрос, у TVDB читается из ответа.

Негодная форма блока переводов (блок пришёл не картой, значения не строки) SHALL приводить к тому же тотальному фолбэку, а не проваливать разбор ответа поиска целиком: ответ метабазы — недоверенный вход.

Подозрение на иную форму ответа SHALL оставлять диагностический след. Признаком служит отсутствие во всей выдаче хотя бы одного ключа языка ожидаемого вида, а не неудача разбора блока: null, пустая карта и словарь кодов другого вида разбираются без ошибки и потому признаком быть SHALL NOT. Штатное «перевода на этот язык нет» (ключи ожидаемого вида есть, нужного среди них нет) следа оставлять SHALL NOT — иначе сигнал неотличим от рутины. Уровень следа задают конвенции логирования проекта и здесь не нормируются; требуется наблюдаемость, а не конкретный уровень.

Настоящее требование не изменяет условий подтверждения матча — они заданы требованиями «Подтверждение матча и каноническое имя» и «Безгодовой второй проход сверки как fallback». Заполнение OriginalTitle расширяет множество названий кандидата, по которым идёт сравнение, с одного до двух. Исход гейта при этом монотонным SHALL NOT считаться: там, где сильный кандидат был один, их может стать двое, и тогда подтверждённого матча нет, а записи уходят кандидатами в review — по действующему требованию, без исключений для TVDB.

Scenario: Перевод на язык настройки есть

  • GIVEN TVDB включён, глобальный language = ru
  • WHEN поиск возвращает запись с primary name 哪吒之魔童降世 и переводом rus = «Нэчжа»
  • THEN Candidate.Title = «Нэчжа»
  • AND Candidate.OriginalTitle = 哪吒之魔童降世

Scenario: Перевода на язык настройки нет — фолбэк на оригинал

  • GIVEN TVDB включён, глобальный language = ru
  • WHEN поиск возвращает запись с primary name Fargo и переводами без ключа rus
  • THEN Candidate.Title = Fargo
  • AND Candidate.OriginalTitle = Fargo

Scenario: Перевод есть, но пустой

  • GIVEN TVDB включён, глобальный language = ru
  • WHEN поиск возвращает запись с primary name Fargo и переводом rus из одних пробелов
  • THEN Candidate.Title = Fargo
  • AND Candidate.OriginalTitle = Fargo

Scenario: Язык по умолчанию — английский

  • GIVEN TVDB включён, глобальный language не задан в конфиге
  • WHEN поиск возвращает запись с переводами eng и rus
  • THEN Candidate.Title берётся из перевода eng

Scenario: Ключ перевода в другом регистре

  • GIVEN TVDB включён, глобальный language = ru
  • WHEN поиск возвращает запись с переводом под ключом RUS вместо rus
  • THEN Candidate.Title берётся из этого перевода

Scenario: Блок переводов отсутствует или пришёл негодной формой

  • GIVEN TVDB включён с любым значением language
  • WHEN поиск возвращает записи без блока переводов либо с блоком, который не разбирается картой
  • THEN Candidate.Title = primary name записи
  • AND Candidate.OriginalTitle = primary name записи
  • AND разбор ответа поиска не проваливается, кандидаты возвращаются
  • AND остаётся диагностический след о подозрении на иную форму ответа

Scenario: Коды языка не того вида — след остаётся

  • GIVEN TVDB включён, глобальный language = ru
  • WHEN вся выдача поиска несёт блоки переводов с ключами другого вида (например, двухбуквенными), либо null, либо пустые
  • THEN Candidate.Title = primary name у каждой записи
  • AND остаётся диагностический след о подозрении на иную форму ответа

Scenario: Перевода на язык нет — следа не остаётся

  • GIVEN TVDB включён, глобальный language = ru
  • WHEN выдача несёт блоки переводов с ключами ожидаемого вида, но без нужного языка
  • THEN Candidate.Title = primary name у каждой записи
  • AND диагностического следа не остаётся: это штатный исход, а не подозрение

Scenario: Запрос поиска не сужается языком

  • GIVEN TVDB включён, глобальный language = ru
  • WHEN выполняется поиск
  • THEN строка запроса поиска не содержит параметра языка
  • AND строка запроса совпадает с той, что уходит при language = en

Scenario: Перевод сделал сильных кандидатов двумя — матч не подтверждён

  • GIVEN план с title «Нэчжа» и original_title Ne Zha, год известен
  • WHEN поиск TVDB возвращает две записи в пределах года ±1: одну с primary name 哪吒之魔童降世 и переводом rus «Нэчжа», другую с primary name Ne Zha
  • THEN гейт даёт двух сильных кандидатов вместо одного
  • AND подтверждённого матча нет, обе записи уходят кандидатами в review