From c12f3d443ce7070bbc9961c6b804df39141711a4 Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 1 Oct 2025 00:08:03 +0300 Subject: [PATCH] Add English localization for admin support menus --- app/handlers/admin/main.py | 69 +++++----- app/handlers/admin/support_settings.py | 170 ++++++++++++++++--------- app/localization/locales/en.json | 60 ++++++++- app/localization/locales/ru.json | 60 ++++++++- locales/en.json | 58 +++++++++ locales/ru.json | 58 +++++++++ 6 files changed, 383 insertions(+), 92 deletions(-) diff --git a/app/handlers/admin/main.py b/app/handlers/admin/main.py index 3be7d267..c6f2bbe1 100644 --- a/app/handlers/admin/main.py +++ b/app/handlers/admin/main.py @@ -71,10 +71,10 @@ async def show_users_submenu( db: AsyncSession ): texts = get_texts(db_user.language) - + await callback.message.edit_text( - "👥 **Управление пользователями и подписками**\n\n" - "Выберите нужный раздел:", + texts.t("ADMIN_USERS_SUBMENU_TITLE", "👥 **Управление пользователями и подписками**\n\n") + + texts.t("ADMIN_SUBMENU_SELECT_SECTION", "Выберите нужный раздел:"), reply_markup=get_admin_users_submenu_keyboard(db_user.language), parse_mode="Markdown" ) @@ -89,10 +89,10 @@ async def show_promo_submenu( db: AsyncSession ): texts = get_texts(db_user.language) - + await callback.message.edit_text( - "💰 **Промокоды и статистика**\n\n" - "Выберите нужный раздел:", + texts.t("ADMIN_PROMO_SUBMENU_TITLE", "💰 **Промокоды и статистика**\n\n") + + texts.t("ADMIN_SUBMENU_SELECT_SECTION", "Выберите нужный раздел:"), reply_markup=get_admin_promo_submenu_keyboard(db_user.language), parse_mode="Markdown" ) @@ -107,10 +107,10 @@ async def show_communications_submenu( db: AsyncSession ): texts = get_texts(db_user.language) - + await callback.message.edit_text( - "📨 **Коммуникации**\n\n" - "Управление рассылками и текстами интерфейса:", + texts.t("ADMIN_COMMUNICATIONS_SUBMENU_TITLE", "📨 **Коммуникации**\n\n") + + texts.t("ADMIN_COMMUNICATIONS_SUBMENU_DESCRIPTION", "Управление рассылками и текстами интерфейса:"), reply_markup=get_admin_communications_submenu_keyboard(db_user.language), parse_mode="Markdown" ) @@ -132,11 +132,15 @@ async def show_support_submenu( if is_moderator_only: # Rebuild keyboard to include only tickets and back to main menu kb = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text="🎫 Тикеты поддержки", callback_data="admin_tickets")], - [InlineKeyboardButton(text="⬅️ Назад", callback_data="back_to_menu")] + [InlineKeyboardButton(text=texts.t("ADMIN_SUPPORT_TICKETS", "🎫 Тикеты поддержки"), callback_data="admin_tickets")], + [InlineKeyboardButton(text=texts.BACK, callback_data="back_to_menu")] ]) await callback.message.edit_text( - "🛟 **Поддержка**\n\n" + ("Доступ к тикетам." if is_moderator_only else "Управление тикетами и настройками поддержки:"), + texts.t("ADMIN_SUPPORT_SUBMENU_TITLE", "🛟 **Поддержка**\n\n") + ( + texts.t("ADMIN_SUPPORT_SUBMENU_DESCRIPTION_MODERATOR", "Доступ к тикетам.") + if is_moderator_only + else texts.t("ADMIN_SUPPORT_SUBMENU_DESCRIPTION", "Управление тикетами и настройками поддержки:") + ), reply_markup=kb, parse_mode="Markdown" ) @@ -149,12 +153,14 @@ async def show_moderator_panel( db_user: User, db: AsyncSession ): + texts = get_texts(db_user.language) kb = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text="🎫 Тикеты поддержки", callback_data="admin_tickets")], - [InlineKeyboardButton(text="⬅️ В главное меню", callback_data="back_to_menu")] + [InlineKeyboardButton(text=texts.t("ADMIN_SUPPORT_TICKETS", "🎫 Тикеты поддержки"), callback_data="admin_tickets")], + [InlineKeyboardButton(text=texts.t("BACK_TO_MAIN_MENU_BUTTON", "⬅️ В главное меню"), callback_data="back_to_menu")] ]) await callback.message.edit_text( - "🧑‍⚖️ Модерация поддержки\n\nДоступ к тикетам поддержки.", + texts.t("ADMIN_SUPPORT_MODERATION_TITLE", "🧑‍⚖️ Модерация поддержки") + "\n\n" + + texts.t("ADMIN_SUPPORT_MODERATION_DESCRIPTION", "Доступ к тикетам поддержки."), parse_mode="HTML", reply_markup=kb ) @@ -168,6 +174,7 @@ async def show_support_audit( db_user: User, db: AsyncSession ): + texts = get_texts(db_user.language) # pagination page = 1 if callback.data.startswith("admin_support_audit_page_"): @@ -185,18 +192,22 @@ async def show_support_audit( offset = (page - 1) * per_page logs = await TicketCRUD.list_support_audit(db, limit=per_page, offset=offset) - lines = ["🧾 Аудит модераторов", ""] + lines = [texts.t("ADMIN_SUPPORT_AUDIT_TITLE", "🧾 Аудит модераторов"), ""] if not logs: - lines.append("Пока пусто") + lines.append(texts.t("ADMIN_SUPPORT_AUDIT_EMPTY", "Пока пусто")) else: for log in logs: - role = "Модератор" if getattr(log, 'is_moderator', False) else "Админ" + role = ( + texts.t("ADMIN_SUPPORT_AUDIT_ROLE_MODERATOR", "Модератор") + if getattr(log, 'is_moderator', False) + else texts.t("ADMIN_SUPPORT_AUDIT_ROLE_ADMIN", "Админ") + ) ts = log.created_at.strftime('%d.%m.%Y %H:%M') if getattr(log, 'created_at', None) else '' action_map = { - 'close_ticket': 'Закрытие тикета', - 'block_user_timed': 'Блокировка (время)', - 'block_user_perm': 'Блокировка (навсегда)', - 'unblock_user': 'Снятие блока', + 'close_ticket': texts.t("ADMIN_SUPPORT_AUDIT_ACTION_CLOSE_TICKET", "Закрытие тикета"), + 'block_user_timed': texts.t("ADMIN_SUPPORT_AUDIT_ACTION_BLOCK_TIMED", "Блокировка (время)"), + 'block_user_perm': texts.t("ADMIN_SUPPORT_AUDIT_ACTION_BLOCK_PERM", "Блокировка (навсегда)"), + 'unblock_user': texts.t("ADMIN_SUPPORT_AUDIT_ACTION_UNBLOCK", "Снятие блока"), } action_text = action_map.get(log.action, log.action) ticket_part = f" тикет #{log.ticket_id}" if log.ticket_id else "" @@ -218,7 +229,7 @@ async def show_support_audit( kb_rows = [] if nav_row: kb_rows.append(nav_row) - kb_rows.append([InlineKeyboardButton(text="⬅️ Назад", callback_data="admin_submenu_support")]) + kb_rows.append([InlineKeyboardButton(text=texts.BACK, callback_data="admin_submenu_support")]) kb = InlineKeyboardMarkup(inline_keyboard=kb_rows) await callback.message.edit_text("\n".join(lines), parse_mode="HTML", reply_markup=kb) @@ -233,10 +244,10 @@ async def show_settings_submenu( db: AsyncSession ): texts = get_texts(db_user.language) - + await callback.message.edit_text( - "⚙️ **Настройки системы**\n\n" - "Управление Remnawave, мониторингом и другими настройками:", + texts.t("ADMIN_SETTINGS_SUBMENU_TITLE", "⚙️ **Настройки системы**\n\n") + + texts.t("ADMIN_SETTINGS_SUBMENU_DESCRIPTION", "Управление Remnawave, мониторингом и другими настройками:"), reply_markup=get_admin_settings_submenu_keyboard(db_user.language), parse_mode="Markdown" ) @@ -251,10 +262,10 @@ async def show_system_submenu( db: AsyncSession ): texts = get_texts(db_user.language) - + await callback.message.edit_text( - "🛠️ **Системные функции**\n\n" - "Отчеты, обновления, логи, резервные копии и системные операции:", + texts.t("ADMIN_SYSTEM_SUBMENU_TITLE", "🛠️ **Системные функции**\n\n") + + texts.t("ADMIN_SYSTEM_SUBMENU_DESCRIPTION", "Отчеты, обновления, логи, резервные копии и системные операции:"), reply_markup=get_admin_system_submenu_keyboard(db_user.language), parse_mode="Markdown" ) diff --git a/app/handlers/admin/support_settings.py b/app/handlers/admin/support_settings.py index 5ab4d3af..ffd9c27f 100644 --- a/app/handlers/admin/support_settings.py +++ b/app/handlers/admin/support_settings.py @@ -29,33 +29,63 @@ def _get_support_settings_keyboard(language: str) -> types.InlineKeyboardMarkup: rows: list[list[types.InlineKeyboardButton]] = [] + status_enabled = texts.t("ADMIN_SUPPORT_SETTINGS_STATUS_ENABLED", "Включены") + status_disabled = texts.t("ADMIN_SUPPORT_SETTINGS_STATUS_DISABLED", "Отключены") + + def mode_button(label_key: str, default: str, active: bool) -> str: + prefix = "🔘" if active else "⚪" + return f"{prefix} {texts.t(label_key, default)}" + rows.append([ types.InlineKeyboardButton( - text=("✅ Пункт 'Техподдержка' в меню" if menu_enabled else "🚫 Пункт 'Техподдержка' в меню"), + text=( + f"{'✅' if menu_enabled else '🚫'} " + f"{texts.t('ADMIN_SUPPORT_SETTINGS_MENU_LABEL', 'Пункт «Техподдержка» в меню')}" + ), callback_data="admin_support_toggle_menu" ) ]) rows.append([ - types.InlineKeyboardButton(text=("🔘 Тикеты" if mode == "tickets" else "⚪ Тикеты"), callback_data="admin_support_mode_tickets"), - types.InlineKeyboardButton(text=("🔘 Контакт" if mode == "contact" else "⚪ Контакт"), callback_data="admin_support_mode_contact"), - types.InlineKeyboardButton(text=("🔘 Оба" if mode == "both" else "⚪ Оба"), callback_data="admin_support_mode_both"), + types.InlineKeyboardButton( + text=mode_button("ADMIN_SUPPORT_SETTINGS_MODE_TICKETS", "Тикеты", mode == "tickets"), + callback_data="admin_support_mode_tickets" + ), + types.InlineKeyboardButton( + text=mode_button("ADMIN_SUPPORT_SETTINGS_MODE_CONTACT", "Контакт", mode == "contact"), + callback_data="admin_support_mode_contact" + ), + types.InlineKeyboardButton( + text=mode_button("ADMIN_SUPPORT_SETTINGS_MODE_BOTH", "Оба", mode == "both"), + callback_data="admin_support_mode_both" + ), ]) rows.append([ - types.InlineKeyboardButton(text="📝 Изменить описание", callback_data="admin_support_edit_desc") + types.InlineKeyboardButton( + text=texts.t("ADMIN_SUPPORT_SETTINGS_EDIT_DESCRIPTION", "📝 Изменить описание"), + callback_data="admin_support_edit_desc" + ) ]) # Notifications block rows.append([ types.InlineKeyboardButton( - text=("🔔 Админ-уведомления: Включены" if admin_notif else "🔕 Админ-уведомления: Отключены"), + text=( + f"{'🔔' if admin_notif else '🔕'} " + f"{texts.t('ADMIN_SUPPORT_SETTINGS_ADMIN_NOTIFICATIONS', 'Админ-уведомления')}: " + f"{status_enabled if admin_notif else status_disabled}" + ), callback_data="admin_support_toggle_admin_notifications" ) ]) rows.append([ types.InlineKeyboardButton( - text=("🔔 Пользовательские уведомления: Включены" if user_notif else "🔕 Пользовательские уведомления: Отключены"), + text=( + f"{'🔔' if user_notif else '🔕'} " + f"{texts.t('ADMIN_SUPPORT_SETTINGS_USER_NOTIFICATIONS', 'Пользовательские уведомления')}: " + f"{status_enabled if user_notif else status_disabled}" + ), callback_data="admin_support_toggle_user_notifications" ) ]) @@ -63,13 +93,17 @@ def _get_support_settings_keyboard(language: str) -> types.InlineKeyboardMarkup: # SLA block rows.append([ types.InlineKeyboardButton( - text=("⏰ SLA: Включено" if sla_enabled else "⏹️ SLA: Отключено"), + text=( + f"{'⏰' if sla_enabled else '⏹️'} " + f"{texts.t('ADMIN_SUPPORT_SETTINGS_SLA_LABEL', 'SLA')}: " + f"{status_enabled if sla_enabled else status_disabled}" + ), callback_data="admin_support_toggle_sla" ) ]) rows.append([ types.InlineKeyboardButton( - text=f"⏳ Время SLA: {sla_minutes} мин", + text=texts.t("ADMIN_SUPPORT_SETTINGS_SLA_TIME", "⏳ Время SLA: {minutes} мин").format(minutes=sla_minutes), callback_data="admin_support_set_sla_minutes" ) ]) @@ -79,15 +113,18 @@ def _get_support_settings_keyboard(language: str) -> types.InlineKeyboardMarkup: mod_count = len(moderators) rows.append([ types.InlineKeyboardButton( - text=f"🧑‍⚖️ Модераторы: {mod_count}", callback_data="admin_support_list_moderators" + text=texts.t("ADMIN_SUPPORT_SETTINGS_MODERATORS_COUNT", "🧑‍⚖️ Модераторы: {count}").format(count=mod_count), + callback_data="admin_support_list_moderators" ) ]) rows.append([ types.InlineKeyboardButton( - text="➕ Назначить модератора", callback_data="admin_support_add_moderator" + text=texts.t("ADMIN_SUPPORT_SETTINGS_ADD_MODERATOR", "➕ Назначить модератора"), + callback_data="admin_support_add_moderator" ), types.InlineKeyboardButton( - text="➖ Удалить модератора", callback_data="admin_support_remove_moderator" + text=texts.t("ADMIN_SUPPORT_SETTINGS_REMOVE_MODERATOR", "➖ Удалить модератора"), + callback_data="admin_support_remove_moderator" ) ]) @@ -108,8 +145,8 @@ async def show_support_settings( texts = get_texts(db_user.language) desc = SupportSettingsService.get_support_info_text(db_user.language) await callback.message.edit_text( - "🛟 Настройки поддержки\n\n" + - "Режим работы и видимость в меню. Ниже текущее описание меню поддержки:\n\n" + + texts.t("ADMIN_SUPPORT_SETTINGS_TITLE", "🛟 Настройки поддержки") + "\n\n" + + texts.t("ADMIN_SUPPORT_SETTINGS_DESCRIPTION", "Режим работы и видимость в меню. Ниже текущее описание меню поддержки:") + "\n\n" + desc, reply_markup=_get_support_settings_keyboard(db_user.language), parse_mode="HTML" @@ -161,11 +198,15 @@ class SupportAdvancedStates(StatesGroup): @admin_required @error_handler async def start_set_sla_minutes(callback: types.CallbackQuery, db_user: User, db: AsyncSession, state: FSMContext): + texts = get_texts(db_user.language) await callback.message.edit_text( - "⏳ Настройка SLA\n\nВведите количество минут ожидания ответа (целое число > 0):", + texts.t( + "ADMIN_SUPPORT_SLA_SETUP_PROMPT", + "⏳ Настройка SLA\n\nВведите количество минут ожидания ответа (целое число > 0):" + ), parse_mode="HTML", reply_markup=types.InlineKeyboardMarkup( - inline_keyboard=[[types.InlineKeyboardButton(text="⬅️ Назад", callback_data="admin_support_settings")]] + inline_keyboard=[[types.InlineKeyboardButton(text=texts.BACK, callback_data="admin_support_settings")]] ) ) await state.set_state(SupportAdvancedStates.waiting_for_sla_minutes) @@ -175,63 +216,53 @@ async def start_set_sla_minutes(callback: types.CallbackQuery, db_user: User, db @admin_required @error_handler async def handle_sla_minutes(message: types.Message, db_user: User, db: AsyncSession, state: FSMContext): + texts = get_texts(db_user.language) text = (message.text or "").strip() try: minutes = int(text) if minutes <= 0 or minutes > 1440: raise ValueError() except Exception: - await message.answer("❌ Введите корректное число минут (1-1440)") + await message.answer(texts.t("ADMIN_SUPPORT_SLA_INVALID", "❌ Введите корректное число минут (1-1440)")) return SupportSettingsService.set_sla_minutes(minutes) await state.clear() markup = types.InlineKeyboardMarkup( - inline_keyboard=[[types.InlineKeyboardButton(text="🗑 Удалить", callback_data="admin_support_delete_msg")]] + inline_keyboard=[[types.InlineKeyboardButton(text=texts.t("DELETE_MESSAGE", "🗑 Удалить"), callback_data="admin_support_delete_msg")]] ) - await message.answer("✅ Значение SLA сохранено", reply_markup=markup) + await message.answer(texts.t("ADMIN_SUPPORT_SLA_SAVED", "✅ Значение SLA сохранено"), reply_markup=markup) @admin_required @error_handler async def start_add_moderator(callback: types.CallbackQuery, db_user: User, db: AsyncSession, state: FSMContext): + texts = get_texts(db_user.language) await callback.message.edit_text( - "🧑‍⚖️ Назначение модератора\n\nОтправьте Telegram ID пользователя (число)", + texts.t( + "ADMIN_SUPPORT_ASSIGN_MODERATOR_PROMPT", + "🧑‍⚖️ Назначение модератора\n\nОтправьте Telegram ID пользователя (число)" + ), parse_mode="HTML", reply_markup=types.InlineKeyboardMarkup( - inline_keyboard=[[types.InlineKeyboardButton(text="⬅️ Назад", callback_data="admin_support_settings")]] + inline_keyboard=[[types.InlineKeyboardButton(text=texts.BACK, callback_data="admin_support_settings")]] ) ) await state.set_state(SupportAdvancedStates.waiting_for_moderator_id) await callback.answer() -@admin_required -@error_handler -async def handle_add_moderator(message: types.Message, db_user: User, db: AsyncSession, state: FSMContext): - text = (message.text or "").strip() - try: - tid = int(text) - except Exception: - await message.answer("❌ Введите корректный Telegram ID (число)") - return - if SupportSettingsService.add_moderator(tid): - markup = types.InlineKeyboardMarkup( - inline_keyboard=[[types.InlineKeyboardButton(text="🗑 Удалить", callback_data="admin_support_delete_msg")]] - ) - await message.answer(f"✅ Пользователь {tid} назначен модератором", reply_markup=markup) - else: - await message.answer("❌ Не удалось сохранить") - await state.clear() - - @admin_required @error_handler async def start_remove_moderator(callback: types.CallbackQuery, db_user: User, db: AsyncSession, state: FSMContext): + texts = get_texts(db_user.language) await callback.message.edit_text( - "🧑‍⚖️ Удаление модератора\n\nОтправьте Telegram ID пользователя (число)", + texts.t( + "ADMIN_SUPPORT_REMOVE_MODERATOR_PROMPT", + "🧑‍⚖️ Удаление модератора\n\nОтправьте Telegram ID пользователя (число)" + ), parse_mode="HTML", reply_markup=types.InlineKeyboardMarkup( - inline_keyboard=[[types.InlineKeyboardButton(text="⬅️ Назад", callback_data="admin_support_settings")]] + inline_keyboard=[[types.InlineKeyboardButton(text=texts.BACK, callback_data="admin_support_settings")]] ) ) await state.set_state(SupportAdvancedStates.waiting_for_moderator_id) @@ -243,24 +274,32 @@ async def start_remove_moderator(callback: types.CallbackQuery, db_user: User, d @admin_required @error_handler async def handle_moderator_id(message: types.Message, db_user: User, db: AsyncSession, state: FSMContext): + texts = get_texts(db_user.language) data = await state.get_data() action = data.get("action", "add") text = (message.text or "").strip() try: tid = int(text) except Exception: - await message.answer("❌ Введите корректный Telegram ID (число)") + await message.answer(texts.t("ADMIN_SUPPORT_INVALID_TELEGRAM_ID", "❌ Введите корректный Telegram ID (число)")) return - ok = False if action == "remove_moderator": ok = SupportSettingsService.remove_moderator(tid) - msg = "✅ Модератор удалён" if ok else "❌ Не удалось удалить" + msg = ( + texts.t("ADMIN_SUPPORT_MODERATOR_REMOVED_SUCCESS", "✅ Модератор {tid} удалён").format(tid=tid) + if ok + else texts.t("ADMIN_SUPPORT_MODERATOR_REMOVED_FAIL", "❌ Не удалось удалить модератора") + ) else: ok = SupportSettingsService.add_moderator(tid) - msg = "✅ Пользователь назначен модератором" if ok else "❌ Не удалось назначить" + msg = ( + texts.t("ADMIN_SUPPORT_MODERATOR_ADDED_SUCCESS", "✅ Пользователь {tid} назначен модератором").format(tid=tid) + if ok + else texts.t("ADMIN_SUPPORT_MODERATOR_ADDED_FAIL", "❌ Не удалось назначить модератора") + ) await state.clear() markup = types.InlineKeyboardMarkup( - inline_keyboard=[[types.InlineKeyboardButton(text="🗑 Удалить", callback_data="admin_support_delete_msg")]] + inline_keyboard=[[types.InlineKeyboardButton(text=texts.t("DELETE_MESSAGE", "🗑 Удалить"), callback_data="admin_support_delete_msg")]] ) await message.answer(msg, reply_markup=markup) @@ -268,13 +307,17 @@ async def handle_moderator_id(message: types.Message, db_user: User, db: AsyncSe @admin_required @error_handler async def list_moderators(callback: types.CallbackQuery, db_user: User, db: AsyncSession): + texts = get_texts(db_user.language) moderators = SupportSettingsService.get_moderators() if not moderators: - await callback.answer("Список пуст", show_alert=True) + await callback.answer(texts.t("ADMIN_SUPPORT_MODERATORS_EMPTY", "Список пуст"), show_alert=True) return - text = "🧑‍⚖️ Модераторы\n\n" + "\n".join([f"• {tid}" for tid in moderators]) + text = ( + texts.t("ADMIN_SUPPORT_MODERATORS_TITLE", "🧑‍⚖️ Модераторы") + + "\n\n" + "\n".join([f"• {tid}" for tid in moderators]) + ) markup = types.InlineKeyboardMarkup( - inline_keyboard=[[types.InlineKeyboardButton(text="⬅️ Назад", callback_data="admin_support_settings")]] + inline_keyboard=[[types.InlineKeyboardButton(text=texts.BACK, callback_data="admin_support_settings")]] ) await callback.message.edit_text(text, parse_mode="HTML", reply_markup=markup) await callback.answer() @@ -311,7 +354,10 @@ async def start_edit_desc(callback: types.CallbackQuery, db_user: User, db: Asyn kb_rows: list[list[types.InlineKeyboardButton]] = [] kb_rows.append([ - types.InlineKeyboardButton(text="📨 Прислать текст", callback_data="admin_support_send_desc") + types.InlineKeyboardButton( + text=texts.t("ADMIN_SUPPORT_SEND_DESCRIPTION", "📨 Прислать текст"), + callback_data="admin_support_send_desc" + ) ]) # Подготовим блок контакта (отдельным инлайном) from app.config import settings @@ -321,19 +367,19 @@ async def start_edit_desc(callback: types.CallbackQuery, db_user: User, db: Asyn ]) text_parts = [ - "📝 Редактирование описания поддержки", + texts.t("ADMIN_SUPPORT_EDIT_DESCRIPTION_TITLE", "📝 Редактирование описания поддержки"), "", - "Текущее описание:", + texts.t("ADMIN_SUPPORT_EDIT_DESCRIPTION_CURRENT", "Текущее описание:"), "", f"{html.escape(current_desc_plain)}", ] if support_contact_display: text_parts += [ "", - "Контакт для режима \u00abКонтакт\u00bb", + texts.t("ADMIN_SUPPORT_EDIT_DESCRIPTION_CONTACT_TITLE", "Контакт для режима «Контакт»"), f"{html.escape(support_contact_display)}", "", - "Добавьте в описание при необходимости.", + texts.t("ADMIN_SUPPORT_EDIT_DESCRIPTION_CONTACT_HINT", "Добавьте в описание при необходимости."), ] await callback.message.edit_text( "\n".join(text_parts), @@ -347,24 +393,26 @@ async def start_edit_desc(callback: types.CallbackQuery, db_user: User, db: Asyn @admin_required @error_handler async def handle_new_desc(message: types.Message, db_user: User, db: AsyncSession, state: FSMContext): + texts = get_texts(db_user.language) new_text = message.html_text or message.text SupportSettingsService.set_support_info_text(db_user.language, new_text) await state.clear() markup = types.InlineKeyboardMarkup( - inline_keyboard=[[types.InlineKeyboardButton(text="🗑 Удалить", callback_data="admin_support_delete_msg")]] + inline_keyboard=[[types.InlineKeyboardButton(text=texts.t("DELETE_MESSAGE", "🗑 Удалить"), callback_data="admin_support_delete_msg")]] ) - await message.answer("✅ Описание обновлено.", reply_markup=markup) + await message.answer(texts.t("ADMIN_SUPPORT_DESCRIPTION_UPDATED", "✅ Описание обновлено."), reply_markup=markup) @admin_required @error_handler async def send_desc_copy(callback: types.CallbackQuery, db_user: User, db: AsyncSession): # send plain text for easy copying + texts = get_texts(db_user.language) current_desc_html = SupportSettingsService.get_support_info_text(db_user.language) current_desc_plain = re.sub(r"<[^>]+>", "", current_desc_html) # attach delete button to the sent message markup = types.InlineKeyboardMarkup( - inline_keyboard=[[types.InlineKeyboardButton(text="🗑 Удалить", callback_data="admin_support_delete_msg")]] + inline_keyboard=[[types.InlineKeyboardButton(text=texts.t("DELETE_MESSAGE", "🗑 Удалить"), callback_data="admin_support_delete_msg")]] ) if len(current_desc_plain) <= 4000: await callback.message.answer(current_desc_plain, reply_markup=markup) @@ -376,7 +424,7 @@ async def send_desc_copy(callback: types.CallbackQuery, db_user: User, db: Async is_last = (chunk + 4000) >= len(current_desc_plain) await callback.message.answer(next_chunk, reply_markup=(markup if is_last else None)) chunk += 4000 - await callback.answer("Текст отправлен ниже") + await callback.answer(texts.t("ADMIN_SUPPORT_DESCRIPTION_SENT", "Текст отправлен ниже")) @error_handler @@ -386,15 +434,15 @@ async def delete_sent_message(callback: types.CallbackQuery, db_user: User, db: may_delete = (settings.is_admin(callback.from_user.id) or SupportSettingsService.is_moderator(callback.from_user.id)) except Exception: may_delete = False + texts = get_texts(db_user.language if db_user else 'ru') if not may_delete: - texts = get_texts(db_user.language if db_user else 'ru') await callback.answer(texts.ACCESS_DENIED, show_alert=True) return try: await callback.message.delete() finally: with contextlib.suppress(Exception): - await callback.answer("Сообщение удалено") + await callback.answer(texts.t("ADMIN_SUPPORT_MESSAGE_DELETED", "Сообщение удалено")) def register_handlers(dp: Dispatcher): diff --git a/app/localization/locales/en.json b/app/localization/locales/en.json index 720b26f5..a93409d3 100644 --- a/app/localization/locales/en.json +++ b/app/localization/locales/en.json @@ -564,5 +564,63 @@ "USER_BLOCKED_FOREVER": "You are blocked from contacting support.", "USER_BLOCKED_UNTIL": "You are blocked until {time}", "VIEW_CLOSED_TICKETS": "🟢 Closed tickets", - "VIEW_TICKET": "👁️ View ticket" + "VIEW_TICKET": "👁️ View ticket", + "ADMIN_USERS_SUBMENU_TITLE": "👥 **User and subscription management**\n\n", + "ADMIN_PROMO_SUBMENU_TITLE": "💰 **Promo codes and statistics**\n\n", + "ADMIN_COMMUNICATIONS_SUBMENU_TITLE": "📨 **Communications**\n\n", + "ADMIN_COMMUNICATIONS_SUBMENU_DESCRIPTION": "Manage broadcasts and interface texts:", + "ADMIN_SUBMENU_SELECT_SECTION": "Choose a section:", + "ADMIN_SUPPORT_SUBMENU_TITLE": "🛟 **Support**\n\n", + "ADMIN_SUPPORT_SUBMENU_DESCRIPTION": "Manage tickets and support settings:", + "ADMIN_SUPPORT_SUBMENU_DESCRIPTION_MODERATOR": "Ticket access only.", + "ADMIN_SUPPORT_MODERATION_TITLE": "🧑‍⚖️ Support moderation", + "ADMIN_SUPPORT_MODERATION_DESCRIPTION": "Access to support tickets.", + "ADMIN_SUPPORT_AUDIT_TITLE": "🧾 Moderator audit", + "ADMIN_SUPPORT_AUDIT_EMPTY": "Nothing here yet", + "ADMIN_SUPPORT_AUDIT_ROLE_MODERATOR": "Moderator", + "ADMIN_SUPPORT_AUDIT_ROLE_ADMIN": "Admin", + "ADMIN_SUPPORT_AUDIT_ACTION_CLOSE_TICKET": "Ticket closed", + "ADMIN_SUPPORT_AUDIT_ACTION_BLOCK_TIMED": "Timed block", + "ADMIN_SUPPORT_AUDIT_ACTION_BLOCK_PERM": "Permanent block", + "ADMIN_SUPPORT_AUDIT_ACTION_UNBLOCK": "Unblock", + "ADMIN_SETTINGS_SUBMENU_TITLE": "⚙️ **System settings**\n\n", + "ADMIN_SETTINGS_SUBMENU_DESCRIPTION": "Manage Remnawave, monitoring and other settings:", + "ADMIN_SYSTEM_SUBMENU_TITLE": "🛠️ **System tools**\n\n", + "ADMIN_SYSTEM_SUBMENU_DESCRIPTION": "Reports, updates, logs, backups and system operations:", + "ADMIN_SUPPORT_SETTINGS_STATUS_ENABLED": "Enabled", + "ADMIN_SUPPORT_SETTINGS_STATUS_DISABLED": "Disabled", + "ADMIN_SUPPORT_SETTINGS_MENU_LABEL": "\"Support\" menu item", + "ADMIN_SUPPORT_SETTINGS_MODE_TICKETS": "Tickets", + "ADMIN_SUPPORT_SETTINGS_MODE_CONTACT": "Contact", + "ADMIN_SUPPORT_SETTINGS_MODE_BOTH": "Both", + "ADMIN_SUPPORT_SETTINGS_EDIT_DESCRIPTION": "📝 Edit description", + "ADMIN_SUPPORT_SETTINGS_ADMIN_NOTIFICATIONS": "Admin notifications", + "ADMIN_SUPPORT_SETTINGS_USER_NOTIFICATIONS": "User notifications", + "ADMIN_SUPPORT_SETTINGS_SLA_LABEL": "SLA", + "ADMIN_SUPPORT_SETTINGS_SLA_TIME": "⏳ SLA time: {minutes} min", + "ADMIN_SUPPORT_SETTINGS_MODERATORS_COUNT": "🧑‍⚖️ Moderators: {count}", + "ADMIN_SUPPORT_SETTINGS_ADD_MODERATOR": "➕ Assign moderator", + "ADMIN_SUPPORT_SETTINGS_REMOVE_MODERATOR": "➖ Remove moderator", + "ADMIN_SUPPORT_SETTINGS_TITLE": "🛟 Support settings", + "ADMIN_SUPPORT_SETTINGS_DESCRIPTION": "Working hours and menu visibility. Current support menu description:", + "ADMIN_SUPPORT_SLA_SETUP_PROMPT": "⏳ SLA configuration\n\nEnter the response wait time in minutes (integer > 0):", + "ADMIN_SUPPORT_SLA_INVALID": "❌ Enter a valid number of minutes (1-1440)", + "ADMIN_SUPPORT_SLA_SAVED": "✅ SLA value saved", + "ADMIN_SUPPORT_ASSIGN_MODERATOR_PROMPT": "🧑‍⚖️ Assign moderator\n\nSend the user's Telegram ID (number)", + "ADMIN_SUPPORT_REMOVE_MODERATOR_PROMPT": "🧑‍⚖️ Remove moderator\n\nSend the user's Telegram ID (number)", + "ADMIN_SUPPORT_INVALID_TELEGRAM_ID": "❌ Enter a valid Telegram ID (number)", + "ADMIN_SUPPORT_MODERATOR_REMOVED_SUCCESS": "✅ Moderator {tid} removed", + "ADMIN_SUPPORT_MODERATOR_REMOVED_FAIL": "❌ Failed to remove moderator", + "ADMIN_SUPPORT_MODERATOR_ADDED_SUCCESS": "✅ User {tid} assigned as moderator", + "ADMIN_SUPPORT_MODERATOR_ADDED_FAIL": "❌ Failed to assign moderator", + "ADMIN_SUPPORT_MODERATORS_EMPTY": "List is empty", + "ADMIN_SUPPORT_MODERATORS_TITLE": "🧑‍⚖️ Moderators", + "ADMIN_SUPPORT_SEND_DESCRIPTION": "📨 Send description", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_TITLE": "📝 Editing support description", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CURRENT": "Current description:", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CONTACT_TITLE": "Contact for \"Contact\" mode", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CONTACT_HINT": "Add to the description if needed.", + "ADMIN_SUPPORT_DESCRIPTION_UPDATED": "✅ Description updated.", + "ADMIN_SUPPORT_DESCRIPTION_SENT": "Description sent below", + "ADMIN_SUPPORT_MESSAGE_DELETED": "Message deleted" } diff --git a/app/localization/locales/ru.json b/app/localization/locales/ru.json index b098b064..64bf1bc1 100644 --- a/app/localization/locales/ru.json +++ b/app/localization/locales/ru.json @@ -564,5 +564,63 @@ "USER_BLOCKED_FOREVER": "Вы заблокированы для обращений в поддержку.", "USER_BLOCKED_UNTIL": "Вы заблокированы до {time}", "VIEW_CLOSED_TICKETS": "🟢 Закрытые тикеты", - "VIEW_TICKET": "👁️ Посмотреть тикет" + "VIEW_TICKET": "👁️ Посмотреть тикет", + "ADMIN_USERS_SUBMENU_TITLE": "👥 **Управление пользователями и подписками**\n\n", + "ADMIN_PROMO_SUBMENU_TITLE": "💰 **Промокоды и статистика**\n\n", + "ADMIN_COMMUNICATIONS_SUBMENU_TITLE": "📨 **Коммуникации**\n\n", + "ADMIN_COMMUNICATIONS_SUBMENU_DESCRIPTION": "Управление рассылками и текстами интерфейса:", + "ADMIN_SUBMENU_SELECT_SECTION": "Выберите нужный раздел:", + "ADMIN_SUPPORT_SUBMENU_TITLE": "🛟 **Поддержка**\n\n", + "ADMIN_SUPPORT_SUBMENU_DESCRIPTION": "Управление тикетами и настройками поддержки:", + "ADMIN_SUPPORT_SUBMENU_DESCRIPTION_MODERATOR": "Доступ к тикетам.", + "ADMIN_SUPPORT_MODERATION_TITLE": "🧑‍⚖️ Модерация поддержки", + "ADMIN_SUPPORT_MODERATION_DESCRIPTION": "Доступ к тикетам поддержки.", + "ADMIN_SUPPORT_AUDIT_TITLE": "🧾 Аудит модераторов", + "ADMIN_SUPPORT_AUDIT_EMPTY": "Пока пусто", + "ADMIN_SUPPORT_AUDIT_ROLE_MODERATOR": "Модератор", + "ADMIN_SUPPORT_AUDIT_ROLE_ADMIN": "Админ", + "ADMIN_SUPPORT_AUDIT_ACTION_CLOSE_TICKET": "Закрытие тикета", + "ADMIN_SUPPORT_AUDIT_ACTION_BLOCK_TIMED": "Блокировка (время)", + "ADMIN_SUPPORT_AUDIT_ACTION_BLOCK_PERM": "Блокировка (навсегда)", + "ADMIN_SUPPORT_AUDIT_ACTION_UNBLOCK": "Снятие блока", + "ADMIN_SETTINGS_SUBMENU_TITLE": "⚙️ **Настройки системы**\n\n", + "ADMIN_SETTINGS_SUBMENU_DESCRIPTION": "Управление Remnawave, мониторингом и другими настройками:", + "ADMIN_SYSTEM_SUBMENU_TITLE": "🛠️ **Системные функции**\n\n", + "ADMIN_SYSTEM_SUBMENU_DESCRIPTION": "Отчеты, обновления, логи, резервные копии и системные операции:", + "ADMIN_SUPPORT_SETTINGS_STATUS_ENABLED": "Включены", + "ADMIN_SUPPORT_SETTINGS_STATUS_DISABLED": "Отключены", + "ADMIN_SUPPORT_SETTINGS_MENU_LABEL": "Пункт «Техподдержка» в меню", + "ADMIN_SUPPORT_SETTINGS_MODE_TICKETS": "Тикеты", + "ADMIN_SUPPORT_SETTINGS_MODE_CONTACT": "Контакт", + "ADMIN_SUPPORT_SETTINGS_MODE_BOTH": "Оба", + "ADMIN_SUPPORT_SETTINGS_EDIT_DESCRIPTION": "📝 Изменить описание", + "ADMIN_SUPPORT_SETTINGS_ADMIN_NOTIFICATIONS": "Админ-уведомления", + "ADMIN_SUPPORT_SETTINGS_USER_NOTIFICATIONS": "Пользовательские уведомления", + "ADMIN_SUPPORT_SETTINGS_SLA_LABEL": "SLA", + "ADMIN_SUPPORT_SETTINGS_SLA_TIME": "⏳ Время SLA: {minutes} мин", + "ADMIN_SUPPORT_SETTINGS_MODERATORS_COUNT": "🧑‍⚖️ Модераторы: {count}", + "ADMIN_SUPPORT_SETTINGS_ADD_MODERATOR": "➕ Назначить модератора", + "ADMIN_SUPPORT_SETTINGS_REMOVE_MODERATOR": "➖ Удалить модератора", + "ADMIN_SUPPORT_SETTINGS_TITLE": "🛟 Настройки поддержки", + "ADMIN_SUPPORT_SETTINGS_DESCRIPTION": "Режим работы и видимость в меню. Ниже текущее описание меню поддержки:", + "ADMIN_SUPPORT_SLA_SETUP_PROMPT": "⏳ Настройка SLA\n\nВведите количество минут ожидания ответа (целое число > 0):", + "ADMIN_SUPPORT_SLA_INVALID": "❌ Введите корректное число минут (1-1440)", + "ADMIN_SUPPORT_SLA_SAVED": "✅ Значение SLA сохранено", + "ADMIN_SUPPORT_ASSIGN_MODERATOR_PROMPT": "🧑‍⚖️ Назначение модератора\n\nОтправьте Telegram ID пользователя (число)", + "ADMIN_SUPPORT_REMOVE_MODERATOR_PROMPT": "🧑‍⚖️ Удаление модератора\n\nОтправьте Telegram ID пользователя (число)", + "ADMIN_SUPPORT_INVALID_TELEGRAM_ID": "❌ Введите корректный Telegram ID (число)", + "ADMIN_SUPPORT_MODERATOR_REMOVED_SUCCESS": "✅ Модератор {tid} удалён", + "ADMIN_SUPPORT_MODERATOR_REMOVED_FAIL": "❌ Не удалось удалить модератора", + "ADMIN_SUPPORT_MODERATOR_ADDED_SUCCESS": "✅ Пользователь {tid} назначен модератором", + "ADMIN_SUPPORT_MODERATOR_ADDED_FAIL": "❌ Не удалось назначить модератора", + "ADMIN_SUPPORT_MODERATORS_EMPTY": "Список пуст", + "ADMIN_SUPPORT_MODERATORS_TITLE": "🧑‍⚖️ Модераторы", + "ADMIN_SUPPORT_SEND_DESCRIPTION": "📨 Прислать текст", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_TITLE": "📝 Редактирование описания поддержки", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CURRENT": "Текущее описание:", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CONTACT_TITLE": "Контакт для режима «Контакт»", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CONTACT_HINT": "Добавьте в описание при необходимости.", + "ADMIN_SUPPORT_DESCRIPTION_UPDATED": "✅ Описание обновлено.", + "ADMIN_SUPPORT_DESCRIPTION_SENT": "Текст отправлен ниже", + "ADMIN_SUPPORT_MESSAGE_DELETED": "Сообщение удалено" } diff --git a/locales/en.json b/locales/en.json index 63729b65..e94449be 100644 --- a/locales/en.json +++ b/locales/en.json @@ -548,11 +548,69 @@ "ADMIN_MAIN_MESSAGES": "📨 Messages", "ADMIN_MAIN_SETTINGS": "⚙️ Settings", "ADMIN_MAIN_SYSTEM": "🛠️ System", + "ADMIN_USERS_SUBMENU_TITLE": "👥 **User and subscription management**\n\n", + "ADMIN_PROMO_SUBMENU_TITLE": "💰 **Promo codes and statistics**\n\n", + "ADMIN_COMMUNICATIONS_SUBMENU_TITLE": "📨 **Communications**\n\n", + "ADMIN_COMMUNICATIONS_SUBMENU_DESCRIPTION": "Manage broadcasts and interface texts:", + "ADMIN_SUBMENU_SELECT_SECTION": "Choose a section:", "ADMIN_COMMUNICATIONS_WELCOME_TEXT": "👋 Welcome message", "ADMIN_COMMUNICATIONS_MENU_MESSAGES": "📢 Menu messages", "ADMIN_SUPPORT_TICKETS": "🎫 Support tickets", "ADMIN_SUPPORT_AUDIT": "🧾 Moderator audit", "ADMIN_SUPPORT_SETTINGS": "🛟 Support settings", + "ADMIN_SUPPORT_SETTINGS_STATUS_ENABLED": "Enabled", + "ADMIN_SUPPORT_SETTINGS_STATUS_DISABLED": "Disabled", + "ADMIN_SUPPORT_SETTINGS_MENU_LABEL": "\"Support\" menu item", + "ADMIN_SUPPORT_SETTINGS_MODE_TICKETS": "Tickets", + "ADMIN_SUPPORT_SETTINGS_MODE_CONTACT": "Contact", + "ADMIN_SUPPORT_SETTINGS_MODE_BOTH": "Both", + "ADMIN_SUPPORT_SETTINGS_EDIT_DESCRIPTION": "📝 Edit description", + "ADMIN_SUPPORT_SETTINGS_ADMIN_NOTIFICATIONS": "Admin notifications", + "ADMIN_SUPPORT_SETTINGS_USER_NOTIFICATIONS": "User notifications", + "ADMIN_SUPPORT_SETTINGS_SLA_LABEL": "SLA", + "ADMIN_SUPPORT_SETTINGS_SLA_TIME": "⏳ SLA time: {minutes} min", + "ADMIN_SUPPORT_SETTINGS_MODERATORS_COUNT": "🧑‍⚖️ Moderators: {count}", + "ADMIN_SUPPORT_SETTINGS_ADD_MODERATOR": "➕ Assign moderator", + "ADMIN_SUPPORT_SETTINGS_REMOVE_MODERATOR": "➖ Remove moderator", + "ADMIN_SUPPORT_SETTINGS_TITLE": "🛟 Support settings", + "ADMIN_SUPPORT_SETTINGS_DESCRIPTION": "Working hours and menu visibility. Current support menu description:", + "ADMIN_SUPPORT_SLA_SETUP_PROMPT": "⏳ SLA configuration\n\nEnter the response wait time in minutes (integer > 0):", + "ADMIN_SUPPORT_SLA_INVALID": "❌ Enter a valid number of minutes (1-1440)", + "ADMIN_SUPPORT_SLA_SAVED": "✅ SLA value saved", + "ADMIN_SUPPORT_ASSIGN_MODERATOR_PROMPT": "🧑‍⚖️ Assign moderator\n\nSend the user's Telegram ID (number)", + "ADMIN_SUPPORT_REMOVE_MODERATOR_PROMPT": "🧑‍⚖️ Remove moderator\n\nSend the user's Telegram ID (number)", + "ADMIN_SUPPORT_INVALID_TELEGRAM_ID": "❌ Enter a valid Telegram ID (number)", + "ADMIN_SUPPORT_MODERATOR_REMOVED_SUCCESS": "✅ Moderator {tid} removed", + "ADMIN_SUPPORT_MODERATOR_REMOVED_FAIL": "❌ Failed to remove moderator", + "ADMIN_SUPPORT_MODERATOR_ADDED_SUCCESS": "✅ User {tid} assigned as moderator", + "ADMIN_SUPPORT_MODERATOR_ADDED_FAIL": "❌ Failed to assign moderator", + "ADMIN_SUPPORT_MODERATORS_EMPTY": "List is empty", + "ADMIN_SUPPORT_MODERATORS_TITLE": "🧑‍⚖️ Moderators", + "ADMIN_SUPPORT_SEND_DESCRIPTION": "📨 Send description", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_TITLE": "📝 Editing support description", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CURRENT": "Current description:", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CONTACT_TITLE": "Contact for \"Contact\" mode", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CONTACT_HINT": "Add to the description if needed.", + "ADMIN_SUPPORT_DESCRIPTION_UPDATED": "✅ Description updated.", + "ADMIN_SUPPORT_DESCRIPTION_SENT": "Description sent below", + "ADMIN_SUPPORT_MESSAGE_DELETED": "Message deleted", + "ADMIN_SUPPORT_SUBMENU_TITLE": "🛟 **Support**\n\n", + "ADMIN_SUPPORT_SUBMENU_DESCRIPTION": "Manage tickets and support settings:", + "ADMIN_SUPPORT_SUBMENU_DESCRIPTION_MODERATOR": "Ticket access only.", + "ADMIN_SUPPORT_MODERATION_TITLE": "🧑‍⚖️ Support moderation", + "ADMIN_SUPPORT_MODERATION_DESCRIPTION": "Access to support tickets.", + "ADMIN_SUPPORT_AUDIT_TITLE": "🧾 Moderator audit", + "ADMIN_SUPPORT_AUDIT_EMPTY": "Nothing here yet", + "ADMIN_SUPPORT_AUDIT_ROLE_MODERATOR": "Moderator", + "ADMIN_SUPPORT_AUDIT_ROLE_ADMIN": "Admin", + "ADMIN_SUPPORT_AUDIT_ACTION_CLOSE_TICKET": "Ticket closed", + "ADMIN_SUPPORT_AUDIT_ACTION_BLOCK_TIMED": "Timed block", + "ADMIN_SUPPORT_AUDIT_ACTION_BLOCK_PERM": "Permanent block", + "ADMIN_SUPPORT_AUDIT_ACTION_UNBLOCK": "Unblock", + "ADMIN_SETTINGS_SUBMENU_TITLE": "⚙️ **System settings**\n\n", + "ADMIN_SETTINGS_SUBMENU_DESCRIPTION": "Manage Remnawave, monitoring and other settings:", + "ADMIN_SYSTEM_SUBMENU_TITLE": "🛠️ **System tools**\n\n", + "ADMIN_SYSTEM_SUBMENU_DESCRIPTION": "Reports, updates, logs, backups and system operations:", "ADMIN_SETTINGS_BOT_CONFIG": "🧩 Bot configuration", "ADMIN_SETTINGS_MAINTENANCE": "🔧 Maintenance", "ADMIN_SYSTEM_UPDATES": "📄 Updates", diff --git a/locales/ru.json b/locales/ru.json index 33a4bf26..29fe6c3c 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -548,11 +548,69 @@ "ADMIN_MAIN_MESSAGES": "📨 Сообщения", "ADMIN_MAIN_SETTINGS": "⚙️ Настройки", "ADMIN_MAIN_SYSTEM": "🛠️ Система", + "ADMIN_USERS_SUBMENU_TITLE": "👥 **Управление пользователями и подписками**\n\n", + "ADMIN_PROMO_SUBMENU_TITLE": "💰 **Промокоды и статистика**\n\n", + "ADMIN_COMMUNICATIONS_SUBMENU_TITLE": "📨 **Коммуникации**\n\n", + "ADMIN_COMMUNICATIONS_SUBMENU_DESCRIPTION": "Управление рассылками и текстами интерфейса:", + "ADMIN_SUBMENU_SELECT_SECTION": "Выберите нужный раздел:", "ADMIN_COMMUNICATIONS_WELCOME_TEXT": "👋 Приветственный текст", "ADMIN_COMMUNICATIONS_MENU_MESSAGES": "📢 Сообщения в меню", "ADMIN_SUPPORT_TICKETS": "🎫 Тикеты поддержки", "ADMIN_SUPPORT_AUDIT": "🧾 Аудит модераторов", "ADMIN_SUPPORT_SETTINGS": "🛟 Настройки поддержки", + "ADMIN_SUPPORT_SETTINGS_STATUS_ENABLED": "Включены", + "ADMIN_SUPPORT_SETTINGS_STATUS_DISABLED": "Отключены", + "ADMIN_SUPPORT_SETTINGS_MENU_LABEL": "Пункт «Техподдержка» в меню", + "ADMIN_SUPPORT_SETTINGS_MODE_TICKETS": "Тикеты", + "ADMIN_SUPPORT_SETTINGS_MODE_CONTACT": "Контакт", + "ADMIN_SUPPORT_SETTINGS_MODE_BOTH": "Оба", + "ADMIN_SUPPORT_SETTINGS_EDIT_DESCRIPTION": "📝 Изменить описание", + "ADMIN_SUPPORT_SETTINGS_ADMIN_NOTIFICATIONS": "Админ-уведомления", + "ADMIN_SUPPORT_SETTINGS_USER_NOTIFICATIONS": "Пользовательские уведомления", + "ADMIN_SUPPORT_SETTINGS_SLA_LABEL": "SLA", + "ADMIN_SUPPORT_SETTINGS_SLA_TIME": "⏳ Время SLA: {minutes} мин", + "ADMIN_SUPPORT_SETTINGS_MODERATORS_COUNT": "🧑‍⚖️ Модераторы: {count}", + "ADMIN_SUPPORT_SETTINGS_ADD_MODERATOR": "➕ Назначить модератора", + "ADMIN_SUPPORT_SETTINGS_REMOVE_MODERATOR": "➖ Удалить модератора", + "ADMIN_SUPPORT_SETTINGS_TITLE": "🛟 Настройки поддержки", + "ADMIN_SUPPORT_SETTINGS_DESCRIPTION": "Режим работы и видимость в меню. Ниже текущее описание меню поддержки:", + "ADMIN_SUPPORT_SLA_SETUP_PROMPT": "⏳ Настройка SLA\n\nВведите количество минут ожидания ответа (целое число > 0):", + "ADMIN_SUPPORT_SLA_INVALID": "❌ Введите корректное число минут (1-1440)", + "ADMIN_SUPPORT_SLA_SAVED": "✅ Значение SLA сохранено", + "ADMIN_SUPPORT_ASSIGN_MODERATOR_PROMPT": "🧑‍⚖️ Назначение модератора\n\nОтправьте Telegram ID пользователя (число)", + "ADMIN_SUPPORT_REMOVE_MODERATOR_PROMPT": "🧑‍⚖️ Удаление модератора\n\nОтправьте Telegram ID пользователя (число)", + "ADMIN_SUPPORT_INVALID_TELEGRAM_ID": "❌ Введите корректный Telegram ID (число)", + "ADMIN_SUPPORT_MODERATOR_REMOVED_SUCCESS": "✅ Модератор {tid} удалён", + "ADMIN_SUPPORT_MODERATOR_REMOVED_FAIL": "❌ Не удалось удалить модератора", + "ADMIN_SUPPORT_MODERATOR_ADDED_SUCCESS": "✅ Пользователь {tid} назначен модератором", + "ADMIN_SUPPORT_MODERATOR_ADDED_FAIL": "❌ Не удалось назначить модератора", + "ADMIN_SUPPORT_MODERATORS_EMPTY": "Список пуст", + "ADMIN_SUPPORT_MODERATORS_TITLE": "🧑‍⚖️ Модераторы", + "ADMIN_SUPPORT_SEND_DESCRIPTION": "📨 Прислать текст", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_TITLE": "📝 Редактирование описания поддержки", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CURRENT": "Текущее описание:", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CONTACT_TITLE": "Контакт для режима «Контакт»", + "ADMIN_SUPPORT_EDIT_DESCRIPTION_CONTACT_HINT": "Добавьте в описание при необходимости.", + "ADMIN_SUPPORT_DESCRIPTION_UPDATED": "✅ Описание обновлено.", + "ADMIN_SUPPORT_DESCRIPTION_SENT": "Текст отправлен ниже", + "ADMIN_SUPPORT_MESSAGE_DELETED": "Сообщение удалено", + "ADMIN_SUPPORT_SUBMENU_TITLE": "🛟 **Поддержка**\n\n", + "ADMIN_SUPPORT_SUBMENU_DESCRIPTION": "Управление тикетами и настройками поддержки:", + "ADMIN_SUPPORT_SUBMENU_DESCRIPTION_MODERATOR": "Доступ к тикетам.", + "ADMIN_SUPPORT_MODERATION_TITLE": "🧑‍⚖️ Модерация поддержки", + "ADMIN_SUPPORT_MODERATION_DESCRIPTION": "Доступ к тикетам поддержки.", + "ADMIN_SUPPORT_AUDIT_TITLE": "🧾 Аудит модераторов", + "ADMIN_SUPPORT_AUDIT_EMPTY": "Пока пусто", + "ADMIN_SUPPORT_AUDIT_ROLE_MODERATOR": "Модератор", + "ADMIN_SUPPORT_AUDIT_ROLE_ADMIN": "Админ", + "ADMIN_SUPPORT_AUDIT_ACTION_CLOSE_TICKET": "Закрытие тикета", + "ADMIN_SUPPORT_AUDIT_ACTION_BLOCK_TIMED": "Блокировка (время)", + "ADMIN_SUPPORT_AUDIT_ACTION_BLOCK_PERM": "Блокировка (навсегда)", + "ADMIN_SUPPORT_AUDIT_ACTION_UNBLOCK": "Снятие блока", + "ADMIN_SETTINGS_SUBMENU_TITLE": "⚙️ **Настройки системы**\n\n", + "ADMIN_SETTINGS_SUBMENU_DESCRIPTION": "Управление Remnawave, мониторингом и другими настройками:", + "ADMIN_SYSTEM_SUBMENU_TITLE": "🛠️ **Системные функции**\n\n", + "ADMIN_SYSTEM_SUBMENU_DESCRIPTION": "Отчеты, обновления, логи, резервные копии и системные операции:", "ADMIN_SETTINGS_BOT_CONFIG": "🧩 Конфигурация бота", "ADMIN_SETTINGS_MAINTENANCE": "🔧 Техработы", "ADMIN_SYSTEM_UPDATES": "📄 Обновления",