recognition: глобальный переключатель языка вывода [general].language

- новое поле [general].language (ru|en, дефолт en) — единый источник языка
  локализованного title детектора и локали запросов к метабазам; original_title
  всегда на языке оригинала
- локаль TMDB (поиск + credits) выводится из него, [metadata.tmdb].language убран
- промпт LLM явно задаёт язык title с fallback на оригинал
This commit is contained in:
av
2026-07-24 14:09:04 +03:00
parent 8309ce0664
commit a2d8140e22
18 changed files with 556 additions and 48 deletions
@@ -0,0 +1,59 @@
## Why
Приложение и медиатека — на русском, но распознавание и метабазы часто отдают
`title` и режиссёра на английском (или, наоборот, на русском там, где хочется
латиницу). Сейчас язык локализованного названия задан неявно и вразнобой: LLM
сам решает, на каком языке вернуть `title`, а TMDB жёстко умолчанием `ru-RU`.
Нужен один явный переключатель языка вывода, единый для детектора и метабаз, с
предсказуемым дефолтом.
## What Changes
- Вводится единый конфиг `[general].language` со значениями `ru`|`en`, **по
умолчанию `en`** (рядом с `[general].timezone` — тот же класс
presentation-настройки отображения). Он задаёт язык *локализованных* полей
вывода — `title` и режиссёра. `original_title` он не затрагивает: оно всегда
остаётся на языке оригинала картины.
- **recognition**: промпт LLM явно требует возвращать `title` на выбранном
языке (локализованное название с fallback на оригинал, если перевода нет).
Контракт `original_title` не меняется.
- **metadata-match**: локаль запросов к TMDB (`language`) выводится из
глобального `language` (`ru``ru-RU`, `en``en-US`), а не задаётся отдельно.
В запрос credits (режиссёр) тоже передаётся эта локаль — best-effort: имена
людей провайдеры локализуют не всегда, где перевода нет, остаётся оригинал.
- **BREAKING (конфиг)**: поле `[metadata.tmdb].language` удаляется — теперь это
единственный способ задать язык, второго не остаётся. Дефолт локали TMDB
меняется с `ru-RU` на `en-US` (следствие общего дефолта `en`).
## Capabilities
### New Capabilities
(нет — новых доменов не вводим)
### Modified Capabilities
- `recognition`: требование к промпту LLM — `title` возвращается на языке,
заданном глобальным `language` (fallback на оригинал при отсутствии
перевода); контракт `original_title` не меняется.
- `metadata-match`: локаль запроса к TMDB выводится из глобального `language`
(вместо отдельного `[metadata.tmdb].language`), та же локаль передаётся в
запрос режиссёра (best-effort).
## Impact
- **Конфиг**: новое поле `[general].language` (валидация `ru`|`en`, дефолт
`en`); удаление `[metadata.tmdb].language`; обновление `config.example.toml`
и `docs/conventions/config.md`.
- **Медиатека на диске**: имя папки Jellyfin строится из локализованного
`title` (`recognize.go` подменяет `plan.Title` каноническим именем матча),
поэтому дефолт `en` даёт английские имена папок. Это косметика: Jellyfin
идентифицирует контент по id-тегу метабазы (TVDB/IMDb в имени папки), а не по
языку названия, поэтому распознавание медиатеки не ломается.
- **Код**: `internal/config` (парсинг/валидация/дефолт нового поля, удаление
старого), `internal/recognize/prompt.go` (язык `title` в промпте),
`internal/metadata/tmdb.go` (локаль из глобального, передача в credits),
проброс `language` из конфига в recognizer и клиента TMDB.
- **Поведение**: дефолтный язык `title`/режиссёра меняется на английский;
существующие деплои, полагавшиеся на `ru-RU`, должны выставить
`language = "ru"`.