Pārlūkot izejas kodu

feat: 反馈功能、多语言

lilu 3 mēneši atpakaļ
vecāks
revīzija
9197863f4b
26 mainītis faili ar 544 papildinājumiem un 69 dzēšanām
  1. BIN
      android/app/libs/libxray.aar
  2. 1 0
      android/app/src/main/kotlin/app/xixi/nomo/XRayService.kt
  3. 54 13
      lib/app/modules/feedback/controllers/feedback_controller.dart
  4. 27 16
      lib/app/modules/feedback/views/feedback_view.dart
  5. 7 5
      lib/app/modules/node/widgets/node_list.dart
  6. 56 35
      lib/app/widgets/country_icon.dart
  7. 20 0
      lib/config/translations/ar_AR/ar_ar_translation.dart
  8. 21 0
      lib/config/translations/de_DE/de_de_translation.dart
  9. 21 0
      lib/config/translations/en_US/en_us_translation.dart
  10. 21 0
      lib/config/translations/es_ES/es_es_translation.dart
  11. 20 0
      lib/config/translations/fa_IR/fa_ir_translation.dart
  12. 21 0
      lib/config/translations/fr_FR/fr_fr_translation.dart
  13. 20 0
      lib/config/translations/hi_IN/hi_in_translation.dart
  14. 21 0
      lib/config/translations/id_ID/id_id_translation.dart
  15. 20 0
      lib/config/translations/ja_JP/ja_jp_translation.dart
  16. 20 0
      lib/config/translations/ko_KR/ko_kr_translation.dart
  17. 20 0
      lib/config/translations/my_MM/my_mm_translation.dart
  18. 21 0
      lib/config/translations/pt_BR/pt_br_translation.dart
  19. 21 0
      lib/config/translations/ru_RU/ru_ru_translation.dart
  20. 10 0
      lib/config/translations/strings_enum.dart
  21. 20 0
      lib/config/translations/th_TH/th_th_translation.dart
  22. 20 0
      lib/config/translations/tk_TM/tk_tm_translation.dart
  23. 21 0
      lib/config/translations/tl_PH/tl_ph_translation.dart
  24. 21 0
      lib/config/translations/tr_TR/tr_tr_translation.dart
  25. 20 0
      lib/config/translations/vi_VN/vi_vn_translation.dart
  26. 20 0
      lib/config/translations/zh_TW/zh_tw_translation.dart

BIN
android/app/libs/libxray.aar


+ 1 - 0
android/app/src/main/kotlin/app/xixi/nomo/XRayService.kt

