recognition: глобальный переключатель языка вывода [general].language
- новое поле [general].language (ru|en, дефолт en) — единый источник языка локализованного title детектора и локали запросов к метабазам; original_title всегда на языке оригинала - локаль TMDB (поиск + credits) выводится из него, [metadata.tmdb].language убран - промпт LLM явно задаёт язык title с fallback на оригинал
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
## Context
|
||||
|
||||
Язык локализованных полей вывода (`title`, режиссёр) сейчас задаётся неявно и в
|
||||
двух местах: промпт LLM говорит модели вернуть «каноническое название» без
|
||||
указания языка (модель решает сама), а клиент TMDB жёстко берёт локаль из
|
||||
`[metadata.tmdb].language` с дефолтом `ru-RU`. Это два несогласованных рычага
|
||||
для одного решения. Требуется один явный переключатель, единый для детектора и
|
||||
метабаз.
|
||||
|
||||
Затрагиваемые пакеты: `internal/config` (новое поле), `internal/recognize`
|
||||
(промпт), `internal/metadata` (TMDB-клиент), проброс из `cmd/jellybit` в
|
||||
recognizer и TMDB. Конвенция конфига — `docs/conventions/config.md`
|
||||
(самодокументируемый `config.example.toml`, валидация на старте).
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Одно top-level поле `language` (`ru`|`en`, дефолт `en`) — единственный
|
||||
источник языка локализованного вывода.
|
||||
- `title` от LLM и локаль TMDB (поиск + credits) следуют этому полю.
|
||||
- `original_title` не затрагивается ни при каком значении.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Гарантированная локализация имён режиссёров (провайдеры их почти не
|
||||
переводят — best-effort).
|
||||
- Языки помимо `ru`/`en`, автоопределение языка по контенту, per-раздача
|
||||
override.
|
||||
- Локаль TVDB/TVMaze: TVDB extended не параметризуем языком в текущем клиенте,
|
||||
TVMaze — англоязычный; вне охвата этого изменения (охват — LLM и TMDB).
|
||||
|
||||
## Decisions
|
||||
|
||||
**1. Место конфига — `[general].language`, значения `ru`|`en`, дефолт `en`.**
|
||||
Поле кросс-каттинг (и recognition, и metadata-match) и по классу — это
|
||||
presentation-настройка отображения, ровно как уже живущий в `[general]`
|
||||
`timezone` («таймзона отображения времени»). Селим рядом с ним — единый дом
|
||||
таких настроек, не плодим второй паттерн места. Значения — короткие языковые
|
||||
коды, а не локали (`ru-RU`): локаль — диалект конкретного провайдера, его
|
||||
выводит сам провайдер (см. решение 2). Значение хранится голой строкой, как
|
||||
остальные enum-подобные поля конфига (`llm.type`, `log.format`) — проект
|
||||
осознанно не типизирует их. Валидация: значение вне `ru`/`en` → ошибка старта
|
||||
(fail-fast, **по образцу `llm.type`**, а не permissive `log.level`), форма
|
||||
сообщения — `unsupported language %q (supported: ru, en)`. Нормализацию «пусто →
|
||||
`en`» держим НЕ в `validate()` (он чист — только собирает ошибки), а в аксессоре
|
||||
`Config` по образцу `DisplayLocation()`; `Default()` при этом сеет `"en"`.
|
||||
Альтернативы отклонены: `[recognition].language` назвала бы поле по одному из
|
||||
двух потребителей; top-level рядом с `timezone` не согласуется с уже сложившимся
|
||||
домом presentation-настроек.
|
||||
|
||||
**2. `[metadata.tmdb].language` удаляется; локаль выводит провайдер.** В общий
|
||||
слой (config/cmd) едет абстрактный `ru`/`en`; диалект TMDB `ru-RU`/`en-US`
|
||||
выводит сам `internal/metadata/tmdb.go` — тотальным `switch` с default-веткой
|
||||
`en-US` (defense in depth: невозможный вход не роняет запрос без локали). Так
|
||||
знание диалекта принадлежит тому, кто на нём говорит: второй локализуемый
|
||||
провайдер (TVDB с иным синтаксисом локали) добавит свой маппинг у себя, а не
|
||||
расширит общий слой. Остаточный `tmdbDefaultLanguage = "ru-RU"` и его fallback в
|
||||
`NewTMDB` **удаляются** — это скрытый второй дефолт, противоречащий заявленному
|
||||
`en-US`. Второго способа задать язык не остаётся (нет рассинхрона). Ломающее
|
||||
изменение конфига переворачивает дефолт локали TMDB `ru-RU`→`en-US`; фиксируем в
|
||||
proposal и миграции. Альтернатива «оставить `[metadata.tmdb].language` как
|
||||
override» отклонена: две ручки для одного решения противоречат «второго способа
|
||||
быть не должно».
|
||||
|
||||
**3. Директива языка в промпте — только для `title`, с fallback на оригинал.**
|
||||
Добавляем в промпт распознавания явную строку: вернуть `title` на языке
|
||||
`language`, при отсутствии перевода — на языке оригинала (не выдумывать).
|
||||
Семантически совпадает с поведением TMDB `language` (локализованное поле с
|
||||
fallback на оригинал), поэтому обе стороны согласованы. Язык прокидывается в
|
||||
`buildMessages`/`systemPrompt` из конфига через `Recognizer`.
|
||||
|
||||
**4. Режиссёр — та же локаль в запрос credits, best-effort.** Клиент TMDB уже
|
||||
передаёт `language` в поиск; тем же значением параметризуем запрос credits
|
||||
(`Director`). Имена людей TMDB локализует не всегда — где перевода нет, приходит
|
||||
оригинал; это не ошибка и не проваливает выборку (best-effort уже закреплён
|
||||
спекой metadata-match).
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Имена режиссёров редко локализованы у провайдеров] → best-effort по спеке:
|
||||
при отсутствии перевода — оригинал; ожидание зафиксировано в proposal, отказа
|
||||
не вызывает.
|
||||
- [Ломающее изменение конфига: удаление `[metadata.tmdb].language` + флип
|
||||
дефолта на `en-US`] → go-toml молча игнорирует неизвестный ключ, поэтому
|
||||
деплой с оставленным `[metadata.tmdb].language = "ru-RU"` тихо переключится на
|
||||
английский. Решение: НЕ вводим отклонение неизвестных ключей (лишняя машинерия
|
||||
ради единственного деплоя), а прописываем обязательный ручной шаг в миграции
|
||||
ниже. Существующие «русские» деплои выставляют `[general].language = "ru"` и
|
||||
убирают старый ключ.
|
||||
- [Дефолт `en` переименует имена папок медиатеки на английский] → косметика:
|
||||
Jellyfin идентифицирует контент по id-тегу метабазы (TVDB/IMDb в имени папки),
|
||||
а не по языку названия, поэтому распознавание не ломается. `original_title`
|
||||
(ось поиска в базах) от `language` не зависит. Осознанный дефолт.
|
||||
- [LLM может проигнорировать языковую директиву на слабой модели] → директива
|
||||
best-effort, как и весь недоверенный вывод LLM; безопасность по-прежнему на
|
||||
валидации и гейте матча, не на языке `title`.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Выкатка бинаря с новым полем; дефолт `en` активен сразу.
|
||||
2. Деплой, где ожидались русские названия, добавляет `[general].language = "ru"`
|
||||
в конфиг и удаляет `[metadata.tmdb].language` (обязательный ручной шаг —
|
||||
валидатор про удалённый ключ не предупреждает).
|
||||
3. Откат — вернуть прежний бинарь; `language` в конфиге игнорируется старой
|
||||
версией (unknown-поле go-toml не роняет парсинг), `[metadata.tmdb].language`
|
||||
при откате нужно вернуть, если он был.
|
||||
Reference in New Issue
Block a user