Parcourir la source

fix: 更新语言,更新顶部倒计时显示方案

(cherry picked from commit 10edab5550103ea7146cfee561c07e2def0737b9)

# Conflicts:
#	pubspec.yaml
lilu il y a 1 mois
Parent
commit
318342ca13
32 fichiers modifiés avec 871 ajouts et 128 suppressions
  1. 15 16
      android/app/src/main/kotlin/app/xixi/nomo/XRayService.kt
  2. 27 3
      lib/app/controllers/api_controller.dart
  3. 1 0
      lib/app/data/models/launch/app_config.dart
  4. 24 1
      lib/app/data/models/launch/app_config.freezed.dart
  5. 2 0
      lib/app/data/models/launch/app_config.g.dart
  6. 1 1
      lib/app/data/sp/ix_sp.dart
  7. 106 75
      lib/app/modules/home/views/home_view.dart
  8. 29 0
      lib/app/modules/subscription/controllers/subscription_controller.dart
  9. 15 1
      lib/app/modules/subscription/views/subscription_view.dart
  10. 48 0
      lib/config/translations/ar_AR/ar_ar_translation.dart
  11. 48 0
      lib/config/translations/de_DE/de_de_translation.dart
  12. 38 1
      lib/config/translations/en_US/en_us_translation.dart
  13. 48 0
      lib/config/translations/es_ES/es_es_translation.dart
  14. 48 0
      lib/config/translations/fa_IR/fa_ir_translation.dart
  15. 48 0
      lib/config/translations/fr_FR/fr_fr_translation.dart
  16. 4 1
      lib/config/translations/hi_IN/hi_in_translation.dart
  17. 4 1
      lib/config/translations/id_ID/id_id_translation.dart
  18. 48 0
      lib/config/translations/ja_JP/ja_jp_translation.dart
  19. 48 0
      lib/config/translations/ko_KR/ko_kr_translation.dart
  20. 48 0
      lib/config/translations/my_MM/my_mm_translation.dart
  21. 4 1
      lib/config/translations/pt_BR/pt_br_translation.dart
  22. 48 0
      lib/config/translations/ru_RU/ru_ru_translation.dart
  23. 4 1
      lib/config/translations/th_TH/th_th_translation.dart
  24. 4 0
      lib/config/translations/tk_TM/tk_tm_translation.dart
  25. 4 1
      lib/config/translations/tl_PH/tl_ph_translation.dart
  26. 4 1
      lib/config/translations/tr_TR/tr_tr_translation.dart
  27. 4 0
      lib/config/translations/vi_VN/vi_vn_translation.dart
  28. 4 0
      lib/config/translations/zh_TW/zh_tw_translation.dart
  29. 31 21
      lib/utils/device_manager.dart
  30. 110 0
      lib/utils/machine_guid_windows.dart
  31. 3 3
      pubspec.lock
  32. 1 0
      pubspec.yaml

+ 15 - 16
android/app/src/main/kotlin/app/xixi/nomo/XRayService.kt