@@ -234,6 +234,7 @@ class XRayService : LifecycleVpnService() {
 
         if (serviceStatus == ServiceStatus.Connected) {
             VLog.i(TAG, "VPN already connected")
+            stop()
             return
         }
 

+ 54 - 13
lib/app/modules/feedback/controllers/feedback_controller.dart

@@ -1,6 +1,10 @@
 import 'package:flutter/material.dart';
 import 'package:get/get.dart';
+import 'package:nomo/app/components/ix_snackbar.dart';
+import 'package:uuid/uuid.dart';
 
+import '../../../../config/translations/strings_enum.dart';
+import '../../../constants/configs.dart';
 import '../../../constants/enums.dart';
 import '../../../controllers/api_controller.dart';
 
@@ -20,9 +24,20 @@ class FeedbackController extends GetxController {
   // 是否正在提交
   final isSubmitting = false.obs;
 
+  // 输入内容是否有效(用于按钮状态)
+  final _feedbackText = ''.obs;
+  final _emailText = ''.obs;
+
   @override
   void onInit() {
     super.onInit();
+    // 监听输入变化
+    feedbackController.addListener(() {
+      _feedbackText.value = feedbackController.text;
+    });
+    emailController.addListener(() {
+      _emailText.value = emailController.text;
+    });
   }
 
   @override
@@ -50,50 +65,76 @@ class FeedbackController extends GetxController {
   /// 提交反馈
   void submitFeedback() async {
     if (feedbackController.text.trim().isEmpty) {
-      Get.snackbar('提示', '请输入反馈内容');
+      IXSnackBar.showIXErrorSnackBar(
+        title: Strings.error.tr,
+        message: Strings.pleaseEnterFeedback.tr,
+      );
       return;
     }
 
     if (emailController.text.trim().isEmpty) {
-      Get.snackbar('提示', '请输入邮箱地址');
+      IXSnackBar.showIXErrorSnackBar(
+        title: Strings.error.tr,
+        message: Strings.pleaseEnterEmail.tr,
+      );
       return;
     }
 
     // 简单的邮箱格式验证
     if (!GetUtils.isEmail(emailController.text.trim())) {
-      Get.snackbar('提示', '请输入有效的邮箱地址');
+      IXSnackBar.showIXErrorSnackBar(
+        title: Strings.error.tr,
+        message: Strings.pleaseEnterValidEmail.tr,
+      );
       return;
     }
-
+    // 取消输入框焦点,关闭软键盘
+    FocusManager.instance.primaryFocus?.unfocus();
     isSubmitting.value = true;
 
     try {
-      // 模拟提交过程
-      await Future.delayed(const Duration(seconds: 2));
       await apiController.uploadApiStatisticsLog([
         {
+          "id": const Uuid().v4(),
+          "time": DateTime.now().millisecondsSinceEpoch,
+          "level": LogLevel.info.name,
           'module': LogModule.NM_FeedbackLog.name,
-          'feedback': feedbackController.text.trim(),
-          'email': emailController.text.trim(),
+          "category": Configs.productCode,
+          "fields": {
+            'content': feedbackController.text.trim(),
+            'email': emailController.text.trim(),
+          },
         },
       ]);
 
       // 提交成功
-      Get.snackbar('成功', '反馈已提交,我们会尽快回复您');
+      IXSnackBar.showIXSnackBar(
+        title: Strings.success.tr,
+        message: Strings.feedbackSubmitted.tr,
+      );
 
       // 清空输入框
       feedbackController.clear();
       emailController.clear();
+
+      // 延迟返回
+      Future.delayed(const Duration(milliseconds: 500), () {
+        Get.back();
+      });
     } catch (e) {
-      Get.snackbar('错误', '提交失败,请稍后重试');
+      IXSnackBar.showIXErrorSnackBar(
+        title: Strings.error.tr,
+        message: Strings.feedbackSubmitFailed.tr,
+      );
     } finally {
       isSubmitting.value = false;
     }
   }
 
-  /// 检查是否可以提交
+  /// 检查是否可以提交(响应式)
   bool get canSubmit {
-    return feedbackController.text.trim().isNotEmpty &&
-        emailController.text.trim().isNotEmpty;
+    // 访问响应式变量以触发 Obx 更新
+    return _feedbackText.value.trim().isNotEmpty &&
+        _emailText.value.trim().isNotEmpty;
   }
 }

+ 27 - 16
lib/app/modules/feedback/views/feedback_view.dart

@@ -130,35 +130,46 @@ class FeedbackView extends BaseView<FeedbackController> {
             child: SafeArea(
               child: Container(
                 alignment: Alignment.bottomCenter,
-                child: Obx(
-                  () => ClickOpacity(
-                    onTap: controller.isSubmitting.value
-                        ? null
-                        : controller.submitFeedback,
-                    child: Container(
+                child: Obx(() {
+                  final canSubmit = controller.canSubmit;
+                  final isSubmitting = controller.isSubmitting.value;
+                  final isEnabled = canSubmit && !isSubmitting;
+
+                  return ClickOpacity(
+                    onTap: isEnabled ? controller.submitFeedback : null,
+                    child: AnimatedContainer(
+                      duration: const Duration(milliseconds: 200),
                       width: double.infinity,
                       height: 50.w,
                       decoration: BoxDecoration(
-                        color:
-                            controller.canSubmit &&
-                                !controller.isSubmitting.value
-                            ? Get.reactiveTheme.shadowColor
+                        color: isEnabled
+                            ? Get.reactiveTheme.primaryColor
                             : Get.reactiveTheme.highlightColor,
                         borderRadius: BorderRadius.circular(12.r),
+                        border: Border.all(
+                          color: isEnabled
+                              ? Get.reactiveTheme.primaryColor
+                              : Get.reactiveTheme.dividerColor,
+                          width: 1.w,
+                        ),
                       ),
                       child: Center(
-                        child: controller.isSubmitting.value
+                        child: isSubmitting
                             ? SpinKitRing(
                                 size: 20.w,
                                 lineWidth: 2.w,
-                                color: Colors.white,
+                                color: Get
+                                    .reactiveTheme
+                                    .textTheme
+                                    .bodyLarge!
+                                    .color!,
                               )
                             : Text(
                                 Strings.send.tr,
                                 style: TextStyle(
                                   fontSize: 16.sp,
-                                  fontWeight: FontWeight.w400,
-                                  color: controller.canSubmit
+                                  fontWeight: FontWeight.w600,
+                                  color: isEnabled
                                       ? Get
                                             .reactiveTheme
                                             .textTheme
@@ -169,8 +180,8 @@ class FeedbackView extends BaseView<FeedbackController> {
                               ),
                       ),
                     ),
-                  ),
-                ),
+                  );
+                }),
               ),
             ),
           ),

+ 7 - 5
lib/app/modules/node/widgets/node_list.dart

@@ -213,12 +213,14 @@ class _CountrySection extends StatelessWidget {
                   const Spacer(),
                   // 箭头图标
                   AnimatedRotation(
-                    turns: expanded ? 0.25 : 0.0,
+                    turns: expanded ? -0.25 : 0.25,
                     duration: const Duration(milliseconds: 300),
                     child: Icon(
                       IconFont.icon02,
                       size: 20.w,
-                      color: Get.reactiveTheme.hintColor,
+                      color: expanded
+                          ? Get.reactiveTheme.textTheme.bodyLarge!.color
+                          : Get.reactiveTheme.hintColor,
                     ),
                   ),
                 ],
@@ -263,15 +265,15 @@ class _CountrySection extends StatelessWidget {
                             margin: EdgeInsets.only(
                               top: 14.w,
                               bottom: 14.w,
-                              left: 24.w,
+                              left: 22.w,
                               right: 14.w,
                             ),
                             child: Row(
                               children: [
                                 CountryIcon(
                                   countryCode: locationIcon,
-                                  width: 32.w,
-                                  height: 24.w,
+                                  width: 24.w,
+                                  height: 18.w,
                                   borderRadius: 4.r,
                                 ),
                                 10.horizontalSpace,

+ 56 - 35
lib/app/widgets/country_icon.dart

@@ -39,14 +39,17 @@ class CountryIcon extends StatefulWidget {
 }
 
 class _CountryIconState extends State<CountryIcon> {
+  /// 静态缓存:存储已查找过的图片路径,避免重复异步检查
+  static final Map<String, _ImageInfo> _imageCache = {};
+
   /// 图片路径
   String? _imagePath;
 
   /// 图片类型:svg 或 png
   String? _imageType;
 
-  /// 是否正在加载
-  bool _isLoading = true;
+  /// 是否首次加载(仅首次加载时显示占位符)
+  bool _isFirstLoad = true;
 
   @override
   void initState() {
@@ -66,49 +69,59 @@ class _CountryIconState extends State<CountryIcon> {
   Future<void> _findImage() async {
     if (!mounted) return;
 
-    setState(() {
-      _isLoading = true;
-    });
-
     final code = widget.countryCode.toLowerCase().trim();
 
     // 如果为空,直接使用默认图标
     if (code.isEmpty) {
-      setState(() {
-        _imagePath = 'assets/flags/xx.svg';
-        _imageType = 'svg';
-        _isLoading = false;
-      });
+      _updateImage('assets/flags/xx.svg', 'svg');
       return;
     }
 
+    // 检查缓存,如果有缓存则直接使用,避免异步等待
+    if (_imageCache.containsKey(code)) {
+      final cached = _imageCache[code]!;
+      _updateImage(cached.path, cached.type);
+      return;
+    }
+
+    // 异步查找图片(不设置 loading 状态,保持旧图片显示)
+    String? foundPath;
+    String? foundType;
+
     // 1. 先检查 flags 目录的 svg
     final flagPath = 'assets/flags/$code.svg';
     if (await _assetExists(flagPath)) {
-      setState(() {
-        _imagePath = flagPath;
-        _imageType = 'svg';
-        _isLoading = false;
-      });
-      return;
+      foundPath = flagPath;
+      foundType = 'svg';
+    } else {
+      // 2. 检查 streaming 目录的 png
+      final streamingPath = 'assets/images/streaming/$code.png';
+      if (await _assetExists(streamingPath)) {
+        foundPath = streamingPath;
+        foundType = 'png';
+      } else {
+        // 3. 使用默认图标
+        foundPath = 'assets/flags/xx.svg';
+        foundType = 'svg';
+      }
     }
 
-    // 2. 检查 streaming 目录的 png
-    final streamingPath = 'assets/images/streaming/$code.png';
-    if (await _assetExists(streamingPath)) {
-      setState(() {
-        _imagePath = streamingPath;
-        _imageType = 'png';
-        _isLoading = false;
-      });
-      return;
-    }
+    // 缓存结果
+    _imageCache[code] = _ImageInfo(foundPath, foundType);
+
+    // 检查 widget 是否仍然需要这个 code 的图片(防止快速切换时的竞态)
+    if (!mounted || widget.countryCode.toLowerCase().trim() != code) return;
 
-    // 3. 使用默认图标
+    _updateImage(foundPath, foundType);
+  }
+
+  /// 更新图片状态
+  void _updateImage(String path, String type) {
+    if (!mounted) return;
     setState(() {
-      _imagePath = 'assets/flags/xx.svg';
-      _imageType = 'svg';
-      _isLoading = false;
+      _imagePath = path;
+      _imageType = type;
+      _isFirstLoad = false;
     });
   }
 
@@ -124,8 +137,8 @@ class _CountryIconState extends State<CountryIcon> {
 
   @override
   Widget build(BuildContext context) {
-    // 加载中显示占位符
-    if (_isLoading || _imagePath == null || _imageType == null) {
+    // 仅首次加载时显示占位符,切换时保持旧图片
+    if (_isFirstLoad || _imagePath == null || _imageType == null) {
       return _buildPlaceholder();
     }
 
@@ -190,14 +203,14 @@ class CountryIconFast extends StatelessWidget {
   final bool preferStreaming;
 
   const CountryIconFast({
-    Key? key,
+    super.key,
     required this.countryCode,
     required this.width,
     required this.height,
     this.borderRadius,
     this.fit,
     this.preferStreaming = false,
-  }) : super(key: key);
+  });
 
   @override
   Widget build(BuildContext context) {
@@ -250,3 +263,11 @@ class CountryIconFast extends StatelessWidget {
     );
   }
 }
+
+/// 图片信息缓存类
+class _ImageInfo {
+  final String path;
+  final String type;
+
+  _ImageInfo(this.path, this.type);
+}

+ 20 - 0
lib/config/translations/ar_AR/ar_ar_translation.dart

@@ -441,4 +441,24 @@ final Map<String, String> arAR = {
   // Remain time
   Strings.remainTime: 'الوقت المتبقي',
   Strings.remainTimeEnded: 'انتهى وقتك المتاح',
+  Strings.expired: 'منتهي الصلاحية',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'تفعيل الإشعارات',
+  Strings.enableNotificationsDesc: 'فعّل الإشعارات لتلقي التحديثات والرسائل المهمة.',
+  Strings.enable: 'تفعيل',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'إظهار النافذة',
+  Strings.quitApp: 'إنهاء',
+  Strings.vpnConnected: 'VPN متصل',
+  Strings.vpnConnecting: 'جاري اتصال VPN',
+  Strings.vpnDisconnected: 'VPN غير متصل',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'يرجى إدخال محتوى الملاحظات',
+  Strings.pleaseEnterEmail: 'يرجى إدخال عنوان البريد الإلكتروني',
+  Strings.pleaseEnterValidEmail: 'يرجى إدخال عنوان بريد إلكتروني صالح',
+  Strings.feedbackSubmitted: 'تم إرسال الملاحظات، سنرد عليك قريباً',
+  Strings.feedbackSubmitFailed: 'فشل الإرسال، يرجى المحاولة مرة أخرى لاحقاً',
 };

+ 21 - 0
lib/config/translations/de_DE/de_de_translation.dart

@@ -446,4 +446,25 @@ const Map<String, String> deDE = {
   // Remain time
   Strings.remainTime: 'Verbleibende Zeit',
   Strings.remainTimeEnded: 'Ihre verfügbare Zeit ist abgelaufen',
+  Strings.expired: 'Abgelaufen',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'Benachrichtigungen aktivieren',
+  Strings.enableNotificationsDesc:
+      'Aktivieren Sie Benachrichtigungen, um wichtige Updates und Nachrichten zu erhalten.',
+  Strings.enable: 'Aktivieren',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'Fenster anzeigen',
+  Strings.quitApp: 'Beenden',
+  Strings.vpnConnected: 'VPN verbunden',
+  Strings.vpnConnecting: 'VPN verbindet',
+  Strings.vpnDisconnected: 'VPN getrennt',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'Bitte geben Sie Feedback-Inhalt ein',
+  Strings.pleaseEnterEmail: 'Bitte geben Sie eine E-Mail-Adresse ein',
+  Strings.pleaseEnterValidEmail: 'Bitte geben Sie eine gültige E-Mail-Adresse ein',
+  Strings.feedbackSubmitted: 'Feedback gesendet, wir werden Ihnen bald antworten',
+  Strings.feedbackSubmitFailed: 'Senden fehlgeschlagen, bitte später erneut versuchen',
 };

+ 21 - 0
lib/config/translations/en_US/en_us_translation.dart

@@ -446,4 +446,25 @@ Map<String, String> enUs = {
   // Remain time
   Strings.remainTime: 'Remain time',
   Strings.remainTimeEnded: 'Your available time has ended',
+  Strings.expired: 'Expired',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'Enable Notifications',
+  Strings.enableNotificationsDesc:
+      'Enable notifications to receive important updates and messages.',
+  Strings.enable: 'Enable',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'Show Window',
+  Strings.quitApp: 'Quit',
+  Strings.vpnConnected: 'VPN Connected',
+  Strings.vpnConnecting: 'VPN Connecting',
+  Strings.vpnDisconnected: 'VPN Disconnected',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'Please enter feedback content',
+  Strings.pleaseEnterEmail: 'Please enter email address',
+  Strings.pleaseEnterValidEmail: 'Please enter a valid email address',
+  Strings.feedbackSubmitted: 'Feedback submitted, we will reply to you soon',
+  Strings.feedbackSubmitFailed: 'Submit failed, please try again later',
 };

+ 21 - 0
lib/config/translations/es_ES/es_es_translation.dart

@@ -451,4 +451,25 @@ const Map<String, String> esEs = {
   // Remain time
   Strings.remainTime: 'Tiempo restante',
   Strings.remainTimeEnded: 'Tu tiempo disponible ha terminado',
+  Strings.expired: 'Expirado',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'Habilitar notificaciones',
+  Strings.enableNotificationsDesc:
+      'Habilita las notificaciones para recibir actualizaciones importantes y mensajes.',
+  Strings.enable: 'Habilitar',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'Mostrar ventana',
+  Strings.quitApp: 'Salir',
+  Strings.vpnConnected: 'VPN conectado',
+  Strings.vpnConnecting: 'VPN conectando',
+  Strings.vpnDisconnected: 'VPN desconectado',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'Por favor ingresa el contenido del comentario',
+  Strings.pleaseEnterEmail: 'Por favor ingresa la dirección de correo electrónico',
+  Strings.pleaseEnterValidEmail: 'Por favor ingresa una dirección de correo electrónico válida',
+  Strings.feedbackSubmitted: 'Comentario enviado, te responderemos pronto',
+  Strings.feedbackSubmitFailed: 'Error al enviar, por favor intenta de nuevo más tarde',
 };

+ 20 - 0
lib/config/translations/fa_IR/fa_ir_translation.dart

@@ -447,4 +447,24 @@ const Map<String, String> faIR = {
   // Remain time
   Strings.remainTime: 'زمان باقی‌مانده',
   Strings.remainTimeEnded: 'زمان در دسترس شما به پایان رسیده است',
+  Strings.expired: 'منقضی شده',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'فعال کردن اعلان‌ها',
+  Strings.enableNotificationsDesc: 'اعلان‌ها را فعال کنید تا به‌روزرسانی‌ها و پیام‌های مهم را دریافت کنید.',
+  Strings.enable: 'فعال کردن',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'نمایش پنجره',
+  Strings.quitApp: 'خروج',
+  Strings.vpnConnected: 'VPN متصل است',
+  Strings.vpnConnecting: 'VPN در حال اتصال',
+  Strings.vpnDisconnected: 'VPN قطع شده',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'لطفاً محتوای بازخورد را وارد کنید',
+  Strings.pleaseEnterEmail: 'لطفاً آدرس ایمیل را وارد کنید',
+  Strings.pleaseEnterValidEmail: 'لطفاً یک آدرس ایمیل معتبر وارد کنید',
+  Strings.feedbackSubmitted: 'بازخورد ارسال شد، به زودی پاسخ خواهیم داد',
+  Strings.feedbackSubmitFailed: 'ارسال ناموفق، لطفاً بعداً دوباره تلاش کنید',
 };

+ 21 - 0
lib/config/translations/fr_FR/fr_fr_translation.dart

@@ -452,4 +452,25 @@ const Map<String, String> frFR = {
   // Remain time
   Strings.remainTime: 'Temps restant',
   Strings.remainTimeEnded: 'Votre temps disponible est terminé',
+  Strings.expired: 'Expiré',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'Activer les notifications',
+  Strings.enableNotificationsDesc:
+      'Activez les notifications pour recevoir des mises à jour importantes et des messages.',
+  Strings.enable: 'Activer',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'Afficher la fenêtre',
+  Strings.quitApp: 'Quitter',
+  Strings.vpnConnected: 'VPN connecté',
+  Strings.vpnConnecting: 'VPN en connexion',
+  Strings.vpnDisconnected: 'VPN déconnecté',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'Veuillez entrer le contenu du commentaire',
+  Strings.pleaseEnterEmail: 'Veuillez entrer l\'adresse e-mail',
+  Strings.pleaseEnterValidEmail: 'Veuillez entrer une adresse e-mail valide',
+  Strings.feedbackSubmitted: 'Commentaire envoyé, nous vous répondrons bientôt',
+  Strings.feedbackSubmitFailed: 'Échec de l\'envoi, veuillez réessayer plus tard',
 };

+ 20 - 0
lib/config/translations/hi_IN/hi_in_translation.dart

@@ -316,4 +316,24 @@ Map<String, String> hiIN = {
   // Remain time
   Strings.remainTime: 'शेष समय',
   Strings.remainTimeEnded: 'आपका उपलब्ध समय समाप्त हो गया है',
+  Strings.expired: 'समाप्त',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'सूचनाएं सक्षम करें',
+  Strings.enableNotificationsDesc: 'महत्वपूर्ण अपडेट और संदेश प्राप्त करने के लिए सूचनाएं सक्षम करें।',
+  Strings.enable: 'सक्षम करें',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'विंडो दिखाएं',
+  Strings.quitApp: 'बाहर निकलें',
+  Strings.vpnConnected: 'VPN कनेक्टेड',
+  Strings.vpnConnecting: 'VPN कनेक्ट हो रहा है',
+  Strings.vpnDisconnected: 'VPN डिस्कनेक्टेड',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'कृपया प्रतिक्रिया सामग्री दर्ज करें',
+  Strings.pleaseEnterEmail: 'कृपया ईमेल पता दर्ज करें',
+  Strings.pleaseEnterValidEmail: 'कृपया एक मान्य ईमेल पता दर्ज करें',
+  Strings.feedbackSubmitted: 'प्रतिक्रिया भेजी गई, हम जल्द ही आपको जवाब देंगे',
+  Strings.feedbackSubmitFailed: 'भेजना विफल, कृपया बाद में पुनः प्रयास करें',
 };

+ 21 - 0
lib/config/translations/id_ID/id_id_translation.dart

@@ -316,4 +316,25 @@ Map<String, String> idID = {
   // Remain time
   Strings.remainTime: 'Waktu tersisa',
   Strings.remainTimeEnded: 'Waktu yang tersedia telah berakhir',
+  Strings.expired: 'Kedaluwarsa',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'Aktifkan notifikasi',
+  Strings.enableNotificationsDesc:
+      'Aktifkan notifikasi untuk menerima pembaruan dan pesan penting.',
+  Strings.enable: 'Aktifkan',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'Tampilkan jendela',
+  Strings.quitApp: 'Keluar',
+  Strings.vpnConnected: 'VPN terhubung',
+  Strings.vpnConnecting: 'VPN menghubungkan',
+  Strings.vpnDisconnected: 'VPN terputus',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'Silakan masukkan konten umpan balik',
+  Strings.pleaseEnterEmail: 'Silakan masukkan alamat email',
+  Strings.pleaseEnterValidEmail: 'Silakan masukkan alamat email yang valid',
+  Strings.feedbackSubmitted: 'Umpan balik terkirim, kami akan segera membalas',
+  Strings.feedbackSubmitFailed: 'Gagal mengirim, silakan coba lagi nanti',
 };

+ 20 - 0
lib/config/translations/ja_JP/ja_jp_translation.dart

@@ -426,4 +426,24 @@ const Map<String, String> jaJP = {
   // Remain time
   Strings.remainTime: '残り時間',
   Strings.remainTimeEnded: '利用可能な時間が終了しました',
+  Strings.expired: '期限切れ',
+
+  // Enable Notifications
+  Strings.enableNotifications: '通知を有効にする',
+  Strings.enableNotificationsDesc: '重要な更新やメッセージを受け取るには通知を有効にしてください。',
+  Strings.enable: '有効にする',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'ウィンドウを表示',
+  Strings.quitApp: '終了',
+  Strings.vpnConnected: 'VPN接続済み',
+  Strings.vpnConnecting: 'VPN接続中',
+  Strings.vpnDisconnected: 'VPN切断',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'フィードバック内容を入力してください',
+  Strings.pleaseEnterEmail: 'メールアドレスを入力してください',
+  Strings.pleaseEnterValidEmail: '有効なメールアドレスを入力してください',
+  Strings.feedbackSubmitted: 'フィードバックを送信しました。すぐにご返信いたします',
+  Strings.feedbackSubmitFailed: '送信に失敗しました。後でもう一度お試しください',
 };

+ 20 - 0
lib/config/translations/ko_KR/ko_kr_translation.dart

@@ -419,4 +419,24 @@ const Map<String, String> koKR = {
   // Remain time
   Strings.remainTime: '남은 시간',
   Strings.remainTimeEnded: '사용 가능한 시간이 종료되었습니다',
+  Strings.expired: '만료됨',
+
+  // Enable Notifications
+  Strings.enableNotifications: '알림 활성화',
+  Strings.enableNotificationsDesc: '중요한 업데이트 및 메시지를 받으려면 알림을 활성화하세요.',
+  Strings.enable: '활성화',
+
+  // Windows tray icon hints
+  Strings.showWindow: '창 표시',
+  Strings.quitApp: '종료',
+  Strings.vpnConnected: 'VPN 연결됨',
+  Strings.vpnConnecting: 'VPN 연결 중',
+  Strings.vpnDisconnected: 'VPN 연결 끊김',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: '피드백 내용을 입력해주세요',
+  Strings.pleaseEnterEmail: '이메일 주소를 입력해주세요',
+  Strings.pleaseEnterValidEmail: '유효한 이메일 주소를 입력해주세요',
+  Strings.feedbackSubmitted: '피드백이 제출되었습니다. 곧 답변드리겠습니다',
+  Strings.feedbackSubmitFailed: '제출 실패, 나중에 다시 시도해주세요',
 };

+ 20 - 0
lib/config/translations/my_MM/my_mm_translation.dart

@@ -455,4 +455,24 @@ const Map<String, String> myMM = {
   // Remain time
   Strings.remainTime: 'ကျန်အချိန်',
   Strings.remainTimeEnded: 'သင့်ရနိုင်သောအချိန် ကုန်ဆုံးသွားပါပြီ',
+  Strings.expired: 'သက်တမ်းကုန်သွားပြီ',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'အသိပေးချက်များဖွင့်မည်',
+  Strings.enableNotificationsDesc: 'အရေးကြီးသော အပ်ဒိတ်များနှင့် မက်ဆေ့ချ်များရရှိရန် အသိပေးချက်များဖွင့်ပါ။',
+  Strings.enable: 'ဖွင့်မည်',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'ဝင်းဒိုးပြမည်',
+  Strings.quitApp: 'ထွက်မည်',
+  Strings.vpnConnected: 'VPN ချိတ်ဆက်ပြီး',
+  Strings.vpnConnecting: 'VPN ချိတ်ဆက်နေသည်',
+  Strings.vpnDisconnected: 'VPN ချိတ်ဆက်မှုပြတ်တောက်သွားပြီ',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'အကြံပြုချက်အကြောင်းအရာထည့်သွင်းပါ',
+  Strings.pleaseEnterEmail: 'အီးမေးလ်လိပ်စာထည့်သွင်းပါ',
+  Strings.pleaseEnterValidEmail: 'တရားဝင်အီးမေးလ်လိပ်စာထည့်သွင်းပါ',
+  Strings.feedbackSubmitted: 'အကြံပြုချက်ပေးပို့ပြီးပါပြီ၊ မကြာမီ ပြန်လည်ဖြေကြားပေးပါမည်',
+  Strings.feedbackSubmitFailed: 'ပေးပို့ခြင်းမအောင်မြင်ပါ၊ နောက်မှထပ်ကြိုးစားပါ',
 };

+ 21 - 0
lib/config/translations/pt_BR/pt_br_translation.dart

@@ -446,4 +446,25 @@ Map<String, String> ptBR = {
   // Remain time
   Strings.remainTime: 'Tempo restante',
   Strings.remainTimeEnded: 'Seu tempo disponível acabou',
+  Strings.expired: 'Expirado',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'Ativar notificações',
+  Strings.enableNotificationsDesc:
+      'Ative as notificações para receber atualizações importantes e mensagens.',
+  Strings.enable: 'Ativar',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'Mostrar janela',
+  Strings.quitApp: 'Sair',
+  Strings.vpnConnected: 'VPN conectada',
+  Strings.vpnConnecting: 'VPN conectando',
+  Strings.vpnDisconnected: 'VPN desconectada',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'Por favor, insira o conteúdo do feedback',
+  Strings.pleaseEnterEmail: 'Por favor, insira o endereço de e-mail',
+  Strings.pleaseEnterValidEmail: 'Por favor, insira um endereço de e-mail válido',
+  Strings.feedbackSubmitted: 'Feedback enviado, responderemos em breve',
+  Strings.feedbackSubmitFailed: 'Falha no envio, por favor tente novamente mais tarde',
 };

+ 21 - 0
lib/config/translations/ru_RU/ru_ru_translation.dart

@@ -451,4 +451,25 @@ const Map<String, String> ruRU = {
   // Remain time
   Strings.remainTime: 'Оставшееся время',
   Strings.remainTimeEnded: 'Ваше доступное время истекло',
+  Strings.expired: 'Истёк',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'Включить уведомления',
+  Strings.enableNotificationsDesc:
+      'Включите уведомления, чтобы получать важные обновления и сообщения.',
+  Strings.enable: 'Включить',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'Показать окно',
+  Strings.quitApp: 'Выйти',
+  Strings.vpnConnected: 'VPN подключён',
+  Strings.vpnConnecting: 'VPN подключается',
+  Strings.vpnDisconnected: 'VPN отключён',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'Пожалуйста, введите содержание отзыва',
+  Strings.pleaseEnterEmail: 'Пожалуйста, введите адрес электронной почты',
+  Strings.pleaseEnterValidEmail: 'Пожалуйста, введите действительный адрес электронной почты',
+  Strings.feedbackSubmitted: 'Отзыв отправлен, мы скоро вам ответим',
+  Strings.feedbackSubmitFailed: 'Отправка не удалась, пожалуйста, попробуйте позже',
 };

+ 10 - 0
lib/config/translations/strings_enum.dart

@@ -465,4 +465,14 @@ class Strings {
   static const String vpnConnected = "VPN Connected";
   static const String vpnConnecting = "VPN Connecting";
   static const String vpnDisconnected = "VPN Disconnected";
+
+  // feedback
+  static const String pleaseEnterFeedback = "Please enter feedback content";
+  static const String pleaseEnterEmail = "Please enter email address";
+  static const String pleaseEnterValidEmail =
+      "Please enter a valid email address";
+  static const String feedbackSubmitted =
+      "Feedback submitted, we will reply to you soon";
+  static const String feedbackSubmitFailed =
+      "Submit failed, please try again later";
 }

+ 20 - 0
lib/config/translations/th_TH/th_th_translation.dart

@@ -316,4 +316,24 @@ Map<String, String> thTH = {
   // Remain time
   Strings.remainTime: 'เวลาที่เหลือ',
   Strings.remainTimeEnded: 'เวลาที่คุณใช้ได้หมดลงแล้ว',
+  Strings.expired: 'หมดอายุ',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'เปิดใช้งานการแจ้งเตือน',
+  Strings.enableNotificationsDesc: 'เปิดใช้งานการแจ้งเตือนเพื่อรับการอัปเดตและข้อความที่สำคัญ',
+  Strings.enable: 'เปิดใช้งาน',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'แสดงหน้าต่าง',
+  Strings.quitApp: 'ออก',
+  Strings.vpnConnected: 'VPN เชื่อมต่อแล้ว',
+  Strings.vpnConnecting: 'VPN กำลังเชื่อมต่อ',
+  Strings.vpnDisconnected: 'VPN ตัดการเชื่อมต่อ',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'กรุณากรอกเนื้อหาความคิดเห็น',
+  Strings.pleaseEnterEmail: 'กรุณากรอกที่อยู่อีเมล',
+  Strings.pleaseEnterValidEmail: 'กรุณากรอกที่อยู่อีเมลที่ถูกต้อง',
+  Strings.feedbackSubmitted: 'ส่งความคิดเห็นแล้ว เราจะตอบกลับคุณเร็วๆ นี้',
+  Strings.feedbackSubmitFailed: 'ส่งไม่สำเร็จ กรุณาลองใหม่ภายหลัง',
 };

+ 20 - 0
lib/config/translations/tk_TM/tk_tm_translation.dart

@@ -443,4 +443,24 @@ Map<String, String> tkTM = {
   // Remain time
   Strings.remainTime: 'Galan wagt',
   Strings.remainTimeEnded: 'Elýeterli wagtyňyz gutardy',
+  Strings.expired: 'Möhleti geçdi',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'Bildirişleri açyň',
+  Strings.enableNotificationsDesc: 'Möhüm täzelenmeleri we habarlary almak üçin bildirişleri açyň.',
+  Strings.enable: 'Açmak',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'Penjireni görkez',
+  Strings.quitApp: 'Çykmak',
+  Strings.vpnConnected: 'VPN birikdirildi',
+  Strings.vpnConnecting: 'VPN birikýär',
+  Strings.vpnDisconnected: 'VPN kesildi',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'Pikirleriň mazmunyny giriziň',
+  Strings.pleaseEnterEmail: 'E-poçta salgysyny giriziň',
+  Strings.pleaseEnterValidEmail: 'Dogry e-poçta salgysyny giriziň',
+  Strings.feedbackSubmitted: 'Pikir iberildi, tiz wagtda jogap bereris',
+  Strings.feedbackSubmitFailed: 'Ibermek şowsuz, soňra gaýtadan synanyşyň',
 };

+ 21 - 0
lib/config/translations/tl_PH/tl_ph_translation.dart

@@ -316,4 +316,25 @@ Map<String, String> tlPH = {
   // Remain time
   Strings.remainTime: 'Natitirang oras',
   Strings.remainTimeEnded: 'Natapos na ang iyong available na oras',
+  Strings.expired: 'Expired na',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'I-enable ang mga notification',
+  Strings.enableNotificationsDesc:
+      'I-enable ang mga notification para makatanggap ng mahahalagang update at mensahe.',
+  Strings.enable: 'I-enable',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'Ipakita ang window',
+  Strings.quitApp: 'Umalis',
+  Strings.vpnConnected: 'Nakakonekta ang VPN',
+  Strings.vpnConnecting: 'Kumokonekta ang VPN',
+  Strings.vpnDisconnected: 'Nadiskonekta ang VPN',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'Mangyaring ilagay ang nilalaman ng feedback',
+  Strings.pleaseEnterEmail: 'Mangyaring ilagay ang email address',
+  Strings.pleaseEnterValidEmail: 'Mangyaring ilagay ang valid na email address',
+  Strings.feedbackSubmitted: 'Naipadala ang feedback, magreresponde kami sa lalong madaling panahon',
+  Strings.feedbackSubmitFailed: 'Nabigo ang pagpapadala, mangyaring subukan muli mamaya',
 };

+ 21 - 0
lib/config/translations/tr_TR/tr_tr_translation.dart

@@ -316,4 +316,25 @@ Map<String, String> trTR = {
   // Remain time
   Strings.remainTime: 'Kalan süre',
   Strings.remainTimeEnded: 'Kullanılabilir süreniz sona erdi',
+  Strings.expired: 'Süresi doldu',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'Bildirimleri etkinleştir',
+  Strings.enableNotificationsDesc:
+      'Önemli güncellemeler ve mesajlar almak için bildirimleri etkinleştirin.',
+  Strings.enable: 'Etkinleştir',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'Pencereyi göster',
+  Strings.quitApp: 'Çıkış',
+  Strings.vpnConnected: 'VPN bağlandı',
+  Strings.vpnConnecting: 'VPN bağlanıyor',
+  Strings.vpnDisconnected: 'VPN bağlantısı kesildi',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'Lütfen geri bildirim içeriğini girin',
+  Strings.pleaseEnterEmail: 'Lütfen e-posta adresini girin',
+  Strings.pleaseEnterValidEmail: 'Lütfen geçerli bir e-posta adresi girin',
+  Strings.feedbackSubmitted: 'Geri bildirim gönderildi, en kısa sürede yanıt vereceğiz',
+  Strings.feedbackSubmitFailed: 'Gönderme başarısız, lütfen daha sonra tekrar deneyin',
 };

+ 20 - 0
lib/config/translations/vi_VN/vi_vn_translation.dart

@@ -357,4 +357,24 @@ Map<String, String> viVN = {
   // Remain time
   Strings.remainTime: 'Thời gian còn lại',
   Strings.remainTimeEnded: 'Thời gian khả dụng của bạn đã hết',
+  Strings.expired: 'Đã hết hạn',
+
+  // Enable Notifications
+  Strings.enableNotifications: 'Bật thông báo',
+  Strings.enableNotificationsDesc: 'Bật thông báo để nhận các cập nhật và tin nhắn quan trọng.',
+  Strings.enable: 'Bật',
+
+  // Windows tray icon hints
+  Strings.showWindow: 'Hiển thị cửa sổ',
+  Strings.quitApp: 'Thoát',
+  Strings.vpnConnected: 'VPN đã kết nối',
+  Strings.vpnConnecting: 'VPN đang kết nối',
+  Strings.vpnDisconnected: 'VPN đã ngắt kết nối',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: 'Vui lòng nhập nội dung phản hồi',
+  Strings.pleaseEnterEmail: 'Vui lòng nhập địa chỉ email',
+  Strings.pleaseEnterValidEmail: 'Vui lòng nhập địa chỉ email hợp lệ',
+  Strings.feedbackSubmitted: 'Phản hồi đã được gửi, chúng tôi sẽ trả lời bạn sớm',
+  Strings.feedbackSubmitFailed: 'Gửi thất bại, vui lòng thử lại sau',
 };

+ 20 - 0
lib/config/translations/zh_TW/zh_tw_translation.dart

@@ -408,4 +408,24 @@ Map<String, String> zhTW = {
   // Remain time
   Strings.remainTime: '剩餘時間',
   Strings.remainTimeEnded: '您的可用時間已結束',
+  Strings.expired: '已過期',
+
+  // Enable Notifications
+  Strings.enableNotifications: '啟用通知',
+  Strings.enableNotificationsDesc: '啟用通知以接收重要更新和訊息。',
+  Strings.enable: '啟用',
+
+  // Windows tray icon hints
+  Strings.showWindow: '顯示視窗',
+  Strings.quitApp: '退出',
+  Strings.vpnConnected: 'VPN 已連線',
+  Strings.vpnConnecting: 'VPN 連線中',
+  Strings.vpnDisconnected: 'VPN 已斷線',
+
+  // Feedback validation
+  Strings.pleaseEnterFeedback: '請輸入意見回饋內容',
+  Strings.pleaseEnterEmail: '請輸入電子郵件地址',
+  Strings.pleaseEnterValidEmail: '請輸入有效的電子郵件地址',
+  Strings.feedbackSubmitted: '意見回饋已提交,我們會盡快回覆您',
+  Strings.feedbackSubmitFailed: '提交失敗,請稍後再試',
 };