From 85bf0fb6fdc45c617890bbcc676b2b353801efb4 Mon Sep 17 00:00:00 2001 From: machka-pasla Date: Thu, 28 Aug 2025 21:52:36 +0300 Subject: [PATCH] Enhance promo export functionality with English localization - Added support for exporting promo activations and all promo codes with captions and CSV headers in English. - Updated the promo export handlers to ensure consistent English messaging for user notifications and CSV content. - Introduced new localization entries for English in the locales files to support the changes. --- bot/handlers/admin/promo/manage.py | 33 +++++++++++++++++++++--------- locales/en.json | 13 ++++++++++++ locales/ru.json | 13 ++++++++++++ 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/bot/handlers/admin/promo/manage.py b/bot/handlers/admin/promo/manage.py index 46198b5..9b60921 100644 --- a/bot/handlers/admin/promo/manage.py +++ b/bot/handlers/admin/promo/manage.py @@ -248,6 +248,7 @@ async def promo_export_activations_handler(callback: types.CallbackQuery, i18n_d if not i18n or not callback.message or not current_lang: return await callback.answer("Error processing request.", show_alert=True) _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) + export_lang = "en" try: promo_id = int(callback.data.split(":")[1]) @@ -267,7 +268,11 @@ async def promo_export_activations_handler(callback: types.CallbackQuery, i18n_d output.seek(0) file = types.BufferedInputFile(output.getvalue().encode('utf-8'), filename=f"promo_{promo.code}_activations.csv") - await callback.message.answer_document(file, caption=_("admin_promo_export_caption", code=promo.code)) + # Force English caption for exports + await callback.message.answer_document( + file, + caption=i18n.gettext(export_lang, "admin_promo_export_caption", code=promo.code) + ) except (ValueError, IndexError): await callback.answer(_("admin_promo_not_found"), show_alert=True) @@ -281,9 +286,10 @@ async def promo_export_all_handler(callback: types.CallbackQuery, i18n_data: dic if not i18n or not callback.message or not current_lang: return await callback.answer("Error processing request.", show_alert=True) _ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs) + export_lang = "en" try: - await callback.answer("πŸ“„ Боздаю CSV Ρ„Π°ΠΉΠ»...", show_alert=True) + await callback.answer(i18n.gettext(export_lang, "admin_promo_export_all_generating"), show_alert=True) # ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ всС ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Ρ‹ all_promos = await promo_code_dal.get_all_promo_codes_with_details(session, limit=10000, offset=0) @@ -291,15 +297,22 @@ async def promo_export_all_handler(callback: types.CallbackQuery, i18n_data: dic output = io.StringIO() writer = csv.writer(output) - # Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ CSV + # CSV headers (forced to English) writer.writerow([ - "Код", "БонусныС Π΄Π½ΠΈ", "ΠœΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹Π΅ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ", "Π’Π΅ΠΊΡƒΡ‰ΠΈΠ΅ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ", - "Бтатус", "АктивСн", "ДСйствитСлСн Π΄ΠΎ", "Π‘ΠΎΠ·Π΄Π°Π½", "Π‘ΠΎΠ·Π΄Π°Π» (Admin ID)" + i18n.gettext(export_lang, "admin_promo_csv_code"), + i18n.gettext(export_lang, "admin_promo_csv_bonus_days"), + i18n.gettext(export_lang, "admin_promo_csv_max_activations"), + i18n.gettext(export_lang, "admin_promo_csv_current_activations"), + i18n.gettext(export_lang, "admin_promo_csv_status"), + i18n.gettext(export_lang, "admin_promo_csv_is_active"), + i18n.gettext(export_lang, "admin_promo_csv_valid_until"), + i18n.gettext(export_lang, "admin_promo_csv_created_at"), + i18n.gettext(export_lang, "admin_promo_csv_created_by_admin_id"), ]) for promo in all_promos: # ΠžΠΏΡ€Π΅Π΄Π΅Π»ΡΠ΅ΠΌ статус - status_emoji, status_text = get_promo_status_emoji_and_text(promo, i18n, current_lang) + status_emoji, status_text = get_promo_status_emoji_and_text(promo, i18n, export_lang) # Π€ΠΎΡ€ΠΌΠΈΡ€ΡƒΠ΅ΠΌ Π΄Π°Π½Π½Ρ‹Π΅ для CSV row = [ @@ -308,8 +321,8 @@ async def promo_export_all_handler(callback: types.CallbackQuery, i18n_data: dic promo.max_activations, promo.current_activations, status_text, - "Π”Π°" if promo.is_active else "НСт", - promo.valid_until.strftime("%Y-%m-%d %H:%M:%S") if promo.valid_until else "Π‘Π΅Π· ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠΉ", + i18n.gettext(export_lang, "csv_yes") if promo.is_active else i18n.gettext(export_lang, "csv_no"), + promo.valid_until.strftime("%Y-%m-%d %H:%M:%S") if promo.valid_until else i18n.gettext(export_lang, "admin_promo_valid_indefinitely"), promo.created_at.strftime("%Y-%m-%d %H:%M:%S") if promo.created_at else "N/A", promo.created_by_admin_id or "N/A" ] @@ -324,11 +337,11 @@ async def promo_export_all_handler(callback: types.CallbackQuery, i18n_data: dic filename=filename ) - caption = f"πŸ“„ Экспорт всСх ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²\nπŸ“Š ВсСго: {len(all_promos)} ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²" + caption = i18n.gettext(export_lang, "admin_promo_export_all_caption", count=len(all_promos)) await callback.message.answer_document(file, caption=caption) except Exception as e: - await callback.answer(f"❌ Ошибка экспорта: {str(e)}", show_alert=True) + await callback.answer(f"❌ Export error: {str(e)}", show_alert=True) @router.callback_query(F.data.startswith("promo_delete:")) diff --git a/locales/en.json b/locales/en.json index 79aed63..41e5b5b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -149,6 +149,19 @@ "admin_promo_not_found": "Promo not found.", "admin_promo_export_csv_button": "πŸ“„ Export to CSV", "admin_promo_export_caption": "πŸ“„ Activations for promo code {code}", + "admin_promo_export_all_generating": "πŸ“„ Generating CSV...", + "admin_promo_export_all_caption": "πŸ“„ Export of all promo codes\nπŸ“Š Total: {count} promo codes", + "admin_promo_csv_code": "Code", + "admin_promo_csv_bonus_days": "Bonus Days", + "admin_promo_csv_max_activations": "Max Activations", + "admin_promo_csv_current_activations": "Current Activations", + "admin_promo_csv_status": "Status", + "admin_promo_csv_is_active": "Active", + "admin_promo_csv_valid_until": "Valid Until", + "admin_promo_csv_created_at": "Created", + "admin_promo_csv_created_by_admin_id": "Created By (Admin ID)", + "csv_yes": "Yes", + "csv_no": "No", "admin_promo_edit_select_field": "Select a field to edit:", "admin_promo_prompt_bonus_days": "Enter the new number of bonus days:", "admin_promo_prompt_max_activations": "Enter the new maximum number of activations:", diff --git a/locales/ru.json b/locales/ru.json index f8dd0b4..e790e7e 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -155,6 +155,19 @@ "admin_promo_not_found": "ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½.", "admin_promo_export_csv_button": "πŸ“„ Экспорт Π² CSV", "admin_promo_export_caption": "πŸ“„ Активации ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π° {code}", + "admin_promo_export_all_generating": "πŸ“„ Боздаю CSV Ρ„Π°ΠΉΠ»...", + "admin_promo_export_all_caption": "πŸ“„ Экспорт всСх ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²\nπŸ“Š ВсСго: {count} ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ΠΎΠ²", + "admin_promo_csv_code": "Код", + "admin_promo_csv_bonus_days": "БонусныС Π΄Π½ΠΈ", + "admin_promo_csv_max_activations": "ΠœΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹Π΅ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ", + "admin_promo_csv_current_activations": "Π’Π΅ΠΊΡƒΡ‰ΠΈΠ΅ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ", + "admin_promo_csv_status": "Бтатус", + "admin_promo_csv_is_active": "АктивСн", + "admin_promo_csv_valid_until": "ДСйствитСлСн Π΄ΠΎ", + "admin_promo_csv_created_at": "Π‘ΠΎΠ·Π΄Π°Π½", + "admin_promo_csv_created_by_admin_id": "Π‘ΠΎΠ·Π΄Π°Π» (Admin ID)", + "csv_yes": "Π”Π°", + "csv_no": "НСт", "admin_promo_edit_select_field": "Π’Ρ‹Π±Π΅Ρ€ΠΈΡ‚Π΅ ΠΏΠΎΠ»Π΅ для рСдактирования:", "admin_promo_prompt_bonus_days": "Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π½ΠΎΠ²ΠΎΠ΅ количСство бонусных Π΄Π½Π΅ΠΉ:", "admin_promo_prompt_max_activations": "Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Π½ΠΎΠ²ΠΎΠ΅ максимальноС количСство Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΉ:",