recognition: глобальный переключатель языка вывода [general].language
- новое поле [general].language (ru|en, дефолт en) — единый источник языка локализованного title детектора и локали запросов к метабазам; original_title всегда на языке оригинала - локаль TMDB (поиск + credits) выводится из него, [metadata.tmdb].language убран - промпт LLM явно задаёт язык title с fallback на оригинал
This commit is contained in:
+24
-14
@@ -13,18 +13,32 @@ import (
|
||||
"git.vakhrushev.me/av/jellybit/internal/logging"
|
||||
)
|
||||
|
||||
const (
|
||||
tmdbDefaultBaseURL = "https://api.themoviedb.org/3"
|
||||
tmdbDefaultLanguage = "ru-RU"
|
||||
)
|
||||
const tmdbDefaultBaseURL = "https://api.themoviedb.org/3"
|
||||
|
||||
// TMDBConfig — настройки клиента TMDB.
|
||||
type TMDBConfig struct {
|
||||
APIKey string
|
||||
Proxy string
|
||||
Timeout time.Duration
|
||||
BaseURL string // пусто → api.themoviedb.org; задаётся в тестах
|
||||
Language string // локаль возвращаемых названий; пусто → ru-RU
|
||||
APIKey string
|
||||
Proxy string
|
||||
Timeout time.Duration
|
||||
BaseURL string // пусто → api.themoviedb.org; задаётся в тестах
|
||||
// Language — абстрактный код языка вывода ("ru" | "en"); диалект локали TMDB
|
||||
// выводит сам клиент (tmdbLocale). Знание диалекта живёт здесь, у провайдера,
|
||||
// который на нём говорит, а не в общем слое конфига.
|
||||
Language string
|
||||
}
|
||||
|
||||
// tmdbLocale переводит абстрактный код языка вывода в диалект локали TMDB.
|
||||
// Тотальна: непокрытый вход (пусто, неизвестный код) → en-US, чтобы запрос
|
||||
// никогда не ушёл без локали (defense in depth поверх валидации конфига).
|
||||
// Множество кодов задаёт config.validate ({ru, en}); при добавлении кода —
|
||||
// синхронно добавь ветку здесь, иначе он молча уедет в en-US.
|
||||
func tmdbLocale(lang string) string {
|
||||
switch lang {
|
||||
case "ru":
|
||||
return "ru-RU"
|
||||
default:
|
||||
return "en-US"
|
||||
}
|
||||
}
|
||||
|
||||
// TMDB — клиент The Movie Database (API v3, авторизация по api_key).
|
||||
@@ -49,14 +63,10 @@ func NewTMDB(cfg TMDBConfig, logger *slog.Logger) (*TMDB, error) {
|
||||
if base == "" {
|
||||
base = tmdbDefaultBaseURL
|
||||
}
|
||||
lang := cfg.Language
|
||||
if lang == "" {
|
||||
lang = tmdbDefaultLanguage
|
||||
}
|
||||
if logger == nil {
|
||||
logger = slog.Default()
|
||||
}
|
||||
return &TMDB{apiKey: cfg.APIKey, baseURL: strings.TrimRight(base, "/"), language: lang, hc: hc, log: logger}, nil
|
||||
return &TMDB{apiKey: cfg.APIKey, baseURL: strings.TrimRight(base, "/"), language: tmdbLocale(cfg.Language), hc: hc, log: logger}, nil
|
||||
}
|
||||
|
||||
func (t *TMDB) Name() string { return "tmdb" }
|
||||
|
||||
@@ -109,13 +109,13 @@ func TestTMDB_ErrorStatus(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTMDB_SearchDefaultLanguage(t *testing.T) {
|
||||
// Язык не задан в конфиге → дефолт ru-RU; original_title не зависит от локали.
|
||||
// Язык не задан в конфиге → дефолт en-US; original_title не зависит от локали.
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if got := r.URL.Query().Get("language"); got != "ru-RU" {
|
||||
t.Errorf("language = %q, want ru-RU", got)
|
||||
if got := r.URL.Query().Get("language"); got != "en-US" {
|
||||
t.Errorf("language = %q, want en-US", got)
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"results":[
|
||||
{"id":155,"title":"Тёмный рыцарь","original_title":"The Dark Knight","release_date":"2008-07-18"}
|
||||
{"id":155,"title":"The Dark Knight","original_title":"The Dark Knight","release_date":"2008-07-18"}
|
||||
]}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
@@ -124,21 +124,22 @@ func TestTMDB_SearchDefaultLanguage(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Search: %v", err)
|
||||
}
|
||||
if got[0].Title != "Тёмный рыцарь" || got[0].OriginalTitle != "The Dark Knight" {
|
||||
if got[0].Title != "The Dark Knight" || got[0].OriginalTitle != "The Dark Knight" {
|
||||
t.Errorf("candidate = %+v", got[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestTMDB_SearchLanguageOverride(t *testing.T) {
|
||||
func TestTMDB_SearchRussianLanguage(t *testing.T) {
|
||||
// language = "ru" → диалект локали TMDB ru-RU (маппинг у провайдера).
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if got := r.URL.Query().Get("language"); got != "en-US" {
|
||||
t.Errorf("language = %q, want en-US", got)
|
||||
if got := r.URL.Query().Get("language"); got != "ru-RU" {
|
||||
t.Errorf("language = %q, want ru-RU", got)
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"results":[]}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
c, err := NewTMDB(TMDBConfig{APIKey: "k", BaseURL: srv.URL, Language: "en-US"}, nil)
|
||||
c, err := NewTMDB(TMDBConfig{APIKey: "k", BaseURL: srv.URL, Language: "ru"}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTMDB: %v", err)
|
||||
}
|
||||
@@ -147,6 +148,25 @@ func TestTMDB_SearchLanguageOverride(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTMDB_DirectorCarriesLanguage(t *testing.T) {
|
||||
// Локаль выведенного языка передаётся и в запрос credits (режиссёр).
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if got := r.URL.Query().Get("language"); got != "ru-RU" {
|
||||
t.Errorf("language = %q, want ru-RU", got)
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"crew":[{"job":"Director","name":"Тимур Бекмамбетов"}]}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
c, err := NewTMDB(TMDBConfig{APIKey: "k", BaseURL: srv.URL, Language: "ru"}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTMDB: %v", err)
|
||||
}
|
||||
if _, err := c.Director(context.Background(), Movie, "1"); err != nil {
|
||||
t.Fatalf("Director: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewTMDB_RequiresKey(t *testing.T) {
|
||||
if _, err := NewTMDB(TMDBConfig{}, nil); err == nil {
|
||||
t.Fatal("want error without api_key")
|
||||
|
||||
Reference in New Issue
Block a user