289 lines
18 KiB
Python
289 lines
18 KiB
Python
"""
|
||
Internationalization module for the Lichess Telegram Bot
|
||
Supports English and Russian languages
|
||
"""
|
||
|
||
from typing import Optional
|
||
|
||
# Translation dictionary (English and Russian)
|
||
TRANSLATIONS = {
|
||
'en': {
|
||
# Start command
|
||
'start_message': (
|
||
"🎯 The bot tracks one or more players on Lichess.\n\n"
|
||
"📈 It shows ratings, daily, yesterday and weekly activity.\n\n"
|
||
"🧩 When specifying a user_token (with puzzle-read permission), you can also get puzzle data.\n\n"
|
||
"⏱️ The bot supports automatic checks at specified intervals\n"
|
||
"📨 …and sends a report if there was activity during that time.\n\n"
|
||
"Example for a week:\n"
|
||
"🧩 Puzzles: 114 (✅ 81 - ❌ 33)\n\n"
|
||
"🔥 Blitz — 5 games • 🔴 -10\n"
|
||
"Rating: 2245\n"
|
||
"✅ Wins: 1\n"
|
||
"❌ Losses: 3\n"
|
||
"🤝 Draws: 1\n\n"
|
||
"🐇 Rapid — 19 games • 🟢 +20\n"
|
||
"Rating: 2248\n"
|
||
"✅ Wins: 8\n"
|
||
"❌ Losses: 4\n"
|
||
"🤝 Draws: 7\n\n"
|
||
"📋 Available commands:\n\n"
|
||
"➕ /addgamer — Add a Lichess player to track (username only)\n"
|
||
"🔑 /addtoken — Add a player with a token to get puzzle data\n"
|
||
"👥 /getgamers — View statistics of your players\n"
|
||
"🚫 /delgamer — Remove a player from the tracked list\n\n"
|
||
"📅 /today — Statistics for today\n"
|
||
"📆 /yesterday — Statistics for yesterday\n"
|
||
"📆 /week — Statistics for the week\n"
|
||
"📈 /lastYear_or_1000games — Statistics for the last year or last 1000 rated games\n\n"
|
||
"⏱️ /setperiod — Set up periodic notifications for the active player\n"
|
||
" ↳ (active player changes in the /getgamers menu)\n\n"
|
||
"👤 /profile — View Lichess profile links for tracked players\n"
|
||
"🌍 /set_lang — Select bot language (English / Russian)\n"
|
||
"💬 /support — Contact the developer for support and feedback"
|
||
),
|
||
|
||
# Add gamer commands
|
||
'addgamer_prompt': "👤 Enter the Lichess username of the player to track:",
|
||
'addtoken_prompt': (
|
||
"🔑 Enter the Lichess API token to get puzzle data.\n"
|
||
"The token is created in profile settings — give it only puzzle:read permission.\n"
|
||
"After that, the player corresponding to this token will be added."
|
||
),
|
||
'token_added': "✅ Token added for player {username}!",
|
||
'gamer_added_with_token': "✅ Player {username} added with token!",
|
||
'gamer_added': "✅ Player {username} successfully added!\n\n🔗 Profile: <a href=\"https://lichess.org/@/{username}\">{username}</a>\n\nTo add another player, use /addgamer",
|
||
'invalid_token': "❌ Invalid token. Please try again.",
|
||
'token_username_error': "❌ Failed to get username from token. Please try again.",
|
||
'empty_username': "❌ Username cannot be empty. Please try again.",
|
||
'user_not_found': "❌ Player {username} not found on Lichess. Check the spelling of the name.",
|
||
'gamer_already_added': "ℹ️ Player {username} is already being tracked.\n\nTo add another player, use /addgamer",
|
||
|
||
# Get gamers
|
||
'no_gamers': "📭 No players in database. Use /addgamer to add.",
|
||
'loading_ratings': "🔄 Loading player ratings...",
|
||
'select_active_gamer': "👥 <b>Player statistics:</b>\n\n",
|
||
'active_gamer_set': "✅ Active player: {username}",
|
||
'gamer_not_found': "❌ Player not found",
|
||
|
||
# Delete gamer
|
||
'no_gamers_to_delete': "📭 You have no tracked players.",
|
||
'loading_gamers': "🔄 Loading player list...",
|
||
'select_gamer_to_delete': "🗑️ <b>Select player to delete:</b>\n\n",
|
||
'gamer_deleted': "✅ Player <b>{username}</b> removed from tracked list.",
|
||
'active_gamer_deleted': "✅ Player <b>{username}</b> removed from tracked list.\n\n⚠️ You deleted the active player. Use the /getgamers command to select a player for which the /today, /yesterday, /week commands will work.",
|
||
'last_gamer_deleted': "✅ Player <b>{username}</b> removed from tracked list.\n\n⚠️ You deleted the last player. Use the /addgamer command to add a new tracked player.",
|
||
'delete_failed': "❌ Failed to delete player",
|
||
|
||
# Stats commands
|
||
'unknown_period': "❌ Unknown period",
|
||
'no_data': "📭 No data",
|
||
'stats_title': "📊 Statistics {username} • {date_range}\n\n",
|
||
'puzzles_section': "🧩 Puzzles: {total} (✅ {solved} - ❌ {unsolved})\n\n",
|
||
'games_section': "{emoji} {game_type} — {games_count} games • {rating_change}\nRating: {rating}\n✅ Wins: {wins}\n❌ Losses: {losses}\n🤝 Draws: {draws}\n\n",
|
||
|
||
# Set period
|
||
'select_gamer_for_period': "⏱️ <b>Select player to set notification period:</b>\n\n",
|
||
'select_period': "⏱️ Select period for player {username}:\n📱 Notifications will be sent to personal messages",
|
||
'notifications_disabled': "✅ Notifications disabled for {username}",
|
||
'period_set': "✅ Period {period} minutes set for {username}\n📱 Notifications will be sent to personal messages",
|
||
'disable_notifications': "❌ Disable notifications",
|
||
'period_minutes': "⏰ {period} minutes",
|
||
|
||
# Period notification
|
||
'period_1_minute': "for 1 minute",
|
||
'period_2_3_4_minutes': "for {period} minutes",
|
||
'period_minutes_text': "for {period} minutes",
|
||
'period_notification_title': "📊 Statistics {username} • {period_text}\n\n",
|
||
'period_puzzles_section': "🧩 Puzzles: {total} (✅ {solved} - ❌ {failed})\n\n",
|
||
'period_games_section': "{emoji} {game_type} — {games_count} games • {rating_change}\nRating: {rating}\n✅ Wins: {wins}\n❌ Losses: {losses}\n🤝 Draws: {draws}\n\n",
|
||
'no_activity': "📭 No activity for this period",
|
||
|
||
# Last year or 1000 games
|
||
'last_year_1000_processing': "⏳ Processing request... This may take a while as requests are very slow.",
|
||
'last_year_1000_player_processing': "🔄 Requesting data for player <b>{username}</b>...",
|
||
|
||
# Stats commands (today/yesterday/week)
|
||
'stats_processing': "⏳ Processing request...",
|
||
'stats_player_processing': "🔄 Requesting data for player <b>{username}</b>...",
|
||
'stats_all_done': "✅ That's all",
|
||
|
||
# Support
|
||
'support_message': (
|
||
"💬 <b>Support & Feedback</b>\n\n"
|
||
"You can write to the service developer directly and report a problem, suggest new features, or ask a question.\n\n"
|
||
"I will be happy to review every message and respond to it.\n\n"
|
||
"The developer accepts messages in <b>English</b> and <b>Russian</b> languages.\n\n"
|
||
"📧 Contact: <a href=\"https://t.me/vrubelr\">@vrubelr</a>\n\n"
|
||
"📦 Bot version: <b>{version}</b>"
|
||
),
|
||
|
||
# Common
|
||
'period_minutes_suffix': "m",
|
||
|
||
# Language selection
|
||
'select_language': "🌐 <b>Select language / Выберите язык:</b>",
|
||
'language_set_en': "✅ Language set to English",
|
||
'language_set_ru': "✅ Язык установлен: Русский",
|
||
|
||
# Profile command
|
||
'select_player_profile': "👤 <b>Select player:</b>",
|
||
'profile_link_sent': "✅ Profile link sent",
|
||
},
|
||
'ru': {
|
||
# Start command
|
||
'start_message': (
|
||
"🎯 Бот отслеживает одного или нескольких игроков на Lichess.\n\n"
|
||
"📈 Показывает рейтинги, активность за сегодня, вчера и за неделю.\n\n"
|
||
"🧩 При указании user_token (с разрешением puzzle-read) можно также получать данные по пазлам.\n\n"
|
||
"⏱️ Бот поддерживает автоматические проверки с заданными интервалами\n"
|
||
"📨 …и отправляет отчет, если была активность за это время.\n\n"
|
||
"Пример за неделю:\n"
|
||
"🧩 Пазлы: 114 (✅ 81 - ❌ 33)\n\n"
|
||
"🔥 Блиц — 5 игр • 🔴 -10\n"
|
||
"Рейтинг: 2245\n"
|
||
"✅ Побед: 1\n"
|
||
"❌ Поражений: 3\n"
|
||
"🤝 Ничьих: 1\n\n"
|
||
"🐇 Рапид — 19 игр • 🟢 +20\n"
|
||
"Рейтинг: 2248\n"
|
||
"✅ Побед: 8\n"
|
||
"❌ Поражений: 4\n"
|
||
"🤝 Ничьих: 7\n\n"
|
||
"📋 Доступные команды:\n\n"
|
||
"➕ /addgamer — Добавить игрока Lichess для отслеживания (только username)\n"
|
||
"🔑 /addtoken — Добавить игрока с токеном для получения данных по пазлам\n"
|
||
"👥 /getgamers — Посмотрите статистику своих игроков\n"
|
||
"🚫 /delgamer — Удалить игрока из списка отслеживаемых\n\n"
|
||
"📅 /today — Статистика за сегодня\n"
|
||
"📆 /yesterday — Статистика за вчера\n"
|
||
"📆 /week — Статистика за неделю\n"
|
||
"📈 /lastYear_or_1000games — Статистика за последний год или последние 1000 рейтинговых игр\n\n"
|
||
"⏱️ /setperiod — Настроить периодические уведомления для активного игрока\n"
|
||
" ↳ (активный игрок меняется в меню /getgamers)\n\n"
|
||
"👤 /profile — Просмотр ссылок на профили Lichess отслеживаемых игроков\n"
|
||
"🌍 /set_lang — Выбрать язык бота\n"
|
||
"💬 /support — Связаться с разработчиком для поддержки и обратной связи"
|
||
),
|
||
|
||
# Add gamer commands
|
||
'addgamer_prompt': "👤 Введите username игрока Lichess для отслеживания:",
|
||
'addtoken_prompt': (
|
||
"🔑 Введите токен API Lichess для получения данных по пазлам.\n"
|
||
"Токен создается в настройках профиля — дайте ему только разрешение puzzle:read.\n"
|
||
"После этого будет добавлен игрок, соответствующий этому токену."
|
||
),
|
||
'token_added': "✅ Токен добавлен для игрока {username}!",
|
||
'gamer_added_with_token': "✅ Игрок {username} добавлен с токеном!",
|
||
'gamer_added': "✅ Игрок {username} успешно добавлен!\n\n🔗 Профиль: <a href=\"https://lichess.org/@/{username}\">{username}</a>\n\nДля добавления следующего игрока воспользуйтесь /addgamer",
|
||
'invalid_token': "❌ Неверный токен. Попробуйте еще раз.",
|
||
'token_username_error': "❌ Не удалось получить username из токена. Попробуйте еще раз.",
|
||
'empty_username': "❌ Username не может быть пустым. Попробуйте еще раз.",
|
||
'user_not_found': "❌ Игрок {username} не найден на Lichess. Проверьте правильность написания имени.",
|
||
'gamer_already_added': "ℹ️ Игрок {username} уже отслеживается.\n\nДля добавления следующего игрока воспользуйтесь /addgamer",
|
||
|
||
# Get gamers
|
||
'no_gamers': "📭 Нет игроков в базе данных. Используйте /addgamer для добавления.",
|
||
'loading_ratings': "🔄 Загрузка рейтингов игроков...",
|
||
'select_active_gamer': "👥 <b>Статистика игроков:</b>\n\n",
|
||
'active_gamer_set': "✅ Активный игрок: {username}",
|
||
'gamer_not_found': "❌ Игрок не найден",
|
||
|
||
# Delete gamer
|
||
'no_gamers_to_delete': "📭 У вас нет отслеживаемых игроков.",
|
||
'loading_gamers': "🔄 Загрузка списка игроков...",
|
||
'select_gamer_to_delete': "🗑️ <b>Выберите игрока для удаления:</b>\n\n",
|
||
'gamer_deleted': "✅ Игрок <b>{username}</b> удален из списка отслеживаемых.",
|
||
'active_gamer_deleted': "✅ Игрок <b>{username}</b> удален из списка отслеживаемых.\n\n⚠️ Вы удалили активного игрока. Используйте команду /getgamers для выбора игрока, для которого будут работать команды /today, /yesterday, /week.",
|
||
'last_gamer_deleted': "✅ Игрок <b>{username}</b> удален из списка отслеживаемых.\n\n⚠️ Вы удалили последнего игрока. Используйте команду /addgamer для добавления нового отслеживаемого игрока.",
|
||
'delete_failed': "❌ Не удалось удалить игрока",
|
||
|
||
# Stats commands
|
||
'unknown_period': "❌ Неизвестный период",
|
||
'no_data': "📭 Нет данных",
|
||
'stats_title': "📊 Статистика {username} • {date_range}\n\n",
|
||
'puzzles_section': "🧩 Пазлы: {total} (✅ {solved} - ❌ {unsolved})\n\n",
|
||
'games_section': "{emoji} {game_type} — {games_count} игр • {rating_change}\nРейтинг: {rating}\n✅ Побед: {wins}\n❌ Поражений: {losses}\n🤝 Ничьих: {draws}\n\n",
|
||
|
||
# Set period
|
||
'select_gamer_for_period': "⏱️ <b>Выберите игрока для установки периода уведомлений:</b>\n\n",
|
||
'select_period': "⏱️ Выберите период для игрока {username}:\n📱 Уведомления будут отправляться в личные сообщения",
|
||
'notifications_disabled': "✅ Уведомления отключены для {username}",
|
||
'period_set': "✅ Период {period} минут установлен для {username}\n📱 Уведомления будут отправляться в личные сообщения",
|
||
'disable_notifications': "❌ Отключить уведомления",
|
||
'period_minutes': "⏰ {period} минут",
|
||
|
||
# Period notification
|
||
'period_1_minute': "за 1 минуту",
|
||
'period_2_3_4_minutes': "за {period} минуты",
|
||
'period_minutes_text': "за {period} минут",
|
||
'period_notification_title': "📊 Статистика {username} • {period_text}\n\n",
|
||
'period_puzzles_section': "🧩 Пазлы: {total} (✅ {solved} - ❌ {failed})\n\n",
|
||
'period_games_section': "{emoji} {game_type} — {games_count} игр • {rating_change}\nРейтинг: {rating}\n✅ Побед: {wins}\n❌ Поражений: {losses}\n🤝 Ничьих: {draws}\n\n",
|
||
'no_activity': "📭 Нет активности за этот период",
|
||
|
||
# Last year or 1000 games
|
||
'last_year_1000_processing': "⏳ Обработка запроса... Это может занять некоторое время, так как запросы очень медленные.",
|
||
'last_year_1000_player_processing': "🔄 Запрос данных для игрока <b>{username}</b>...",
|
||
|
||
# Stats commands (today/yesterday/week)
|
||
'stats_processing': "⏳ Обработка запроса...",
|
||
'stats_player_processing': "🔄 Запрос данных для игрока <b>{username}</b>...",
|
||
'stats_all_done': "✅ Это всё",
|
||
|
||
# Support
|
||
'support_message': (
|
||
"💬 <b>Поддержка и обратная связь</b>\n\n"
|
||
"Вы можете написать разработчику сервиса напрямую и сообщить о проблеме, предложить новые функции или задать вопрос.\n\n"
|
||
"Я буду рад рассмотреть каждое сообщение и ответить на него.\n\n"
|
||
"Разработчик принимает сообщения на <b>английском</b> и <b>русском</b> языках.\n\n"
|
||
"📧 Контакт: <a href=\"https://t.me/vrubelr\">@vrubelr</a>\n\n"
|
||
"📦 Версия бота: <b>{version}</b>"
|
||
),
|
||
|
||
# Common
|
||
'period_minutes_suffix': "м",
|
||
|
||
# Language selection
|
||
'select_language': "🌐 <b>Выберите язык / Select language:</b>",
|
||
'language_set_en': "✅ Language set to English",
|
||
'language_set_ru': "✅ Язык установлен: Русский",
|
||
|
||
# Profile command
|
||
'select_player_profile': "👤 <b>Выберите игрока:</b>",
|
||
'profile_link_sent': "✅ Ссылка на профиль отправлена",
|
||
}
|
||
}
|
||
|
||
|
||
def t(key: str, lang: str = 'en', **kwargs) -> str:
|
||
"""
|
||
Translate a key to the specified language.
|
||
|
||
Args:
|
||
key: Translation key
|
||
lang: Language code ('en' or 'ru')
|
||
**kwargs: Format arguments for the translation string
|
||
|
||
Returns:
|
||
Translated string with formatted arguments
|
||
"""
|
||
# Default to English if language not supported
|
||
if lang not in TRANSLATIONS:
|
||
lang = 'en'
|
||
|
||
translation = TRANSLATIONS[lang].get(key)
|
||
|
||
# Fallback to English if translation not found
|
||
if translation is None:
|
||
translation = TRANSLATIONS['en'].get(key, key)
|
||
|
||
# Format the string if kwargs provided
|
||
if kwargs:
|
||
try:
|
||
return translation.format(**kwargs)
|
||
except KeyError:
|
||
# If formatting fails, return the translation as-is
|
||
return translation
|
||
|
||
return translation
|