Add uk and nl

This commit is contained in:
heboba
2026-02-26 20:40:18 +00:00
parent 756f53ef3b
commit e3ae678fea
3 changed files with 193 additions and 14 deletions

View File

@@ -30,6 +30,8 @@ I18N: dict[str, dict[str, str]] = {
"button_ratio_100": "🆕 Новые 100%",
"button_lang_ru": "🇺🇦 Русский",
"button_lang_en": "🇺🇸 English",
"button_lang_uk": "🇺🇦 Українська",
"button_lang_nl": "🇳🇱 Nederlands",
"button_back": "⬅️ Назад",
"start_text": (
"Я бот для Spotify daily vibe playlist.\n"
@@ -41,7 +43,7 @@ I18N: dict[str, dict[str, str]] = {
"/setsize N - размер плейлиста\n"
"/setratio X - доля новых треков (0.5..1.0)\n"
"/sync - обновить лайкнутые треки из Spotify\n"
"/lang [ru|en] - сменить язык"
"/lang [ru|en|uk|nl] - сменить язык"
),
"help_text": (
"Я бот для Spotify daily vibe playlist.\n"
@@ -53,7 +55,7 @@ I18N: dict[str, dict[str, str]] = {
"/setsize N - размер плейлиста\n"
"/setratio X - доля новых треков (0.5..1.0)\n"
"/sync - обновить лайкнутые треки из Spotify\n"
"/lang [ru|en] - сменить язык"
"/lang [ru|en|uk|nl] - сменить язык"
),
"connect_open_link": "Открой ссылку и авторизуй Spotify:\n{url}",
"spotify_connected_notice": "Spotify подключен: {display_name}\nТеперь можно /generate",
@@ -71,7 +73,7 @@ I18N: dict[str, dict[str, str]] = {
"sync_done": "Лайкнутые треки обновлены: {count}",
"lang_choose": "Выбери язык интерфейса:",
"lang_set": "Язык интерфейса: {lang_name}",
"lang_invalid": "Поддерживаются только `ru` и `en`.",
"lang_invalid": "Поддерживаются только `ru`, `en`, `uk` и `nl`.",
"unknown_action": "Не понял команду. Используйте кнопки ниже или /help.",
"main_menu_hint": "Главное меню",
"settings_menu_hint": "Настройки",
@@ -105,6 +107,8 @@ I18N: dict[str, dict[str, str]] = {
"button_ratio_100": "🆕 New 100%",
"button_lang_ru": "🇺🇦 Русский",
"button_lang_en": "🇺🇸 English",
"button_lang_uk": "🇺🇦 Українська",
"button_lang_nl": "🇳🇱 Nederlands",
"button_back": "⬅️ Back",
"start_text": (
"I am a Spotify daily vibe playlist bot.\n"
@@ -116,7 +120,7 @@ I18N: dict[str, dict[str, str]] = {
"/setsize N - playlist size\n"
"/setratio X - min new ratio (0.5..1.0)\n"
"/sync - sync liked tracks from Spotify\n"
"/lang [ru|en] - change language"
"/lang [ru|en|uk|nl] - change language"
),
"help_text": (
"I am a Spotify daily vibe playlist bot.\n"
@@ -128,7 +132,7 @@ I18N: dict[str, dict[str, str]] = {
"/setsize N - playlist size\n"
"/setratio X - min new ratio (0.5..1.0)\n"
"/sync - sync liked tracks from Spotify\n"
"/lang [ru|en] - change language"
"/lang [ru|en|uk|nl] - change language"
),
"connect_open_link": "Open the link and authorize Spotify:\n{url}",
"spotify_connected_notice": "Spotify connected: {display_name}\nNow you can use /generate",
@@ -146,7 +150,7 @@ I18N: dict[str, dict[str, str]] = {
"sync_done": "Liked tracks synced: {count}",
"lang_choose": "Choose interface language:",
"lang_set": "Interface language: {lang_name}",
"lang_invalid": "Only `ru` and `en` are supported.",
"lang_invalid": "Only `ru`, `en`, `uk`, and `nl` are supported.",
"unknown_action": "I did not understand that. Use the buttons below or /help.",
"main_menu_hint": "Main menu",
"settings_menu_hint": "Settings",
@@ -163,9 +167,164 @@ I18N: dict[str, dict[str, str]] = {
"status_last_run": "Last run: {status}, tracks={tracks}",
"status_last_url": "{url}",
},
"uk": {
"button_connect": "🔗 Підключити Spotify",
"button_generate": "✨ Згенерувати",
"button_status": "📊 Статус",
"button_latest": "🎵 Останній плейлист",
"button_sync": "🔄 Оновити лайки",
"button_help": "❓ Допомога",
"button_settings": "⚙️ Налаштування",
"button_language": "🌐 Мова / Language",
"button_size_20": "📏 Розмір 20",
"button_size_30": "📏 Розмір 30",
"button_size_50": "📏 Розмір 50",
"button_ratio_60": "🆕 Нові 60%",
"button_ratio_80": "🆕 Нові 80%",
"button_ratio_100": "🆕 Нові 100%",
"button_lang_ru": "🇺🇦 Русский",
"button_lang_en": "🇺🇸 English",
"button_lang_uk": "🇺🇦 Українська",
"button_lang_nl": "🇳🇱 Nederlands",
"button_back": "⬅️ Назад",
"start_text": (
"Я бот для Spotify daily vibe playlist.\n"
"Створюю плейлисти з вашого recent listening + liked tracks і намагаюся давати більше нових треків та менше повторів.\n\n"
"/connect - підключити Spotify\n"
"/status - статус акаунта та останнього плейлиста\n"
"/generate - згенерувати плейлист зараз\n"
"/latest - показати посилання на останній плейлист\n"
"/setsize N - розмір плейлиста\n"
"/setratio X - частка нових треків (0.5..1.0)\n"
"/sync - оновити лайкнуті треки зі Spotify\n"
"/lang [ru|en|uk|nl] - змінити мову"
),
"help_text": (
"Я бот для Spotify daily vibe playlist.\n"
"Створюю плейлисти з вашого recent listening + liked tracks і намагаюся давати більше нових треків та менше повторів.\n\n"
"/connect - підключити Spotify\n"
"/status - статус акаунта та останнього плейлиста\n"
"/generate - згенерувати плейлист зараз\n"
"/latest - показати посилання на останній плейлист\n"
"/setsize N - розмір плейлиста\n"
"/setratio X - частка нових треків (0.5..1.0)\n"
"/sync - оновити лайкнуті треки зі Spotify\n"
"/lang [ru|en|uk|nl] - змінити мову"
),
"connect_open_link": "Відкрийте посилання та авторизуйте Spotify:\n{url}",
"spotify_connected_notice": "Spotify підключено: {display_name}\nТепер можна /generate",
"user_not_found_start": "Користувача не знайдено. Напишіть /start",
"no_playlist_yet": "Ще немає згенерованого плейлиста.",
"generate_wait": "Генерую плейлист, це може зайняти 20-60 секунд...",
"error_prefix": "Помилка: {message}",
"size_usage": "Використання: /setsize 30",
"size_invalid": "Розмір має бути числом від 5 до 100.",
"size_set": "Розмір плейлиста встановлено: {value}",
"ratio_usage": "Використання: /setratio 0.8",
"ratio_invalid": "Значення має бути від 0.5 до 1.0",
"ratio_set": "Мінімальна частка нових треків: {value:.2f}",
"sync_need_connect": "Спочатку /connect",
"sync_done": "Лайкнуті треки оновлено: {count}",
"lang_choose": "Оберіть мову інтерфейсу:",
"lang_set": "Мова інтерфейсу: {lang_name}",
"lang_invalid": "Підтримуються лише `ru`, `en`, `uk` та `nl`.",
"unknown_action": "Не зрозумів команду. Використовуйте кнопки нижче або /help.",
"main_menu_hint": "Головне меню",
"settings_menu_hint": "Налаштування",
"status_yes": "так",
"status_no": "ні",
"status_text": (
"Connected: {connected}\n"
"Spotify user: {spotify_user}\n"
"Liked tracks cached: {saved_count}\n"
"Playlist size: {playlist_size}\n"
"Min new ratio: {min_new_ratio:.2f}\n"
"Last generated: {last_generated}"
),
"status_last_run": "Last run: {status}, tracks={tracks}",
"status_last_url": "{url}",
},
"nl": {
"button_connect": "🔗 Spotify koppelen",
"button_generate": "✨ Genereren",
"button_status": "📊 Status",
"button_latest": "🎵 Laatste playlist",
"button_sync": "🔄 Likes syncen",
"button_help": "❓ Help",
"button_settings": "⚙️ Instellingen",
"button_language": "🌐 Taal / Language",
"button_size_20": "📏 Grootte 20",
"button_size_30": "📏 Grootte 30",
"button_size_50": "📏 Grootte 50",
"button_ratio_60": "🆕 Nieuw 60%",
"button_ratio_80": "🆕 Nieuw 80%",
"button_ratio_100": "🆕 Nieuw 100%",
"button_lang_ru": "🇺🇦 Русский",
"button_lang_en": "🇺🇸 English",
"button_lang_uk": "🇺🇦 Українська",
"button_lang_nl": "🇳🇱 Nederlands",
"button_back": "⬅️ Terug",
"start_text": (
"Ik ben een Spotify daily vibe playlist-bot.\n"
"Ik maak playlists op basis van je recent listening + liked tracks en probeer ze fris te houden met minder herhalingen.\n\n"
"/connect - Spotify koppelen\n"
"/status - accountstatus en laatste playlist\n"
"/generate - nu een playlist genereren\n"
"/latest - link naar de laatste playlist tonen\n"
"/setsize N - playlistgrootte\n"
"/setratio X - minimale nieuwe ratio (0.5..1.0)\n"
"/sync - liked tracks uit Spotify synchroniseren\n"
"/lang [ru|en|uk|nl] - taal wijzigen"
),
"help_text": (
"Ik ben een Spotify daily vibe playlist-bot.\n"
"Ik maak playlists op basis van je recent listening + liked tracks en probeer ze fris te houden met minder herhalingen.\n\n"
"/connect - Spotify koppelen\n"
"/status - accountstatus en laatste playlist\n"
"/generate - nu een playlist genereren\n"
"/latest - link naar de laatste playlist tonen\n"
"/setsize N - playlistgrootte\n"
"/setratio X - minimale nieuwe ratio (0.5..1.0)\n"
"/sync - liked tracks uit Spotify synchroniseren\n"
"/lang [ru|en|uk|nl] - taal wijzigen"
),
"connect_open_link": "Open de link en autoriseer Spotify:\n{url}",
"spotify_connected_notice": "Spotify gekoppeld: {display_name}\nJe kunt nu /generate gebruiken",
"user_not_found_start": "Gebruiker niet gevonden. Stuur /start",
"no_playlist_yet": "Nog geen gegenereerde playlist.",
"generate_wait": "Playlist wordt gegenereerd, dit kan 20-60 seconden duren...",
"error_prefix": "Fout: {message}",
"size_usage": "Gebruik: /setsize 30",
"size_invalid": "Grootte moet een getal zijn van 5 t/m 100.",
"size_set": "Playlistgrootte ingesteld op: {value}",
"ratio_usage": "Gebruik: /setratio 0.8",
"ratio_invalid": "Waarde moet tussen 0.5 en 1.0 zijn",
"ratio_set": "Minimale nieuwe ratio: {value:.2f}",
"sync_need_connect": "Gebruik eerst /connect",
"sync_done": "Liked tracks gesynchroniseerd: {count}",
"lang_choose": "Kies de interfacetaal:",
"lang_set": "Interfacetaal: {lang_name}",
"lang_invalid": "Alleen `ru`, `en`, `uk` en `nl` worden ondersteund.",
"unknown_action": "Ik begreep dat niet. Gebruik de knoppen hieronder of /help.",
"main_menu_hint": "Hoofdmenu",
"settings_menu_hint": "Instellingen",
"status_yes": "ja",
"status_no": "nee",
"status_text": (
"Connected: {connected}\n"
"Spotify user: {spotify_user}\n"
"Liked tracks cached: {saved_count}\n"
"Playlist size: {playlist_size}\n"
"Min new ratio: {min_new_ratio:.2f}\n"
"Last generated: {last_generated}"
),
"status_last_run": "Last run: {status}, tracks={tracks}",
"status_last_url": "{url}",
},
}
LANG_NAMES = {"ru": "Русский", "en": "English"}
LANG_NAMES = {"ru": "Русский", "en": "English", "uk": "Українська", "nl": "Nederlands"}
SUPPORTED_LANGS = frozenset(LANG_NAMES.keys())
class TelegramBotRunner:
@@ -216,7 +375,7 @@ class TelegramBotRunner:
await self.application.bot.send_message(chat_id=int(chat_id), text=text, disable_web_page_preview=False)
async def send_spotify_connected_notice(self, chat_id: str, display_name: str) -> None:
lang = self._chat_lang.get(str(chat_id), "ru")
lang = self._chat_lang.get(str(chat_id), "en")
await self.send_message(chat_id, self._t(lang, "spotify_connected_notice", display_name=display_name))
async def _ensure_user(self, update: Update):
@@ -241,17 +400,24 @@ class TelegramBotRunner:
chat = update.effective_chat
if chat:
cached = self._chat_lang.get(str(chat.id))
if cached in {"ru", "en"}:
if cached in SUPPORTED_LANGS:
return cached
user = update.effective_user
tg_lang = (getattr(user, "language_code", None) or "").lower()
lang = "ru" if tg_lang.startswith("ru") else "en"
if tg_lang.startswith("uk") or tg_lang.startswith("ua"):
lang = "uk"
elif tg_lang.startswith("ru"):
lang = "ru"
elif tg_lang.startswith("nl"):
lang = "nl"
else:
lang = "en"
if chat:
self._chat_lang[str(chat.id)] = lang
return lang
def _set_chat_lang(self, chat_id: str, lang: Lang) -> None:
if lang in {"ru", "en"}:
if lang in SUPPORTED_LANGS:
self._chat_lang[str(chat_id)] = lang
async def _reply(
@@ -299,12 +465,13 @@ class TelegramBotRunner:
def _language_menu(self, lang: Lang) -> ReplyKeyboardMarkup:
kb = [
[self._t(lang, "button_lang_ru"), self._t(lang, "button_lang_en")],
[self._t(lang, "button_lang_uk"), self._t(lang, "button_lang_nl")],
[self._t(lang, "button_back")],
]
return ReplyKeyboardMarkup(kb, resize_keyboard=True, is_persistent=True)
def _button_labels(self, key: str) -> set[str]:
return {I18N["ru"][key], I18N["en"][key]}
return {I18N[code][key] for code in SUPPORTED_LANGS if key in I18N.get(code, {})}
async def on_text_button(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
text = (update.effective_message.text if update.effective_message else "") or ""
@@ -331,6 +498,12 @@ class TelegramBotRunner:
if text in self._button_labels("button_lang_en"):
await self._set_language_from_ui(update, "en", reply_markup=self._settings_menu("en"))
return
if text in self._button_labels("button_lang_uk"):
await self._set_language_from_ui(update, "uk", reply_markup=self._settings_menu("uk"))
return
if text in self._button_labels("button_lang_nl"):
await self._set_language_from_ui(update, "nl", reply_markup=self._settings_menu("nl"))
return
if text in self._button_labels("button_back"):
await self._reply(update, self._t(lang, "main_menu_hint"), lang=lang)
return
@@ -398,6 +571,12 @@ class TelegramBotRunner:
if requested.startswith("en"):
await self._set_language_from_ui(update, "en")
return
if requested.startswith("uk") or requested.startswith("ua"):
await self._set_language_from_ui(update, "uk")
return
if requested.startswith("nl"):
await self._set_language_from_ui(update, "nl")
return
await self._reply(update, self._t(lang, "lang_invalid"), lang=lang)
return
await self._reply(