@@ -523,14 +523,14 @@ class XRayService : LifecycleVpnService() {
                 // 检查倒计时是否结束
                 if (timerMode == TIMER_MODE_COUNTDOWN && currentTime <= 0) {
                     VLog.i(TAG, "倒计时结束 - 关闭VPN (elapsed: ${elapsedTime}ms)")
-                    sendTimerUpdate(0L)
+//                    sendTimerUpdate(0L)
                     stop()
                     sendStatusMessage(VPN_STATE_ERROR, ERROR_REMAIN_TIME, "No available time")
                     return@launch
                 }
                 if (timerMode == TIMER_MODE_NORMAL && currentTime >= remainTime) {
                     VLog.i(TAG, "可用时间已结束 - 关闭VPN (elapsed: ${elapsedTime}ms)")
-                    sendTimerUpdate(0L)
+//                    sendTimerUpdate(0L)
                     stop()
                     sendStatusMessage(VPN_STATE_ERROR, ERROR_REMAIN_TIME, "No available time")
                     return@launch
@@ -540,7 +540,7 @@ class XRayService : LifecycleVpnService() {
                 updateTimerNotification(currentTime)
 
                 // 发送计时更新广播
-                sendTimerUpdate(currentTime)
+//                sendTimerUpdate(currentTime)
 
                 // Peek 定时上报(屏幕关闭时不上报,节省资源)
                 peekElapsedSeconds++
@@ -573,19 +573,18 @@ class XRayService : LifecycleVpnService() {
      * 发送计时更新广播
      * 屏幕关闭时不发送,节省资源
      */
-    private fun sendTimerUpdate(currentTime: Long) {
-        // 屏幕关闭或应用在后台时不发送广播,节省资源
-        if (!isScreenOn || !isAppForeground) {
-            return
-        }
-
-        val intent = Intent(TIMER_BROADCAST).apply {
-            setPackage(packageName)  // 必须设置包名,否则 RECEIVER_NOT_EXPORTED 接收不到
-            putExtra("currentTime", currentTime)
-            putExtra("mode", timerMode)
-        }
-        sendBroadcast(intent)
-    }
+//    private fun sendTimerUpdate(currentTime: Long) {
+//        // 屏幕关闭或应用在后台时不发送广播,节省资源
+//        if (!isScreenOn || !isAppForeground) {
+//            return
+//        }
+//        val intent = Intent(TIMER_BROADCAST).apply {
+//            setPackage(packageName)  // 必须设置包名,否则 RECEIVER_NOT_EXPORTED 接收不到
+//            putExtra("currentTime", currentTime)
+//            putExtra("mode", timerMode)
+//        }
+//        sendBroadcast(intent)
+//    }
 
     /**
      * 更新通知显示计时

+ 27 - 3
lib/app/controllers/api_controller.dart

@@ -111,11 +111,13 @@ class ApiController extends GetxService with WidgetsBindingObserver {
     if (state == AppLifecycleState.paused) {
       isBackground = true;
       ApiStatistics.instance.onAppPaused();
+      stopRemainTimeCountdown();
     } else if (state == AppLifecycleState.resumed) {
       if (isBackground) {
         isBackground = false;
         asyncHandleLaunch(isRefreshLaunch: true);
         ApiStatistics.instance.onAppResumed();
+        updateRemainTime(IXSP.getUser()?.remainTime ?? 0);
       }
     }
   }
@@ -147,6 +149,8 @@ class ApiController extends GetxService with WidgetsBindingObserver {
         log(TAG, 'get install referrer error: $e');
         fp.refer = '';
       }
+    } else if (Platform.isWindows) {
+      fp.channel = 'universal';
     }
 
     // 读取应用信息
@@ -168,6 +172,12 @@ class ApiController extends GetxService with WidgetsBindingObserver {
       fp.deviceOs = androidOsInfo.version.release;
       fp.deviceBrand = androidOsInfo.brand;
       fp.androidId = androidOsInfo.id;
+    } else if (Platform.isWindows) {
+      fp.platform = Platforms.windows;
+      final windowsInfo = await deviceInfo.windowsInfo;
+      fp.deviceModel = windowsInfo.productName;
+      fp.deviceOs = windowsInfo.csdVersion;
+      fp.deviceBrand = windowsInfo.computerName;
     }
     //获取设备尺寸
     fp.deviceHeight = Get.height.toInt();
@@ -1138,13 +1148,27 @@ class ApiController extends GetxService with WidgetsBindingObserver {
   /// 格式化剩余时间显示
   String _formatRemainTime(int totalSeconds) {
     if (totalSeconds <= 0) {
-      return '00:00:00';
+      return '';
     }
 
-    final hours = totalSeconds ~/ 3600;
+    final days = totalSeconds ~/ 86400;
+    final hours = (totalSeconds % 86400) ~/ 3600;
     final minutes = (totalSeconds % 3600) ~/ 60;
     final seconds = totalSeconds % 60;
 
-    return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
+    // 大于1天
+    if (days > 1) {
+      return '$days days';
+    }
+    // 等于1天
+    if (days == 1) {
+      return '1 day';
+    }
+    // 大于1小时
+    if (hours >= 1) {
+      return '$hours h';
+    }
+    // 小于1小时,显示 mm:ss
+    return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
   }
 }

+ 1 - 0
lib/app/data/models/launch/app_config.dart

@@ -49,6 +49,7 @@ class AppConfig with _$AppConfig {
     int? adTimeoutDuration,
     int? reportActiveInterval,
     int? serverTime,
+    int? vipRemainNotice,
     SmartGeo? smartGeo,
   }) = _AppConfig;
 

+ 24 - 1
lib/app/data/models/launch/app_config.freezed.dart

@@ -62,6 +62,7 @@ mixin _$AppConfig {
   int? get adTimeoutDuration => throw _privateConstructorUsedError;
   int? get reportActiveInterval => throw _privateConstructorUsedError;
   int? get serverTime => throw _privateConstructorUsedError;
+  int? get vipRemainNotice => throw _privateConstructorUsedError;
   SmartGeo? get smartGeo => throw _privateConstructorUsedError;
 
   /// Serializes this AppConfig to a JSON map.
@@ -119,6 +120,7 @@ abstract class $AppConfigCopyWith<$Res> {
     int? adTimeoutDuration,
     int? reportActiveInterval,
     int? serverTime,
+    int? vipRemainNotice,
     SmartGeo? smartGeo,
   });
 
@@ -179,6 +181,7 @@ class _$AppConfigCopyWithImpl<$Res, $Val extends AppConfig>
     Object? adTimeoutDuration = freezed,
     Object? reportActiveInterval = freezed,
     Object? serverTime = freezed,
+    Object? vipRemainNotice = freezed,
     Object? smartGeo = freezed,
   }) {
     return _then(
@@ -339,6 +342,10 @@ class _$AppConfigCopyWithImpl<$Res, $Val extends AppConfig>
                 ? _value.serverTime
                 : serverTime // ignore: cast_nullable_to_non_nullable
                       as int?,
+            vipRemainNotice: freezed == vipRemainNotice
+                ? _value.vipRemainNotice
+                : vipRemainNotice // ignore: cast_nullable_to_non_nullable
+                      as int?,
             smartGeo: freezed == smartGeo
                 ? _value.smartGeo
                 : smartGeo // ignore: cast_nullable_to_non_nullable
@@ -412,6 +419,7 @@ abstract class _$$AppConfigImplCopyWith<$Res>
     int? adTimeoutDuration,
     int? reportActiveInterval,
     int? serverTime,
+    int? vipRemainNotice,
     SmartGeo? smartGeo,
   });
 
@@ -472,6 +480,7 @@ class __$$AppConfigImplCopyWithImpl<$Res>
     Object? adTimeoutDuration = freezed,
     Object? reportActiveInterval = freezed,
     Object? serverTime = freezed,
+    Object? vipRemainNotice = freezed,
     Object? smartGeo = freezed,
   }) {
     return _then(
@@ -632,6 +641,10 @@ class __$$AppConfigImplCopyWithImpl<$Res>
             ? _value.serverTime
             : serverTime // ignore: cast_nullable_to_non_nullable
                   as int?,
+        vipRemainNotice: freezed == vipRemainNotice
+            ? _value.vipRemainNotice
+            : vipRemainNotice // ignore: cast_nullable_to_non_nullable
+                  as int?,
         smartGeo: freezed == smartGeo
             ? _value.smartGeo
             : smartGeo // ignore: cast_nullable_to_non_nullable
@@ -684,6 +697,7 @@ class _$AppConfigImpl with DiagnosticableTreeMixin implements _AppConfig {
     this.adTimeoutDuration,
     this.reportActiveInterval,
     this.serverTime,
+    this.vipRemainNotice,
     this.smartGeo,
   }) : _apiIps = apiIps,
        _apiUrls = apiUrls,
@@ -947,11 +961,13 @@ class _$AppConfigImpl with DiagnosticableTreeMixin implements _AppConfig {
   @override
   final int? serverTime;
   @override
+  final int? vipRemainNotice;
+  @override
   final SmartGeo? smartGeo;
 
   @override
   String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
-    return 'AppConfig(apiIps: $apiIps, apiUrls: $apiUrls, routerApiUrls: $routerApiUrls, appStatUrls: $appStatUrls, assetUrls: $assetUrls, logFileUploadUrls: $logFileUploadUrls, realityApiUrls: $realityApiUrls, realityAppStatUrls: $realityAppStatUrls, realityLogFileUploadUrls: $realityLogFileUploadUrls, autoPing: $autoPing, autoPingInterval: $autoPingInterval, backupApiUrls: $backupApiUrls, cacheDataEffectiveDays: $cacheDataEffectiveDays, contacts: $contacts, follows: $follows, providers: $providers, signalThresholds: $signalThresholds, packetLossThresholds: $packetLossThresholds, skipGeo: $skipGeo, speedTestTargetList: $speedTestTargetList, websiteUrl: $websiteUrl, visitorDisabled: $visitorDisabled, privacyAgreement: $privacyAgreement, email: $email, blackPkgs: $blackPkgs, whitePkgs: $whitePkgs, accelerationSampleCount: $accelerationSampleCount, minAccelerationSampleCount: $minAccelerationSampleCount, connectionWarningThreshold: $connectionWarningThreshold, connectiveTestUrl: $connectiveTestUrl, disabledLogModules: $disabledLogModules, realTimeDbPath: $realTimeDbPath, boostTimeInterval: $boostTimeInterval, pingDisplayMode: $pingDisplayMode, peekTimeInterval: $peekTimeInterval, enableAd: $enableAd, adTimeoutDuration: $adTimeoutDuration, reportActiveInterval: $reportActiveInterval, serverTime: $serverTime, smartGeo: $smartGeo)';
+    return 'AppConfig(apiIps: $apiIps, apiUrls: $apiUrls, routerApiUrls: $routerApiUrls, appStatUrls: $appStatUrls, assetUrls: $assetUrls, logFileUploadUrls: $logFileUploadUrls, realityApiUrls: $realityApiUrls, realityAppStatUrls: $realityAppStatUrls, realityLogFileUploadUrls: $realityLogFileUploadUrls, autoPing: $autoPing, autoPingInterval: $autoPingInterval, backupApiUrls: $backupApiUrls, cacheDataEffectiveDays: $cacheDataEffectiveDays, contacts: $contacts, follows: $follows, providers: $providers, signalThresholds: $signalThresholds, packetLossThresholds: $packetLossThresholds, skipGeo: $skipGeo, speedTestTargetList: $speedTestTargetList, websiteUrl: $websiteUrl, visitorDisabled: $visitorDisabled, privacyAgreement: $privacyAgreement, email: $email, blackPkgs: $blackPkgs, whitePkgs: $whitePkgs, accelerationSampleCount: $accelerationSampleCount, minAccelerationSampleCount: $minAccelerationSampleCount, connectionWarningThreshold: $connectionWarningThreshold, connectiveTestUrl: $connectiveTestUrl, disabledLogModules: $disabledLogModules, realTimeDbPath: $realTimeDbPath, boostTimeInterval: $boostTimeInterval, pingDisplayMode: $pingDisplayMode, peekTimeInterval: $peekTimeInterval, enableAd: $enableAd, adTimeoutDuration: $adTimeoutDuration, reportActiveInterval: $reportActiveInterval, serverTime: $serverTime, vipRemainNotice: $vipRemainNotice, smartGeo: $smartGeo)';
   }
 
   @override
@@ -1017,6 +1033,7 @@ class _$AppConfigImpl with DiagnosticableTreeMixin implements _AppConfig {
       ..add(DiagnosticsProperty('adTimeoutDuration', adTimeoutDuration))
       ..add(DiagnosticsProperty('reportActiveInterval', reportActiveInterval))
       ..add(DiagnosticsProperty('serverTime', serverTime))
+      ..add(DiagnosticsProperty('vipRemainNotice', vipRemainNotice))
       ..add(DiagnosticsProperty('smartGeo', smartGeo));
   }
 
@@ -1138,6 +1155,8 @@ class _$AppConfigImpl with DiagnosticableTreeMixin implements _AppConfig {
                 other.reportActiveInterval == reportActiveInterval) &&
             (identical(other.serverTime, serverTime) ||
                 other.serverTime == serverTime) &&
+            (identical(other.vipRemainNotice, vipRemainNotice) ||
+                other.vipRemainNotice == vipRemainNotice) &&
             (identical(other.smartGeo, smartGeo) ||
                 other.smartGeo == smartGeo));
   }
@@ -1185,6 +1204,7 @@ class _$AppConfigImpl with DiagnosticableTreeMixin implements _AppConfig {
     adTimeoutDuration,
     reportActiveInterval,
     serverTime,
+    vipRemainNotice,
     smartGeo,
   ]);
 
@@ -1243,6 +1263,7 @@ abstract class _AppConfig implements AppConfig {
     final int? adTimeoutDuration,
     final int? reportActiveInterval,
     final int? serverTime,
+    final int? vipRemainNotice,
     final SmartGeo? smartGeo,
   }) = _$AppConfigImpl;
 
@@ -1328,6 +1349,8 @@ abstract class _AppConfig implements AppConfig {
   @override
   int? get serverTime;
   @override
+  int? get vipRemainNotice;
+  @override
   SmartGeo? get smartGeo;
 
   /// Create a copy of AppConfig

+ 2 - 0
lib/app/data/models/launch/app_config.g.dart

@@ -90,6 +90,7 @@ _$AppConfigImpl _$$AppConfigImplFromJson(
   adTimeoutDuration: (json['adTimeoutDuration'] as num?)?.toInt(),
   reportActiveInterval: (json['reportActiveInterval'] as num?)?.toInt(),
   serverTime: (json['serverTime'] as num?)?.toInt(),
+  vipRemainNotice: (json['vipRemainNotice'] as num?)?.toInt(),
   smartGeo: json['smartGeo'] == null
       ? null
       : SmartGeo.fromJson(json['smartGeo'] as Map<String, dynamic>),
@@ -136,6 +137,7 @@ Map<String, dynamic> _$$AppConfigImplToJson(_$AppConfigImpl instance) =>
       'adTimeoutDuration': instance.adTimeoutDuration,
       'reportActiveInterval': instance.reportActiveInterval,
       'serverTime': instance.serverTime,
+      'vipRemainNotice': instance.vipRemainNotice,
       'smartGeo': instance.smartGeo,
     };
 

+ 1 - 1
lib/app/data/sp/ix_sp.dart

@@ -63,7 +63,7 @@ class IXSP {
   /// get if the current theme type is light
   static bool getThemeIsLight() =>
       _sharedPreferences.getBool(_lightThemeKey) ??
-      true; // todo set the default theme (true for light, false for dark)
+      false; // todo set the default theme (true for light, false for dark)
 
   /// save current locale
   static Future<void> setCurrentLanguage(String languageCode) =>

+ 106 - 75
lib/app/modules/home/views/home_view.dart

@@ -16,6 +16,7 @@ import '../../../widgets/click_opacity.dart';
 import '../../../widgets/country_icon.dart';
 import '../../../widgets/ix_image.dart';
 import '../controllers/home_controller.dart';
+import '../../../data/sp/ix_sp.dart';
 
 import '../widgets/connection_round_button.dart';
 import '../widgets/menu_list.dart';
@@ -145,61 +146,62 @@ class HomeView extends BaseView<HomeController> {
               : DarkThemeColors.homeFreeColor;
           return ClipRRect(
             borderRadius: BorderRadius.circular(100.r),
-            child: Container(
-              decoration: BoxDecoration(color: bgColor.withValues(alpha: 0.05)),
-              child: Stack(
-                children: [
-                  // 左上角光晕
-                  Positioned(
-                    left: -8.w,
-                    top: -16.w,
-                    child: Container(
-                      width: 32.w,
-                      height: 32.w,
-                      decoration: BoxDecoration(
-                        shape: BoxShape.circle,
-                        gradient: RadialGradient(
-                          colors: [
-                            bgColor.withValues(alpha: 0.85),
-                            bgColor.withValues(alpha: 0.05),
-                          ],
-                          stops: const [0.0, 1.0],
-                        ),
-                      ),
-                    ),
-                  ),
-                  // 右下角光晕
-                  Positioned(
-                    right: 12.w,
-                    bottom: -12.w,
-                    child: ImageFiltered(
-                      imageFilter: ImageFilter.blur(
-                        sigmaX: 8,
-                        sigmaY: 8,
-                        tileMode: TileMode.decal,
-                      ),
+            child: ClickOpacity(
+              onTap: () => Get.toNamed(Routes.SUBSCRIPTION),
+              child: Container(
+                decoration: BoxDecoration(
+                  color: bgColor.withValues(alpha: 0.05),
+                ),
+                child: Stack(
+                  children: [
+                    // 左上角光晕
+                    Positioned(
+                      left: -8.w,
+                      top: -16.w,
                       child: Container(
-                        width: 42.w,
-                        height: 16.w,
+                        width: 32.w,
+                        height: 32.w,
                         decoration: BoxDecoration(
-                          borderRadius: BorderRadius.circular(8.w),
-                          color: bgColor.withValues(alpha: 0.85),
+                          shape: BoxShape.circle,
+                          gradient: RadialGradient(
+                            colors: [
+                              bgColor.withValues(alpha: 0.85),
+                              bgColor.withValues(alpha: 0.05),
+                            ],
+                            stops: const [0.0, 1.0],
+                          ),
                         ),
                       ),
                     ),
-                  ),
-                  // 内容
-                  Padding(
-                    padding: EdgeInsets.symmetric(
-                      horizontal: 6.w,
-                      vertical: 4.w,
+                    // 右下角光晕
+                    Positioned(
+                      right: 12.w,
+                      bottom: -12.w,
+                      child: ImageFiltered(
+                        imageFilter: ImageFilter.blur(
+                          sigmaX: 8,
+                          sigmaY: 8,
+                          tileMode: TileMode.decal,
+                        ),
+                        child: Container(
+                          width: 42.w,
+                          height: 16.w,
+                          decoration: BoxDecoration(
+                            borderRadius: BorderRadius.circular(8.w),
+                            color: bgColor.withValues(alpha: 0.85),
+                          ),
+                        ),
+                      ),
                     ),
-                    child: Row(
-                      children: [
-                        Obx(
-                          () => ClickOpacity(
-                            onTap: () => Get.toNamed(Routes.SUBSCRIPTION),
-                            child: IXImage(
+                    Padding(
+                      padding: EdgeInsets.symmetric(
+                        horizontal: 6.w,
+                        vertical: 4.w,
+                      ),
+                      child: Row(
+                        children: [
+                          Obx(
+                            () => IXImage(
                               source: controller.apiController.userLevel == 3
                                   ? Assets.homePremium
                                   : controller.apiController.userLevel == 9999
@@ -212,33 +214,14 @@ class HomeView extends BaseView<HomeController> {
                               sourceType: ImageSourceType.asset,
                             ),
                           ),
-                        ),
-                        Obx(
-                          () => Text(
-                            controller.apiController.isGuest &&
-                                    !controller.apiController.isPremium &&
-                                    controller.apiController.remainTimeSeconds >
-                                        0
-                                ? controller.apiController.remainTimeFormatted
-                                : controller.coreController.timer,
-                            style: TextStyle(
-                              fontSize: 13.sp,
-                              height: 1.5,
-                              fontStyle: FontStyle.italic,
-                              fontWeight: FontWeight.w500,
-                              fontFeatures: [FontFeature.tabularFigures()],
-                              color:
-                                  controller.apiController.userLevel == 3 ||
-                                      controller.apiController.userLevel == 9999
-                                  ? Get.reactiveTheme.textTheme.bodyLarge!.color
-                                  : Get.reactiveTheme.hintColor,
-                            ),
-                          ),
-                        ),
-                      ],
+                          _buildReminder(),
+                        ],
+                      ),
                     ),
-                  ),
-                ],
+
+                    // 倒计时提醒
+                  ],
+                ),
               ),
             ),
           );
@@ -267,6 +250,54 @@ class HomeView extends BaseView<HomeController> {
     );
   }
 
+  Widget _buildReminder() {
+    // return Obx(
+    //   () => Text(
+    //     controller.apiController.isGuest &&
+    //             !controller.apiController.isPremium &&
+    //             controller.apiController.remainTimeSeconds > 0
+    //         ? controller.apiController.remainTimeFormatted
+    //         : controller.coreController.timer,
+    //     style: TextStyle(
+    //       fontSize: 13.sp,
+    //       height: 1.5,
+    //       fontStyle: FontStyle.italic,
+    //       fontWeight: FontWeight.w500,
+    //       fontFeatures: [FontFeature.tabularFigures()],
+    //       color:
+    //           controller.apiController.userLevel == 3 ||
+    //               controller.apiController.userLevel == 9999
+    //           ? Get.reactiveTheme.textTheme.bodyLarge!.color
+    //           : Get.reactiveTheme.hintColor,
+    //     ),
+    //   ),
+    // );
+
+    return Obx(() {
+      final vipRemainNoticeSeconds =
+          (IXSP.getAppConfig()?.vipRemainNotice ?? 600) * 60;
+      final showCountdown =
+          controller.apiController.remainTimeSeconds > 0 &&
+          controller.apiController.remainTimeSeconds < vipRemainNoticeSeconds;
+      if (!showCountdown) return const SizedBox.shrink();
+      return Text(
+        '${controller.apiController.remainTimeFormatted} ',
+        style: TextStyle(
+          fontSize: 13.sp,
+          height: 1.5,
+          fontStyle: FontStyle.italic,
+          fontWeight: FontWeight.w500,
+          fontFeatures: [FontFeature.tabularFigures()],
+          color:
+              controller.apiController.userLevel == 3 ||
+                  controller.apiController.userLevel == 9999
+              ? Get.reactiveTheme.textTheme.bodyLarge!.color
+              : Get.reactiveTheme.hintColor,
+        ),
+      );
+    });
+  }
+
   Widget _buildConnectionButton() {
     return Obx(
       () => ConnectionRoundButton(

+ 29 - 0
lib/app/modules/subscription/controllers/subscription_controller.dart

@@ -479,4 +479,33 @@ class SubscriptionController extends GetxController {
     }
     return '';
   }
+
+  // ==================== 当前订阅相关方法 ====================
+
+  /// 是否有当前订阅(userLevel == 3 且 planInfo 存在)
+  bool get hasCurrentSubscription {
+    final user = IXSP.getUser();
+    return user?.userLevel == 3 && user?.planInfo != null;
+  }
+
+  /// 获取当前订阅套餐信息
+  ChannelPlan? get currentPlanInfo {
+    final user = IXSP.getUser();
+    if (user?.userLevel == 3) {
+      return user?.planInfo;
+    }
+    return null;
+  }
+
+  /// 获取当前订阅套餐标题
+  String get currentPlanTitle {
+    return currentPlanInfo?.title ?? '';
+  }
+
+  /// 获取当前订阅套餐价格显示
+  String get currentPlanPriceDisplay {
+    final plan = currentPlanInfo;
+    if (plan == null) return '';
+    return '${plan.title}-${plan.subTitle}';
+  }
 }

+ 15 - 1
lib/app/modules/subscription/views/subscription_view.dart

@@ -129,6 +129,20 @@ class SubscriptionView extends GetView<SubscriptionController> {
 
   // 当前订阅信息
   Widget _buildCurrentSubscription() {
+    // 判断是否有订阅
+    if (!controller.hasCurrentSubscription) {
+      // 没有订阅,只显示钻石图标
+      return Center(
+        child: IXImage(
+          source: Assets.subscriptionDiamond,
+          width: 92.w,
+          height: 80.w,
+          sourceType: ImageSourceType.asset,
+        ),
+      );
+    }
+
+    // 有订阅,显示当前套餐信息
     return Row(
       mainAxisAlignment: MainAxisAlignment.center,
       children: [
@@ -166,7 +180,7 @@ class SubscriptionView extends GetView<SubscriptionController> {
               ),
               10.verticalSpaceFromWidth,
               Text(
-                Strings.yearPlanPrice.trParams({'price': '\$40.00'}),
+                controller.currentPlanPriceDisplay,
                 style: TextStyle(
                   fontSize: 14.sp,
                   height: 1.4,

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

@@ -393,4 +393,52 @@ final Map<String, String> arAR = {
   Strings.vpnServiceEmptyError: 'خدمة VPN غير متوفرة',
   Strings.vpnRouterError: 'خطأ في تكوين توجيه VPN',
   Strings.vpnPermissionDeniedError: 'تم رفض إذن VPN',
+
+  // Languages
+  Strings.enLang: 'English',
+  Strings.esLang: 'Español',
+  Strings.frLang: 'Français',
+  Strings.deLang: 'Deutsch',
+  Strings.jaLang: '日本語',
+  Strings.koLang: '한국어',
+  Strings.faLang: 'فارسی',
+  Strings.myLang: 'မြန်မာဘာသာ',
+  Strings.arLang: 'عربي',
+  Strings.ruLang: 'Русский',
+  Strings.zhTWLang: '繁體中文',
+  Strings.tkLang: 'Türkmençe',
+  Strings.ptBRLang: 'Português (Brasil)',
+  Strings.viLang: 'Tiếng Việt',
+  Strings.idLang: 'Bahasa Indonesia',
+  Strings.tlLang: 'Filipino',
+  Strings.thLang: 'ไทย',
+  Strings.hiLang: 'हिन्दी',
+  Strings.trLang: 'Türkçe',
+
+  // Signup
+  Strings.signingUp: 'جاري التسجيل...',
+  Strings.signUpSuccessful: 'تم التسجيل بنجاح',
+
+  // Login
+  Strings.loggingIn: 'جاري تسجيل الدخول...',
+  Strings.loginSuccessful: 'تم تسجيل الدخول بنجاح',
+
+  // Logout
+  Strings.loggingOut: 'جاري تسجيل الخروج...',
+  Strings.logoutSuccessful: 'تم تسجيل الخروج بنجاح',
+
+  // Change password
+  Strings.changingPassword: 'جاري تغيير كلمة المرور...',
+  Strings.changePasswordSuccessful: 'تم تغيير كلمة المرور بنجاح',
+
+  // Delete account
+  Strings.deletingAccount: 'جاري حذف الحساب...',
+  Strings.deleteAccountSuccessful: 'تم حذف الحساب بنجاح',
+  Strings.deleteAccountConfirmMessage:
+      'سيؤدي حذف حسابك إلى إزالة بياناتك ومعلومات عضويتك بشكل دائم. لا يمكن التراجع عن هذا الإجراء.',
+  Strings.deleteAccountConfirmButton: 'حذف',
+
+  // Remain time
+  Strings.remainTime: 'الوقت المتبقي',
+  Strings.remainTimeEnded: 'انتهى وقتك المتاح',
 };

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

@@ -398,4 +398,52 @@ const Map<String, String> deDE = {
   Strings.vpnServiceEmptyError: 'VPN-Dienst nicht verfügbar',
   Strings.vpnRouterError: 'VPN-Routing-Konfigurationsfehler',
   Strings.vpnPermissionDeniedError: 'VPN-Berechtigung verweigert',
+
+  // Languages
+  Strings.enLang: 'English',
+  Strings.esLang: 'Español',
+  Strings.frLang: 'Français',
+  Strings.deLang: 'Deutsch',
+  Strings.jaLang: '日本語',
+  Strings.koLang: '한국어',
+  Strings.faLang: 'فارسی',
+  Strings.myLang: 'မြန်မာဘာသာ',
+  Strings.arLang: 'عربي',
+  Strings.ruLang: 'Русский',
+  Strings.zhTWLang: '繁體中文',
+  Strings.tkLang: 'Türkmençe',
+  Strings.ptBRLang: 'Português (Brasil)',
+  Strings.viLang: 'Tiếng Việt',
+  Strings.idLang: 'Bahasa Indonesia',
+  Strings.tlLang: 'Filipino',
+  Strings.thLang: 'ไทย',
+  Strings.hiLang: 'हिन्दी',
+  Strings.trLang: 'Türkçe',
+
+  // Signup
+  Strings.signingUp: 'Registrierung läuft...',
+  Strings.signUpSuccessful: 'Registrierung erfolgreich',
+
+  // Login
+  Strings.loggingIn: 'Anmeldung läuft...',
+  Strings.loginSuccessful: 'Anmeldung erfolgreich',
+
+  // Logout
+  Strings.loggingOut: 'Abmeldung läuft...',
+  Strings.logoutSuccessful: 'Abmeldung erfolgreich',
+
+  // Change password
+  Strings.changingPassword: 'Passwort wird geändert...',
+  Strings.changePasswordSuccessful: 'Passwort erfolgreich geändert',
+
+  // Delete account
+  Strings.deletingAccount: 'Konto wird gelöscht...',
+  Strings.deleteAccountSuccessful: 'Konto erfolgreich gelöscht',
+  Strings.deleteAccountConfirmMessage:
+      'Das Löschen Ihres Kontos entfernt dauerhaft Ihre Daten und Mitgliedschaftsinformationen. Diese Aktion kann nicht rückgängig gemacht werden.',
+  Strings.deleteAccountConfirmButton: 'Löschen',
+
+  // Remain time
+  Strings.remainTime: 'Verbleibende Zeit',
+  Strings.remainTimeEnded: 'Ihre verfügbare Zeit ist abgelaufen',
 };

+ 38 - 1
lib/config/translations/en_US/en_us_translation.dart

@@ -380,7 +380,17 @@ Map<String, String> enUs = {
   Strings.pushNotifications: 'Push Notifications',
   Strings.upgradeNow: 'Upgrade Now',
 
-  // 新语言
+  // Languages
+  Strings.enLang: 'English',
+  Strings.esLang: 'Español',
+  Strings.frLang: 'Français',
+  Strings.deLang: 'Deutsch',
+  Strings.jaLang: '日本語',
+  Strings.koLang: '한국어',
+  Strings.faLang: 'فارسی',
+  Strings.myLang: 'မြန်မာဘာသာ',
+  Strings.arLang: 'عربي',
+  Strings.ruLang: 'Русский',
   Strings.zhTWLang: '繁體中文',
   Strings.tkLang: 'Türkmençe',
   Strings.ptBRLang: 'Português (Brasil)',
@@ -391,6 +401,29 @@ Map<String, String> enUs = {
   Strings.hiLang: 'हिन्दी',
   Strings.trLang: 'Türkçe',
 
+  // Signup
+  Strings.signingUp: 'Signing up...',
+  Strings.signUpSuccessful: 'Sign up successful',
+
+  // Login
+  Strings.loggingIn: 'Logging in...',
+  Strings.loginSuccessful: 'Login successful',
+
+  // Logout
+  Strings.loggingOut: 'Logging out...',
+  Strings.logoutSuccessful: 'Logout successful',
+
+  // Change password
+  Strings.changingPassword: 'Changing password...',
+  Strings.changePasswordSuccessful: 'Change password successful',
+
+  // Delete account
+  Strings.deletingAccount: 'Deleting account...',
+  Strings.deleteAccountSuccessful: 'Delete account successful',
+  Strings.deleteAccountConfirmMessage:
+      'Deleting your account will permanently remove your data and membership information. This action cannot be undone.',
+  Strings.deleteAccountConfirmButton: 'Delete',
+
   // Bind email / Member benefits
   Strings.bindEmailMemberBenefits: 'Bind email/Member benefits',
   Strings.bindingAccountEmailProtectsPreRights:
@@ -409,4 +442,8 @@ Map<String, String> enUs = {
   Strings.vpnServiceEmptyError: 'VPN service is unavailable',
   Strings.vpnRouterError: 'VPN routing configuration error',
   Strings.vpnPermissionDeniedError: 'VPN permission denied',
+
+  // Remain time
+  Strings.remainTime: 'Remain time',
+  Strings.remainTimeEnded: 'Your available time has ended',
 };

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

@@ -403,4 +403,52 @@ const Map<String, String> esEs = {
   Strings.vpnServiceEmptyError: 'Servicio VPN no disponible',
   Strings.vpnRouterError: 'Error de configuración de enrutamiento VPN',
   Strings.vpnPermissionDeniedError: 'Permiso de VPN denegado',
+
+  // Languages
+  Strings.enLang: 'English',
+  Strings.esLang: 'Español',
+  Strings.frLang: 'Français',
+  Strings.deLang: 'Deutsch',
+  Strings.jaLang: '日本語',
+  Strings.koLang: '한국어',
+  Strings.faLang: 'فارسی',
+  Strings.myLang: 'မြန်မာဘာသာ',
+  Strings.arLang: 'عربي',
+  Strings.ruLang: 'Русский',
+  Strings.zhTWLang: '繁體中文',
+  Strings.tkLang: 'Türkmençe',
+  Strings.ptBRLang: 'Português (Brasil)',
+  Strings.viLang: 'Tiếng Việt',
+  Strings.idLang: 'Bahasa Indonesia',
+  Strings.tlLang: 'Filipino',
+  Strings.thLang: 'ไทย',
+  Strings.hiLang: 'हिन्दी',
+  Strings.trLang: 'Türkçe',
+
+  // Signup
+  Strings.signingUp: 'Registrando...',
+  Strings.signUpSuccessful: 'Registro exitoso',
+
+  // Login
+  Strings.loggingIn: 'Iniciando sesión...',
+  Strings.loginSuccessful: 'Inicio de sesión exitoso',
+
+  // Logout
+  Strings.loggingOut: 'Cerrando sesión...',
+  Strings.logoutSuccessful: 'Cierre de sesión exitoso',
+
+  // Change password
+  Strings.changingPassword: 'Cambiando contraseña...',
+  Strings.changePasswordSuccessful: 'Contraseña cambiada exitosamente',
+
+  // Delete account
+  Strings.deletingAccount: 'Eliminando cuenta...',
+  Strings.deleteAccountSuccessful: 'Cuenta eliminada exitosamente',
+  Strings.deleteAccountConfirmMessage:
+      'Eliminar tu cuenta eliminará permanentemente tus datos e información de membresía. Esta acción no se puede deshacer.',
+  Strings.deleteAccountConfirmButton: 'Eliminar',
+
+  // Remain time
+  Strings.remainTime: 'Tiempo restante',
+  Strings.remainTimeEnded: 'Tu tiempo disponible ha terminado',
 };

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

@@ -399,4 +399,52 @@ const Map<String, String> faIR = {
   Strings.vpnServiceEmptyError: 'سرویس VPN در دسترس نیست',
   Strings.vpnRouterError: 'خطای پیکربندی مسیریابی VPN',
   Strings.vpnPermissionDeniedError: 'مجوز VPN رد شد',
+
+  // Languages
+  Strings.enLang: 'English',
+  Strings.esLang: 'Español',
+  Strings.frLang: 'Français',
+  Strings.deLang: 'Deutsch',
+  Strings.jaLang: '日本語',
+  Strings.koLang: '한국어',
+  Strings.faLang: 'فارسی',
+  Strings.myLang: 'မြန်မာဘာသာ',
+  Strings.arLang: 'عربي',
+  Strings.ruLang: 'Русский',
+  Strings.zhTWLang: '繁體中文',
+  Strings.tkLang: 'Türkmençe',
+  Strings.ptBRLang: 'Português (Brasil)',
+  Strings.viLang: 'Tiếng Việt',
+  Strings.idLang: 'Bahasa Indonesia',
+  Strings.tlLang: 'Filipino',
+  Strings.thLang: 'ไทย',
+  Strings.hiLang: 'हिन्दी',
+  Strings.trLang: 'Türkçe',
+
+  // Signup
+  Strings.signingUp: 'در حال ثبت نام...',
+  Strings.signUpSuccessful: 'ثبت نام موفق',
+
+  // Login
+  Strings.loggingIn: 'در حال ورود...',
+  Strings.loginSuccessful: 'ورود موفق',
+
+  // Logout
+  Strings.loggingOut: 'در حال خروج...',
+  Strings.logoutSuccessful: 'خروج موفق',
+
+  // Change password
+  Strings.changingPassword: 'در حال تغییر رمز عبور...',
+  Strings.changePasswordSuccessful: 'رمز عبور با موفقیت تغییر کرد',
+
+  // Delete account
+  Strings.deletingAccount: 'در حال حذف حساب...',
+  Strings.deleteAccountSuccessful: 'حساب با موفقیت حذف شد',
+  Strings.deleteAccountConfirmMessage:
+      'حذف حساب شما به طور دائم داده‌ها و اطلاعات عضویت شما را حذف می‌کند. این عمل قابل بازگشت نیست.',
+  Strings.deleteAccountConfirmButton: 'حذف',
+
+  // Remain time
+  Strings.remainTime: 'زمان باقی‌مانده',
+  Strings.remainTimeEnded: 'زمان در دسترس شما به پایان رسیده است',
 };

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

@@ -404,4 +404,52 @@ const Map<String, String> frFR = {
   Strings.vpnServiceEmptyError: 'Service VPN indisponible',
   Strings.vpnRouterError: 'Erreur de configuration du routage VPN',
   Strings.vpnPermissionDeniedError: 'Autorisation VPN refusée',
+
+  // Languages
+  Strings.enLang: 'English',
+  Strings.esLang: 'Español',
+  Strings.frLang: 'Français',
+  Strings.deLang: 'Deutsch',
+  Strings.jaLang: '日本語',
+  Strings.koLang: '한국어',
+  Strings.faLang: 'فارسی',
+  Strings.myLang: 'မြန်မာဘာသာ',
+  Strings.arLang: 'عربي',
+  Strings.ruLang: 'Русский',
+  Strings.zhTWLang: '繁體中文',
+  Strings.tkLang: 'Türkmençe',
+  Strings.ptBRLang: 'Português (Brasil)',
+  Strings.viLang: 'Tiếng Việt',
+  Strings.idLang: 'Bahasa Indonesia',
+  Strings.tlLang: 'Filipino',
+  Strings.thLang: 'ไทย',
+  Strings.hiLang: 'हिन्दी',
+  Strings.trLang: 'Türkçe',
+
+  // Signup
+  Strings.signingUp: 'Inscription en cours...',
+  Strings.signUpSuccessful: 'Inscription réussie',
+
+  // Login
+  Strings.loggingIn: 'Connexion en cours...',
+  Strings.loginSuccessful: 'Connexion réussie',
+
+  // Logout
+  Strings.loggingOut: 'Déconnexion en cours...',
+  Strings.logoutSuccessful: 'Déconnexion réussie',
+
+  // Change password
+  Strings.changingPassword: 'Changement de mot de passe...',
+  Strings.changePasswordSuccessful: 'Mot de passe changé avec succès',
+
+  // Delete account
+  Strings.deletingAccount: 'Suppression du compte...',
+  Strings.deleteAccountSuccessful: 'Compte supprimé avec succès',
+  Strings.deleteAccountConfirmMessage:
+      'La suppression de votre compte supprimera définitivement vos données et informations d\'adhésion. Cette action est irréversible.',
+  Strings.deleteAccountConfirmButton: 'Supprimer',
+
+  // Remain time
+  Strings.remainTime: 'Temps restant',
+  Strings.remainTimeEnded: 'Votre temps disponible est terminé',
 };

+ 4 - 1
lib/config/translations/hi_IN/hi_in_translation.dart

@@ -312,5 +312,8 @@ Map<String, String> hiIN = {
   Strings.vpnServiceEmptyError: 'VPN सेवा उपलब्ध नहीं है',
   Strings.vpnRouterError: 'VPN रूटिंग कॉन्फ़िगरेशन त्रुटि',
   Strings.vpnPermissionDeniedError: 'VPN अनुमति अस्वीकृत',
-};
 
+  // Remain time
+  Strings.remainTime: 'शेष समय',
+  Strings.remainTimeEnded: 'आपका उपलब्ध समय समाप्त हो गया है',
+};

+ 4 - 1
lib/config/translations/id_ID/id_id_translation.dart

@@ -312,5 +312,8 @@ Map<String, String> idID = {
   Strings.vpnServiceEmptyError: 'Layanan VPN tidak tersedia',
   Strings.vpnRouterError: 'Kesalahan konfigurasi routing VPN',
   Strings.vpnPermissionDeniedError: 'Izin VPN ditolak',
-};
 
+  // Remain time
+  Strings.remainTime: 'Waktu tersisa',
+  Strings.remainTimeEnded: 'Waktu yang tersedia telah berakhir',
+};

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

@@ -378,4 +378,52 @@ const Map<String, String> jaJP = {
   Strings.vpnServiceEmptyError: 'VPNサービスが利用できません',
   Strings.vpnRouterError: 'VPNルーティング設定エラー',
   Strings.vpnPermissionDeniedError: 'VPN権限が拒否されました',
+
+  // Languages
+  Strings.enLang: 'English',
+  Strings.esLang: 'Español',
+  Strings.frLang: 'Français',
+  Strings.deLang: 'Deutsch',
+  Strings.jaLang: '日本語',
+  Strings.koLang: '한국어',
+  Strings.faLang: 'فارسی',
+  Strings.myLang: 'မြန်မာဘာသာ',
+  Strings.arLang: 'عربي',
+  Strings.ruLang: 'Русский',
+  Strings.zhTWLang: '繁體中文',
+  Strings.tkLang: 'Türkmençe',
+  Strings.ptBRLang: 'Português (Brasil)',
+  Strings.viLang: 'Tiếng Việt',
+  Strings.idLang: 'Bahasa Indonesia',
+  Strings.tlLang: 'Filipino',
+  Strings.thLang: 'ไทย',
+  Strings.hiLang: 'हिन्दी',
+  Strings.trLang: 'Türkçe',
+
+  // Signup
+  Strings.signingUp: 'サインアップ中...',
+  Strings.signUpSuccessful: 'サインアップ成功',
+
+  // Login
+  Strings.loggingIn: 'ログイン中...',
+  Strings.loginSuccessful: 'ログイン成功',
+
+  // Logout
+  Strings.loggingOut: 'ログアウト中...',
+  Strings.logoutSuccessful: 'ログアウト成功',
+
+  // Change password
+  Strings.changingPassword: 'パスワード変更中...',
+  Strings.changePasswordSuccessful: 'パスワード変更成功',
+
+  // Delete account
+  Strings.deletingAccount: 'アカウント削除中...',
+  Strings.deleteAccountSuccessful: 'アカウント削除成功',
+  Strings.deleteAccountConfirmMessage:
+      'アカウントを削除すると、データと会員情報が完全に削除されます。この操作は元に戻すことができません。',
+  Strings.deleteAccountConfirmButton: '削除',
+
+  // Remain time
+  Strings.remainTime: '残り時間',
+  Strings.remainTimeEnded: '利用可能な時間が終了しました',
 };

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

@@ -371,4 +371,52 @@ const Map<String, String> koKR = {
   Strings.vpnServiceEmptyError: 'VPN 서비스를 사용할 수 없습니다',
   Strings.vpnRouterError: 'VPN 라우팅 구성 오류',
   Strings.vpnPermissionDeniedError: 'VPN 권한이 거부되었습니다',
+
+  // Languages
+  Strings.enLang: 'English',
+  Strings.esLang: 'Español',
+  Strings.frLang: 'Français',
+  Strings.deLang: 'Deutsch',
+  Strings.jaLang: '日本語',
+  Strings.koLang: '한국어',
+  Strings.faLang: 'فارسی',
+  Strings.myLang: 'မြန်မာဘာသာ',
+  Strings.arLang: 'عربي',
+  Strings.ruLang: 'Русский',
+  Strings.zhTWLang: '繁體中文',
+  Strings.tkLang: 'Türkmençe',
+  Strings.ptBRLang: 'Português (Brasil)',
+  Strings.viLang: 'Tiếng Việt',
+  Strings.idLang: 'Bahasa Indonesia',
+  Strings.tlLang: 'Filipino',
+  Strings.thLang: 'ไทย',
+  Strings.hiLang: 'हिन्दी',
+  Strings.trLang: 'Türkçe',
+
+  // Signup
+  Strings.signingUp: '가입 중...',
+  Strings.signUpSuccessful: '가입 성공',
+
+  // Login
+  Strings.loggingIn: '로그인 중...',
+  Strings.loginSuccessful: '로그인 성공',
+
+  // Logout
+  Strings.loggingOut: '로그아웃 중...',
+  Strings.logoutSuccessful: '로그아웃 성공',
+
+  // Change password
+  Strings.changingPassword: '비밀번호 변경 중...',
+  Strings.changePasswordSuccessful: '비밀번호 변경 성공',
+
+  // Delete account
+  Strings.deletingAccount: '계정 삭제 중...',
+  Strings.deleteAccountSuccessful: '계정 삭제 성공',
+  Strings.deleteAccountConfirmMessage:
+      '계정을 삭제하면 데이터와 멤버십 정보가 영구적으로 제거됩니다. 이 작업은 되돌릴 수 없습니다.',
+  Strings.deleteAccountConfirmButton: '삭제',
+
+  // Remain time
+  Strings.remainTime: '남은 시간',
+  Strings.remainTimeEnded: '사용 가능한 시간이 종료되었습니다',
 };

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

@@ -407,4 +407,52 @@ const Map<String, String> myMM = {
   Strings.vpnServiceEmptyError: 'VPN ဝန်ဆောင်မှု မရရှိနိုင်ပါ',
   Strings.vpnRouterError: 'VPN routing စီစဉ်မှု အမှား',
   Strings.vpnPermissionDeniedError: 'VPN ခွင့်ပြုချက် ငြင်းပယ်ခံရပါပြီ',
+
+  // Languages
+  Strings.enLang: 'English',
+  Strings.esLang: 'Español',
+  Strings.frLang: 'Français',
+  Strings.deLang: 'Deutsch',
+  Strings.jaLang: '日本語',
+  Strings.koLang: '한국어',
+  Strings.faLang: 'فارسی',
+  Strings.myLang: 'မြန်မာဘာသာ',
+  Strings.arLang: 'عربي',
+  Strings.ruLang: 'Русский',
+  Strings.zhTWLang: '繁體中文',
+  Strings.tkLang: 'Türkmençe',
+  Strings.ptBRLang: 'Português (Brasil)',
+  Strings.viLang: 'Tiếng Việt',
+  Strings.idLang: 'Bahasa Indonesia',
+  Strings.tlLang: 'Filipino',
+  Strings.thLang: 'ไทย',
+  Strings.hiLang: 'हिन्दी',
+  Strings.trLang: 'Türkçe',
+
+  // Signup
+  Strings.signingUp: 'စာရင်းသွင်းနေသည်...',
+  Strings.signUpSuccessful: 'စာရင်းသွင်းခြင်း အောင်မြင်ပါသည်',
+
+  // Login
+  Strings.loggingIn: 'အကောင့်ဝင်နေသည်...',
+  Strings.loginSuccessful: 'အကောင့်ဝင်ခြင်း အောင်မြင်ပါသည်',
+
+  // Logout
+  Strings.loggingOut: 'အကောင့်ထွက်နေသည်...',
+  Strings.logoutSuccessful: 'အကောင့်ထွက်ခြင်း အောင်မြင်ပါသည်',
+
+  // Change password
+  Strings.changingPassword: 'စကားဝှက်ပြောင်းလဲနေသည်...',
+  Strings.changePasswordSuccessful: 'စကားဝှက်ပြောင်းလဲခြင်း အောင်မြင်ပါသည်',
+
+  // Delete account
+  Strings.deletingAccount: 'အကောင့်ဖျက်နေသည်...',
+  Strings.deleteAccountSuccessful: 'အကောင့်ဖျက်ခြင်း အောင်မြင်ပါသည်',
+  Strings.deleteAccountConfirmMessage:
+      'သင့်အကောင့်ကို ဖျက်ခြင်းသည် သင့်ဒေတာနှင့် အသင်းဝင်အချက်အလက်များကို အပြီးအပိုင်ဖယ်ရှားမည်ဖြစ်သည်။ ဤလုပ်ဆောင်ချက်ကို ပြန်ပြင်၍မရပါ။',
+  Strings.deleteAccountConfirmButton: 'ဖျက်မည်',
+
+  // Remain time
+  Strings.remainTime: 'ကျန်အချိန်',
+  Strings.remainTimeEnded: 'သင့်ရနိုင်သောအချိန် ကုန်ဆုံးသွားပါပြီ',
 };

+ 4 - 1
lib/config/translations/pt_BR/pt_br_translation.dart

@@ -442,5 +442,8 @@ Map<String, String> ptBR = {
   Strings.vpnServiceEmptyError: 'Serviço VPN indisponível',
   Strings.vpnRouterError: 'Erro de configuração de roteamento VPN',
   Strings.vpnPermissionDeniedError: 'Permissão de VPN negada',
-};
 
+  // Remain time
+  Strings.remainTime: 'Tempo restante',
+  Strings.remainTimeEnded: 'Seu tempo disponível acabou',
+};

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

@@ -403,4 +403,52 @@ const Map<String, String> ruRU = {
   Strings.vpnServiceEmptyError: 'VPN-сервис недоступен',
   Strings.vpnRouterError: 'Ошибка конфигурации маршрутизации VPN',
   Strings.vpnPermissionDeniedError: 'Разрешение VPN отклонено',
+
+  // Languages
+  Strings.enLang: 'English',
+  Strings.esLang: 'Español',
+  Strings.frLang: 'Français',
+  Strings.deLang: 'Deutsch',
+  Strings.jaLang: '日本語',
+  Strings.koLang: '한국어',
+  Strings.faLang: 'فارسی',
+  Strings.myLang: 'မြန်မာဘာသာ',
+  Strings.arLang: 'عربي',
+  Strings.ruLang: 'Русский',
+  Strings.zhTWLang: '繁體中文',
+  Strings.tkLang: 'Türkmençe',
+  Strings.ptBRLang: 'Português (Brasil)',
+  Strings.viLang: 'Tiếng Việt',
+  Strings.idLang: 'Bahasa Indonesia',
+  Strings.tlLang: 'Filipino',
+  Strings.thLang: 'ไทย',
+  Strings.hiLang: 'हिन्दी',
+  Strings.trLang: 'Türkçe',
+
+  // Signup
+  Strings.signingUp: 'Регистрация...',
+  Strings.signUpSuccessful: 'Регистрация успешна',
+
+  // Login
+  Strings.loggingIn: 'Вход в систему...',
+  Strings.loginSuccessful: 'Вход выполнен успешно',
+
+  // Logout
+  Strings.loggingOut: 'Выход из системы...',
+  Strings.logoutSuccessful: 'Выход выполнен успешно',
+
+  // Change password
+  Strings.changingPassword: 'Изменение пароля...',
+  Strings.changePasswordSuccessful: 'Пароль успешно изменён',
+
+  // Delete account
+  Strings.deletingAccount: 'Удаление аккаунта...',
+  Strings.deleteAccountSuccessful: 'Аккаунт успешно удалён',
+  Strings.deleteAccountConfirmMessage:
+      'Удаление аккаунта безвозвратно удалит ваши данные и информацию о членстве. Это действие нельзя отменить.',
+  Strings.deleteAccountConfirmButton: 'Удалить',
+
+  // Remain time
+  Strings.remainTime: 'Оставшееся время',
+  Strings.remainTimeEnded: 'Ваше доступное время истекло',
 };

+ 4 - 1
lib/config/translations/th_TH/th_th_translation.dart

@@ -312,5 +312,8 @@ Map<String, String> thTH = {
   Strings.vpnServiceEmptyError: 'บริการ VPN ไม่พร้อมใช้งาน',
   Strings.vpnRouterError: 'ข้อผิดพลาดการกำหนดค่าเส้นทาง VPN',
   Strings.vpnPermissionDeniedError: 'สิทธิ์ VPN ถูกปฏิเสธ',
-};
 
+  // Remain time
+  Strings.remainTime: 'เวลาที่เหลือ',
+  Strings.remainTimeEnded: 'เวลาที่คุณใช้ได้หมดลงแล้ว',
+};

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

@@ -439,4 +439,8 @@ Map<String, String> tkTM = {
   Strings.vpnServiceEmptyError: 'VPN hyzmaty elýeterli däl',
   Strings.vpnRouterError: 'VPN marşrutlaşdyryş sazlaýyş ýalňyşlygy',
   Strings.vpnPermissionDeniedError: 'VPN rugsady ret edildi',
+
+  // Remain time
+  Strings.remainTime: 'Galan wagt',
+  Strings.remainTimeEnded: 'Elýeterli wagtyňyz gutardy',
 };

+ 4 - 1
lib/config/translations/tl_PH/tl_ph_translation.dart

@@ -312,5 +312,8 @@ Map<String, String> tlPH = {
   Strings.vpnServiceEmptyError: 'Hindi available ang serbisyo ng VPN',
   Strings.vpnRouterError: 'Error sa configuration ng VPN routing',
   Strings.vpnPermissionDeniedError: 'Tinanggihan ang pahintulot sa VPN',
-};
 
+  // Remain time
+  Strings.remainTime: 'Natitirang oras',
+  Strings.remainTimeEnded: 'Natapos na ang iyong available na oras',
+};

+ 4 - 1
lib/config/translations/tr_TR/tr_tr_translation.dart

@@ -312,5 +312,8 @@ Map<String, String> trTR = {
   Strings.vpnServiceEmptyError: 'VPN hizmeti kullanılamıyor',
   Strings.vpnRouterError: 'VPN yönlendirme yapılandırma hatası',
   Strings.vpnPermissionDeniedError: 'VPN izni reddedildi',
-};
 
+  // Remain time
+  Strings.remainTime: 'Kalan süre',
+  Strings.remainTimeEnded: 'Kullanılabilir süreniz sona erdi',
+};

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

@@ -353,4 +353,8 @@ Map<String, String> viVN = {
   Strings.vpnServiceEmptyError: 'Dịch vụ VPN không khả dụng',
   Strings.vpnRouterError: 'Lỗi cấu hình định tuyến VPN',
   Strings.vpnPermissionDeniedError: 'Quyền VPN bị từ chối',
+
+  // 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',
 };

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

@@ -404,4 +404,8 @@ Map<String, String> zhTW = {
   Strings.vpnServiceEmptyError: 'VPN 服務不可用',
   Strings.vpnRouterError: 'VPN 路由配置錯誤',
   Strings.vpnPermissionDeniedError: 'VPN 權限被拒絕',
+
+  // Remain time
+  Strings.remainTime: '剩餘時間',
+  Strings.remainTimeEnded: '您的可用時間已結束',
 };

+ 31 - 21
lib/utils/device_manager.dart

@@ -11,6 +11,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
 
 import '../app/data/sp/ix_sp.dart';
 import 'log/logger.dart';
+import 'machine_guid_windows.dart';
 import 'permission_manager.dart';
 
 class DeviceManager {
@@ -93,7 +94,7 @@ class DeviceManager {
       } catch (e) {
         log(TAG, 'Error reading from iOS keychain: $e');
       }
-    } else {
+    } else if (Platform.isAndroid) {
       // Android 从文件夹获取
       try {
         final downloadsDir = await _getDownloadsDirectory();
@@ -119,6 +120,14 @@ class DeviceManager {
       } catch (e) {
         log(TAG, 'Error getting device ID from Android folder: $e');
       }
+    } else if (Platform.isWindows) {
+      try {
+        final machineId = getMachineGuid();
+        await IXSP.setDeviceId(machineId);
+        return machineId;
+      } catch (e) {
+        log(TAG, 'Error getting machine ID from Windows: $e');
+      }
     }
     return null;
   }
@@ -170,6 +179,10 @@ class DeviceManager {
       final deviceId = _generateHash(jsonEncode(deviceInfo));
       await _saveDeviceId(deviceId);
       return deviceId;
+    } else if (Platform.isWindows) {
+      final deviceId = const Uuid().v4();
+      await IXSP.setDeviceId(deviceId);
+      return deviceId;
     }
 
     return _generateFallbackId();
@@ -177,29 +190,26 @@ class DeviceManager {
 
   /// 确保设备文件夹存在
   static Future<void> _ensureDeviceFolder(String deviceId) async {
-    // iOS 不需要创建文件夹,数据存储在钥匙串中
-    if (Platform.isIOS) {
-      return;
-    }
-
-    try {
-      final downloadsDir = await _getDownloadsDirectory();
-      if (downloadsDir == null || !await downloadsDir.exists()) {
-        throw Exception('Downloads directory not found');
-      }
+    if (Platform.isAndroid) {
+      try {
+        final downloadsDir = await _getDownloadsDirectory();
+        if (downloadsDir == null || !await downloadsDir.exists()) {
+          throw Exception('Downloads directory not found');
+        }
 
-      final folderName = '$_folderPrefix$deviceId';
-      final deviceFolder = Directory('${downloadsDir.path}/$folderName');
+        final folderName = '$_folderPrefix$deviceId';
+        final deviceFolder = Directory('${downloadsDir.path}/$folderName');
 
-      if (!await deviceFolder.exists()) {
-        await deviceFolder.create();
-        // 创建.nomedia文件
-        final nomedia = File('${deviceFolder.path}/.nomedia');
-        await nomedia.create();
+        if (!await deviceFolder.exists()) {
+          await deviceFolder.create();
+          // 创建.nomedia文件
+          final nomedia = File('${deviceFolder.path}/.nomedia');
+          await nomedia.create();
+        }
+      } catch (e) {
+        log(TAG, 'Error ensuring device folder: $e');
+        rethrow;
       }
-    } catch (e) {
-      log(TAG, 'Error ensuring device folder: $e');
-      rethrow;
     }
   }
 

+ 110 - 0
lib/utils/machine_guid_windows.dart

@@ -0,0 +1,110 @@
+// ignore_for_file: unused_import, non_constant_identifier_names, unused_field
+// ignore_for_file: constant_identifier_names
+
+import 'dart:convert';
+import 'dart:ffi';
+import 'dart:io';
+import 'package:ffi/ffi.dart';
+
+String getMachineGuid() {
+  if (!Platform.isWindows) {
+    throw Exception('"getMachineGuid" only supports Windows.');
+  }
+
+  return using<String>((alloc) {
+    final hkey = calloc<IntPtr>();
+
+    _Native.RegOpenKeyEx(hkey);
+
+    try {
+      return _Native.RegQueryValueEx(alloc, hkey);
+    } catch (e) {
+      rethrow;
+    } finally {
+      _Native.RegCloseKey(hkey);
+    }
+  });
+}
+
+class _Native {
+  // Load the advapi32.dll library
+  static final _advapi32 = DynamicLibrary.open('Advapi32.dll');
+
+  // Define the RegOpenKeyExW function signature
+  static final _RegOpenKeyExA = _advapi32.lookupFunction<
+      Int32 Function(IntPtr, Pointer<Utf8>, Uint32, Uint32, Pointer<IntPtr>),
+      int Function(
+          int, Pointer<Utf8>, int, int, Pointer<IntPtr>)>('RegOpenKeyExA');
+
+  // Define the RegCloseKey function signature
+  static final _RegCloseKey = _advapi32
+      .lookupFunction<Int32 Function(IntPtr), int Function(int)>('RegCloseKey');
+
+  // Define the RegQueryValueExW function signature
+  static final _RegQueryValueExA = _advapi32.lookupFunction<
+      Int32 Function(IntPtr, Pointer<Utf8>, Pointer<IntPtr>, Pointer<Uint32>,
+          Pointer<Uint8>, Pointer<Uint32>),
+      int Function(int, Pointer<Utf8>, Pointer<IntPtr>, Pointer<Uint32>,
+          Pointer<Uint8>, Pointer<Uint32>)>('RegQueryValueExA');
+
+  // Define the HKEY_LOCAL_MACHINE constant
+  static const _HKEY_LOCAL_MACHINE = 0x80000002;
+  static const _KEY_QUERY_VALUE = 0x0001;
+  static const _KEY_WOW64_64KEY = 0x0100;
+
+  static const kSuccess = 0;
+  static const kRegKey = "SOFTWARE\\Microsoft\\Cryptography";
+  static const kValueKey = "MachineGuid";
+
+  static void RegOpenKeyEx(Pointer<IntPtr> hkey) {
+    final res = _RegOpenKeyExA(
+      _HKEY_LOCAL_MACHINE,
+      kRegKey.toNativeUtf8(),
+      0,
+      _KEY_QUERY_VALUE | _KEY_WOW64_64KEY,
+      hkey,
+    );
+    if (res != _Native.kSuccess) {
+      throw Exception('Failed to open registry: $res');
+    }
+  }
+
+  static void RegCloseKey(Pointer<IntPtr> hkey) {
+    _RegCloseKey(hkey.value);
+  }
+
+  static String RegQueryValueEx(Arena alloc, Pointer<IntPtr> hkey) {
+    final dataTypePtr = alloc<Uint32>();
+    dataTypePtr.value = 0;
+    final dataSizePtr = alloc<Uint32>();
+
+    var res = _RegQueryValueExA(
+      hkey.value,
+      kValueKey.toNativeUtf8(),
+      nullptr,
+      dataTypePtr,
+      nullptr,
+      dataSizePtr,
+    );
+
+    if (kSuccess != res) {
+      throw Exception('Failed to get registry value size: $res');
+    }
+
+    final data = alloc<Uint8>(dataSizePtr.value);
+
+    res = _RegQueryValueExA(
+      hkey.value,
+      kValueKey.toNativeUtf8(),
+      nullptr,
+      dataTypePtr,
+      data,
+      dataSizePtr,
+    );
+    if (kSuccess != res) {
+      throw Exception('Failed to get registry value: $res');
+    }
+
+    return utf8.decode(data.asTypedList(dataSizePtr.value));
+  }
+}

+ 3 - 3
pubspec.lock

@@ -418,13 +418,13 @@ packages:
     source: hosted
     version: "1.3.3"
   ffi:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: ffi
-      sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
+      sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.4"
+    version: "2.1.5"
   file:
     dependency: transitive
     description:

+ 1 - 0
pubspec.yaml

@@ -87,6 +87,7 @@ dependencies:
   flutter_app_group_directory: ^1.1.0 # App Group目录
   shelf_web_socket: ^3.0.0 # windows 进程通信
   shelf: ^1.4.2 # windows 进程通信
+  ffi: ^2.1.5 # FFI
   # hive_ce: ^2.17.0 # 本地缓存
 
 dev_dependencies: