瀏覽代碼

🚀 Refactor Blade

- Optimize Blade JavaScript code.
- Refactored multiple admin controllers for improved validation, error handling, and query efficiency.
- Added ProxyConfig trait to centralize proxy configuration options.
- Updated NodeStatusDetection to use model relationships for heartbeat checks.
- Improved category, label, and country management logic and error logging.
- Added new Blade components for admin UI and updated language files and assets for better localization and frontend support.
- Bug fixed & introduced more bug :)
BrettonYe 7 月之前
父節點
當前提交
ad3662cda0
共有 100 個文件被更改,包括 4923 次插入3714 次删除
  1. 22 26
      app/Channels/Library/WeChat.php
  2. 4 7
      app/Console/Commands/NodeStatusDetection.php
  3. 26 0
      app/Helpers/ProxyConfig.php
  4. 2 3
      app/Http/Controllers/Admin/AffiliateController.php
  5. 3 8
      app/Http/Controllers/Admin/ArticleController.php
  6. 1 13
      app/Http/Controllers/Admin/CertController.php
  7. 17 7
      app/Http/Controllers/Admin/Config/CategoryController.php
  8. 8 2
      app/Http/Controllers/Admin/Config/CountryController.php
  9. 1 1
      app/Http/Controllers/Admin/Config/EmailFilterController.php
  10. 24 6
      app/Http/Controllers/Admin/Config/LabelController.php
  11. 16 4
      app/Http/Controllers/Admin/Config/LevelController.php
  12. 21 4
      app/Http/Controllers/Admin/Config/SsConfigController.php
  13. 4 11
      app/Http/Controllers/Admin/CouponController.php
  14. 20 11
      app/Http/Controllers/Admin/InviteController.php
  15. 8 6
      app/Http/Controllers/Admin/LogsController.php
  16. 6 6
      app/Http/Controllers/Admin/MarketingController.php
  17. 20 15
      app/Http/Controllers/Admin/NodeAuthController.php
  18. 36 27
      app/Http/Controllers/Admin/NodeController.php
  19. 15 7
      app/Http/Controllers/Admin/PermissionController.php
  20. 46 10
      app/Http/Controllers/Admin/RoleController.php
  21. 18 6
      app/Http/Controllers/Admin/RuleController.php
  22. 28 9
      app/Http/Controllers/Admin/RuleGroupController.php
  23. 24 7
      app/Http/Controllers/Admin/ShopController.php
  24. 16 2
      app/Http/Controllers/Admin/SubscribeController.php
  25. 50 42
      app/Http/Controllers/Admin/SystemController.php
  26. 17 5
      app/Http/Controllers/Admin/TicketController.php
  27. 41 28
      app/Http/Controllers/Admin/ToolsController.php
  28. 41 25
      app/Http/Controllers/Admin/UserController.php
  29. 6 1
      app/Http/Controllers/Admin/UserGroupController.php
  30. 8 13
      app/Http/Controllers/AdminController.php
  31. 2 3
      app/Http/Controllers/Api/Client/ClientController.php
  32. 11 19
      app/Http/Controllers/AuthController.php
  33. 55 1
      app/Http/Controllers/OAuthController.php
  34. 1 1
      app/Http/Controllers/User/AffiliateController.php
  35. 2 4
      app/Http/Controllers/User/ArticleController.php
  36. 19 8
      app/Http/Controllers/User/InviteController.php
  37. 13 12
      app/Http/Controllers/User/NodeController.php
  38. 24 20
      app/Http/Controllers/User/ShopController.php
  39. 40 22
      app/Http/Controllers/User/SubscribeController.php
  40. 50 24
      app/Http/Controllers/User/TicketController.php
  41. 4 4
      app/Http/Controllers/UserController.php
  42. 1 1
      app/Http/Requests/Admin/PermissionRequest.php
  43. 1 1
      app/Http/Requests/Admin/UserGroupRequest.php
  44. 1 1
      app/Http/Requests/Admin/UserStoreRequest.php
  45. 1 1
      app/Http/Requests/Admin/UserUpdateRequest.php
  46. 2 0
      app/Models/CouponLog.php
  47. 5 0
      app/Models/Goods.php
  48. 20 0
      app/Models/Node.php
  49. 53 0
      app/Models/NodeCertificate.php
  50. 0 6
      app/Models/NodeHeartbeat.php
  51. 3 1
      app/Models/NotificationLog.php
  52. 5 0
      app/Models/User.php
  53. 2 2
      app/Services/OrderService.php
  54. 11 16
      app/Services/ProxyService.php
  55. 35 70
      app/Utils/Helpers.php
  56. 1 1
      app/Utils/Payments/PaymentManager.php
  57. 0 23
      app/View/Components/Alert.php
  58. 29 4
      app/helpers.php
  59. 1 3
      composer.json
  60. 18 26
      config/common.php
  61. 0 0
      public/assets/bundle/app.min.css
  62. 3 1
      public/assets/global/js/Plugin/bootstrap-datepicker.js
  63. 2 2
      public/assets/global/vendor/bootstrap-datepicker/bootstrap-datepicker.min.js
  64. 1 0
      public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.de.min.js
  65. 1 0
      public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.en.min.js
  66. 1 0
      public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.fa.min.js
  67. 1 0
      public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ja.min.js
  68. 1 0
      public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ko.min.js
  69. 1 0
      public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ru.min.js
  70. 1 0
      public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.vi.min.js
  71. 1 0
      public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js
  72. 271 0
      public/assets/js/config/admin.js
  73. 513 0
      public/assets/js/config/common.js
  74. 72 72
      resources/lang/de.json
  75. 403 464
      resources/lang/de/admin.php
  76. 41 41
      resources/lang/de/auth.php
  77. 42 42
      resources/lang/de/common.php
  78. 20 20
      resources/lang/de/errors.php
  79. 235 103
      resources/lang/de/model.php
  80. 37 37
      resources/lang/de/notification.php
  81. 176 166
      resources/lang/de/user.php
  82. 1 1
      resources/lang/en.json
  83. 8 2
      resources/lang/en/admin.php
  84. 1 0
      resources/lang/en/common.php
  85. 106 106
      resources/lang/fa.json
  86. 465 582
      resources/lang/fa/admin.php
  87. 43 43
      resources/lang/fa/auth.php
  88. 50 50
      resources/lang/fa/common.php
  89. 23 23
      resources/lang/fa/errors.php
  90. 240 108
      resources/lang/fa/model.php
  91. 34 34
      resources/lang/fa/notification.php
  92. 182 172
      resources/lang/fa/user.php
  93. 105 105
      resources/lang/ja.json
  94. 363 444
      resources/lang/ja/admin.php
  95. 47 47
      resources/lang/ja/auth.php
  96. 57 57
      resources/lang/ja/common.php
  97. 23 23
      resources/lang/ja/errors.php
  98. 198 198
      resources/lang/ja/model.php
  99. 36 36
      resources/lang/ja/notification.php
  100. 159 149
      resources/lang/ja/user.php

+ 22 - 26
app/Channels/Library/WeChat.php

@@ -20,8 +20,8 @@ class WeChat
     }
 
     public function encryptMsg(string $sReplyMsg, ?int $sTimeStamp, string $sNonce, string &$sEncryptMsg): int
-    { //将公众平台回复用户的消息加密打包.
-        $array = $this->prpcrypt_encrypt($sReplyMsg); //加密
+    { // 将公众平台回复用户的消息加密打包.
+        $array = $this->prpcrypt_encrypt($sReplyMsg); // 加密
 
         if ($array[0] !== 0) {
             return $array[0];
@@ -44,11 +44,11 @@ class WeChat
     public function prpcrypt_encrypt(string $data): array
     {
         try {
-            //拼接
+            // 拼接
             $data = Str::random().pack('N', strlen($data)).$data.sysConfig('wechat_cid');
-            //添加PKCS#7填充
+            // 添加PKCS#7填充
             $data = $this->pkcs7_encode($data);
-            //加密
+            // 加密
             $encrypted = openssl_encrypt($data, 'AES-256-CBC', $this->key, OPENSSL_ZERO_PADDING, $this->iv);
 
             return [0, $encrypted];
@@ -61,7 +61,7 @@ class WeChat
 
     public function pkcs7_encode(string $data): string
     {// 对需要加密的明文进行填充补位
-        //计算需要填充的位数
+        // 计算需要填充的位数
         $padding = 32 - (strlen($data) % 32);
         $padding = ($padding === 0) ? 32 : $padding;
         $pattern = chr($padding);
@@ -102,15 +102,13 @@ XML;
 
     public function decryptMsg(string $sMsgSignature, ?int $sTimeStamp, string $sNonce, string $sPostData, string &$sMsg)
     { // 检验消息的真实性,并且获取解密后的明文.
-        //提取密文
-        $array = $this->extract($sPostData);
-
-        if ($array[0] !== 0) {
-            return $array[0];
+        // 提取密文
+        [$code, $encrypt] = $this->extract($sPostData);
+        if ($code !== 0) {
+            return $code;
         }
 
         $sTimeStamp = $sTimeStamp ?? time();
-        $encrypt = $array[1];
 
         $this->verifySignature($sMsgSignature, $sTimeStamp, $sNonce, $encrypt, $sMsg); // 验证安全签名
     }
@@ -138,23 +136,19 @@ XML;
 
     public function verifySignature(string $sMsgSignature, string $sTimeStamp, string $sNonce, string $sEcho, string &$sMsg): int
     { // 验证URL
-        //verify msg_signature
-        $array = $this->extract($sEcho);
+        // verify msg_signature
+        [$code, $encrypt] = $this->extract($sEcho);
 
-        if ($array[0] !== 0) {
-            return $array[0];
+        if ($code !== 0) {
+            return $code;
         }
 
-        $encrypt = $array[1];
+        [$code, $signature] = $this->getSHA1($sTimeStamp, $sNonce, $encrypt);
 
-        $array = $this->getSHA1($sTimeStamp, $sNonce, $encrypt);
-
-        if ($array[0] !== 0) {
-            return $array[0];
+        if ($code !== 0) {
+            return $code;
         }
 
-        $signature = $array[1];
-
         if ($sMsgSignature !== $signature) {
             Log::critical(trans('notification.error', ['channel' => trans('admin.system.notification.channel.wechat'), 'reason' => trans('notification.sign_failed')]));
 
@@ -162,12 +156,14 @@ XML;
         }
 
         $sMsg = $encrypt;
+
+        return 0;
     }
 
     public function prpcrypt_decrypt(string $encrypted): array
     {
         try {
-            //解密
+            // 解密
             $decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $this->key, OPENSSL_ZERO_PADDING, $this->iv);
         } catch (Exception $e) {
             Log::critical(trans('notification.error', ['channel' => trans('admin.system.notification.channel.wechat'), 'reason' => var_export($e->getMessage(), true)]));
@@ -175,12 +171,12 @@ XML;
             return [-40007, null]; // DecryptAESError
         }
         try {
-            //删除PKCS#7填充
+            // 删除PKCS#7填充
             $result = $this->pkcs7_decode($decrypted);
             if (strlen($result) < 16) {
                 return [];
             }
-            //拆分
+            // 拆分
             $content = substr($result, 16, strlen($result));
             $len_list = unpack('N', substr($content, 0, 4));
             $xml_len = $len_list[1];

+ 4 - 7
app/Console/Commands/NodeStatusDetection.php

@@ -3,7 +3,6 @@
 namespace App\Console\Commands;
 
 use App\Models\Node;
-use App\Models\NodeHeartbeat;
 use App\Models\User;
 use App\Notifications\NodeBlocked;
 use App\Notifications\NodeOffline;
@@ -44,10 +43,9 @@ class NodeStatusDetection extends Command
     private function checkNodeStatus(): void
     {
         $offlineCheckTimes = sysConfig('offline_check_times');
-        $onlineNode = NodeHeartbeat::recently()->distinct()->pluck('node_id');
 
         $data = [];
-        foreach (Node::whereRelayNodeId(null)->whereStatus(1)->whereNotIn('id', $onlineNode)->get() as $node) {
+        foreach (Node::whereRelayNodeId(null)->whereStatus(1)->whereDoesntHave('latestHeartbeat')->get() as $node) {
             // 近期无节点负载信息则认为是后端炸了
             if ($offlineCheckTimes > 0) {
                 $times = $this->updateCache('offline_check_times'.$node->id, 24);
@@ -90,11 +88,11 @@ class NodeStatusDetection extends Command
                     $status = (new NetworkDetection)->networkStatus($ip, $node->port ?? 22);
 
                     if ($node->detection_type !== 1 && $status['icmp'] !== 1) {
-                        $data[$node_id][$ip]['icmp'] = config('common.network_status')[$status['icmp']];
+                        $data[$node_id][$ip]['icmp'] = trans("admin.network_status.{$status['icmp']}");
                     }
 
                     if ($node->detection_type !== 2 && $status['tcp'] !== 1) {
-                        $data[$node_id][$ip]['tcp'] = config('common.network_status')[$status['tcp']];
+                        $data[$node_id][$ip]['tcp'] = trans("admin.network_status.{$status['tcp']}");
                     }
 
                     sleep(2);
@@ -132,8 +130,7 @@ class NodeStatusDetection extends Command
 
     private function reliveNode(): void
     {
-        $onlineNode = NodeHeartbeat::recently()->distinct()->pluck('node_id');
-        foreach (Node::whereRelayNodeId(null)->whereStatus(0)->whereIn('id', $onlineNode)->where('detection_type', '<>', 0)->get() as $node) {
+        foreach (Node::whereRelayNodeId(null)->whereStatus(0)->where('detection_type', '<>', 0)->whereHas('latestHeartbeat')->get() as $node) {
             $ips = $node->ips();
             $reachableIPs = 0;
 

+ 26 - 0
app/Helpers/ProxyConfig.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Helpers;
+
+use App\Models\SsConfig;
+
+trait ProxyConfig
+{
+    private function proxyConfigOptions(): array
+    {
+        // 一次性获取所有配置数据
+        $configs = SsConfig::get(['name', 'type'])->groupBy('type')->map(fn ($items) => $items->pluck('name', 'name'));
+
+        // 获取默认配置项
+        $defaults = SsConfig::where('is_default', 1)->pluck('name', 'type');
+
+        return [
+            'methods' => $configs->get(1, []),
+            'protocols' => $configs->get(2, []),
+            'obfs' => $configs->get(3, []),
+            'methodDefault' => $defaults->get(1),
+            'protocolDefault' => $defaults->get(2),
+            'obfsDefault' => $defaults->get(3),
+        ];
+    }
+}

+ 2 - 3
app/Http/Controllers/Admin/AffiliateController.php

@@ -14,6 +14,7 @@ class AffiliateController extends Controller
     public function index(Request $request): View
     { // 提现申请列表
         $query = ReferralApply::with('user:id,username');
+
         $request->whenFilled('username', function ($username) use ($query) {
             $query->whereHas('user', function ($query) use ($username) {
                 $query->where('username', 'like', "%$username%");
@@ -42,9 +43,7 @@ class AffiliateController extends Controller
         if ($aff->update(['status' => $status])) {
             // 将关联的返现单更新状态
             if ($status === 1 || $status === 2) {
-                if ($aff->referral_logs()->update(['status' => $status])) {
-                    return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.action')])]);
-                }
+                $aff->referral_logs()->update(['status' => $status]);
             }
 
             return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.action')])]);

+ 3 - 8
app/Http/Controllers/Admin/ArticleController.php

@@ -19,7 +19,6 @@ class ArticleController extends Controller
 {
     public function index(Request $request): View
     { // 文章列表
-        $categories = Article::whereNotNull('category')->distinct()->get('category');
         $articles = Article::query();
 
         foreach (['id', 'category', 'language', 'type'] as $field) {
@@ -28,9 +27,7 @@ class ArticleController extends Controller
             });
         }
 
-        $articles = $articles->latest()->orderByDesc('sort')->paginate()->appends($request->except('page'));
-
-        return view('admin.article.index', compact('articles', 'categories'));
+        return view('admin.article.index', ['articles' => $articles->latest()->orderByDesc('sort')->paginate()->appends($request->except('page')), 'categories' => Article::whereNotNull('category')->distinct()->pluck('category', 'category')]);
     }
 
     public function store(ArticleRequest $request): RedirectResponse
@@ -67,9 +64,7 @@ class ArticleController extends Controller
 
     public function create(): View
     { // 添加文章页面
-        $categories = Article::whereNotNull('category')->distinct()->get('category');
-
-        return view('admin.article.info', compact('categories'));
+        return view('admin.article.info', ['categories' => Article::whereNotNull('category')->distinct()->pluck('category')]);
     }
 
     public function show(Article $article): View
@@ -81,7 +76,7 @@ class ArticleController extends Controller
 
     public function edit(Article $article): View
     { // 编辑文章页面
-        $categories = Article::whereNotNull('category')->distinct()->get('category');
+        $categories = Article::whereNotNull('category')->distinct()->pluck('category');
 
         return view('admin.article.info', compact('article', 'categories'));
     }

+ 1 - 13
app/Http/Controllers/Admin/CertController.php

@@ -15,19 +15,7 @@ class CertController extends Controller
 {
     public function index(): View
     {
-        $certs = NodeCertificate::orderBy('id')->paginate()->appends(request('page'));
-        foreach ($certs as $cert) {
-            if ($cert->pem) {
-                $certInfo = openssl_x509_parse($cert->pem);
-                if ($certInfo) {
-                    $cert->issuer = $certInfo['issuer']['O'] ?? null;
-                    $cert->from = date('Y-m-d', $certInfo['validFrom_time_t']) ?: null;
-                    $cert->to = date('Y-m-d', $certInfo['validTo_time_t']) ?: null;
-                }
-            }
-        }
-
-        return view('admin.node.cert.index', ['certs' => $certs]);
+        return view('admin.node.cert.index', ['certs' => NodeCertificate::orderBy('id')->paginate()->appends(request('page'))]);
     }
 
     public function store(CertRequest $request): RedirectResponse

+ 17 - 7
app/Http/Controllers/Admin/Config/CategoryController.php

@@ -13,14 +13,23 @@ use Validator;
 class CategoryController extends Controller
 {
     public function store(Request $request): JsonResponse
-    { // 添加等级
-        $validator = Validator::make($request->all(), ['name' => 'required']);
+    { // 添加分类
+        $validator = Validator::make($request->all(), [
+            'name' => 'required',
+            'sort' => 'nullable|numeric',
+        ]);
 
         if ($validator->fails()) {
             return response()->json(['status' => 'fail', 'message' => $validator->errors()->all()]);
         }
 
-        if (GoodsCategory::create($validator->validated())) {
+        $data = $validator->validated();
+        // 如果没有提供sort值,则设为0
+        if (! isset($data['sort'])) {
+            $data['sort'] = 0;
+        }
+
+        if (GoodsCategory::create($data)) {
             return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.add')])]);
         }
 
@@ -28,7 +37,7 @@ class CategoryController extends Controller
     }
 
     public function update(Request $request, GoodsCategory $category): JsonResponse
-    { // 编辑等级
+    { // 编辑分类
         $validator = Validator::make($request->all(), [
             'name' => 'required',
             'sort' => 'required|numeric',
@@ -37,6 +46,7 @@ class CategoryController extends Controller
         if ($validator->fails()) {
             return response()->json(['status' => 'fail', 'message' => $validator->errors()->all()]);
         }
+
         if ($category->update($validator->validated())) {
             return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.edit')])]);
         }
@@ -45,8 +55,8 @@ class CategoryController extends Controller
     }
 
     public function destroy(GoodsCategory $category): JsonResponse
-    { // 删除等级
-        // 校验该等级下是否存在关联账号
+    { // 删除分类
+        // 校验该分类下是否存在关联商品
         if ($category->goods()->exists()) {
             return response()->json(['status' => 'fail', 'message' => trans('common.exists_error', ['attribute' => trans('model.goods.category')])]);
         }
@@ -56,7 +66,7 @@ class CategoryController extends Controller
                 return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.delete')])]);
             }
         } catch (Exception $e) {
-            Log::error(trans('common.error_action_item', ['action' => trans('common.delete'), 'attribute' => trans('model.common.level')]).': '.$e->getMessage());
+            Log::error(trans('common.error_action_item', ['action' => trans('common.delete'), 'attribute' => trans('model.goods.category')]).': '.$e->getMessage());
 
             return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.delete')]).', '.$e->getMessage()]);
         }

+ 8 - 2
app/Http/Controllers/Admin/Config/CountryController.php

@@ -23,8 +23,14 @@ class CountryController extends Controller
             return response()->json(['status' => 'fail', 'message' => $validator->errors()->all()]);
         }
 
-        if (Country::create($validator->validated())) {
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.add')])]);
+        try {
+            if (Country::create($validator->validated())) {
+                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.add')])]);
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.add'), 'attribute' => trans('model.node.country')]).': '.$e->getMessage());
+
+            return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.add')]).', '.$e->getMessage()]);
         }
 
         return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.add')])]);

+ 1 - 1
app/Http/Controllers/Admin/Config/EmailFilterController.php

@@ -15,7 +15,7 @@ class EmailFilterController extends Controller
 {
     public function index(): View
     { // 邮箱过滤列表
-        return view('admin.config.emailFilter', ['filters' => EmailFilter::orderByDesc('id')->paginate()]);
+        return view('admin.config.emailFilter', ['filters' => EmailFilter::select(['id', 'type', 'words'])->orderByDesc('id')->paginate()]);
     }
 
     public function store(Request $request): JsonResponse

+ 24 - 6
app/Http/Controllers/Admin/Config/LabelController.php

@@ -23,8 +23,14 @@ class LabelController extends Controller
             return response()->json(['status' => 'fail', 'message' => $validator->errors()->all()]);
         }
 
-        if (Label::create($validator->validated())) {
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.add')])]);
+        try {
+            if (Label::create($validator->validated())) {
+                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.add')])]);
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.add'), 'attribute' => trans('model.node.label')]).': '.$e->getMessage());
+
+            return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.add')]).', '.$e->getMessage()]);
         }
 
         return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.add')])]);
@@ -41,8 +47,14 @@ class LabelController extends Controller
             return response()->json(['status' => 'fail', 'message' => $validator->errors()->all()]);
         }
 
-        if ($label->update($validator->validated())) {
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.edit')])]);
+        try {
+            if ($label->update($validator->validated())) {
+                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.edit')])]);
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.edit'), 'attribute' => trans('model.node.label')]).': '.$e->getMessage());
+
+            return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.edit')]).', '.$e->getMessage()]);
         }
 
         return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.edit')])]);
@@ -51,13 +63,19 @@ class LabelController extends Controller
     public function destroy(Label $label): JsonResponse
     { // 删除标签
         try {
-            $label->delete();
+            // 先从所有节点中移除该标签
+            $label->nodes()->detach();
 
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.delete')])]);
+            // 然后删除标签
+            if ($label->delete()) {
+                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.delete')])]);
+            }
         } catch (Exception $e) {
             Log::error(trans('common.error_action_item', ['action' => trans('common.delete'), 'attribute' => trans('model.node.label')]).': '.$e->getMessage());
 
             return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.delete')]).', '.$e->getMessage()]);
         }
+
+        return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.delete')])]);
     }
 }

+ 16 - 4
app/Http/Controllers/Admin/Config/LevelController.php

@@ -23,8 +23,14 @@ class LevelController extends Controller
             return response()->json(['status' => 'fail', 'message' => $validator->errors()->all()]);
         }
 
-        if (Level::create($validator->validated())) {
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.add')])]);
+        try {
+            if (Level::create($validator->validated())) {
+                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.add')])]);
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.add'), 'attribute' => trans('model.common.level')]).': '.$e->getMessage());
+
+            return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.add')]).', '.$e->getMessage()]);
         }
 
         return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.add')])]);
@@ -41,8 +47,14 @@ class LevelController extends Controller
             return response()->json(['status' => 'fail', 'message' => $validator->errors()->all()]);
         }
 
-        if ($level->update($validator->validated())) {
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.edit')])]);
+        try {
+            if ($level->update($validator->validated())) {
+                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.edit')])]);
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.edit'), 'attribute' => trans('model.common.level')]).': '.$e->getMessage());
+
+            return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.edit')]).', '.$e->getMessage()]);
         }
 
         return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.edit')])]);

+ 21 - 4
app/Http/Controllers/Admin/Config/SsConfigController.php

@@ -23,8 +23,14 @@ class SsConfigController extends Controller
             return response()->json(['status' => 'fail', 'message' => $validator->errors()->all()]);
         }
 
-        if (SsConfig::create($validator->validated())) {
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.add')])]);
+        try {
+            if (SsConfig::create($validator->validated())) {
+                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.add')])]);
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.add'), 'attribute' => trans('user.node.info')]).': '.$e->getMessage());
+
+            return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.add')]).', '.$e->getMessage()]);
         }
 
         return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.add')])]);
@@ -32,8 +38,14 @@ class SsConfigController extends Controller
 
     public function update(SsConfig $ss): JsonResponse
     { // 设置SS默认配置
-        if ($ss->setDefault()) {
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.edit')])]);
+        try {
+            if ($ss->setDefault()) {
+                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.edit')])]);
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.edit'), 'attribute' => trans('user.node.info')]).': '.$e->getMessage());
+
+            return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.edit')]).', '.$e->getMessage()]);
         }
 
         return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.edit')])]);
@@ -41,6 +53,11 @@ class SsConfigController extends Controller
 
     public function destroy(SsConfig $ss): JsonResponse
     { // 删除SS配置
+        // 检查是否为默认配置
+        if ($ss->is_default) {
+            return response()->json(['status' => 'fail', 'message' => trans('admin.setting.common.config_default_cannot_delete')]);
+        }
+
         try {
             if ($ss->delete()) {
                 return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.delete')])]);

+ 4 - 11
app/Http/Controllers/Admin/CouponController.php

@@ -38,12 +38,8 @@ class CouponController extends Controller
     }
 
     public function show(Coupon $coupon): View
-    { // 优惠券列表
-        return view('admin.coupon.show', [
-            'coupon' => $coupon,
-            'userGroups' => UserGroup::all()->pluck('name', 'id')->toArray(),
-            'levels' => Level::all()->pluck('name', 'level')->toArray(),
-        ]);
+    { // 优惠券详情
+        return view('admin.coupon.show', ['coupon' => $coupon, 'userGroups' => UserGroup::pluck('name', 'id'), 'levels' => Level::pluck('name', 'level')]);
     }
 
     public function store(CouponRequest $request): RedirectResponse
@@ -94,16 +90,13 @@ class CouponController extends Controller
         } catch (Exception $e) {
             Log::error(trans('common.error_action_item', ['action' => trans('common.generate'), 'attribute' => trans('model.coupon.attribute')]).': '.$e->getMessage());
 
-            return redirect()->back()->withInput()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.generate')]).', '.$e->getMessage());
+            return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.generate')]).', '.$e->getMessage());
         }
     }
 
     public function create(): View
     { // 添加优惠券页面
-        return view('admin.coupon.create', [
-            'userGroups' => UserGroup::all()->pluck('name', 'id')->toArray(),
-            'levels' => Level::all()->pluck('name', 'level')->toArray(),
-        ]);
+        return view('admin.coupon.create', ['userGroups' => UserGroup::pluck('name', 'id'), 'levels' => Level::pluck('name', 'level')]);
     }
 
     public function destroy(Coupon $coupon): JsonResponse

+ 20 - 11
app/Http/Controllers/Admin/InviteController.php

@@ -16,30 +16,39 @@ class InviteController extends Controller
 {
     public function index(): View
     { // 邀请码列表
-        return view('admin.aff.invite', [
-            'inviteList' => Invite::with(['invitee:id,username', 'inviter:id,username'])->orderBy('status')->orderByDesc('id')->paginate(15)->appends(request('page')),
-        ]);
+        return view('admin.aff.invite', ['inviteList' => Invite::with(['invitee:id,username', 'inviter:id,username'])->orderBy('status')->orderByDesc('id')->paginate(15)->appends(request('page'))]);
     }
 
     public function generate(): JsonResponse
     { // 生成邀请码
+        $invites = [];
+        $expirationDate = date('Y-m-d H:i:s', strtotime(sysConfig('admin_invite_days').' days'));
+
         for ($i = 0; $i < 10; $i++) {
-            $obj = new Invite;
-            $obj->code = strtoupper(substr(md5(microtime().Str::random(6)), 8, 12));
-            $obj->dateline = date('Y-m-d H:i:s', strtotime(sysConfig('admin_invite_days').' days'));
-            $obj->save();
+            $invites[] = [
+                'code' => strtoupper(substr(md5(microtime().Str::random(6)), 8, 12)),
+                'dateline' => $expirationDate,
+            ];
         }
 
+        Invite::insert($invites);
+
         return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.generate')])]);
     }
 
     public function export(): void
     { // 导出邀请码
-        $inviteList = Invite::whereStatus(0)->orderBy('id')->get();
+        $inviteList = Invite::whereStatus(0)->select(['code', 'dateline'])->get();
+
         $filename = trans('user.invite.attribute').'_'.date('Ymd').'.xlsx';
 
         $spreadsheet = new Spreadsheet;
-        $spreadsheet->getProperties()->setCreator('ProxyPanel')->setLastModifiedBy('ProxyPanel')->setTitle(trans('user.invite.attribute'))->setSubject(trans('user.invite.attribute'));
+        $spreadsheet->getProperties()
+            ->setCreator('ProxyPanel')
+            ->setLastModifiedBy('ProxyPanel')
+            ->setTitle(trans('user.invite.attribute'))
+            ->setSubject(trans('user.invite.attribute'));
+
         $spreadsheet->setActiveSheetIndex(0);
         $sheet = $spreadsheet->getActiveSheet();
         $sheet->setTitle(trans('user.invite.attribute'));
@@ -49,10 +58,10 @@ class InviteController extends Controller
             $sheet->fromArray([$vo->code, $vo->dateline], null, 'A'.($k + 2));
         }
 
-        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); // 输出07Excel文件
-        // header('Content-Type:application/vnd.ms-excel'); // 输出Excel03版本文件
+        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
         header('Content-Disposition: attachment;filename="'.$filename.'"');
         header('Cache-Control: max-age=0');
+
         try {
             $writer = new Xlsx($spreadsheet);
             $writer->save('php://output');

+ 8 - 6
app/Http/Controllers/Admin/LogsController.php

@@ -50,7 +50,7 @@ class LogsController extends Controller
             if ($value) {
                 $query->where('coupon_id', '<>', null);
             } else {
-                $query->where('coupon_id', 'null');
+                $query->where('coupon_id', null);
             }
         });
 
@@ -85,7 +85,7 @@ class LogsController extends Controller
 
     public function trafficLog(Request $request): View
     { // 流量日志
-        $query = UserDataFlowLog::with(['user', 'node']);
+        $query = UserDataFlowLog::with(['user:id,username,port', 'node:id,name']);
 
         $request->whenFilled('port', function ($value) use ($query) {
             $query->whereHas('user', function ($query) use ($value) {
@@ -120,7 +120,7 @@ class LogsController extends Controller
             $log->d = formatBytes($log->d);
             $log->log_time = date('Y-m-d H:i:s', $log->log_time);
         }
-        $nodes = Node::whereStatus(1)->orderByDesc('sort')->latest()->get();
+        $nodes = Node::whereStatus(1)->orderByDesc('sort')->latest()->pluck('name', 'id');
 
         return view('admin.logs.traffic', compact(['totalTraffic', 'dataFlowLogs', 'nodes']));
     }
@@ -142,7 +142,7 @@ class LogsController extends Controller
 
     public function onlineIPMonitor(Request $request, ?int $id = null): View
     { // 在线IP监控(实时)
-        $query = NodeOnlineIp::with(['node:id,name', 'user:id,username'])->where('created_at', '>=', strtotime('-2 minutes'));
+        $query = NodeOnlineIp::with(['node:id,name', 'user:id,username,port'])->where('created_at', '>=', strtotime('-2 minutes'));
 
         if ($id !== null) {
             $query->whereHas('user', static function ($query) use ($id) {
@@ -183,7 +183,7 @@ class LogsController extends Controller
 
         return view('admin.logs.onlineIPMonitor', [
             'onlineIPLogs' => $onlineIPLogs,
-            'nodes' => Node::whereStatus(1)->orderByDesc('sort')->latest()->get(),
+            'nodes' => Node::whereStatus(1)->orderByDesc('sort')->latest()->pluck('name', 'id'),
         ]);
     }
 
@@ -244,7 +244,9 @@ class LogsController extends Controller
 
         $userList = $query->orderBy('id')->paginate(15)->appends($request->except('page'));
 
-        $nodeOnlineIPs = NodeOnlineIp::with('node:id,name')->where('created_at', '>=', strtotime('-10 minutes'))->latest()->distinct()->get();
+        // 获取最近10分钟的在线IP记录
+        $nodeOnlineIPs = NodeOnlineIp::with('node:id,name')->where('created_at', '>=', strtotime('-10 minutes'))->latest()->distinct()->get(['user_id', 'node_id', 'port', 'ip', 'type', 'created_at']);
+
         foreach ($userList as $user) {
             // 最近5条在线IP记录,如果后端设置为60秒上报一次,则为10分钟内的在线IP
             $user->onlineIPList = $nodeOnlineIPs->where('port', $user->port)->take(5);

+ 6 - 6
app/Http/Controllers/Admin/MarketingController.php

@@ -29,8 +29,8 @@ class MarketingController extends Controller
 
         return view('admin.article.marketing', [
             'marketingMessages' => $query->latest()->paginate(15)->appends($request->except('page')),
-            'userGroups' => UserGroup::all()->pluck('name', 'id')->toArray(),
-            'levels' => Level::all()->pluck('name', 'level')->toArray(),
+            'userGroups' => UserGroup::pluck('name', 'id'),
+            'levels' => Level::pluck('name', 'level'),
         ]);
     }
 
@@ -57,7 +57,7 @@ class MarketingController extends Controller
             $users = $this->userStat($request);
             if ($users->isNotEmpty()) {
                 Notification::send($users, new Custom($title, $content, ['mail']));
-                Helpers::addMarketing($users->pluck('id')->toJson(), '1', $title, $content);
+                Helpers::addMarketing($users->pluck('id')->toJson(), 1, $title, $content);
 
                 return response()->json(['status' => 'success', 'message' => trans('admin.marketing.processed')]);
             }
@@ -74,7 +74,7 @@ class MarketingController extends Controller
 
         foreach (['id', 'username', 'status', 'enable', 'user_group_id', 'level'] as $field) {
             $request->whenFilled($field, function ($value) use ($users, $field) {
-                $users->whereIn($field, array_map('trim', explode(',', $value)));
+                $users->whereIn($field, is_string($value) ? array_map('trim', explode(',', $value)) : (array) $value);
             });
         }
 
@@ -93,7 +93,7 @@ class MarketingController extends Controller
 
         // 最近N分钟活跃过
         $request->whenFilled('lastAlive', function ($value) use ($users) {
-            $users->where('t', '>=', now()->subMinutes($value)->timestamp);
+            $users->where('t', '>=', now()->subMinutes((int) $value)->timestamp);
         });
 
         $paidOrderCondition = function ($query) {
@@ -129,6 +129,6 @@ class MarketingController extends Controller
             $users->whereIn('id', (new UserHourlyDataFlow)->trafficAbnormal());
         });
 
-        return $request->isMethod('POST') ? $users->get() : $users->count();
+        return $request->isMethod('POST') ? $users->select('id')->get() : $users->count();
     }
 }

+ 20 - 15
app/Http/Controllers/Admin/NodeAuthController.php

@@ -5,29 +5,40 @@ namespace App\Http\Controllers\Admin;
 use App\Http\Controllers\Controller;
 use App\Models\Node;
 use App\Models\NodeAuth;
-use Exception;
 use Illuminate\Contracts\View\View;
 use Illuminate\Http\JsonResponse;
-use Log;
 use Str;
 
 class NodeAuthController extends Controller
 {
     public function index(): View
     { // 节点授权列表
-        return view('admin.node.auth', ['authorizations' => NodeAuth::with('node:id,name,type,server,ip,ipv6')->has('node')->orderBy('node_id')->paginate()->appends(request('page'))]);
+        $authorizations = NodeAuth::with(['node:id,name,type,server,ip,ipv6'])
+            ->orderBy('node_id')
+            ->paginate()
+            ->appends(request('page'));
+
+        return view('admin.node.auth', compact('authorizations'));
     }
 
     public function store(): JsonResponse
     { // 添加节点授权
-        $nodes = Node::whereStatus(1)->doesntHave('auth')->orderBy('id')->get();
+        $nodes = Node::whereStatus(1)->doesntHave('auth')->pluck('id');
 
         if ($nodes->isEmpty()) {
             return response()->json(['status' => 'success', 'message' => trans('admin.node.auth.empty')]);
         }
-        $nodes->each(function ($node) {
-            $node->auth()->create(['key' => Str::random(), 'secret' => Str::random(8)]);
-        });
+
+        $authData = [];
+        foreach ($nodes as $node_id) {
+            $authData[] = [
+                'node_id' => $node_id,
+                'key' => Str::random(),
+                'secret' => Str::random(8),
+            ];
+        }
+
+        NodeAuth::insert($authData);
 
         return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.generate')])]);
     }
@@ -43,14 +54,8 @@ class NodeAuthController extends Controller
 
     public function destroy(NodeAuth $auth): JsonResponse
     { // 删除节点授权
-        try {
-            if ($auth->delete()) {
-                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.delete')])]);
-            }
-        } catch (Exception $e) {
-            Log::error(trans('common.error_action_item', ['action' => trans('common.delete'), 'attribute' => trans('admin.menu.node.auth')]).': '.$e->getMessage());
-
-            return response()->json(['status' => 'fail', 'message' => trans('common.error_action_item', ['action' => trans('common.delete'), 'attribute' => trans('admin.menu.node.auth')]).', '.$e->getMessage()]);
+        if ($auth->delete()) {
+            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.delete')])]);
         }
 
         return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.delete')])]);

+ 36 - 27
app/Http/Controllers/Admin/NodeController.php

@@ -3,6 +3,7 @@
 namespace App\Http\Controllers\Admin;
 
 use App\Helpers\DataChart;
+use App\Helpers\ProxyConfig;
 use App\Http\Controllers\Controller;
 use App\Http\Requests\Admin\NodeRequest;
 use App\Jobs\VNet\reloadNode;
@@ -13,6 +14,7 @@ use App\Models\Node;
 use App\Models\NodeCertificate;
 use App\Models\RuleGroup;
 use App\Utils\NetworkDetection;
+use Arr;
 use Exception;
 use Illuminate\Contracts\View\View;
 use Illuminate\Http\JsonResponse;
@@ -22,7 +24,7 @@ use Log;
 
 class NodeController extends Controller
 {
-    use DataChart;
+    use DataChart, ProxyConfig;
 
     public function index(Request $request): View
     { // 节点列表
@@ -34,15 +36,10 @@ class NodeController extends Controller
                 'hourlyDataFlows' => function ($query) {
                     $query->whereDate('created_at', now()->toDateString());
                 },
-                'onlineLogs' => function ($query) {
-                    $query->where('log_time', '>=', strtotime('-5 minutes'))->orderBy('log_time', 'desc');
-                },
-                'heartbeats' => function ($query) {
-                    $query->where('log_time', '>=', strtotime('-'.sysConfig('recently_heartbeat').' minutes'))->orderBy('log_time', 'desc');
-                },
+                'latestOnlineLog',
+                'latestHeartbeat',
                 'childNodes',
-            ])
-            ->withCount('onlineLogs'); // 提前统计在线人数
+            ]);
 
         $request->whenFilled('status', function ($value) use ($query) {
             $query->where('status', $value);
@@ -50,16 +47,17 @@ class NodeController extends Controller
 
         $nodeList = $query->orderByDesc('sort')->orderBy('id')->paginate(15)->appends($request->except('page'))->through(function ($node) {
             // 预处理每个节点的数据
-            $node->online_users = $node->onlineLogs->first()?->online_user; // 在线人数
-            $node->transfer = formatBytes(
-                $node->dailyDataFlows->sum(fn ($item) => $item->u + $item->d) +
-                $node->hourlyDataFlows->sum(fn ($item) => $item->u + $item->d)
-            ); // 已产生流量
+            $node->online_users = $node->latestOnlineLog?->online_user; // 在线人数
+
+            // 计算流量总和
+            $dailyTransfer = $node->dailyDataFlows->sum(fn ($item) => $item->u + $item->d);
+            $hourlyTransfer = $node->hourlyDataFlows->sum(fn ($item) => $item->u + $item->d);
+            $node->transfer = formatBytes($dailyTransfer + $hourlyTransfer); // 已产生流量
 
-            $node_info = $node->heartbeats->first(); // 近期负载
+            $node_info = $node->latestHeartbeat; // 近期负载
             $node->isOnline = ! empty($node_info?->load);
-            $node->load = $node_info->load ?? false;
-            $node->uptime = formatTime($node_info->uptime ?? 0);
+            $node->load = $node_info?->load ?? false;
+            $node->uptime = formatTime($node_info?->uptime);
 
             return $node;
         });
@@ -91,10 +89,11 @@ class NodeController extends Controller
         return view('admin.node.info', [
             'nodes' => Node::orderBy('id')->pluck('id', 'name'),
             'countries' => Country::orderBy('code')->get(),
-            'levels' => Level::orderBy('level')->get(),
-            'ruleGroups' => RuleGroup::orderBy('id')->get(),
-            'labels' => Label::orderByDesc('sort')->orderBy('id')->get(),
-            'certs' => NodeCertificate::orderBy('id')->get(),
+            'levels' => Level::orderBy('level')->pluck('name', 'level'),
+            'ruleGroups' => RuleGroup::orderBy('id')->pluck('name', 'id'),
+            'labels' => Label::orderByDesc('sort')->orderBy('id')->pluck('name', 'id'),
+            'certs' => NodeCertificate::orderBy('id')->pluck('domain', 'id'),
+            ...$this->proxyConfigOptions(),
         ]);
     }
 
@@ -189,14 +188,23 @@ class NodeController extends Controller
 
     public function edit(Node $node): View
     { // 编辑节点页面
+        $node->load('labels:id');
+        $nodeArray = $node->toArray();
+
         return view('admin.node.info', [
-            'node' => $node->load('labels'),
+            'node' => array_merge(
+                Arr::except($nodeArray, ['details', 'profile']),
+                $nodeArray['details'] ?? [],
+                $nodeArray['profile'] ?? [],
+                ['labels' => $node->labels->pluck('id')->toArray()]// 将标签ID列表作为一维数组
+            ),
             'nodes' => Node::whereNotIn('id', [$node->id])->orderBy('id')->pluck('id', 'name'),
             'countries' => Country::orderBy('code')->get(),
-            'levels' => Level::orderBy('level')->get(),
-            'ruleGroups' => RuleGroup::orderBy('id')->get(),
-            'labels' => Label::orderByDesc('sort')->orderBy('id')->get(),
-            'certs' => NodeCertificate::orderBy('id')->get(),
+            'levels' => Level::orderBy('level')->pluck('name', 'level'),
+            'ruleGroups' => RuleGroup::orderBy('id')->pluck('name', 'id'),
+            'labels' => Label::orderByDesc('sort')->orderBy('id')->pluck('name', 'id'),
+            'certs' => NodeCertificate::orderBy('id')->pluck('domain', 'id'),
+            ...$this->proxyConfigOptions(),
         ]);
     }
 
@@ -247,7 +255,8 @@ class NodeController extends Controller
     { // 刷新节点地理位置
         $ret = false;
         if ($id) {
-            $ret = Node::findOrFail($id)->refresh_geo();
+            $node = Node::findOrFail($id);
+            $ret = $node->refresh_geo();
         } else {
             foreach (Node::whereStatus(1)->get() as $node) {
                 $result = $node->refresh_geo();

+ 15 - 7
app/Http/Controllers/Admin/PermissionController.php

@@ -29,11 +29,15 @@ class PermissionController extends Controller
 
     public function store(PermissionRequest $request): RedirectResponse
     {
-        if ($permission = Permission::create($request->validated())) {
+        try {
+            $permission = Permission::create($request->validated());
+
             return redirect()->route('admin.permission.edit', $permission)->with('successMsg', trans('common.success_item', ['attribute' => trans('common.add')]));
-        }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.add'), 'attribute' => trans('model.permission.attribute')]).': '.$e->getMessage());
 
-        return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.add')]));
+            return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.add')]).', '.$e->getMessage());
+        }
     }
 
     public function create(): View
@@ -43,16 +47,20 @@ class PermissionController extends Controller
 
     public function edit(Permission $permission): View
     {
-        return view('admin.permission.info', compact('permission'));
+        return view('admin.permission.info', ['permission' => $permission->makeHidden(['created_at', 'updated_at', 'guard_name'])]);
     }
 
     public function update(PermissionRequest $request, Permission $permission): RedirectResponse
     {
-        if ($permission->update($request->validated())) {
+        try {
+            $permission->update($request->validated());
+
             return redirect()->back()->with('successMsg', trans('common.success_item', ['attribute' => trans('common.update')]));
-        }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.update'), 'attribute' => trans('model.permission.attribute')]).': '.$e->getMessage());
 
-        return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.update')]));
+            return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.update')]).', '.$e->getMessage());
+        }
     }
 
     public function destroy(Permission $permission): JsonResponse

+ 46 - 10
app/Http/Controllers/Admin/RoleController.php

@@ -16,15 +16,39 @@ class RoleController extends Controller
 {
     public function index(): View
     {
-        return view('admin.role.index', ['roles' => Role::with('permissions')->paginate(15)]);
+        // 预加载角色权限,但只选择需要的字段
+        $roles = Role::with('permissions:description,name')->paginate(15);
+
+        // 预先处理权限描述,避免在 Blade 模板中重复处理
+        $processedRoles = $roles->through(function ($role) {
+            if ($role->name !== 'Super Admin') {
+                // 提前获取权限描述集合,避免在模板中重复调用
+                $role->permission_descriptions = $role->permissions->pluck('description');
+            }
+
+            return $role;
+        });
+
+        return view('admin.role.index', ['roles' => $processedRoles]);
     }
 
     public function store(RoleRequest $request): RedirectResponse
     {
-        if ($role = Role::create($request->only(['name', 'description']))) {
-            $role->givePermissionTo($request->input('permissions') ?? []);
+        try {
+            $role = Role::create($request->only(['name', 'description']));
+
+            if ($role) {
+                $permissions = $request->input('permissions') ?? [];
+                if (! empty($permissions)) {
+                    $role->givePermissionTo($permissions);
+                }
 
-            return redirect()->route('admin.role.edit', $role)->with('successMsg', trans('common.success_item', ['attribute' => trans('common.add')]));
+                return redirect()->route('admin.role.edit', $role)->with('successMsg', trans('common.success_item', ['attribute' => trans('common.add')]));
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.add'), 'attribute' => trans('model.role.attribute')]).': '.$e->getMessage());
+
+            return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.add')]).', '.$e->getMessage());
         }
 
         return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.add')]));
@@ -32,14 +56,19 @@ class RoleController extends Controller
 
     public function create(): View
     {
-        return view('admin.role.info', ['permissions' => Permission::all()->pluck('description', 'name')]);
+        return view('admin.role.info', ['permissions' => Permission::orderBy('name')->pluck('description', 'name')]);
     }
 
     public function edit(Role $role): View
     {
+        $role->load('permissions:name');
+
         return view('admin.role.info', [
-            'role' => $role->load('permissions'),
-            'permissions' => Permission::all()->pluck('description', 'name'),
+            'role' => array_merge(
+                $role->toArray(),
+                ['permissions' => $role->permissions->pluck('name')->toArray()]
+            ),
+            'permissions' => Permission::orderBy('name')->pluck('description', 'name'),
         ]);
     }
 
@@ -49,10 +78,16 @@ class RoleController extends Controller
             return redirect()->back()->withInput()->withErrors(trans('admin.role.modify_admin_error'));
         }
 
-        if ($role->update($request->only(['name', 'description']))) {
-            $role->syncPermissions($request->input('permissions') ?: []);
+        try {
+            if ($role->update($request->only(['name', 'description']))) {
+                $role->syncPermissions($request->input('permissions', []));
 
-            return redirect()->back()->with('successMsg', trans('common.success_item', ['attribute' => trans('common.edit')]));
+                return redirect()->back()->with('successMsg', trans('common.success_item', ['attribute' => trans('common.edit')]));
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.edit'), 'attribute' => trans('model.role.attribute')]).': '.$e->getMessage());
+
+            return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.edit')]).', '.$e->getMessage());
         }
 
         return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.edit')]));
@@ -64,6 +99,7 @@ class RoleController extends Controller
             if ($role->name === 'Super Admin') {
                 return response()->json(['status' => 'fail', 'message' => trans('admin.role.modify_admin_error')]);
             }
+
             $role->delete();
         } catch (Exception $e) {
             Log::error(trans('common.error_action_item', ['action' => trans('common.delete'), 'attribute' => trans('model.role.attribute')]).': '.$e->getMessage());

+ 18 - 6
app/Http/Controllers/Admin/RuleController.php

@@ -28,8 +28,14 @@ class RuleController extends Controller
 
     public function store(RuleRequest $request): JsonResponse
     { // 添加审计规则
-        if (Rule::create($request->validated())) {
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.add')])]);
+        try {
+            if (Rule::create($request->validated())) {
+                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.add')])]);
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.add'), 'attribute' => trans('model.rule.attribute')]).': '.$e->getMessage());
+
+            return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.add')]).', '.$e->getMessage()]);
         }
 
         return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.add')])]);
@@ -37,8 +43,14 @@ class RuleController extends Controller
 
     public function update(RuleRequest $request, Rule $rule): JsonResponse
     { // 编辑审计规则
-        if ($rule->update($request->validated())) {
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.edit')])]);
+        try {
+            if ($rule->update($request->validated())) {
+                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.edit')])]);
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.edit'), 'attribute' => trans('model.rule.attribute')]).': '.$e->getMessage());
+
+            return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.edit')]).', '.$e->getMessage()]);
         }
 
         return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.edit')])]);
@@ -76,8 +88,8 @@ class RuleController extends Controller
         });
 
         return view('admin.rule.log', [
-            'nodes' => Node::all(),
-            'rules' => Rule::all(),
+            'nodes' => Node::pluck('name', 'id'),
+            'rules' => Rule::pluck('name', 'id'),
             'ruleLogs' => $query->latest()->paginate(15)->appends($request->except('page')),
         ]);
     }

+ 28 - 9
app/Http/Controllers/Admin/RuleGroupController.php

@@ -21,10 +21,21 @@ class RuleGroupController extends Controller
 
     public function store(RuleGroupRequest $request): RedirectResponse
     {
-        if ($group = RuleGroup::create($request->only('name', 'type'))) {
-            $group->rules()->attach($request->input('rules'));
+        try {
+            $group = RuleGroup::create($request->only('name', 'type'));
+
+            if ($group) {
+                $rules = $request->input('rules');
+                if (! empty($rules)) {
+                    $group->rules()->attach($rules);
+                }
+
+                return redirect(route('admin.rule.group.edit', $group))->with('successMsg', trans('common.success_item', ['attribute' => trans('common.add')]));
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.add'), 'attribute' => trans('model.rule_group.attribute')]).': '.$e->getMessage());
 
-            return redirect(route('admin.rule.group.edit', $group))->with('successMsg', trans('common.success_item', ['attribute' => trans('common.add')]));
+            return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.add')]).', '.$e->getMessage());
         }
 
         return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.add')]));
@@ -32,23 +43,31 @@ class RuleGroupController extends Controller
 
     public function create(): View
     {
-        return view('admin.rule.group.info', ['rules' => Rule::all()]);
+        return view('admin.rule.group.info', ['rules' => Rule::pluck('name', 'id')]);
     }
 
     public function edit(RuleGroup $group): View
     {
+        $group->load('rules:id');
+
         return view('admin.rule.group.info', [
-            'ruleGroup' => $group,
-            'rules' => Rule::all(),
+            'ruleGroup' => array_merge($group->toArray(), ['rules' => $group->rules->pluck('id')->map('strval')->toArray()]),
+            'rules' => Rule::pluck('name', 'id'),
         ]);
     }
 
     public function update(RuleGroupRequest $request, RuleGroup $group): RedirectResponse
     {
-        if ($group->update($request->only(['name', 'type']))) {
-            $group->rules()->sync($request->input('rules'));
+        try {
+            if ($group->update($request->only(['name', 'type']))) {
+                $group->rules()->sync($request->input('rules', []));
+
+                return redirect()->back()->with('successMsg', trans('common.success_item', ['attribute' => trans('common.edit')]));
+            }
+        } catch (Exception $e) {
+            Log::error(trans('common.error_action_item', ['action' => trans('common.edit'), 'attribute' => trans('model.rule_group.attribute')]).': '.$e->getMessage());
 
-            return redirect()->back()->with('successMsg', trans('common.success_item', ['attribute' => trans('common.edit')]));
+            return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.edit')]).', '.$e->getMessage());
         }
 
         return redirect()->back()->withInput()->withErrors(trans('common.failed_item', ['attribute' => trans('common.edit')]));

+ 24 - 7
app/Http/Controllers/Admin/ShopController.php

@@ -8,6 +8,7 @@ use App\Http\Requests\Admin\ShopUpdateRequest;
 use App\Models\Goods;
 use App\Models\GoodsCategory;
 use App\Models\Level;
+use App\Models\Order;
 use Arr;
 use Exception;
 use Illuminate\Contracts\View\View;
@@ -32,12 +33,28 @@ class ShopController extends Controller
 
         $goodsList = $query->orderByDesc('status')->paginate(10)->appends($request->except('page'));
 
-        foreach ($goodsList->load('orders') as $goods) {
-            $goods->use_count = $goods->orders->whereIn('status', [2, 3])->where('is_expire', 0)->count();
-            $goods->total_count = $goods->orders->whereIn('status', [2, 3])->count();
+        // 优化订单统计查询,使用更高效的方式
+        $goodsIds = $goodsList->pluck('id')->toArray();
+
+        // 批量获取订单统计数据
+        $orderStats = Order::whereIn('goods_id', $goodsIds)
+            ->whereIn('status', [2, 3])
+            ->selectRaw('goods_id, is_expire, count(*) as count')
+            ->groupBy('goods_id', 'is_expire')
+            ->get()
+            ->groupBy('goods_id');
+
+        // 为每个商品设置使用统计
+        foreach ($goodsList as $goods) {
+            $stats = $orderStats->get($goods->id, collect());
+            $usedCount = $stats->where('is_expire', 0)->sum('count');
+            $totalCount = $stats->sum('count');
+
+            $goods->use_count = $usedCount;
+            $goods->total_count = $totalCount;
         }
 
-        return view('admin.shop.index', ['goodsList' => $goodsList]);
+        return view('admin.shop.index', compact('goodsList'));
     }
 
     public function store(ShopStoreRequest $request): RedirectResponse
@@ -84,15 +101,15 @@ class ShopController extends Controller
 
     public function create(): View
     {
-        return view('admin.shop.info', ['levels' => Level::orderBy('level')->get(), 'categories' => GoodsCategory::all()]);
+        return view('admin.shop.info', ['levels' => Level::orderBy('level')->pluck('name', 'id'), 'categories' => GoodsCategory::pluck('name', 'id')]);
     }
 
     public function edit(Goods $good): View
     {
         return view('admin.shop.info', [
             'good' => $good,
-            'levels' => Level::orderBy('level')->get(),
-            'categories' => GoodsCategory::all(),
+            'levels' => Level::orderBy('level')->pluck('name', 'id'),
+            'categories' => GoodsCategory::pluck('name', 'id'),
         ]);
     }
 

+ 16 - 2
app/Http/Controllers/Admin/SubscribeController.php

@@ -46,8 +46,22 @@ class SubscribeController extends Controller
             $query->whereBetween('request_time', [$request->input('start').' 00:00:00', $request->input('end').' 23:59:59']);
         }
 
-        $subscribeLogs = $query->latest()->paginate(20)->appends($request->except('page'))->through(function ($log) {
-            $log->ipInfo = $log->request_ip ? optional(IP::getIPInfo($log->request_ip))['address'] ?? null : null;
+        $subscribeLogs = $query->latest()->paginate(20)->appends($request->except('page'));
+
+        // 批量获取 IP 信息以减少查询次数
+        $ipList = $subscribeLogs->pluck('request_ip')->filter()->unique()->toArray();
+        $ipInfoMap = [];
+
+        foreach ($ipList as $ip) {
+            if ($ip) {
+                $ipInfo = IP::getIPInfo($ip);
+                $ipInfoMap[$ip] = $ipInfo ? ($ipInfo['address'] ?? null) : null;
+            }
+        }
+
+        // 将 IP 信息附加到日志记录中
+        $subscribeLogs->getCollection()->transform(function ($log) use ($ipInfoMap) {
+            $log->ipInfo = $log->request_ip ? ($ipInfoMap[$log->request_ip] ?? null) : null;
 
             return $log;
         });

+ 50 - 42
app/Http/Controllers/Admin/SystemController.php

@@ -54,8 +54,8 @@ class SystemController extends Controller
 
         // 预处理复杂数据
         // 解析时间类配置
-        $config['tasks_clean'] = parseTime($config['tasks_clean']);
-        $config['tasks_close'] = parseTime($config['tasks_close']);
+        $config['tasks_clean'] = parseTime($config['tasks_clean'] ?? []);
+        $config['tasks_close'] = parseTime($config['tasks_close'] ?? []);
 
         $paymentForms = PaymentManager::getSettingsFormData();
 
@@ -98,10 +98,13 @@ class SystemController extends Controller
 
         $channels = ['database', 'mail'];
 
+        // 预先获取所有配置值,减少数据库查询
+        $configValues = Config::whereIn('name', array_merge(...array_values($configMap)))->pluck('value', 'name')->toArray();
+
         // 遍历映射,检查配置项是否存在
         foreach ($configMap as $channel => $configKeys) {
-            $allConfigsExist = array_reduce($configKeys, static function ($carry, $configKey) {
-                return $carry && sysConfig($configKey);
+            $allConfigsExist = array_reduce($configKeys, static function ($carry, $configKey) use ($configValues) {
+                return $carry && ! empty($configValues[$configKey]);
             }, true);
 
             if ($allConfigsExist) {
@@ -134,63 +137,68 @@ class SystemController extends Controller
 
     public function setExtend(Request $request): RedirectResponse  // 设置涉及到上传的设置
     {
-        if ($request->hasAny(['website_home_logo', 'website_home_logo'])) { // 首页LOGO
-            if ($request->hasFile('website_home_logo')) {
-                $validator = validator()->make($request->all(), ['website_home_logo' => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048']);
+        // 处理LOGO上传
+        if ($request->hasAny(['website_home_logo', 'website_logo'])) {
+            $logoType = null;
+            $file = null;
 
-                if ($validator->fails()) {
-                    return redirect()->route('admin.system.index', '#other')->withErrors($validator->errors());
-                }
+            if ($request->hasFile('website_home_logo')) {
+                $logoType = 'website_home_logo';
                 $file = $request->file('website_home_logo');
-                $file->move('uploads/logo', $file->getClientOriginalName());
-                if (Config::findOrNew('website_home_logo')->update(['value' => 'uploads/logo/'.$file->getClientOriginalName()])) {
-                    return redirect()->route('admin.system.index', '#other')->with('successMsg', trans('common.success_item', ['attribute' => trans('common.update')]));
-                }
+            } elseif ($request->hasFile('website_logo')) {
+                $logoType = 'website_logo';
+                $file = $request->file('website_logo');
             }
-            if ($request->hasFile('website_logo')) { // 站内LOGO
-                $validator = validator()->make($request->all(), ['website_logo' => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048']);
+
+            if ($logoType && $file) {
+                $validator = validator()->make($request->all(), [$logoType => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048']);
 
                 if ($validator->fails()) {
                     return redirect()->route('admin.system.index', '#other')->withErrors($validator->errors());
                 }
-                $file = $request->file('website_logo');
-                $file->move('uploads/logo', $file->getClientOriginalName());
-                if (Config::findOrNew('website_logo')->update(['value' => 'uploads/logo/'.$file->getClientOriginalName()])) {
+
+                $fileName = $file->getClientOriginalName();
+                $file->move('uploads/logo', $fileName);
+
+                $configKey = $logoType;
+                if (Config::findOrNew($configKey)->update(['value' => 'uploads/logo/'.$fileName])) {
                     return redirect()->route('admin.system.index', '#other')->with('successMsg', trans('common.success_item', ['attribute' => trans('common.update')]));
                 }
-            }
 
-            return redirect()->route('admin.system.index', '#other')->withErrors(trans('common.failed_item', ['attribute' => trans('common.update')]));
+                return redirect()->route('admin.system.index', '#other')->withErrors(trans('common.failed_item', ['attribute' => trans('common.update')]));
+            }
         }
 
+        // 处理支付二维码上传
         if ($request->hasAny(['alipay_qrcode', 'wechat_qrcode'])) {
-            if ($request->hasFile('alipay_qrcode')) {
-                $validator = validator()->make($request->all(), ['alipay_qrcode' => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048']);
+            $qrcodeType = null;
+            $file = null;
 
-                if ($validator->fails()) {
-                    return redirect()->route('admin.system.index', '#payment')->withErrors($validator->errors());
-                }
+            if ($request->hasFile('alipay_qrcode')) {
+                $qrcodeType = 'alipay_qrcode';
                 $file = $request->file('alipay_qrcode');
-                $file->move('uploads/images', $file->getClientOriginalName());
-                if (Config::findOrNew('alipay_qrcode')->update(['value' => 'uploads/images/'.$file->getClientOriginalName()])) {
-                    return redirect()->route('admin.system.index', '#payment')->with('successMsg', trans('common.success_item', ['attribute' => trans('common.update')]));
-                }
+            } elseif ($request->hasFile('wechat_qrcode')) {
+                $qrcodeType = 'wechat_qrcode';
+                $file = $request->file('wechat_qrcode');
             }
 
-            if ($request->hasFile('wechat_qrcode')) { // 站内LOGO
-                $validator = validator()->make($request->all(), ['wechat_qrcode' => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048']);
+            if ($qrcodeType && $file) {
+                $validator = validator()->make($request->all(), [$qrcodeType => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048']);
 
                 if ($validator->fails()) {
                     return redirect()->route('admin.system.index', '#payment')->withErrors($validator->errors());
                 }
-                $file = $request->file('wechat_qrcode');
-                $file->move('uploads/images', $file->getClientOriginalName());
-                if (Config::findOrNew('wechat_qrcode')->update(['value' => 'uploads/images/'.$file->getClientOriginalName()])) {
+
+                $fileName = $file->getClientOriginalName();
+                $file->move('uploads/images', $fileName);
+
+                $configKey = $qrcodeType;
+                if (Config::findOrNew($configKey)->update(['value' => 'uploads/images/'.$fileName])) {
                     return redirect()->route('admin.system.index', '#payment')->with('successMsg', trans('common.success_item', ['attribute' => trans('common.update')]));
                 }
-            }
 
-            return redirect()->route('admin.system.index', '#payment')->withErrors(trans('common.failed_item', ['attribute' => trans('common.update')]));
+                return redirect()->route('admin.system.index', '#payment')->withErrors(trans('common.failed_item', ['attribute' => trans('common.update')]));
+            }
         }
 
         return redirect()->route('admin.system.index');
@@ -278,13 +286,13 @@ class SystemController extends Controller
     public function common(): View
     {
         return view('admin.config.common', [
-            'methods' => SsConfig::type(1)->get(),
-            'protocols' => SsConfig::type(2)->get(),
-            'categories' => GoodsCategory::all(),
-            'obfsList' => SsConfig::type(3)->get(),
+            'methods' => SsConfig::select(['id', 'name', 'is_default'])->type(1)->get(),
+            'protocols' => SsConfig::select(['id', 'name', 'is_default'])->type(2)->get(),
+            'obfsList' => SsConfig::select(['id', 'name', 'is_default'])->type(3)->get(),
+            'categories' => GoodsCategory::select(['id', 'name', 'sort'])->get(),
             'countries' => Country::all(),
             'levels' => Level::all(),
-            'labels' => Label::with('nodes')->get(),
+            'labels' => Label::withCount('nodes')->get(),
         ]);
     }
 }

+ 17 - 5
app/Http/Controllers/Admin/TicketController.php

@@ -15,8 +15,8 @@ class TicketController extends Controller
 {
     public function index(Request $request): View
     { // 工单列表
-        $query = Ticket::where(static function ($query) {
-            $query->whereAdminId(auth()->id())->orwhere('admin_id');
+        $query = Ticket::where(function ($query) {
+            $query->where('admin_id', auth()->id())->orWhereNull('admin_id');
         })->with('user');
 
         $request->whenFilled('username', function ($username) use ($query) {
@@ -31,9 +31,19 @@ class TicketController extends Controller
     public function store(TicketRequest $request): JsonResponse
     { // 创建工单
         $data = $request->validated();
-        $user = User::find($data['uid']) ?: User::whereUsername($data['username'])->first();
 
-        if ($user === auth()->user()) {
+        $user = null;
+        if (! empty($data['uid'])) {
+            $user = User::find($data['uid']);
+        } elseif (! empty($data['username'])) {
+            $user = User::whereUsername($data['username'])->first();
+        }
+
+        if (! $user) {
+            return response()->json(['status' => 'fail', 'message' => trans('admin.marketing.targeted_users_not_found')]);
+        }
+
+        if ($user->id === auth()->id()) {
             return response()->json(['status' => 'fail', 'message' => trans('admin.ticket.self_send')]);
         }
 
@@ -49,7 +59,7 @@ class TicketController extends Controller
         return view('admin.ticket.reply', [
             'ticket' => $ticket,
             'user' => $ticket->user,
-            'replyList' => $ticket->reply()->with('ticket:id,status', 'admin:id,username,qq', 'user:id,username,qq')->oldest()->get(),
+            'replyList' => $ticket->reply()->with(['ticket:id,status', 'admin:id,username,qq', 'user:id,username,qq'])->oldest()->get(),
         ]);
     }
 
@@ -58,6 +68,8 @@ class TicketController extends Controller
         $content = substr(str_replace(['atob', 'eval'], '', clean($request->input('content'))), 0, 300);
 
         if ($ticket->reply()->create(['admin_id' => auth()->id(), 'content' => $content])) {
+            $ticket->update(['status' => 1]);
+
             return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('user.ticket.reply')])]);
         }
 

+ 41 - 28
app/Http/Controllers/Admin/ToolsController.php

@@ -2,6 +2,7 @@
 
 namespace App\Http\Controllers\Admin;
 
+use App\Helpers\ProxyConfig;
 use App\Http\Controllers\Controller;
 use App\Models\User;
 use App\Utils\IP;
@@ -16,6 +17,8 @@ use Symfony\Component\HttpFoundation\BinaryFileResponse;
 
 class ToolsController extends Controller
 {
+    use ProxyConfig;
+
     public function decompile(Request $request): JsonResponse|View
     { // SS(R)链接反解析
         if ($request->isMethod('POST')) {
@@ -41,9 +44,6 @@ class ToolsController extends Controller
                 $txt .= "\r\n".base64url_decode($str);
             }
 
-            // 生成转换好的JSON文件
-            // file_put_contents(public_path('downloads/decompile.json'), $txt);
-
             return response()->json(['status' => 'success', 'data' => $txt, 'message' => trans('common.success_item', ['attribute' => trans('admin.tools.decompile.attribute')])]);
         }
 
@@ -67,13 +67,13 @@ class ToolsController extends Controller
 
             // 校验格式
             $content = json_decode($content, true);
-            if (empty($content->port_password)) {
+            if (! isset($content['port_password']) || ! is_array($content['port_password'])) {
                 return response()->json(['status' => 'fail', 'message' => trans('admin.tools.convert.missing_error')]);
             }
 
             // 转换成SSR格式JSON
             $data = [];
-            foreach ($content->port_password as $port => $passwd) {
+            foreach ($content['port_password'] as $port => $passwd) {
                 $data[] = [
                     'u' => 0,
                     'd' => 0,
@@ -98,14 +98,14 @@ class ToolsController extends Controller
             return response()->json(['status' => 'success', 'data' => $json, 'message' => trans('common.success_item', ['attribute' => trans('common.convert')])]);
         }
 
-        return view('admin.tools.convert');
+        return view('admin.tools.convert', $this->proxyConfigOptions());
     }
 
     public function download(Request $request): BinaryFileResponse
     { // 下载转换好的JSON文件
         $type = (int) $request->input('type');
         if (empty($type)) {
-            abort(trans('admin.tools.convert.params_unknown'));
+            abort(400, trans('admin.tools.convert.params_unknown'));
         }
 
         if ($type === 1) {
@@ -115,7 +115,7 @@ class ToolsController extends Controller
         }
 
         if (! file_exists($filePath)) {
-            abort(trans('admin.tools.convert.file_missing'));
+            abort(404, trans('admin.tools.convert.file_missing'));
         }
 
         return response()->download($filePath);
@@ -141,34 +141,44 @@ class ToolsController extends Controller
 
             $save_path = realpath(storage_path('uploads'));
             $new_name = md5($file->getClientOriginalExtension()).'.json';
-            $file->move($save_path, $new_name);
+
+            try {
+                $file->move($save_path, $new_name);
+            } catch (Exception $e) {
+                Log::error(trans('common.error_action_item', ['action' => trans('common.import'), 'attribute' => trans('admin.menu.tools.import')]).': '.$e->getMessage());
+
+                return redirect()->back()->withErrors(trans('admin.tools.import.file_error'));
+            }
 
             // 读取文件内容
-            $data = file_get_contents($save_path.'/'.$new_name);
+            $file_path = $save_path.'/'.$new_name;
+            $data = file_get_contents($file_path);
+
+            // 删除临时文件
+            @unlink($file_path);
+
             $data = json_decode($data, true);
-            if (! $data) {
+            if (! $data || ! is_array($data)) {
                 return redirect()->back()->withErrors(trans('admin.tools.import.format_error', ['type' => 'JSON']));
             }
 
             try {
                 DB::beginTransaction();
                 foreach ($data as $user) {
-                    $obj = new User;
-                    $obj->nickname = $user->user;
-                    $obj->username = $user->user;
-                    $obj->password = '123456';
-                    $obj->port = $user->port;
-                    $obj->passwd = $user->passwd;
-                    $obj->vmess_id = $user->uuid;
-                    $obj->transfer_enable = $user->transfer_enable;
-                    $obj->method = $user->method;
-                    $obj->protocol = $user->protocol;
-                    $obj->obfs = $user->obfs;
-                    $obj->expired_at = '2099-01-01';
-                    $obj->reg_ip = IP::getClientIp();
-                    $obj->created_at = now();
-                    $obj->updated_at = now();
-                    $obj->save();
+                    User::create([
+                        'nickname' => $user['user'] ?? ('User_'.time()),
+                        'username' => $user['user'] ?? ('user_'.time().'_'.rand(1000, 9999)),
+                        'password' => bcrypt('123456'),
+                        'port' => $user['port'] ?? 0,
+                        'passwd' => $user['passwd'] ?? '',
+                        'vmess_id' => $user['uuid'] ?? '',
+                        'transfer_enable' => $user['transfer_enable'] ?? 0,
+                        'method' => $user['method'] ?? '',
+                        'protocol' => $user['protocol'] ?? '',
+                        'obfs' => $user['obfs'] ?? '',
+                        'expired_at' => '2099-01-01',
+                        'reg_ip' => IP::getClientIp(),
+                    ]);
                 }
 
                 DB::commit();
@@ -195,6 +205,7 @@ class ToolsController extends Controller
         }
 
         $logs = $this->tail($file, 10000);
+        $url = [];
         if ($logs) {
             foreach ($logs as $log) {
                 if (str_contains($log, 'TCP connecting')) {
@@ -217,7 +228,7 @@ class ToolsController extends Controller
             }
         }
 
-        return view('admin.tools.analysis', ['urlList' => array_unique($url ?? [])]);
+        return view('admin.tools.analysis', ['urlList' => array_unique($url)]);
     }
 
     private function tail(string $file, int $n, int $base = 5): array|false
@@ -246,6 +257,8 @@ class ToolsController extends Controller
             }
         }
 
+        fclose($fp);
+
         return array_slice($lines, 0, $n);
     }
 

+ 41 - 25
app/Http/Controllers/Admin/UserController.php

@@ -2,6 +2,7 @@
 
 namespace App\Http\Controllers\Admin;
 
+use App\Helpers\ProxyConfig;
 use App\Http\Controllers\Controller;
 use App\Http\Requests\Admin\UserStoreRequest;
 use App\Http\Requests\Admin\UserUpdateRequest;
@@ -21,16 +22,17 @@ use Exception;
 use Illuminate\Contracts\View\View;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
-use Illuminate\Support\Collection;
 use Log;
 use Spatie\Permission\Models\Role;
 use Str;
 
 class UserController extends Controller
 {
+    use ProxyConfig;
+
     public function index(Request $request): View
     {
-        $query = User::with('subscribe');
+        $query = User::with(['subscribe:user_id,code']);
 
         foreach (['id', 'port', 'status', 'enable', 'user_group_id', 'level'] as $field) {
             $request->whenFilled($field, function ($value) use ($query, $field) {
@@ -76,7 +78,7 @@ class UserController extends Controller
         });
 
         return view('admin.user.index', [
-            'userList' => $query->with('subscribe:user_id,code')->sortable(['id' => 'desc'])->paginate(15)->appends($request->except('page')),
+            'userList' => $query->sortable(['id' => 'desc'])->paginate(15)->appends($request->except('page')),
             'userGroups' => UserGroup::pluck('name', 'id'),
             'levels' => Level::orderBy('level')->pluck('name', 'level'),
         ]);
@@ -89,7 +91,7 @@ class UserController extends Controller
         $data['password'] = $data['password'] ?? Str::random();
         $data['port'] = $data['port'] ?? Helpers::getPort();
         $data['passwd'] = $data['passwd'] ?? Str::random();
-        $data['vmess_id'] = $data['uuid'] ?? Str::uuid();
+        $data['vmess_id'] = $data['vmess_id'] ?: Str::uuid();
         Arr::forget($data, 'uuid');
         $data['transfer_enable'] *= GiB;
         $data['expired_at'] = $data['expired_at'] ?? date('Y-m-d', strtotime('next year'));
@@ -122,35 +124,35 @@ class UserController extends Controller
 
     public function create(): View
     {
-        return view('admin.user.info', [
-            'levels' => Level::orderBy('level')->pluck('name', 'level'),
-            'userGroups' => UserGroup::orderBy('id')->pluck('name', 'id'),
-            'roles' => $this->getAvailableRoles(),
-        ]);
+        return view('admin.user.info', $this->getUserViewData());
+    }
+
+    public function edit(User $user): View
+    {
+        return view('admin.user.info', [...$this->getUserViewData(), 'user' => $user->load('inviter:id,username')]);
     }
 
-    private function getAvailableRoles(): ?Collection
+    /**
+     * 获取用户创建/编辑页面的共享数据.
+     */
+    private function getUserViewData(): array
     {
         $editor = auth()->user();
+        $roles = null;
         if ($editor->hasRole('Super Admin')) { // 超级管理员直接获取全部角色
-            return Role::pluck('description', 'name');
+            $roles = Role::pluck('description', 'name');
         }
 
         if ($editor->can('give roles')) { // 有权者只能获得已有角色,防止权限泛滥
-            return $editor->roles()->pluck('description', 'name');
+            $roles = $editor->roles()->pluck('description', 'name');
         }
 
-        return null;
-    }
-
-    public function edit(User $user): View
-    {
-        return view('admin.user.info', [
-            'user' => $user->load('inviter:id,username'),
+        return [
             'levels' => Level::orderBy('level')->pluck('name', 'level'),
             'userGroups' => UserGroup::orderBy('id')->pluck('name', 'id'),
-            'roles' => $this->getAvailableRoles(),
-        ]);
+            'roles' => $roles,
+            ...$this->proxyConfigOptions(),
+        ];
     }
 
     public function destroy(User $user): JsonResponse
@@ -216,7 +218,7 @@ class UserController extends Controller
     {
         $data = $request->validated();
         $data['passwd'] = $request->input('passwd') ?? Str::random();
-        $data['vmess_id'] = $data['uuid'] ?? Str::uuid();
+        $data['vmess_id'] = $data['vmess_id'] ?: Str::uuid();
         Arr::forget($data, ['roles', 'uuid', 'password']);
         $data['transfer_enable'] *= GiB;
         $data['enable'] = $data['status'] < 0 ? 0 : $data['enable'];
@@ -299,11 +301,25 @@ class UserController extends Controller
         return response()->json(['status' => 'success', 'data' => $proxyService->getUserProxyConfig($server, $request->input('type') !== 'text'), 'title' => $server['type']]);
     }
 
-    public function oauth(): View
+    public function oauth(Request $request): View
     {
-        $list = UserOauth::with('user:id,username')->paginate(15)->appends(\request('page'));
+        $query = UserOauth::with('user:id,username');
 
-        return view('admin.user.oauth', compact('list'));
+        // 用户名过滤
+        $request->whenFilled('username', function ($value) use ($query) {
+            $query->whereHas('user', function ($userQuery) use ($value) {
+                $userQuery->where('username', 'like', "%$value%");
+            });
+        });
+
+        // 类型过滤
+        $request->whenFilled('type', function ($value) use ($query) {
+            $query->where('type', $value);
+        });
+
+        return view('admin.user.oauth', [
+            'list' => $query->paginate(15)->appends(\request('page')),
+        ]);
     }
 
     public function VNetInfo(User $user): JsonResponse

+ 6 - 1
app/Http/Controllers/Admin/UserGroupController.php

@@ -37,8 +37,13 @@ class UserGroupController extends Controller
 
     public function edit(UserGroup $group): View
     {
+        $group->load('nodes:id');
+
         return view('admin.user.group.info', [
-            'group' => $group,
+            'group' => array_merge(
+                $group->toArray(),
+                ['nodes' => $group->nodes->pluck('id')->map('strval')->toArray()]
+            ),
             'nodes' => Node::whereStatus(1)->pluck('name', 'id'),
         ]);
     }

+ 8 - 13
app/Http/Controllers/AdminController.php

@@ -20,15 +20,10 @@ class AdminController extends Controller
         $past = strtotime('-'.sysConfig('expire_days').' days');
         $today = today();
 
-        $stats = cache()->remember('user_stats', now()->addMinutes(5), function () use ($today, $past) {
+        $stats = cache()->remember('user_stats', now()->addMinutes(5), function () use ($today) {
             $dailyTrafficUsage = NodeHourlyDataFlow::whereDate('created_at', $today)->sum(DB::raw('u + d'));
 
             return [
-                'activeUserCount' => User::where('t', '>=', $past)->count(), // 活跃用户数
-                'inactiveUserCount' => User::whereEnable(1)->where('t', '<', $past)->count(), // 不活跃用户数
-                'expireWarningUserCount' => User::whereBetween('expired_at', [$today, today()->addDays(sysConfig('expire_days'))])->count(), // 临近过期用户数
-                'largeTrafficUserCount' => User::whereRaw('(u + d)/transfer_enable >= 0.9')->where('status', '<>', -1)->count(), // 流量使用超过90%的用户
-                'flowAbnormalUserCount' => count((new UserHourlyDataFlow)->trafficAbnormal()), // 1小时内流量异常用户
                 'monthlyTrafficUsage' => formatBytes(NodeDailyDataFlow::whereNull('node_id')->whereMonth('created_at', now()->month)->sum(DB::raw('u + d'))),
                 'dailyTrafficUsage' => $dailyTrafficUsage ? formatBytes($dailyTrafficUsage) : 0,
                 'totalTrafficUsage' => formatBytes(NodeDailyDataFlow::whereNull('node_id')->where('created_at', '>=', now()->subDays(30))->sum(DB::raw('u + d'))),
@@ -39,14 +34,14 @@ class AdminController extends Controller
             'totalUserCount' => User::count(), // 总用户数
             'todayRegister' => User::whereDate('created_at', $today)->count(), // 今日注册用户
             'enableUserCount' => User::whereEnable(1)->count(), // 有效用户数
-            'activeUserCount' => $stats['activeUserCount'],
+            'activeUserCount' => User::where('t', '>=', $past)->count(), // 活跃用户数
             'payingUserCount' => User::has('paidOrders')->count(), // 付费用户数
-            'payingNewUserCount' => User::whereDate('created_at', $today)->has('paidOrders')->count(), // 不活跃用户数
-            'inactiveUserCount' => $stats['inactiveUserCount'],
-            'onlineUserCount' => User::where('t', '>=', strtotime('-10 minutes'))->count(), // 10分钟内在线用户数,
-            'expireWarningUserCount' => $stats['expireWarningUserCount'],
-            'largeTrafficUserCount' => $stats['largeTrafficUserCount'],
-            'flowAbnormalUserCount' => $stats['flowAbnormalUserCount'],
+            'payingNewUserCount' => User::whereDate('created_at', $today)->has('paidOrders')->count(), // 今日新增付费用户
+            'inactiveUserCount' => User::whereEnable(1)->where('t', '<', $past)->count(), // 不活跃用户数
+            'onlineUserCount' => User::where('t', '>=', strtotime('-10 minutes'))->count(), // 10分钟内在线用户数
+            'expireWarningUserCount' => User::whereBetween('expired_at', [$today, today()->addDays(sysConfig('expire_days'))])->count(), // 临近过期用户数
+            'largeTrafficUserCount' => User::whereRaw('(u + d)/transfer_enable >= 0.9')->where('status', '<>', -1)->count(), // 流量使用超过90%的用户
+            'flowAbnormalUserCount' => count((new UserHourlyDataFlow)->trafficAbnormal()), // 1小时内流量异常用户
             'nodeCount' => Node::count(),
             'abnormalNodeCount' => Node::whereStatus(0)->count(),
             'monthlyTrafficUsage' => $stats['monthlyTrafficUsage'],

+ 2 - 3
app/Http/Controllers/Api/Client/ClientController.php

@@ -198,15 +198,14 @@ class ClientController extends Controller
     public function getProxyList(ProxyService $proxyService): JsonResponse
     {
         $servers = [];
-        foreach ($proxyService->getNodeList(null, false) as $node) {
+        foreach ($proxyService->getNodeList(null, false)->load('latestOnlineLog') as $node) {
             $server = $proxyService->getProxyConfig($node);
             if ($server['type'] === '`shadowsocks`' || $server['type'] === 'shadowsocksr') {
                 $server['type'] = 1;
             }
 
-            $online_log = $node->onlineLogs->where('log_time', '>=', strtotime('-5 minutes'))->sortBy('log_time')->first(); // 在线人数
             $server['node_ip'] = filter_var($server['host'], FILTER_VALIDATE_IP) ? $server['host'] : gethostbyname($server['host']);
-            $server['online'] = $online_log->online_user ?? 0;
+            $server['online'] = $node->latestOnlineLog?->online_user ?? 0; // 在线人数
             $this->getOnlineCount($server, $server['online']);
             $servers[] = $server;
         }

+ 11 - 19
app/Http/Controllers/AuthController.php

@@ -56,8 +56,8 @@ class AuthController extends Controller
         if (! auth()->attempt($data, $request->has('remember'))) {
             return redirect()->back()->withInput()->withErrors(trans('auth.error.login_failed'));
         }
-        $user = auth()->getUser();
 
+        $user = auth()->getUser();
         if (! $user) {
             return redirect()->back()->withInput()->withErrors(trans('auth.error.login_error'));
         }
@@ -131,7 +131,7 @@ class AuthController extends Controller
     {
         session()->put('register_token', Str::random());
 
-        return view('auth.register', ['emailList' => (int) sysConfig('is_email_filtering') !== 2 ? false : EmailFilter::whereType(2)->get()]);
+        return view('auth.register', ['emailList' => sysConfig('is_email_filtering') === '2' ? EmailFilter::whereType(2)->get() : false]);
     }
 
     public function register(RegisterRequest $request): RedirectResponse
@@ -185,8 +185,7 @@ class AuthController extends Controller
                 return redirect()->back()->withInput($request->except('verify_code'))->withErrors(trans('auth.captcha.error.timeout'));
             }
 
-            $verifyCode->status = 1;
-            $verifyCode->save();
+            $verifyCode->update(['status' => 1]);
         }
 
         // 是否校验验证码
@@ -342,13 +341,10 @@ class AuthController extends Controller
 
     private function addVerifyUrl(int $uid, string $email): string
     { // 生成申请的请求地址
-        $token = md5(sysConfig('website_name').$email.microtime());
-        $verify = new Verify;
-        $verify->user_id = $uid;
-        $verify->token = $token;
-        $verify->save();
-
-        return $token;
+        return Verify::create([
+            'user_id' => $uid,
+            'token' => md5(sysConfig('website_name').$email.microtime()),
+        ])->token;
     }
 
     public function resetPassword(Request $request): RedirectResponse|View
@@ -436,8 +432,7 @@ class AuthController extends Controller
             }
 
             // 置为已使用
-            $verify->status = 1;
-            $verify->save();
+            $verify->update(['status' => 1]);
 
             return redirect()->route('login')->with('successMsg', trans('auth.password.reset.success'));
         }
@@ -449,8 +444,7 @@ class AuthController extends Controller
 
         if (time() - strtotime($verify->created_at) >= 1800) {
             // 置为已失效
-            $verify->status = 2;
-            $verify->save();
+            $verify->update(['status' => 2]);
         }
 
         return view('auth.reset', ['verify' => Verify::type(1)->whereToken($token)->first()]); // 重新获取一遍verify
@@ -531,8 +525,7 @@ class AuthController extends Controller
             session()->flash('errorMsg', trans('auth.error.url_timeout'));
 
             // 置为已失效
-            $verify->status = 2;
-            $verify->save();
+            $verify->update(['status' => 2]);
 
             return view('auth.active');
         }
@@ -545,8 +538,7 @@ class AuthController extends Controller
         }
 
         // 置为已使用
-        $verify->status = 1;
-        $verify->save();
+        $verify->update(['status' => 1]);
 
         // 账号激活后给邀请人送流量
         $inviter = $user->inviter;

+ 55 - 1
app/Http/Controllers/OAuthController.php

@@ -2,10 +2,12 @@
 
 namespace App\Http\Controllers;
 
+use App\Models\Invite;
 use App\Models\User;
 use App\Models\UserOauth;
 use App\Utils\Helpers;
 use App\Utils\IP;
+use Hashids\Hashids;
 use Illuminate\Http\RedirectResponse;
 use Laravel\Socialite\Facades\Socialite;
 use Str;
@@ -85,7 +87,31 @@ class OAuthController extends Controller
             $userAuth = UserOauth::whereType($provider)->whereIdentifier($registerInfo->getId())->first();
 
             if (! $userAuth) { // 第三方账号未被绑定
-                $user = Helpers::addUser($registerInfo->getEmail(), Str::random(), MiB * sysConfig('default_traffic'), (int) sysConfig('default_days'), $registerInfo->getNickname(), 1);
+                // 获取邀请信息
+                $affArr = $this->getAff();
+                $inviter_id = $affArr['inviter_id'];
+
+                // 计算流量值(包括邀请奖励流量)
+                $transfer_enable = MiB * ((int) sysConfig('default_traffic') + ($inviter_id ? (int) sysConfig('referral_traffic') : 0));
+
+                // 创建用户并传入邀请者 ID
+                $user = Helpers::addUser($registerInfo->getEmail(), Str::random(), $transfer_enable, (int) sysConfig('default_days'), $inviter_id, $registerInfo->getNickname(), 1);
+
+                // 更新邀请码(如果使用了邀请码)
+                if ($affArr['code_id'] && sysConfig('is_invite_register')) {
+                    Invite::find($affArr['code_id'])?->update(['invitee_id' => $user->id, 'status' => 1]);
+                }
+
+                // 清除邀请人Cookie
+                cookie()->unqueue('register_aff');
+
+                // 给邀请人增加流量(如果有的话)
+                if ($inviter_id) {
+                    $referralUser = User::find($inviter_id);
+                    if ($referralUser && $referralUser->expiration_date >= date('Y-m-d')) {
+                        $referralUser->incrementData(sysConfig('referral_traffic') * MiB);
+                    }
+                }
 
                 $user->userAuths()->create([
                     'type' => $provider,
@@ -100,6 +126,34 @@ class OAuthController extends Controller
         return redirect()->route('login')->withErrors(trans('auth.oauth.registered'));
     }
 
+    private function getAff(): array
+    { // 获取邀请信息
+        $data = ['inviter_id' => null, 'code_id' => 0]; // 邀请人ID 与 邀请码ID
+
+        // 检查cookie中的邀请信息(通过Affiliate中间件设置)
+        $cookieAff = request()?->cookie('register_aff');
+        if ($cookieAff) {
+            $data['inviter_id'] = $this->setInviter($cookieAff);
+        }
+
+        return $data;
+    }
+
+    private function setInviter(string|int $aff): ?int
+    {
+        $uid = 0;
+        if (is_numeric($aff)) {
+            $uid = (int) $aff;
+        } else {
+            $decode = (new Hashids(sysConfig('affiliate_link_salt'), 8))->decode($aff);
+            if ($decode) {
+                $uid = $decode[0];
+            }
+        }
+
+        return $uid && User::whereId($uid)->exists() ? $uid : null;
+    }
+
     private function handleLogin(User $user): RedirectResponse
     {
         auth()->login($user);

+ 1 - 1
app/Http/Controllers/User/AffiliateController.php

@@ -17,7 +17,7 @@ class AffiliateController extends Controller
     public function index(): Response|View
     {
         if (ReferralLog::uid()->doesntExist() && Order::uid()->whereStatus(2)->doesntExist()) {
-            return Response::view('auth.error', ['message' => trans('user.purchase.required').'<a class="btn btn-sm btn-danger" href="/">'.trans('common.back').'</a>'], 402);
+            return response()->view('auth.error', ['message' => trans('user.purchase.required').'<a class="btn btn-sm btn-danger" href="/">'.trans('common.back').'</a>'], 402);
         }
 
         return view('user.referral', [

+ 2 - 4
app/Http/Controllers/User/ArticleController.php

@@ -24,9 +24,7 @@ class ArticleController extends Controller
     }
 
     public function show(Article $article): JsonResponse
-    { // 公告详情
-        $articleService = new ArticleService($article);
-
-        return response()->json(['title' => $article->title, 'content' => $articleService->getContent()]);
+    { // 文章详情
+        return response()->json(['title' => $article->title, 'content' => (new ArticleService($article))->getContent()]);
     }
 }

+ 19 - 8
app/Http/Controllers/User/InviteController.php

@@ -5,9 +5,11 @@ namespace App\Http\Controllers\User;
 use App\Http\Controllers\Controller;
 use App\Models\Invite;
 use App\Models\Order;
+use Exception;
 use Illuminate\Contracts\View\View;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Response;
+use Log;
 use Str;
 
 class InviteController extends Controller
@@ -15,7 +17,7 @@ class InviteController extends Controller
     public function index(): Response|View
     { // 邀请页面
         if (Order::uid()->active()->where('origin_amount', '>', 0)->doesntExist()) {
-            return Response::view('auth.error', ['message' => trans('user.purchase.required').' <a class="btn btn-sm btn-danger" href="/">'.trans('common.back').'</a>'], 402);
+            return response()->view('auth.error', ['message' => trans('user.purchase.required').' <a class="btn btn-sm btn-danger" href="/">'.trans('common.back').'</a>'], 402);
         }
 
         return view('user.invite', [
@@ -30,17 +32,26 @@ class InviteController extends Controller
     public function store(): JsonResponse
     { // 生成邀请码
         $user = auth()->user();
+
+        // 检查用户是否还有邀请码配额
         if ($user->invite_num <= 0) {
             return response()->json(['status' => 'fail', 'message' => trans('user.invite.generate_failed')]);
         }
-        $invite = $user->invites()->create([
-            'code' => strtoupper(mb_substr(md5(microtime().Str::random()), 8, 12)),
-            'dateline' => date('Y-m-d H:i:s', strtotime(sysConfig('user_invite_days').' days')),
-        ]);
-        if ($invite) {
-            $user->decrement('invite_num');
 
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.generate')])]);
+        try {
+            $invite = $user->invites()->create([
+                'code' => strtoupper(Str::random(12)), // 简化邀请码生成逻辑
+                'dateline' => now()->addDays((int) sysConfig('user_invite_days')),
+            ]);
+
+            if ($invite) {
+                $user->decrement('invite_num');
+
+                return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.generate')])]);
+            }
+        } catch (Exception $e) {
+            // 记录异常但不暴露给用户
+            Log::error('Failed to generate invite code: '.$e->getMessage());
         }
 
         return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.generate')])]);

+ 13 - 12
app/Http/Controllers/User/NodeController.php

@@ -4,7 +4,6 @@ namespace App\Http\Controllers\User;
 
 use App\Http\Controllers\Controller;
 use App\Models\Node;
-use App\Models\NodeHeartbeat;
 use App\Services\ProxyService;
 use Illuminate\Contracts\View\View;
 use Illuminate\Http\JsonResponse;
@@ -14,20 +13,22 @@ class NodeController extends Controller
 {
     public function index(): View
     { // 节点列表
-        $nodeList = auth()->user()->nodes()->whereIn('is_display', [1, 3])->with(['labels', 'level_table'])->get(); // 获取当前用户可用节点
-        $onlineNode = NodeHeartbeat::recently()->distinct()->pluck('node_id')->toArray();
-        foreach ($nodeList as $node) {
-            $node->offline = ! in_array($node->id, $onlineNode, true); // 节点在线状态
-        }
-
-        return view('user.nodeList', [
-            'nodesGeo' => $nodeList->pluck('name', 'geo')->toArray(),
-            'nodeList' => $nodeList,
-        ]);
+        $nodes = auth()->user()->nodes()->whereIn('is_display', [1, 3])->with(['labels', 'level_table:level,name', 'latestHeartbeat'])->orderByDesc('sort')->orderBy('id')->get(); // 获取当前用户可用节点
+
+        // 直接在节点集合上标记在线状态和标签名称
+        $nodes->each(function ($node) {
+            $node->offline = is_null($node->latestHeartbeat);
+            $node->label_names = $node->labels->sortByDesc('sort')->sortBy('id')->pluck('name');
+        });
+
+        // 提取节点地理位置信息用于地图显示
+        $nodesGeo = $nodes->pluck('name', 'geo');
+
+        return view('user.nodeList', compact('nodesGeo', 'nodes'));
     }
 
     public function show(Request $request, Node $node, ProxyService $proxyServer): JsonResponse
-    { // 节点详细
+    { // 节点详细信息
         $server = $proxyServer->getProxyConfig($node);
 
         return response()->json(['status' => 'success', 'data' => $proxyServer->getUserProxyConfig($server, $request->input('type') !== 'text'), 'title' => $server['type']]);

+ 24 - 20
app/Http/Controllers/User/ShopController.php

@@ -20,29 +20,32 @@ class ShopController extends Controller
     public function index(): View
     { // 商品列表
         $user = auth()->user();
-        // 余额充值商品,只取10个
-        $renewOrder = Order::userActivePlan($user->id)->first();
-        $renewPrice = $renewOrder->goods->renew ?? 0;
-        // 有重置日时按照重置日为标准,否则就以过期日为标准
-        $dataPlusDays = $user->reset_time ?? $user->expired_at;
 
-        $goodsList = Goods::whereStatus(1)->where('type', '<=', '2')->orderByDesc('type')->orderByDesc('sort')->get();
+        // 获取可用商品列表
+        $goodsList = Goods::whereStatus(1)->where('type', '<=', 2)->orderByDesc('type')->orderByDesc('sort')->get();
 
-        if ($user && $nodes = $user->userGroup) {
-            $nodes = $nodes->nodes();
-        } else {
-            $nodes = Node::all();
-        }
-        foreach ($goodsList as $goods) {
-            $goods->node_count = $nodes->where('level', '<=', $goods->level)->where('status', 1)->count();
-            $goods->node_countries = $nodes->where('level', '<=', $goods->level)->where('status', 1)->pluck('country_code')->unique();
-        }
+        // 获取用户节点信息
+        $nodes = $user->userGroup ? $user->userGroup->nodes() : Node::query();
+
+        // 为每个商品计算节点数量和国家
+        $goodsList->each(function ($goods) use ($nodes) {
+            $filteredNodes = $nodes->where('level', '<=', $goods->level)->where('status', 1);
+            $goods->node_count = $filteredNodes->count();
+            $goods->node_countries = $filteredNodes->pluck('country_code')->unique();
+        });
+
+        // 获取续费订单和价格
+        $renewOrder = Order::userActivePlan($user->id)->first();
+        $renewPrice = $renewOrder?->goods->renew ?? 0;
+
+        // 计算数据增加天数
+        $dataPlusDays = $user->reset_time ?? $user->expired_at;
 
         return view('user.services', [
             'chargeGoodsList' => Goods::type(3)->orderBy('price')->get(),
             'goodsList' => $goodsList,
             'renewTraffic' => $renewPrice ? Helpers::getPriceTag($renewPrice) : 0,
-            'dataPlusDays' => $dataPlusDays > date('Y-m-d') ? $dataPlusDays->diffInDays() : 0,
+            'dataPlusDays' => $dataPlusDays > now() ? $dataPlusDays->diffInDays() : 0,
         ]);
     }
 
@@ -51,16 +54,17 @@ class ShopController extends Controller
         $user = auth()->user();
         $order = Order::userActivePlan()->firstOrFail();
         $renewCost = $order->goods->renew;
+
+        // 检查余额是否足够
         if ($user->credit < $renewCost) {
             return response()->json(['status' => 'fail', 'message' => trans('user.payment.insufficient_balance')]);
         }
 
+        // 重置用户流量
         $user->update(['u' => 0, 'd' => 0]);
 
-        // 记录余额操作日志
+        // 记录余额操作日志并扣费
         Helpers::addUserCreditLog($user->id, null, $user->credit, $user->credit - $renewCost, -1 * $renewCost, 'The user manually reset the data.');
-
-        // 扣余额
         $user->updateCredit(-$renewCost);
 
         return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.reset')])]);
@@ -96,7 +100,7 @@ class ShopController extends Controller
         $dataPlusDays = $user->reset_time ?? $user->expired_at;
 
         return view('user.buy', [
-            'dataPlusDays' => $dataPlusDays > date('Y-m-d') ? $dataPlusDays->diffInDays() : 0,
+            'dataPlusDays' => $dataPlusDays > now() ? $dataPlusDays->diffInDays() : 0,
             'activePlan' => Order::userActivePlan()->exists(),
             'goods' => $good,
         ]);

+ 40 - 22
app/Http/Controllers/User/SubscribeController.php

@@ -24,6 +24,7 @@ class SubscribeController extends Controller
 
     public function index(Request $request, string $code)
     {
+        // 检查订阅码格式
         if (! preg_match('/^[0-9A-Za-z]+$/', $code)) {
             return redirect()->route('login');
         }
@@ -43,65 +44,82 @@ class SubscribeController extends Controller
 
     public function getSubscribeByCode(Request $request, string $code): RedirectResponse|string
     { // 通过订阅码获取订阅信息
-        self::$subType = is_numeric($request->input('type')) ? $request->input('type') : null;
-        // 检查订阅码是否有效
+        self::$subType = is_numeric($request->input('type')) ? (int) $request->input('type') : null;
+
+        // 检查订阅码格式
         if (! preg_match('/^[0-9A-Za-z]+$/', $code)) {
-            $this->failed(trans('errors.subscribe.unknown'));
+            return $this->failed(trans('errors.subscribe.unknown'));
         }
 
+        // 检查订阅是否存在
         $subscribe = UserSubscribe::whereCode($code)->first();
         if (! $subscribe) {
-            $this->failed(trans('errors.subscribe.unknown'));
+            return $this->failed(trans('errors.subscribe.unknown'));
         }
 
+        // 检查订阅状态
         if ($subscribe->status !== 1) {
-            $this->failed(trans('errors.subscribe.sub_banned'));
+            return $this->failed(trans('errors.subscribe.sub_banned'));
         }
 
+        // 检查用户是否有效
         $user = $subscribe->user;
-        if (! $user) { // 检查用户是否有效
-            $this->failed(trans('errors.subscribe.user'));
+        if (! $user) {
+            return $this->failed(trans('errors.subscribe.user'));
         }
 
+        // 检查用户状态
         if ($user->status === -1) {
-            $this->failed(trans('errors.subscribe.user_disabled'));
+            return $this->failed(trans('errors.subscribe.user_disabled'));
         }
 
         if ($user->enable !== 1) {
             if ($user->ban_time) {
-                $this->failed(trans('errors.subscribe.banned_until', ['time' => $user->ban_time]));
+                return $this->failed(trans('errors.subscribe.banned_until', ['time' => $user->ban_time]));
             }
 
             if ($user->unused_traffic <= 0) {
-                $this->failed(trans('errors.subscribe.out'));
+                return $this->failed(trans('errors.subscribe.out'));
             }
 
             if ($user->expiration_date < now()->toDateString()) {
-                $this->failed(trans('errors.subscribe.expired'));
+                return $this->failed(trans('errors.subscribe.expired'));
             }
 
-            $this->failed(trans('errors.subscribe.question'));
+            return $this->failed(trans('errors.subscribe.question'));
         }
-        $this->proxyServer->setUser($user);
 
+        // 设置用户并更新订阅信息
+        $this->proxyServer->setUser($user);
         $subscribe->increment('times'); // 更新访问次数
-        $this->subscribeLog($subscribe->id, IP::getClientIp(), json_encode(['Host' => $request->getHost(), 'User-Agent' => $request->userAgent()])); // 记录每次请求
 
-        return $this->proxyServer->getProxyText(strtolower($request->input('target') ?? ($request->userAgent() ?? '')), self::$subType);
+        // 记录订阅日志
+        $this->subscribeLog($subscribe->id, IP::getClientIp(), json_encode([
+            'Host' => $request->getHost(),
+            'User-Agent' => $request->userAgent(),
+        ]));
+
+        // 返回订阅内容
+        return $this->proxyServer->getProxyText(
+            strtolower($request->input('target') ?? ($request->userAgent() ?? '')),
+            self::$subType
+        );
     }
 
-    private function failed(string $text): void
+    private function failed(string $text): string
     { // 抛出错误的节点信息,用于兼容防止客户端订阅失败
         $this->proxyServer->failedProxyReturn($text, self::$subType ?? 1);
+
+        return '';
     }
 
     private function subscribeLog(int $subscribeId, ?string $ip, string $headers): void
     { // 写入订阅访问日志
-        $log = new UserSubscribeLog;
-        $log->user_subscribe_id = $subscribeId;
-        $log->request_ip = $ip;
-        $log->request_time = now();
-        $log->request_header = $headers;
-        $log->save();
+        UserSubscribeLog::create([
+            'user_subscribe_id' => $subscribeId,
+            'request_ip' => $ip,
+            'request_time' => now(),
+            'request_header' => $headers,
+        ]);
     }
 }

+ 50 - 24
app/Http/Controllers/User/TicketController.php

@@ -19,54 +19,80 @@ class TicketController extends Controller
 
     public function store(Request $request): JsonResponse
     { // 添加工单
-        $title = $request->input('title');
-        $content = substr(str_replace(['atob', 'eval'], '', clean($request->input('content'))), 0, 300);
+        $validatedData = $request->validate([
+            'title' => 'required|string|max:255',
+            'content' => 'required|string|max:300',
+        ]);
 
-        if (empty($title) || empty($content)) {
-            return response()->json([
-                'status' => 'fail', 'message' => trans('validation.required', ['attribute' => ucfirst(trans('validation.attributes.title')).'&'.ucfirst(trans('validation.attributes.content'))]),
-            ]);
-        }
+        // 清理内容,防止恶意代码
+        $title = $validatedData['title'];
+        $content = substr(str_replace(['atob', 'eval'], '', clean($validatedData['content'])), 0, 300);
+
+        $ticket = auth()->user()->tickets()->create(compact('title', 'content'));
 
-        if (auth()->user()->tickets()->create(compact('title', 'content'))) {
+        if ($ticket) {
             // 通知相关管理员
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.submit')])]);
+            return response()->json([
+                'status' => 'success',
+                'message' => trans('common.success_item', ['attribute' => trans('common.submit')]),
+            ]);
         }
 
-        return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.create')])]);
+        return response()->json([
+            'status' => 'fail',
+            'message' => trans('common.failed_item', ['attribute' => trans('common.create')]),
+        ]);
     }
 
     public function edit(Ticket $ticket): View
     { // 回复工单
-        return view('user.replyTicket', [
-            'ticket' => $ticket,
-            'replyList' => $ticket->reply()->with('ticket:id,status', 'admin:id,username,qq', 'user:id,username,qq')->oldest()->get(),
-        ]);
+        $replyList = $ticket->reply()
+            ->with('ticket:id,status', 'admin:id,username,qq', 'user:id,username,qq')
+            ->oldest()
+            ->get();
+
+        return view('user.replyTicket', compact('ticket', 'replyList'));
     }
 
     public function reply(Request $request, Ticket $ticket): JsonResponse
     {
-        $content = substr(str_replace(['atob', 'eval'], '', clean($request->input('content'))), 0, 300);
+        $validatedData = $request->validate([
+            'content' => 'required|string|max:300',
+        ]);
+
+        // 清理内容,防止恶意代码
+        $content = substr(str_replace(['atob', 'eval'], '', clean($validatedData['content'])), 0, 300);
 
-        if (empty($content)) {
+        $reply = $ticket->reply()->create([
+            'user_id' => auth()->id(),
+            'content' => $content,
+        ]);
+
+        if ($reply) {
             return response()->json([
-                'status' => 'fail', 'message' => trans('validation.required', ['attribute' => ucfirst(trans('validation.attributes.title')).'&'.ucfirst(trans('validation.attributes.content'))]),
+                'status' => 'success',
+                'message' => trans('common.success_item', ['attribute' => trans('user.ticket.reply')]),
             ]);
         }
 
-        if ($ticket->reply()->create(['user_id' => auth()->id(), 'content' => $content])) {
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('user.ticket.reply')])]);
-        }
-
-        return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('user.ticket.reply')])]);
+        return response()->json([
+            'status' => 'fail',
+            'message' => trans('common.failed_item', ['attribute' => trans('user.ticket.reply')]),
+        ]);
     }
 
     public function close(Ticket $ticket): JsonResponse
     { // 关闭工单
         if ($ticket->close()) {
-            return response()->json(['status' => 'success', 'message' => trans('common.success_item', ['attribute' => trans('common.close')])]);
+            return response()->json([
+                'status' => 'success',
+                'message' => trans('common.success_item', ['attribute' => trans('common.close')]),
+            ]);
         }
 
-        return response()->json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('common.close')])]);
+        return response()->json([
+            'status' => 'fail',
+            'message' => trans('common.failed_item', ['attribute' => trans('common.close')]),
+        ]);
     }
 }

+ 4 - 4
app/Http/Controllers/UserController.php

@@ -28,9 +28,7 @@ class UserController extends Controller
         }
         $user = auth()->user();
 
-        $user->load(['subscribe', 'loginLogs' => function ($query) {
-            $query->latest()->first();
-        }]);
+        $user->load(['subscribe', 'latestLoginLog']);
 
         return view('user.index', [
             'remainDays' => $userService->getRemainingDays(),
@@ -42,7 +40,7 @@ class UserController extends Controller
             'isTrafficWarning' => $userService->isTrafficWarning(), // 流量异常判断
             'paying_user' => $userService->isActivePaying(), // 付费用户判断
             'user' => $user->only(['sub_url', 'unused_traffic', 'expiration_date', 'ban_time']),
-            'userLoginLog' => $user->loginLogs->first(), // 近期登录日志
+            'userLoginLog' => $user->latestLoginLog,
             'subType' => $nodeService->getActiveNodeTypes($user->nodes()),
             'subscribe' => $user->subscribe->only(['status', 'ban_desc']),
             ...$this->dataFlowChart($user->id)]);
@@ -67,6 +65,7 @@ class UserController extends Controller
         if (! $user->incrementData($traffic)) {
             return response()->json(['status' => 'fail', 'title' => trans('common.failed'), 'message' => trans('user.home.attendance.failed')]);
         }
+
         Helpers::addUserTrafficModifyLog($user->id, $user->transfer_enable, $user->transfer_enable + $traffic, trans('user.home.attendance.attribute'));
 
         cache()->put('userCheckIn_'.$user->id, '1', sysConfig('checkin_interval') ? sysConfig('checkin_interval') * Minute : Day); // 多久后可以再签到
@@ -84,6 +83,7 @@ class UserController extends Controller
     {
         $user = auth()->user();
         $url = null;
+
         if ($request->has(['password', 'new_password'])) { // 修改密码
             $url = url()->previous().'#account';
             $data = $request->only(['password', 'new_password']);

+ 1 - 1
app/Http/Requests/Admin/PermissionRequest.php

@@ -9,7 +9,7 @@ class PermissionRequest extends FormRequest
     public function rules(): array
     {
         return [
-            'name' => 'required|string',
+            'name' => 'required|string|unique:permissions,name',
             'description' => 'required|string',
         ];
     }

+ 1 - 1
app/Http/Requests/Admin/UserGroupRequest.php

@@ -9,7 +9,7 @@ class UserGroupRequest extends FormRequest
     public function rules(): array
     {
         return [
-            'name' => 'required|string',
+            'name' => 'required|string|unique:user_group,name',
             'nodes' => 'nullable|exists:node,id',
         ];
     }

+ 1 - 1
app/Http/Requests/Admin/UserStoreRequest.php

@@ -14,7 +14,7 @@ class UserStoreRequest extends FormRequest
             'password' => 'nullable|string',
             'port' => 'nullable|numeric',
             'passwd' => 'nullable|string',
-            'uuid' => 'nullable|uuid',
+            'vmess_id' => 'nullable|uuid',
             'transfer_enable' => 'required|numeric|min:0',
             'enable' => 'required|boolean',
             'method' => 'required|exists:ss_config,name',

+ 1 - 1
app/Http/Requests/Admin/UserUpdateRequest.php

@@ -14,7 +14,7 @@ class UserUpdateRequest extends FormRequest
             'password' => 'nullable|string',
             'port' => 'required|numeric|exclude_if:port,0|gt:0|unique:user,port,'.$this->user->id,
             'passwd' => 'required|string',
-            'uuid' => 'required|uuid',
+            'vmess_id' => 'nullable|uuid',
             'transfer_enable' => 'required|numeric|min:0',
             'enable' => 'required|boolean',
             'method' => 'required|exists:ss_config,name',

+ 2 - 0
app/Models/CouponLog.php

@@ -14,6 +14,8 @@ class CouponLog extends Model
 
     protected $table = 'coupon_log';
 
+    protected $guarded = [];
+
     public function coupon(): BelongsTo
     {
         return $this->belongsTo(User::class);

+ 5 - 0
app/Models/Goods.php

@@ -29,6 +29,11 @@ class Goods extends Model
         return $this->hasMany(Order::class);
     }
 
+    public function category(): BelongsTo
+    {
+        return $this->belongsTo(GoodsCategory::class, 'category_id');
+    }
+
     public function scopeType(Builder $query, int $type): Builder
     {
         return $query->whereType($type)->whereStatus(1)->orderByDesc('sort');

+ 20 - 0
app/Models/Node.php

@@ -37,6 +37,16 @@ class Node extends Model
         return $this->hasMany(NodeHeartbeat::class);
     }
 
+    public function latestHeartbeat(): HasOne
+    {
+        return $this->hasOne(NodeHeartbeat::class)->ofMany(
+            ['log_time' => 'max'],
+            function ($query) {
+                $query->where('log_time', '>=', strtotime(sysConfig('recently_heartbeat')));
+            }
+        );
+    }
+
     public function onlineIps(): HasMany
     {
         return $this->hasMany(NodeOnlineIp::class);
@@ -47,6 +57,16 @@ class Node extends Model
         return $this->hasMany(NodeOnlineLog::class);
     }
 
+    public function latestOnlineLog(): HasOne
+    {
+        return $this->hasOne(NodeOnlineLog::class)->ofMany(
+            ['log_time' => 'max'],
+            function ($query) {
+                $query->where('log_time', '>=', strtotime('-5 minutes'));
+            }
+        );
+    }
+
     public function userDataFlowLogs(): HasMany
     {
         return $this->hasMany(UserDataFlowLog::class);

+ 53 - 0
app/Models/NodeCertificate.php

@@ -2,6 +2,7 @@
 
 namespace App\Models;
 
+use Illuminate\Database\Eloquent\Casts\Attribute;
 use Illuminate\Database\Eloquent\Model;
 
 /**
@@ -12,4 +13,56 @@ class NodeCertificate extends Model
     protected $table = 'node_certificate';
 
     protected $guarded = [];
+
+    protected $appends = ['issuer', 'from', 'to'];
+
+    private $certInfo = null;
+
+    protected function getCertInfo(): ?array
+    {
+        if ($this->certInfo === null && $this->pem) {
+            $this->certInfo = openssl_x509_parse($this->pem) ?: false;
+        }
+
+        return $this->certInfo ?: null;
+    }
+
+    protected function issuer(): Attribute
+    {
+        return Attribute::make(
+            get: function () {
+                $certInfo = $this->getCertInfo();
+
+                return $certInfo ? ($certInfo['issuer']['O'] ?? null) : null;
+            }
+        );
+    }
+
+    protected function from(): Attribute
+    {
+        return Attribute::make(
+            get: function () {
+                $certInfo = $this->getCertInfo();
+                if ($certInfo && isset($certInfo['validFrom_time_t'])) {
+                    return date('Y-m-d', $certInfo['validFrom_time_t']);
+                }
+
+                return null;
+            }
+        );
+    }
+
+    protected function to(): Attribute
+    {
+        return Attribute::make(
+            get: function () {
+                $certInfo = $this->getCertInfo();
+                if ($certInfo && isset($certInfo['validTo_time_t'])) {
+                    return date('Y-m-d', $certInfo['validTo_time_t']);
+                }
+
+                return null;
+            }
+        );
+    }
 }

+ 0 - 6
app/Models/NodeHeartbeat.php

@@ -2,7 +2,6 @@
 
 namespace App\Models;
 
-use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Model;
 
 /**
@@ -15,9 +14,4 @@ class NodeHeartbeat extends Model
     protected $table = 'node_heartbeat';
 
     protected $guarded = [];
-
-    public function scopeRecently(Builder $query): Builder
-    {
-        return $query->where('log_time', '>=', strtotime('-'.sysConfig('recently_heartbeat').' minutes'))->latest('log_time');
-    }
 }

+ 3 - 1
app/Models/NotificationLog.php

@@ -16,6 +16,8 @@ class NotificationLog extends Model
     // 通知类型
     public function getTypeLabelAttribute(): string
     {
-        return config('common.notification.labels')[$this->type] ?? trans('common.status.unknown');
+        $type = config('common.notification.labels')[$this->type];
+
+        return trans("admin.system.notification.channel.{$type}");
     }
 }

+ 5 - 0
app/Models/User.php

@@ -113,6 +113,11 @@ class User extends Authenticatable
         return $this->HasMany(UserLoginLog::class);
     }
 
+    public function latestLoginLog(): HasOne
+    {
+        return $this->hasOne(UserLoginLog::class)->latestOfMany();
+    }
+
     public function subscribe(): HasOne
     {
         return $this->hasOne(UserSubscribe::class);

+ 2 - 2
app/Services/OrderService.php

@@ -71,7 +71,7 @@ class OrderService
     private function activatePackage(): bool
     { // 激活流量包
         if (self::$user->incrementData(self::$goods->traffic * MiB)) {
-            return Helpers::addUserTrafficModifyLog($this->order->user_id, self::$user->transfer_enable - self::$goods->traffic * MiB, self::$user->transfer_enable, trans('[:payment] plus the user’s purchased data plan.', ['payment' => $this->order->pay_way]));
+            return Helpers::addUserTrafficModifyLog($this->order->user_id, self::$user->transfer_enable - self::$goods->traffic * MiB, self::$user->transfer_enable, trans("[:payment] plus the user's purchased data plan.", ['payment' => $this->order->pay_way]));
         }
 
         return false;
@@ -95,7 +95,7 @@ class OrderService
         }
 
         if (self::$user->update($updateData)) {
-            return Helpers::addUserTrafficModifyLog($this->order->user_id, $oldData, self::$user->transfer_enable, trans('[:payment] plus the user’s purchased data plan.', ['payment' => $this->order->pay_way]), $this->order->id);
+            return Helpers::addUserTrafficModifyLog($this->order->user_id, $oldData, self::$user->transfer_enable, trans("[:payment] plus the user's purchased data plan.", ['payment' => $this->order->pay_way]), $this->order->id);
         }
 
         return false;

+ 11 - 16
app/Services/ProxyService.php

@@ -8,6 +8,7 @@ use App\Utils\Clients\Protocols\Text;
 use App\Utils\Clients\Protocols\URLSchemes;
 use Arr;
 use Exception;
+use Illuminate\Database\Eloquent\Collection;
 use ReflectionClass;
 use RuntimeException;
 
@@ -50,7 +51,7 @@ class ProxyService
         return self::$servers ?? [];
     }
 
-    public function getNodeList(?int $type = null, bool $isConfig = true): array
+    public function getNodeList(?int $type = null, bool $isConfig = true): array|Collection
     {
         $query = $this->getUser()->nodes()->whereIn('is_display', [2, 3]); // 获取这个账号可用节点
 
@@ -155,23 +156,17 @@ class ProxyService
             ...$node->profile,
         ];
 
-        if (! empty($node->profile['passwd']) && $node->port) { // 单端口使用中转的端口
-            $config += [
-                'port' => $node->port,
-                'protocol_param' => "$user->port:$user->passwd",
-            ];
+        if ($node->profile['passwd'] && $node->port) {
+            // 单端口使用中转的端口
+            $config['port'] = $node->port;
+            $config['protocol_param'] = $user->port.':'.$user->passwd;
         } else {
-            $config += [
-                'port' => $user->port,
-                'protocol_param' => $user->passwd,
-            ];
-
+            $config['port'] = $user->port;
+            $config['passwd'] = $user->passwd;
             if ($node->type === 1) {
-                $config += [
-                    'method' => $user->method,
-                    'protocol' => $user->protocol,
-                    'obfs' => $user->obfs,
-                ];
+                $config['method'] = $user->method;
+                $config['protocol'] = $user->protocol;
+                $config['obfs'] = $user->obfs;
             }
         }
 

+ 35 - 70
app/Utils/Helpers.php

@@ -19,27 +19,11 @@ class Helpers
 {
     private static array $denyPorts = [1068, 1109, 1434, 3127, 3128, 3129, 3130, 3332, 4444, 5554, 6669, 8080, 8081, 8082, 8181, 8282, 9996, 17185, 24554, 35601, 60177, 60179]; // 不生成的端口
 
-    public static function methodList()
-    { // 加密方式
-        return SsConfig::type(1)->get();
-    }
-
-    public static function protocolList()
-    { // 协议
-        return SsConfig::type(2)->get();
-    }
-
-    public static function obfsList()
-    { // 混淆
-        return SsConfig::type(3)->get();
-    }
-
     public static function makeSubscribeCode(): string
     { // 生成用户的订阅码
-        $code = Str::random();
-        if (UserSubscribe::whereCode($code)->exists()) {
-            $code = self::makeSubscribeCode();
-        }
+        do {
+            $code = Str::random();
+        } while (UserSubscribe::whereCode($code)->exists());
 
         return $code;
     }
@@ -91,8 +75,14 @@ class Helpers
         }
 
         if ($isRandPort) {
+            $attempts = 0;
             do {
                 $port = random_int($minPort, $maxPort);
+                $attempts++;
+                // 防止无限循环
+                if ($attempts > 100) {
+                    throw new RuntimeException('Unable to find available port after 100 attempts.');
+                }
             } while (in_array($port, $occupiedPorts, true));
         } else {
             $port = $minPort;
@@ -109,23 +99,38 @@ class Helpers
 
     public static function getDefaultMethod(): string
     { // 获取默认加密方式
-        $config = SsConfig::default()->type(1)->first();
+        static $method = null;
+
+        if ($method === null) {
+            $config = SsConfig::default()->type(1)->first();
+            $method = $config->name ?? 'aes-256-cfb';
+        }
 
-        return $config->name ?? 'aes-256-cfb';
+        return $method;
     }
 
     public static function getDefaultProtocol(): string
     { // 获取默认协议
-        $config = SsConfig::default()->type(2)->first();
+        static $protocol = null;
 
-        return $config->name ?? 'origin';
+        if ($protocol === null) {
+            $config = SsConfig::default()->type(2)->first();
+            $protocol = $config->name ?? 'origin';
+        }
+
+        return $protocol;
     }
 
     public static function getDefaultObfs(): string
     { // 获取默认混淆
-        $config = SsConfig::default()->type(3)->first();
+        static $obfs = null;
 
-        return $config->name ?? 'plain';
+        if ($obfs === null) {
+            $config = SsConfig::default()->type(3)->first();
+            $obfs = $config->name ?? 'plain';
+        }
+
+        return $obfs;
     }
 
     /**
@@ -141,17 +146,7 @@ class Helpers
      */
     public static function addNotificationLog(string $title, string $content, int $type, int $status = 1, ?string $error = null, ?string $msgId = null, string $address = 'admin'): int
     {
-        $log = new NotificationLog;
-        $log->type = $type;
-        $log->msg_id = $msgId;
-        $log->address = $address;
-        $log->title = $title;
-        $log->content = $content;
-        $log->status = $status;
-        $log->error = $error;
-        $log->save();
-
-        return $log->id;
+        return NotificationLog::create(['type' => $type, 'msg_id' => $msgId, 'address' => $address, 'title' => $title, 'content' => $content, 'status' => $status, 'error' => $error])->id;
     }
 
     /**
@@ -164,13 +159,7 @@ class Helpers
      */
     public static function addCouponLog(string $description, int $couponId, ?int $goodsId = null, ?int $orderId = null): bool
     {
-        $log = new CouponLog;
-        $log->coupon_id = $couponId;
-        $log->goods_id = $goodsId;
-        $log->order_id = $orderId;
-        $log->description = $description;
-
-        return $log->save();
+        return CouponLog::create(['coupon_id' => $couponId, 'goods_id' => $goodsId, 'order_id' => $orderId, 'description' => $description])->wasRecentlyCreated;
     }
 
     /**
@@ -185,16 +174,7 @@ class Helpers
      */
     public static function addUserCreditLog(int $userId, ?int $orderId, float|int $before, float|int $after, float|int $amount, ?string $description = null): bool
     {
-        $log = new UserCreditLog;
-        $log->user_id = $userId;
-        $log->order_id = $orderId;
-        $log->before = $before;
-        $log->after = $after;
-        $log->amount = $amount;
-        $log->description = $description;
-        $log->created_at = now();
-
-        return $log->save();
+        return UserCreditLog::create(['user_id' => $userId, 'order_id' => $orderId, 'before' => $before, 'after' => $after, 'amount' => $amount, 'description' => $description, 'created_at' => now()])->wasRecentlyCreated;
     }
 
     /**
@@ -208,14 +188,7 @@ class Helpers
      */
     public static function addUserTrafficModifyLog(int $userId, int $before, int $after, ?string $description = null, ?int $orderId = null): bool
     {
-        $log = new UserDataModifyLog;
-        $log->user_id = $userId;
-        $log->order_id = $orderId;
-        $log->before = $before;
-        $log->after = $after;
-        $log->description = $description;
-
-        return $log->save();
+        return UserDataModifyLog::create(['user_id' => $userId, 'order_id' => $orderId, 'before' => $before, 'after' => $after, 'description' => $description])->wasRecentlyCreated;
     }
 
     /**
@@ -230,15 +203,7 @@ class Helpers
      */
     public static function addMarketing(string $receiver, int $type, string $title, string $content, int $status = 1, ?string $error = null): bool
     {
-        $marketing = new Marketing;
-        $marketing->type = $type;
-        $marketing->receiver = $receiver;
-        $marketing->title = $title;
-        $marketing->content = $content;
-        $marketing->error = $error;
-        $marketing->status = $status;
-
-        return $marketing->save();
+        return Marketing::create(['type' => $type, 'receiver' => $receiver, 'title' => $title, 'content' => $content, 'error' => $error, 'status' => $status])->wasRecentlyCreated;
     }
 
     /**

+ 1 - 1
app/Utils/Payments/PaymentManager.php

@@ -70,7 +70,7 @@ class PaymentManager
 
     public static function getLabels(bool $history = false): array
     {
-        return cache()->rememberForever('payment_labels', function () use ($history) {
+        return cache()->rememberForever('payment_labels'.app()->getLocale(), function () use ($history) {
             if ($history) {
                 $labels = [
                     'bitpayx' => trans('admin.system.payment.channel.bitpayx'),

+ 0 - 23
app/View/Components/Alert.php

@@ -1,23 +0,0 @@
-<?php
-
-namespace App\View\Components;
-
-use Illuminate\View\Component;
-
-class Alert extends Component
-{
-    public $type;
-
-    public $message;
-
-    public function __construct($type, $message)
-    {
-        $this->type = $type;
-        $this->message = $message;
-    }
-
-    public function render()
-    {
-        return view('components.alert');
-    }
-}

+ 29 - 4
app/helpers.php

@@ -49,8 +49,11 @@ if (! function_exists('formatBytes')) {
 
 // 秒转时间
 if (! function_exists('formatTime')) {
-    function formatTime(int $seconds): string
+    function formatTime(?int $seconds): string
     {
+        if (! $seconds) {
+            return '-';
+        }
         $interval = CarbonInterval::seconds($seconds);
 
         return $interval->cascade()->forHumans();
@@ -101,10 +104,32 @@ if (! function_exists('string_urlsafe')) {
 if (! function_exists('localized_date')) {
     function localized_date($date): string
     {
-        $locale = app()->getLocale();
+        if (! $date) {
+            return '';
+        }
+
         $carbon = Carbon::parse($date);
-        $format = config("common.language.$locale.3") ?? 'Y-m-d';
+        $locale = app()->getLocale();
+        $carbon->setLocale($locale);
+
+        // 获取原始字符串表示
+        $dateStr = is_string($date) ? $date : $date->format('Y-m-d H:i:s');
+
+        // 使用正则检测精度
+        if (preg_match('/(\d{4}-\d{2}-\d{2}) (\d{2}):(\d{2}):(\d{2})/', $dateStr, $matches)) {
+            $hours = (int) $matches[2];
+            $minutes = (int) $matches[3];
+            $seconds = (int) $matches[4];
+
+            if ($seconds > 0) {
+                return $carbon->isoFormat('LL LTS'); // 显示完整时间
+            }
+
+            if ($minutes > 0 || $hours > 0) {
+                return $carbon->isoFormat('LL LT'); // 显示到分钟
+            }
+        }
 
-        return $carbon->format($format);
+        return $carbon->isoFormat('LL'); // 只显示日期
     }
 }

+ 1 - 3
composer.json

@@ -78,9 +78,7 @@
       "@php artisan package:discover --ansi"
     ],
     "post-update-cmd": [
-      "@php artisan vendor:publish --tag=laravel-assets --ansi --force",
-      "@php artisan ide-helper:generate",
-      "@php artisan ide-helper:meta"
+      "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
     ],
     "post-root-package-install": [
       "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""

+ 18 - 26
config/common.php

@@ -38,38 +38,30 @@ return [
             'telegram' => 'fa-telegram',
         ],
     ],
-
-    'network_status' => [
-        1 => '✔️正 常',
-        2 => '🛑 海外阻断',
-        3 => '🛑 国内阻断',
-        4 => '❌ 断 连',
-    ],
-
     'notification' => [
         'labels' => [
-            1 => '邮件',
-            2 => 'ServerChan',
-            3 => 'Bark',
-            4 => 'Telegram',
-            5 => '微信企业',
-            6 => 'TG酱',
-            7 => 'PushPlus',
-            8 => '爱语飞飞',
-            9 => 'PushDear',
-            10 => '钉钉',
+            1 => 'email',
+            2 => 'serverchan',
+            3 => 'bark',
+            4 => 'telegram',
+            5 => 'wechat',
+            6 => 'tg_chat',
+            7 => 'pushplus',
+            8 => 'iyuu',
+            9 => 'pushdeer',
+            10 => 'dingtalk',
         ],
     ],
 
     'language' => [
-        'de' => ['Deutsch', 'de', 'de-DE', 'd.m.Y'],
-        'en' => ['English', 'us', 'en-US', 'F d, Y'],
-        'fa' => ['فارسی', 'ir', 'fa-IR', 'Y/m/d'],
-        'ja' => ['日本語', 'jp', 'ja-JP', 'Y年m月d日'],
-        'ko' => ['한국어', 'kr', 'ko-KR', 'Y년 m월 d일'],
-        'vi' => ['Tiếng Việt', 'vn', 'vi-VN', 'd/m/Y'],
-        'zh_CN' => ['简体中文', 'cn', 'zh-CN', 'Y年m月d日'],
-        'ru' => ['Русский', 'ru', 'ru', 'd.m.Y'],
+        'de' => ['Deutsch', 'de', 'de-DE'],
+        'en' => ['English', 'us', 'en-US'],
+        'fa' => ['فارسی', 'ir', 'fa-IR'],
+        'ja' => ['日本語', 'jp', 'ja-JP'],
+        'ko' => ['한국어', 'kr', 'ko-KR'],
+        'ru' => ['Русский', 'ru', 'ru'],
+        'vi' => ['Tiếng Việt', 'vn', 'vi-VN'],
+        'zh_CN' => ['简体中文', 'cn', 'zh-CN'],
     ],
 
     'currency' => [

File diff suppressed because it is too large
+ 0 - 0
public/assets/bundle/app.min.css


+ 3 - 1
public/assets/global/js/Plugin/bootstrap-datepicker.js

@@ -39,7 +39,9 @@
       key: "getDefaults",
       value: function getDefaults() {
         return {
-          autoclose: true
+          language: document.documentElement.lang || 'en',
+          autoclose: true,
+          todayHighlight: true
         };
       }
     }]);

File diff suppressed because it is too large
+ 2 - 2
public/assets/global/vendor/bootstrap-datepicker/bootstrap-datepicker.min.js


+ 1 - 0
public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.de.min.js

@@ -0,0 +1 @@
+!function(a){a.fn.datepicker.dates.de={days:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],daysShort:["So","Mo","Di","Mi","Do","Fr","Sa"],daysMin:["So","Mo","Di","Mi","Do","Fr","Sa"],months:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthsShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],today:"Heute",monthsTitle:"Monate",clear:"Löschen",weekStart:1,format:"dd.mm.yyyy"}}(jQuery);

+ 1 - 0
public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.en.min.js

@@ -0,0 +1 @@
+!function(a){a.fn.datepicker.dates["en"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);

+ 1 - 0
public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.fa.min.js

@@ -0,0 +1 @@
+!function(a){a.fn.datepicker.dates.fa={days:["یک‌شنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنج‌شنبه","جمعه","شنبه","یک‌شنبه"],daysShort:["یک","دو","سه","چهار","پنج","جمعه","شنبه","یک"],daysMin:["ی","د","س","چ","پ","ج","ش","ی"],months:["ژانویه","فوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],monthsShort:["ژان","فور","مار","آور","مه","ژون","ژوی","اوت","سپت","اکت","نوا","دسا"],today:"امروز",clear:"پاک کن",weekStart:1,format:"yyyy/mm/dd"}}(jQuery);

+ 1 - 0
public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ja.min.js

@@ -0,0 +1 @@
+!function(a){a.fn.datepicker.dates.ja={days:["日曜","月曜","火曜","水曜","木曜","金曜","土曜"],daysShort:["日","月","火","水","木","金","土"],daysMin:["日","月","火","水","木","金","土"],months:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],monthsShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],today:"今日",format:"yyyy/mm/dd",titleFormat:"yyyy年mm月",clear:"クリア"}}(jQuery);

+ 1 - 0
public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ko.min.js

@@ -0,0 +1 @@
+!function(a){a.fn.datepicker.dates.ko={days:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"],daysShort:["일","월","화","수","목","금","토"],daysMin:["일","월","화","수","목","금","토"],months:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],monthsShort:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],today:"오늘",clear:"삭제",format:"yyyy-mm-dd",titleFormat:"yyyy년mm월",weekStart:0}}(jQuery);

+ 1 - 0
public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ru.min.js

@@ -0,0 +1 @@
+!function(a){a.fn.datepicker.dates.ru={days:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"],daysShort:["Вск","Пнд","Втр","Срд","Чтв","Птн","Суб"],daysMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],today:"Сегодня",clear:"Очистить",format:"dd.mm.yyyy",weekStart:1,monthsTitle:"Месяцы"}}(jQuery);

+ 1 - 0
public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.vi.min.js

@@ -0,0 +1 @@
+!function(a){a.fn.datepicker.dates.vi={days:["Chủ nhật","Thứ hai","Thứ ba","Thứ tư","Thứ năm","Thứ sáu","Thứ bảy"],daysShort:["CN","Thứ 2","Thứ 3","Thứ 4","Thứ 5","Thứ 6","Thứ 7"],daysMin:["CN","T2","T3","T4","T5","T6","T7"],months:["Tháng 1","Tháng 2","Tháng 3","Tháng 4","Tháng 5","Tháng 6","Tháng 7","Tháng 8","Tháng 9","Tháng 10","Tháng 11","Tháng 12"],monthsShort:["Th1","Th2","Th3","Th4","Th5","Th6","Th7","Th8","Th9","Th10","Th11","Th12"],today:"Hôm nay",clear:"Xóa",format:"dd/mm/yyyy"}}(jQuery);

+ 1 - 0
public/assets/global/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js

@@ -0,0 +1 @@
+!function(a){a.fn.datepicker.dates["zh-CN"]={days:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],daysShort:["周日","周一","周二","周三","周四","周五","周六"],daysMin:["日","一","二","三","四","五","六"],months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthsShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],today:"今天",monthsTitle:"选择月份",clear:"清除",format:"yyyy-mm-dd",titleFormat:"yyyy年mm月",weekStart:1}}(jQuery);

+ 271 - 0
public/assets/js/config/admin.js

@@ -0,0 +1,271 @@
+/**
+ * 自动填充表单字段
+ * @param {Object} data - 要填充的数据对象
+ * @param {Object} options - 配置选项
+ * @param {string} options.formSelector - 表单选择器,默认为 'form'
+ * @param {Array} options.skipFields - 跳过的字段名
+ */
+function autoPopulateForm(data, options = {}) {
+    if (!data) return;
+
+    const defaults = {
+        formSelector: "form", skipFields: []
+    };
+
+    const settings = { ...defaults, ...options };
+
+    // 获取表单内的所有输入元素
+    const $form = $(settings.formSelector);
+    const $inputs = $form.find("input, select, textarea");
+
+    $inputs.each(function() {
+        const $element = $(this);
+        const name = $element.attr("name");
+        const id = $element.attr("id");
+
+        // 使用 name 作为主要查找键,id 作为备选
+        const fieldKey = name || id;
+
+        // 跳过没有名称或ID的元素,以及明确指定跳过的字段
+        if (!fieldKey || settings.skipFields.includes(fieldKey)) {
+            return; // continue to next element
+        }
+
+        // 处理数组字段(如 roles[])
+        const cleanFieldKey = fieldKey.replace(/\[\]$/, "");
+
+        // 从数据对象中获取对应值
+        let value = getObjectValue(data, cleanFieldKey);
+
+        // 如果找不到值,尝试使用原始字段名
+        if (value === undefined && cleanFieldKey !== fieldKey) {
+            value = getObjectValue(data, fieldKey);
+        }
+
+        if (value !== undefined) {
+            // 根据元素类型设置值
+            setElementValue($element, value);
+        }
+    });
+}
+
+/**
+ * 收集表单数据
+ * @param {string|Object} formSelector - 表单选择器或jQuery对象
+ * @param {Object} options - 配置选项
+ * @param {Array} options.excludeFields - 排除的字段
+ * @param {Array} options.removeEmpty - 过滤掉空字符串
+ * @returns {Object} 表单数据对象
+ */
+function collectFormData(formSelector, options = {}) {
+    const $form = typeof formSelector === "string" ? $(formSelector) : formSelector;
+    const defaults = {
+        excludeFields: [],
+        removeEmpty: false,
+    };
+
+    const settings = { ...defaults, ...options };
+    const formData = {};
+
+    // 收集所有表单元素的值
+    $form.find('input:not([hidden]), select, textarea').each(function() {
+        const $element = $(this);
+        const name = $element.attr('name');
+        const type = $element.attr('type');
+        const tagName = $element.prop('tagName').toLowerCase();
+
+        // 跳过没有名称的字段和排除的字段
+        if (!name || settings.excludeFields.includes(name)) {
+            return;
+        }
+
+        // 处理数组字段
+        const isArrayField = name.endsWith('[]');
+        const fieldName = isArrayField ? name.slice(0, -2) : name;
+
+        let value;
+
+        // 标准值收集
+        switch (tagName) {
+            case 'input':
+                switch (type) {
+                    case 'checkbox':
+                        if (isArrayField) {
+                            if (!formData[fieldName]) formData[fieldName] = [];
+                            if ($element.is(':checked')) {
+                                formData[fieldName].push($element.val());
+                            }
+                        } else if($element.attr("data-plugin") === "switchery"){
+                            value = $element.is(':checked') ? 1 : 0;
+                        }
+                        else{
+                            value = $element.is(':checked') ? $element.val() : null;
+                        }
+                        break;
+                    case 'radio':
+                        if ($element.is(':checked')) {
+                            value = $element.val();
+                        }
+                        break;
+                    default:
+                        // 特殊处理 datepicker 元素
+                        if ($element.attr("data-plugin") === "datepicker" || $element.parent().attr("data-plugin") === "datepicker" || $element.parent().hasClass("input-daterange")) {
+                            value = formatDateToYMD($element.datepicker('getDate'));
+                        } else if ($element.attr("data-plugin") === "asColorPicker") {
+                            // asColorPicker 取值
+                            value = $element.asColorPicker('val');
+                        } else {
+                            value = $element.val();
+                        }
+                }
+                break;
+
+            case 'select':
+                if ($element.prop('multiple')) {
+                    value = $element.val() || [];
+                } else {
+                    value = $element.val();
+                }
+                break;
+
+            case 'textarea':
+                value = $element.val();
+                break;
+        }
+
+        // 处理数组字段
+        if (isArrayField) {
+            if (!formData[fieldName]) formData[fieldName] = [];
+            if (value !== undefined && value !== null) {
+                if (Array.isArray(value)) {
+                    formData[fieldName] = [...formData[fieldName], ...value];
+                } else {
+                    formData[fieldName].push(value);
+                }
+            }
+        } else if (value !== undefined) {
+            // 避免覆盖已设置的值(如radio按钮)
+            if (formData[fieldName] === undefined || type !== 'radio' || value !== null) {
+                formData[fieldName] = value;
+            }
+        }
+    });
+
+    // 去除空值
+    if (settings.removeEmpty) {
+        return Object.fromEntries(
+            Object.entries(formData).filter(([_, value]) => {
+                if (Array.isArray(value)) {
+                    return value.length > 0;
+                }
+                return value !== "" && value !== null && value !== undefined;
+            })
+        );
+    }
+
+    return formData;
+}
+
+
+/**
+ * 格式化日期为 Y-m-d 格式
+ */
+function formatDateToYMD(date) {
+    if (!date) {
+        return '';
+    }
+
+    const year = date.getFullYear();
+    const month = String(date.getMonth() + 1).padStart(2, '0');
+    const day = String(date.getDate()).padStart(2, '0');
+    return `${year}-${month}-${day}`;
+}
+
+/**
+ * 从嵌套对象中获取值
+ * @param {Object} obj - 源对象
+ * @param {string} path - 属性路径,支持点号分隔
+ * @returns {*} 属性值或 undefined
+ */
+function getObjectValue(obj, path) {
+    if (!obj || !path) return undefined;
+
+    // 处理点号分隔的路径
+    const keys = path.split(".");
+    let current = obj;
+
+    for (let i = 0; i < keys.length; i++) {
+        if (current === null || current === undefined) {
+            return undefined;
+        }
+        current = current[keys[i]];
+    }
+
+    return current;
+}
+
+/**
+ * 设置元素值
+ * @param {jQuery} $element - jQuery元素对象
+ * @param {*} value - 要设置的值
+ */
+function setElementValue($element, value) {
+    const type = $element.attr("type");
+    const tagName = $element.prop("tagName").toLowerCase();
+
+    switch (tagName) {
+        case "input":
+            switch (type) {
+                case "radio":
+                    $element.filter(`[value="${value}"]`).click();
+                    break;
+                case "checkbox":
+                    if (Array.isArray(value)) {
+                        $element.each(function() {
+                            const $this = $(this);
+                            const isChecked = value.includes($this.val());
+                            if ($this.is(':checked') !== isChecked) {
+                                $this.click();
+                            }
+                        });
+                    } else {
+                        const shouldBeChecked = value === true || value === 1 || value === "1";
+                        if ($element.is(':checked') !== shouldBeChecked) {
+                            $element.click();
+                        }
+                    }
+                    break;
+                default:
+                    // 特殊处理 datepicker 元素
+                    if ($element.attr("data-plugin") === "datepicker" || $element.parent().attr("data-plugin") === "datepicker" || $element.parent().hasClass("input-daterange")) {
+                        $element.datepicker("setDate", new Date(value));
+                        return;
+                    }
+                    
+                    // 特殊处理 asColorPicker 元素
+                    if ($element.attr("data-plugin") === "asColorPicker") {
+                        $element.asColorPicker('val', value);
+                        return;
+                    }
+
+                    $element.val(value);
+            }
+            break;
+
+        case "select":
+            if ($element.attr("data-plugin") === "multiSelect") {
+                $element.multiSelect('select', value);
+            }else if ($element.attr("data-plugin") === "selectpicker") {
+                $element.selectpicker("val", value);
+                $element.selectpicker("refresh");
+            } else {
+                $element.val(value);
+            }
+            break;
+
+        case "textarea":
+            $element.val(value);
+            break;
+    }
+}
+

+ 513 - 0
public/assets/js/config/common.js

@@ -0,0 +1,513 @@
+/**
+ * ProxyPanel 通用JavaScript函数
+ */
+
+const jsRoute = (template, id) => template.replace("PLACEHOLDER", id);
+
+/**
+ * 基础AJAX请求 - 仅提供最基础的功能
+ * @param {Object} options - 请求选项
+ * @param {string} options.url - 请求URL
+ * @param {string} options.method - HTTP方法 (GET, POST, PUT, DELETE, PATCH)
+ * @param {Object} options.data - 请求数据
+ * @param {string} options.dataType - 预期服务器响应数据类型
+ * @param {function} options.beforeSend - 请求发送前回调
+ * @param {function} options.success - 请求成功回调
+ * @param {function} options.error - 请求失败回调
+ * @param {function} options.complete - 请求完成后回调(无论成功失败)
+ */
+function ajaxRequest(options) {
+    // 默认值
+    const defaults = {
+        method: "GET", dataType: "json", data: {}
+    };
+    const s = { ...defaults, ...options };
+
+    if (["POST", "PUT", "DELETE", "PATCH"].includes(s.method)) {
+        if (typeof CSRF_TOKEN !== "undefined" && (!s.data || !s.data._token)) {
+            s.data = { ...(s.data || {}), _token: CSRF_TOKEN };
+        }
+    }
+
+    // loading 包装(在这里集中处理,避免重复)
+    if (s.loadingSelector) {
+        const origBefore = s.beforeSend;
+        s.beforeSend = function (xhr) {
+            $(s.loadingSelector).show();
+            if (typeof origBefore === "function") origBefore(xhr);
+        };
+        const origComplete = s.complete;
+        s.complete = function (xhr, status) {
+            $(s.loadingSelector).hide();
+            if (typeof origComplete === "function") origComplete(xhr, status);
+        };
+    }
+
+    return $.ajax({
+        url: s.url,
+        method: s.method,
+        data: s.data,
+        dataType: s.dataType,
+        beforeSend: s.beforeSend,
+        success: s.success,
+        error: s.error,
+        complete: s.complete
+    });
+}
+
+function ajaxMethod(method, url, data = {}, options = {}) {
+    if (!options.success) options.success = ret => handleResponse(ret);
+    return ajaxRequest({ url, method, data, ...options });
+}
+
+const ajaxGet    = (url, data = {}, options = {}) => ajaxRequest({url:url, data: data, ...options});
+const ajaxPost   = (url, data = {}, options = {}) => ajaxMethod("POST", url, data, options);
+const ajaxPut    = (url, data = {}, options = {}) => ajaxMethod("PUT", url, data, options);
+const ajaxDelete = (url, data = {}, options = {}) => ajaxMethod("DELETE", url, data, options);
+const ajaxPatch  = (url, data = {}, options = {}) => ajaxMethod("PATCH", url, data, options);
+
+/**
+ * 处理加载指示器的辅助函数
+ * @param {Object} settings - AJAX设置对象
+ * @param {string} settings.loadingSelector - 加载指示器选择器 (提供该参数即表示需要显示加载指示器)
+ * @returns {Object} 修改后的设置对象
+ */
+function handleLoadingIndicator(settings) {
+    // 如果提供了loadingSelector,则显示加载指示器
+    if (settings.loadingSelector) {
+        const originalBeforeSend = settings.beforeSend;
+        settings.beforeSend = function (xhr) {
+            $(settings.loadingSelector).show();
+            if (originalBeforeSend) originalBeforeSend(xhr);
+        };
+
+        const originalComplete = settings.complete;
+        settings.complete = function (xhr, status) {
+            $(settings.loadingSelector).hide();
+            if (originalComplete) originalComplete(xhr, status);
+        };
+    }
+    return settings;
+}
+
+/**
+ * 显示确认对话框
+ * @param {Object} options - 对话框选项
+ * @param {string} options.title - 对话框标题
+ * @param {string} options.text - 对话框文本内容
+ * @param {string} options.html - 对话框HTML内容 (优先级高于text)
+ * @param {string} options.icon - 图标类型 (success, error, warning, info, question)
+ * @param {string} options.cancelButtonText - 取消按钮文本
+ * @param {string} options.confirmButtonText - 确认按钮文本
+ * @param {function} options.onConfirm - 确认回调函数
+ * @param {function} options.onCancel - 取消回调函数
+ */
+function showConfirm(options) {
+    // 默认值
+    const defaults = {
+        icon: "question",
+        allowEnterKey: false,
+        showCancelButton: true,
+        cancelButtonText: typeof TRANS !== "undefined" ? TRANS.btn.close : "Cancel",
+        confirmButtonText: typeof TRANS !== "undefined" ? TRANS.btn.confirm : "Confirm"
+    };
+
+    // 如果没有提供title,使用默认title
+    if (!options.title) {
+        options.title = typeof TRANS !== "undefined" ? TRANS.confirm_title : "Confirm";
+    }
+
+    // 如果没有提供文本内容,使用默认文本
+    if (!options.html && !options.text) {
+        options.text = typeof TRANS !== "undefined" ? TRANS.confirm_action : "Are you sure you want to perform this action?";
+    }
+
+    // 保存回调函数并从选项中移除它们,因为SweetAlert2不接受这些参数
+    const onConfirm = options.onConfirm;
+    const onCancel = options.onCancel;
+    delete options.onConfirm;
+    delete options.onCancel;
+
+    // 合并默认值和用户选项
+    const alertOptions = {...defaults, ...options};
+
+    swal.fire(alertOptions).then((result) => {
+        if (result.value && typeof onConfirm === "function") {
+            onConfirm(result);
+        } else if (!result.value && typeof onCancel === "function") {
+            onCancel(result);
+        }
+    });
+}
+
+/**
+ * 显示操作结果提示
+ * @param {Object} options - 提示选项
+ * @param {string} options.title - 提示标题
+ * @param {string} options.message - 提示消息
+ * @param {string} options.icon - 图标类型 (success, error, warning, info)
+ * @param {boolean} options.autoClose - 是否自动关闭
+ * @param {number} options.timer - 自动关闭时间 (毫秒)
+ * @param {boolean} options.showConfirmButton - 是否显示确认按钮
+ * @param {string} options.html - HTML内容
+ * @param {function} options.callback - 关闭后回调
+ */
+function showMessage(options) {
+    const alertOptions = {
+        title: options.title || options.message,
+        icon: options.icon || "info",
+        html: options.html,
+        showConfirmButton: options.showConfirmButton !== undefined ? options.showConfirmButton : !options.autoClose
+    };
+
+    // 修改逻辑:如果autoClose没有被明确设置为false,并且showConfirmButton没有被明确设置为true,则自动关闭
+    if (options.autoClose !== false && options.showConfirmButton !== true) {
+        alertOptions.timer = options.timer || 1500;
+    }   
+
+    // 如果同时提供了title和message,并且没有html,则将message作为html内容显示
+    if (options.title && options.message && !options.html) {
+        alertOptions.text = options.message;
+    }
+
+    swal.fire(alertOptions).then(() => {
+        if (typeof options.callback === "function") {
+            options.callback();
+        }
+    });
+}
+
+/**
+ * 通用错误处理函数,支持三种错误显示方式
+ * @param {Object} xhr - AJAX响应对象
+ * @param {Object} options - 错误处理选项
+ * @param {string} options.validation - 验证错误显示类型: 'field', 'element', 'swal'
+ * @param {string} options.default - 默认错误显示类型: 'swal'(默认), 'field', 'element'
+ * @param {string|Object} options.form - 表单选择器或jQuery对象 (type='field'时使用)
+ * @param {string} options.element - 错误信息显示元素的选择器 (type='element'时使用)
+ * @param {function} options.onError - 自定义错误处理回调
+ */
+function handleErrors(xhr, options = {}) {
+    const defaults = {
+        validation: 'field', // 验证错误默认使用字段显示
+        default: 'swal' // 其他错误默认使用swal显示
+    };
+
+    const settings = {...defaults, ...options};
+
+    // 如果有自定义错误处理回调,优先执行
+    if (typeof settings.onError === "function") {
+        return settings.onError(xhr);
+    }
+
+    // 处理验证错误 (422)
+    if (xhr.status === 422 && xhr.responseJSON && xhr.responseJSON.errors) {
+        const errors = xhr.responseJSON.errors;
+
+        switch (settings.validation) {
+            case 'field':
+                // 在表单字段上显示错误 (添加is-invalid类和错误信息)
+                if (settings.form) {
+                    const $form = typeof settings.form === "string" ? $(settings.form) : settings.form;
+                    // 清除之前的错误状态
+                    $form.find(".is-invalid").removeClass("is-invalid");
+                    $form.find(".invalid-feedback").remove();
+
+                    // 显示每个字段的错误
+                    Object.keys(errors).forEach(field => {
+                        const $field = $form.find(`[name="${field}"]`);
+                        if ($field.length) {
+                            // 添加错误样式
+                            $field.addClass("is-invalid");
+
+                            // 添加错误消息
+                            const errorMessage = errors[field][0];
+                            const $feedback = $("<div>").addClass("invalid-feedback").text(errorMessage);
+                            $field.after($feedback);
+                        }
+                    });
+
+                    // 滚动到第一个错误
+                    const $firstError = $form.find(".is-invalid").first();
+                    if ($firstError.length) {
+                        $("html, body").animate({
+                            scrollTop: $firstError.offset().top - 100
+                        }, 500);
+                    }
+                }
+                break;
+
+            case 'element':
+                // 在指定元素中显示错误列表
+                if (settings.element) {
+                    let errorStr = "";
+                    $.each(errors, function (index, values) {
+                        // values 是一个数组,可能包含多个错误消息
+                        $.each(values, function (i, value) {
+                            errorStr += "<li>" + value + "</li>";
+                        });
+                    });
+                    $(settings.element).html("<ul>" + errorStr + "</ul>").show();
+                }
+                break;
+
+            case 'swal':
+            default:
+                // 使用swal显示错误
+                let errorStr = "";
+                $.each(errors, function (index, values) {
+                    // values 是一个数组,可能包含多个错误消息
+                    $.each(values, function (i, value) {
+                        errorStr += "<li>" + value + "</li>";
+                    });
+                });
+                showMessage({
+                    title: xhr.responseJSON.message || (typeof TRANS !== "undefined" ? TRANS.operation_failed : "Operation failed"), html: "<ul>" + errorStr + "</ul>", icon: "error"
+                });
+                break;
+        }
+        return true;
+    }
+
+    // 处理其他类型的错误
+    const errorMessage = xhr.responseJSON?.message || xhr.statusText || (typeof TRANS !== "undefined" ? TRANS.request_failed : "Request failed");
+
+    switch (settings.default) {
+        case 'element':
+            if (settings.element) {
+                $(settings.element).html(errorMessage).show();
+            }
+            break;
+
+        case 'field':
+            // 对于非验证错误,如果指定了表单,可以在表单顶部显示错误
+            if (settings.form) {
+                const $form = typeof settings.form === "string" ? $(settings.form) : settings.form;
+                // 可以根据需要实现特定的显示方式
+                showMessage({
+                    title: errorMessage, icon: "error"
+                });
+            }
+            break;
+
+        case 'swal':
+        default:
+            showMessage({
+                title: errorMessage, icon: "error"
+            });
+            break;
+    }
+
+    return false;
+}
+
+/**
+ * 处理AJAX响应结果
+ * @param {Object} response - AJAX响应
+ * @param {Object} options - 处理选项
+ * @param {boolean} options.reload - 成功后是否刷新页面
+ * @param {string} options.redirectUrl - 成功后重定向URL
+ * @param {function} options.onSuccess - 成功回调
+ * @param {function} options.onError - 错误回调
+ * @param {boolean} options.showMessage - 是否显示消息提示
+ * @returns {Object} 原始响应
+ */
+function handleResponse(response, options = {}) {
+    const defaults = {
+        reload: true, showMessage: true
+    };
+
+    const settings = {...defaults, ...options};
+
+    if (response.status === "success") {
+        if (settings.showMessage) {
+            showMessage({
+                title: response.message || (typeof TRANS !== "undefined" ? TRANS.operation_success : "Operation successful"), icon: "success", showConfirmButton: false, callback: function () {
+                    if (typeof settings.onSuccess === "function") {
+                        settings.onSuccess(response);
+                    } else if (settings.redirectUrl) {
+                        window.location.href = settings.redirectUrl;
+                    } else if (settings.reload) {
+                        window.location.reload();
+                    }
+                }
+            });
+        } else {
+            if (typeof settings.onSuccess === "function") {
+                settings.onSuccess(response);
+            } else if (settings.redirectUrl) {
+                window.location.href = settings.redirectUrl;
+            } else if (settings.reload) {
+                window.location.reload();
+            }
+        }
+    } else {
+        if (settings.showMessage) {
+            showMessage({
+                title: response.message || (typeof TRANS !== "undefined" ? TRANS.operation_failed : "Operation failed"), icon: "error", showConfirmButton: true, callback: function () {
+                    if (typeof settings.onError === "function") {
+                        settings.onError(response);
+                    }
+                }
+            });
+        } else if (typeof settings.onError === "function") {
+            settings.onError(response);
+        }
+    }
+
+    return response;
+}
+
+/**
+ * 重置搜索表单
+ */
+function resetSearchForm() {
+    window.location.href = window.location.href.split("?")[0];
+}
+
+/**
+ * 初始化表单选择器变化时自动提交
+ * @param {string} formSelector - 表单选择器
+ * @param {string} excludeSelector - 排除的选择器
+ */
+function initAutoSubmitSelects(formSelector = "form:not(.modal-body form)", excludeSelector = ".modal-body select") {
+    $(formSelector).on("submit", function () {
+        const $form = $(this);
+        $form.find("input:not([type=\"submit\"]), select").filter(function () {
+            return this.value === "";
+        }).prop("disabled", true);
+
+        // 恢复 disabled 要使用闭包 $form
+        setTimeout(function () {
+            $form.find(":disabled").prop("disabled", false);
+        }, 0);
+    });
+
+    // 只对非排除选择器的 select 绑定 change 自动提交
+    $(`select`).not(excludeSelector).on("change", function () {
+        $(this).closest("form").trigger("submit");
+    });
+}
+
+/**
+ * 复制文本到剪贴板
+ * @param {string} text - 要复制的文本
+ * @param {Object} options - 选项
+ * @param {boolean} options.showMessage - 是否显示消息提示
+ * @param {string} options.successMessage - 复制成功消息
+ * @param {string} options.errorMessage - 复制失败消息
+ * @param {function} options.onSuccess - 复制成功回调
+ * @param {function} options.onError - 复制失败回调
+ * @returns {boolean} 是否复制成功
+ */
+function copyToClipboard(text, options = {}) {
+    const defaults = {
+        showMessage: true,
+        successMessage: typeof TRANS !== "undefined" ? TRANS.copy.success : "Copy successful",
+        errorMessage: typeof TRANS !== "undefined" ? TRANS.copy.failed : "Copy failed, please copy manually"
+    };
+
+    const settings = {...defaults, ...options};
+
+    if (navigator.clipboard && window.isSecureContext) {
+        navigator.clipboard.writeText(text).then(() => {
+            if (settings.showMessage) {
+                showMessage({
+                    title: settings.successMessage, icon: "success", autoClose: true
+                });
+            }
+            if (typeof settings.onSuccess === "function") {
+                settings.onSuccess();
+            }
+        }).catch(err => {
+            console.error("Copy failed: ", err);
+            if (settings.showMessage) {
+                showMessage({
+                    title: settings.errorMessage, icon: "error"
+                });
+            }
+            if (typeof settings.onError === "function") {
+                settings.onError(err);
+            }
+        });
+        return true;
+    } else {
+        const textarea = document.createElement("textarea");
+        textarea.value = text;
+        textarea.style.position = "fixed";
+        textarea.style.opacity = 0;
+        document.body.appendChild(textarea);
+        textarea.select();
+
+        let success = false;
+        try {
+            success = document.execCommand("copy");
+            if (success && settings.showMessage) {
+                showMessage({
+                    title: settings.successMessage, icon: "success", autoClose: true
+                });
+            }
+            if (success && typeof settings.onSuccess === "function") {
+                settings.onSuccess();
+            }
+        } catch (err) {
+            console.error("Unable to copy text: ", err);
+            if (settings.showMessage) {
+                showMessage({
+                    title: settings.errorMessage, icon: "error"
+                });
+            }
+            if (typeof settings.onError === "function") {
+                settings.onError(err);
+            }
+        }
+
+        document.body.removeChild(textarea);
+        return success;
+    }
+}
+
+/**
+ * 通用删除确认功能
+ * @param {string} url - 删除请求的URL
+ * @param {string} attribute - 要删除的实体属性名称
+ * @param {string} name - 要删除项目的ID或名称
+ * @param {Object} options - 附加选项
+ * @param {string} options.title - 自定义标题
+ * @param {string} options.text - 自定义文本内容
+ * @param {string} options.html - 自定义HTML内容
+ * @param {string} options.icon - 自定义图标 (success, error, warning, info, question)
+ * @param {function} options.callback - 成功后的回调函数 (等同于onSuccess)
+ * @param {function} options.onSuccess - 成功后的回调函数
+ * @param {function} options.onError - 错误后的回调函数
+ * @param {boolean} options.reload - 成功后是否刷新页面
+ * @param {string} options.redirectUrl - 成功后重定向URL
+ */
+function confirmDelete(url, name, attribute, options = {}) {
+    // 获取翻译文本
+    const defaults = {
+        titleMessage: typeof TRANS !== "undefined" ? TRANS.warning : "Warning",
+    };
+
+    let text = options.text;
+    if (!text && typeof TRANS !== "undefined" && TRANS.confirm && TRANS.confirm.delete) {
+        text = TRANS.confirm.delete.replace("{attribute}", attribute || "").replace("{name}", name || "");
+    } else if (!text) {
+        text = typeof TRANS !== "undefined" ? TRANS.confirm_delete.replace("{attribute}", attribute || "").replace("{name}", name || "") : `Are you sure you want to delete {attribute} [{name}]?`.replace("{attribute}", attribute || "").replace("{name}", name || "");
+    }
+
+    // 显示确认对话框
+    showConfirm({
+        title: options.title || defaults.titleMessage, icon: options.icon || "warning", text: text, html: options.html, onConfirm: function () {
+            // 使用ajaxDelete函数发送删除请求
+            ajaxDelete(url,{}, {
+                success: function (response) {
+                    // 使用通用响应处理
+                    handleResponse(response, {
+                        reload: options.reload !== false, redirectUrl: options.redirectUrl, onSuccess: options.callback || options.onSuccess, onError: options.onError
+                    });
+                }
+            });
+        }
+    });
+}

+ 72 - 72
resources/lang/de.json

@@ -1,47 +1,47 @@
 {
   "(and :count more error)": "(und :count weiterer Fehler)",
-  "(and :count more errors)": "(und :count weitere Fehler)",
-  "----「:job」Completed, Used :time seconds ----": "----「:job」abgeschlossen, benötigte :time Sekunden ----",
-  "[Auto Task] Blocked service: Abnormal traffic within 1 hour": "[Automatische Aufgabe] Dienst blockiert: Ungewöhnlicher Datenverkehr innerhalb von 1 Stunde",
-  "[Auto Task] Blocked service: Run out of traffic": "[Automatische Aufgabe] Dienst blockiert: Datenvolumen aufgebraucht",
-  "[Auto Task] Blocked Subscription: Subscription with abnormal requests within 24 hours": "[Automatische Aufgabe] Abonnement blockiert: Ungewöhnliche Anfragen innerhalb von 24 Stunden",
-  "[Auto Task] Unblocked Service: Account ban expired": "[Automatische Aufgabe] Dienst freigegeben: Kontosperre abgelaufen",
-  "[Auto Task] Unblocked Service: Account has available data traffic": "[Automatische Aufgabe] Dienst freigegeben: Konto hat verfügbares Datenvolumen",
-  "[Daily Task] Account Expiration: Block Login & Clear Account": "[Tägliche Aufgabe] Konto abgelaufen: Login blockieren und Konto löschen",
-  "[Daily Task] Account Expiration: Stop Service": "[Tägliche Aufgabe] Konto abgelaufen: Dienst stoppen",
-  "[Daily Task] Reset Account Traffic, Next Reset Date: :date": "[Tägliche Aufgabe] Kontodatenvolumen zurücksetzen, nächstes Rücksetzdatum: :date",
-  "[Service Timer] Service Expiration": "[Dienst-Timer] Dienst abgelaufen",
+  "(and :count more errors)": "(und weitere :count Fehler)",
+  "----「:job」Completed, Used :time seconds ----": "----「:job」Abgeschlossen, benötigte Zeit :time Sekunden ----",
+  "[Auto Task] Blocked service: Abnormal traffic within 1 hour": "[Automatische Aufgabe] Service gesperrt: Abnormaler Traffic innerhalb 1 Stunde",
+  "[Auto Task] Blocked service: Run out of traffic": "[Automatische Aufgabe] Service gesperrt: Traffic aufgebraucht",
+  "[Auto Task] Blocked Subscription: Subscription with abnormal requests within 24 hours": "[Automatische Aufgabe] Abonnement gesperrt: Abnormale Anfragen innerhalb 24 Stunden",
+  "[Auto Task] Unblocked Service: Account ban expired": "[Automatische Aufgabe] Service entsperrt: Kontosperre abgelaufen",
+  "[Auto Task] Unblocked Service: Account has available data traffic": "[Automatische Aufgabe] Service entsperrt: Konto hat verfügbaren Traffic",
+  "[Daily Task] Account Expiration: Block Login & Clear Account": "[Tägliche Aufgabe] Konto abgelaufen: Login gesperrt und Kontodaten gelöscht",
+  "[Daily Task] Account Expiration: Stop Service": "[Tägliche Aufgabe] Konto abgelaufen: Service gestoppt",
+  "[Daily Task] Reset Account Traffic, Next Reset Date: :date": "[Tägliche Aufgabe] Konto-Traffic zurückgesetzt, nächstes Reset-Datum: :date",
+  "[Service Timer] Service Expiration": "[Service-Timer] Service abgelaufen",
   "A Timeout Occurred": "Ein Timeout ist aufgetreten",
   "Accepted": "Akzeptiert",
   "All rights reserved.": "Alle Rechte vorbehalten.",
   "Already Reported": "Bereits gemeldet",
-  "Bad Gateway": "Schlechtes Tor",
-  "Bad Request": "Schlechte Anfrage",
-  "Bandwidth Limit Exceeded": "Bandbreitengrenze überschritten",
-  "Client Closed Request": "Client-Closed-Request",
+  "Bad Gateway": "Fehlerhaftes Gateway",
+  "Bad Request": "Fehlerhafte Anfrage",
+  "Bandwidth Limit Exceeded": "Bandbreitenlimit überschritten",
+  "Client Closed Request": "Client hat Anfrage geschlossen",
   "Conflict": "Konflikt",
   "Connection Closed Without Response": "Verbindung ohne Antwort geschlossen",
   "Connection Timed Out": "Verbindung abgelaufen",
-  "Continue": "Weiter",
+  "Continue": "Fortfahren",
   "Created": "Erstellt",
   "Daily Data Usage Report": "Täglicher Datenverbrauchsbericht",
-  "Expectation Failed": "Erwartung gescheitert",
+  "Expectation Failed": "Erwartung fehlgeschlagen",
   "Failed Dependency": "Fehlgeschlagene Abhängigkeit",
-  "Forbidden": "Verboten",
+  "Forbidden": "Zugriff verweigert",
   "Found": "Gefunden",
-  "Gateway Timeout": "Gateway-Zeitüberschreitung",
-  "Go to page :page": "Gehe zur Seite :page",
-  "Gone": "Gegangen",
+  "Gateway Timeout": "Gateway-Timeout",
+  "Go to page :page": "Gehe zu Seite :page",
+  "Gone": "Nicht verfügbar",
   "Hello!": "Hallo!",
-  "HTTP Version Not Supported": "HTTP Version nicht unterstützt",
+  "HTTP Version Not Supported": "HTTP-Version nicht unterstützt",
   "I'm a teapot": "Ich bin eine Teekanne",
-  "If you did not create an account, no further action is required.": "Wenn Sie kein Konto erstellt haben, sind keine weiteren Handlungen nötig.",
-  "If you did not request a password reset, no further action is required.": "Wenn Sie kein Zurücksetzen des Passworts beantragt haben, sind keine weiteren Handlungen nötig.",
-  "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Sollten Sie Schwierigkeiten haben, die Schaltfläche \":actionText\" zu klicken, kopieren Sie den nachfolgenden Link\n in Ihre Adresszeile des Browsers.",
+  "If you did not create an account, no further action is required.": "Wenn Sie kein Konto erstellt haben, ist keine weitere Aktion erforderlich.",
+  "If you did not request a password reset, no further action is required.": "Wenn Sie kein Passwort-Reset angefordert haben, ist keine weitere Aktion erforderlich.",
+  "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Falls Sie Probleme beim Klicken auf die Schaltfläche \":actionText\" haben, kopieren Sie den folgenden Link in Ihren Browser:",
   "IM Used": "IM verwendet",
-  "Insufficient Storage": "Ungenügende Speicherung",
+  "Insufficient Storage": "Unzureichender Speicher",
   "Internal Server Error": "Interner Serverfehler",
-  "Invalid JSON was returned from the route.": "Von der Route wurde ein ungültiger JSON-Code zurückgegeben.",
+  "Invalid JSON was returned from the route.": "Ungültiges JSON wurde von der Route zurückgegeben.",
   "Invalid SSL Certificate": "Ungültiges SSL-Zertifikat",
   "Invoice Detail": "Rechnungsdetails",
   "Length Required": "Länge erforderlich",
@@ -52,90 +52,90 @@
   "Maintenance Mode": "Wartungsmodus",
   "Method Not Allowed": "Methode nicht erlaubt",
   "Misdirected Request": "Fehlgeleitete Anfrage",
-  "Moved Permanently": "Permanent verschoben",
-  "Multi-Status": "Multistatus",
-  "Multiple Choices": "Mehrfachauswahl",
+  "Moved Permanently": "Dauerhaft verschoben",
+  "Multi-Status": "Multi-Status",
+  "Multiple Choices": "Mehrere Auswahlmöglichkeiten",
   "Network Authentication Required": "Netzwerkauthentifizierung erforderlich",
-  "Network Connect Timeout Error": "Zeitüberschreitungsfehler bei Netzwerkverbindung",
-  "Network Read Timeout Error": "Zeitüberschreitungsfehler beim Lesen des Netzwerks",
+  "Network Connect Timeout Error": "Netzwerkverbindungs-Timeout-Fehler",
+  "Network Read Timeout Error": "Netzwerk-Lese-Timeout-Fehler",
   "No Content": "Kein Inhalt",
-  "Non-Authoritative Information": "Nicht maßgebende Informationen",
-  "Not Acceptable": "Nicht annehmbar",
+  "Non-Authoritative Information": "Nicht-autoritative Information",
+  "Not Acceptable": "Nicht akzeptabel",
   "Not Extended": "Nicht erweitert",
-  "Not Found": "Nicht gefunden",
+  "Not Found": "Seite nicht gefunden",
   "Not Implemented": "Nicht implementiert",
-  "Not Modified": "Nicht modifiziert",
+  "Not Modified": "Nicht geändert",
   "of": "von",
   "OK": "OK",
   "Origin Is Unreachable": "Ursprung ist nicht erreichbar",
   "Page Expired": "Seite abgelaufen",
-  "Pagination Navigation": "Seitennummerierungsnavigation",
+  "Pagination Navigation": "Seitennavigation",
   "Partial Content": "Teilinhalt",
   "Payload Too Large": "Nutzlast zu groß",
   "Payment for #:sn has been received! Total amount: :amount.": "Zahlung für #:sn wurde erhalten! Gesamtbetrag: :amount.",
   "Payment Received": "Zahlung erhalten",
   "Payment Required": "Zahlung erforderlich",
   "Permanent Redirect": "Permanente Weiterleitung",
-  "Please click the button below to verify your email address.": "Bitte klicken Sie auf die Schaltfläche, um Ihre E-Mail-Adresse zu bestätigen.",
+  "Please click the button below to verify your email address.": "Bitte klicken Sie auf die Schaltfläche unten, um Ihre E-Mail-Adresse zu verifizieren.",
   "Precondition Failed": "Vorbedingung fehlgeschlagen",
-  "Precondition Required": "Voraussetzung erforderlich",
+  "Precondition Required": "Vorbedingung erforderlich",
   "Processing": "Verarbeitung",
   "Proxy Authentication Required": "Proxy-Authentifizierung erforderlich",
   "Railgun Error": "Railgun-Fehler",
   "Range Not Satisfiable": "Bereich nicht erfüllbar",
   "Regards": "Mit freundlichen Grüßen",
   "Register": "Registrieren",
-  "Request Header Fields Too Large": "Kopfzeilenfelder zu groß anfordern",
-  "Request Timeout": "Zeitüberschreitung anfordern",
+  "Request Header Fields Too Large": "Anfrage-Header-Felder zu groß",
+  "Request Timeout": "Anfrage-Timeout",
   "Reset Content": "Inhalt zurücksetzen",
   "Reset Password": "Passwort zurücksetzen",
-  "Reset Password Notification": "Benachrichtigung zum Zurücksetzen des Passworts",
+  "Reset Password Notification": "Passwort-Reset-Benachrichtigung",
   "results": "Ergebnisse",
-  "Retry With": "Wiederhole mit",
-  "See Other": "Andere sehen",
-  "Server Error": "Interner Fehler",
+  "Retry With": "Wiederholen mit",
+  "See Other": "Siehe andere",
+  "Server Error": "Serverfehler",
   "Service Unavailable": "Service nicht verfügbar",
   "Session Has Expired": "Sitzung ist abgelaufen",
-  "Showing": "Zeigen",
-  "SSL Handshake Failed": "SSL Handshake fehlgeschlagen",
-  "Subscription link receive abnormal access and banned by the system": "Abonnementlink erhält ungewöhnlichen Zugriff und wurde vom System gesperrt",
-  "Switching Protocols": "Schaltprotokolle",
+  "Showing": "Anzeige",
+  "SSL Handshake Failed": "SSL-Handshake fehlgeschlagen",
+  "Subscription link receive abnormal access and banned by the system": "Abonnement-Link erhielt abnormalen Zugriff und wurde vom System gesperrt",
+  "Switching Protocols": "Protokollwechsel",
   "Temporary Redirect": "Temporäre Weiterleitung",
-  "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "Vielen Dank für Ihre Anmeldung! Bevor Sie beginnen, müssen Sie Ihre E-Mail-Adresse bestätigen, indem Sie auf den Link klicken, den wir gerade an Ihre E-Mail gesendet haben! Wenn Sie keine E-Mail erhalten haben, senden wir Ihnen gerne eine weitere.",
-  "The given data was invalid.": "Die gegebenen Daten waren ungültig.",
-  "The response is not a streamed response.": "Die Antwort ist keine gestreamte Antwort.",
+  "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "Vielen Dank für Ihre Registrierung! Bevor Sie beginnen, müssen Sie Ihre E-Mail verifizieren, indem Sie auf den Link klicken, den wir gerade an Ihre E-Mail gesendet haben! Falls Sie keine E-Mail erhalten haben, senden wir Ihnen gerne eine weitere.",
+  "The given data was invalid.": "Die angegebenen Daten waren ungültig.",
+  "The response is not a streamed response.": "Die Antwort ist keine Stream-Antwort.",
   "The response is not a view.": "Die Antwort ist keine Ansicht.",
-  "This password reset link will expire in :count minutes.": "Dieser Link zum Zurücksetzen des Passworts läuft in :count Minuten ab.",
+  "This password reset link will expire in :count minutes.": "Dieser Passwort-Reset-Link läuft in :count Minuten ab.",
   "to": "bis",
   "Toggle navigation": "Navigation umschalten",
   "Too Early": "Zu früh",
-  "Too Many Requests": "Zu viele Anfragen",
+  "Too Many Requests": "Zu viele Anfragen.",
   "Unauthorized": "Nicht autorisiert",
   "Unavailable For Legal Reasons": "Aus rechtlichen Gründen nicht verfügbar",
   "Unknown Error": "Unbekannter Fehler",
-  "Unprocessable Entity": "Unverfügbare Entität",
+  "Unprocessable Entity": "Nicht verarbeitbare Entität",
   "Unsupported Media Type": "Nicht unterstützter Medientyp",
   "Upgrade Required": "Upgrade erforderlich",
   "URI Too Long": "URI zu lang",
   "Use Proxy": "Proxy verwenden",
   "Variant Also Negotiates": "Variante verhandelt auch",
-  "Verify Email Address": "E-Mail-Adresse bestätigen",
+  "Verify Email Address": "E-Mail-Adresse verifizieren",
   "Verify Your Email Address": "Verifizieren Sie Ihre E-Mail-Adresse",
   "Web Server is Down": "Webserver ist ausgefallen",
-  "Whoops!": "Ups!",
-  "You are receiving this email because we received a password reset request for your account.": "Sie erhalten diese E-Mail, weil wir einen Antrag auf eine Zurücksetzung Ihres Passworts bekommen haben.",
-  "You have not responded this ticket in :num hours, System has closed your ticket.": "Sie haben auf dieses Ticket innerhalb von :num Stunden nicht geantwortet, das System hat Ihr Ticket geschlossen.",
-  "You must have a valid subscription to view the content in this area!": "Sie müssen ein gültiges Abonnement haben, um den Inhalt in diesem Bereich zu sehen!",
-  "Your subscription has been disabled by the administrator, please contact the administrator to restore it": "Ihr Abonnement wurde vom Administrator deaktiviert, bitte kontaktieren Sie den Administrator, um es wiederherzustellen.",
-  "Manually add in dashboard.": "Manuell im Dashboard hinzufügen",
-  "Manually edit in dashboard.": "Manuell im Dashboard bearbeiten",
-  "Batch generate user accounts in dashboard.": "Benutzerkonten im Hintergrund massenweise erstellen",
-  "Coupon used in order.": "Gutschein im Auftrag verwendet",
-  "Order canceled, coupon reinstated.": "Bestellung storniert, Gutschein wiederhergestellt",
-  "Used for credit recharge.": "Zur Aufladung des Guthabens verwendet",
-  "The user manually reset the data.": "Benutzer hat Daten zurückgesetzt",
-  "Recharge using a recharge voucher.": "Mit einem Aufladegutschein aufladen",
-  "The user topped up the balance.": "Der Benutzer hat das Guthaben aufgeladen",
-  "Purchased an item.": "Einen Artikel gekauft",
-  "[:payment] plus the user’s purchased data plan.": "[:payment] plus das vom Benutzer gekaufte Datenpaket"
+  "Whoops!": "Hoppla!",
+  "You are receiving this email because we received a password reset request for your account.": "Sie erhalten diese E-Mail, weil wir eine Passwort-Reset-Anfrage für Ihr Konto erhalten haben.",
+  "You have not responded this ticket in :num hours, System has closed your ticket.": "Sie haben dieses Ticket :num Stunden lang nicht beantwortet, das System hat Ihr Ticket automatisch geschlossen.",
+  "You must have a valid subscription to view the content in this area!": "Sie müssen ein gültiges Abonnement haben, um den Inhalt in diesem Bereich anzuzeigen!",
+  "Your subscription has been disabled by the administrator, please contact the administrator to restore it": "Ihr Abonnement wurde vom Administrator deaktiviert, bitte kontaktieren Sie den Administrator zur Wiederherstellung.",
+  "Manually add in dashboard.": "Manuell im Dashboard hinzufügen.",
+  "Manually edit in dashboard.": "Manuell im Dashboard bearbeiten.",
+  "Batch generate user accounts in dashboard.": "Batch-Generierung von Benutzerkonten im Dashboard.",
+  "Coupon used in order.": "Gutschein in Bestellung verwendet.",
+  "Order canceled, coupon reinstated.": "Bestellung storniert, Gutschein wiederhergestellt.",
+  "Used for credit recharge.": "Für Guthaben-Aufladung verwendet.",
+  "The user manually reset the data.": "Benutzer hat Traffic manuell zurückgesetzt.",
+  "Recharge using a recharge voucher.": "Aufladung mit Aufladegutschein.",
+  "The user topped up the balance.": "Benutzer hat Guthaben aufgeladen.",
+  "Purchased an item.": "Artikel gekauft.",
+  "[:payment] plus the user's purchased data plan.": "[:payment] plus vom Benutzer gekauftes Datenpaket."
 }

File diff suppressed because it is too large
+ 403 - 464
resources/lang/de/admin.php


+ 41 - 41
resources/lang/de/auth.php

@@ -3,87 +3,87 @@
 declare(strict_types=1);
 
 return [
-    'accept_term' => 'Ich habe gelesen und akzeptiere',
+    'accept_term' => 'Ich habe gelesen und stimme zu',
     'active' => [
-        'attribute' => 'Aktivieren',
+        'attribute' => 'Aktivierung',
         'error' => [
-            'activated' => 'Konto bereits aktiviert, erneute Aktivierung nicht erforderlich',
-            'disable' => 'Die Kontoaktivierung ist deaktiviert, Sie können sich direkt anmelden!',
-            'throttle' => 'Sie haben das Aktivierungsanfragelimit erreicht, bitte versuchen Sie es später erneut! Bei Fragen kontaktieren Sie :email.',
+            'activated' => 'Konto bereits aktiviert, bitte melden Sie sich direkt an!',
+            'disable' => 'Diese Website hat die Kontoaktivierungsfunktion deaktiviert, Sie können sich direkt anmelden!',
+            'throttle' => 'Sie haben das Aktivierungsanfrage-Limit erreicht, bitte versuchen Sie es später erneut!',
         ],
-        'promotion' => 'Konto noch nicht aktiviert, bitte zuerst [:action]!',
-        'sent' => 'Aktivierungs-E-Mail wurde an Ihr Postfach gesendet, bitte überprüfen Sie es (einschließlich des Spam-Ordners).',
+        'promotion' => 'Konto noch nicht aktiviert, bitte zuerst „:action"!',
+        'sent' => 'Aktivierungslink wurde an Ihre E-Mail-Adresse gesendet, bitte warten oder prüfen Sie den Spam-Ordner',
     ],
-    'aup' => 'Akzeptable Nutzungsrichtlinien',
+    'aup' => 'Nutzungsbedingungen',
     'captcha' => [
         'attribute' => 'Captcha',
         'error' => [
-            'failed' => 'Captcha-Überprüfung fehlgeschlagen, bitte erneut versuchen',
-            'timeout' => 'Captcha ist abgelaufen, bitte aktualisieren und erneut versuchen.',
+            'failed' => 'Captcha falsch eingegeben, bitte erneut eingeben!',
+            'timeout' => 'Captcha ist abgelaufen, bitte aktualisieren und erneut versuchen!',
         ],
-        'required' => 'Bitte vervollständigen Sie das Captcha!',
-        'sent' => 'Captcha wurde an Ihre E-Mail gesendet, bitte überprüfen Sie es (einschließlich des Spam-Ordners).',
+        'required' => 'Bitte vervollständigen Sie die Captcha-Verifikation korrekt',
+        'sent' => 'Captcha wurde an Ihre E-Mail-Adresse gesendet, bitte warten oder prüfen Sie den Spam-Ordner',
     ],
     'email' => [
         'error' => [
-            'banned' => 'Ihr E-Mail-Anbieter ist blockiert, bitte verwenden Sie eine andere E-Mail.',
-            'invalid' => 'Ihre E-Mail wird nicht unterstützt.',
+            'banned' => 'Diese Website unterstützt Ihren E-Mail-Anbieter nicht, bitte wechseln Sie die E-Mail-Adresse!',
+            'invalid' => 'Die von Ihnen eingegebene E-Mail-Adresse steht nicht auf der Liste der unterstützten E-Mail-Adressen!',
         ],
     ],
     'error' => [
         'account_baned' => 'Ihr Konto wurde gesperrt!',
-        'login_error' => 'Anmeldefehler, bitte versuchen Sie es später erneut!',
-        'login_failed' => 'Anmeldung fehlgeschlagen, bitte überprüfen Sie Ihren Benutzernamen und Ihr Passwort!',
-        'not_found_user' => 'Kein Konto gefunden, bitte versuchen Sie andere Anmeldemethoden.',
-        'repeat_request' => 'Bitte keine wiederholten Anfragen, aktualisieren und erneut versuchen.',
-        'url_timeout' => 'Der Link ist abgelaufen, bitte erneut anfordern.',
+        'login_error' => 'Fehler beim Anmeldeprozess, bitte versuchen Sie es später erneut!',
+        'login_failed' => 'Anmeldung fehlgeschlagen, bitte prüfen Sie, ob Konto oder Passwort korrekt sind!',
+        'not_found_user' => 'Kein verknüpftes Konto gefunden, bitte verwenden Sie andere Anmeldemethoden!',
+        'repeat_request' => 'Bitte keine wiederholten Anfragen, aktualisieren Sie und versuchen Sie es erneut!',
+        'url_timeout' => 'Link ist ungültig geworden, bitte führen Sie den Vorgang erneut aus!',
     ],
-    'failed' => 'Diese Kombination aus Zugangsdaten wurde nicht in unserer Datenbank gefunden.',
+    'failed' => 'Benutzername oder Passwort falsch.',
     'invite' => [
-        'get' => 'Einladungscode erhalten',
-        'not_required' => 'Kein Einladungscode erforderlich, Sie können sich direkt registrieren!',
-        'unavailable' => 'Ungültiger Einladungscode, bitte erneut versuchen.',
+        'get' => 'Einladungscode abrufen',
+        'not_required' => 'Kein Einladungscode erforderlich, direkte Registrierung möglich!',
+        'unavailable' => 'Einladungscode ungültig, bitte erneut versuchen!',
     ],
     'login' => 'Anmelden',
     'logout' => 'Abmelden',
-    'maintenance' => 'Wartung',
-    'maintenance_tip' => 'In Wartung',
+    'maintenance' => 'Systemwartung',
+    'maintenance_tip' => 'System wird gewartet, bitte besuchen Sie uns später!',
     'oauth' => [
         'login_failed' => 'Drittanbieter-Anmeldung fehlgeschlagen!',
         'register' => 'Schnellregistrierung',
-        'registered' => 'Bereits registriert, bitte direkt anmelden.',
+        'registered' => 'Bereits registriert, bitte melden Sie sich direkt an',
     ],
     'one-click_login' => 'Ein-Klick-Anmeldung',
     'optional' => 'Optional',
     'password' => [
         'forget' => 'Passwort vergessen?',
         'new' => 'Neues Passwort eingeben',
-        'original' => 'Aktuelles Passwort',
+        'original' => 'Altes Passwort',
         'reset' => [
             'attribute' => 'Passwort zurücksetzen',
             'error' => [
-                'demo' => 'Ändern des Administratorpassworts im Demomodus nicht möglich.',
-                'disabled' => 'Passwortzurücksetzung deaktiviert, bitte kontaktieren Sie :email für Unterstützung.',
-                'same' => 'Das neue Passwort darf nicht mit dem alten übereinstimmen, bitte erneut eingeben.',
-                'throttle' => 'Sie können das Passwort nur :time Mal in 24 Stunden zurücksetzen, bitte nicht zu häufig operieren.',
-                'wrong' => 'Falsches Passwort, bitte erneut versuchen.',
+                'demo' => 'Demo-Umgebung verbietet Änderung des Administrator-Passworts!',
+                'disabled' => 'Diese Website hat die Passwort-Reset-Funktion deaktiviert!',
+                'same' => 'Neues Passwort kann nicht mit dem alten Passwort identisch sein, bitte neu festlegen!',
+                'throttle' => 'Passwort kann nur :time Mal alle 24 Stunden zurückgesetzt werden, bitte nicht häufig verwenden!',
+                'wrong' => 'Altes Passwort falsch, bitte erneut eingeben!',
             ],
-            'sent' => 'Zurücksetzungslink wurde an Ihr Postfach gesendet, bitte überprüfen Sie es (einschließlich des Spam-Ordners).',
-            'success' => 'Neues Passwort erfolgreich zurückgesetzt, Sie können sich jetzt anmelden.',
+            'sent' => 'Der Reset-Link wurde erfolgreich versendet. Bitte überprüfen Sie Ihre E-Mails (auch den Spam-Ordner)',
+            'success' => 'Neues Passwort erfolgreich festgelegt, bitte gehen Sie zur Anmeldeseite.',
         ],
     ],
     'register' => [
         'attribute' => 'Registrieren',
-        'code' => 'Registrierungscode',
+        'code' => 'Registrierungsverifikationscode',
         'error' => [
-            'disable' => 'Entschuldigung, wir nehmen derzeit keine neuen Benutzer auf.',
-            'throttle' => 'Anti-Bot-System aktiviert! Bitte vermeiden Sie häufige Einreichungen!',
+            'disable' => 'Entschuldigung, diese Website hat den Registrierungskanal vorübergehend geschlossen',
+            'throttle' => 'Anti-Spam-Mechanismus aktiviert, bitte nicht häufig registrieren',
         ],
-        'failed' => 'Registrierung fehlgeschlagen, bitte später erneut versuchen.',
-        'promotion' => 'Noch kein Konto? Bitte gehen Sie zu ',
+        'failed' => 'Registrierung fehlgeschlagen, bitte versuchen Sie es später erneut',
+        'promotion' => 'Sie haben noch kein Konto? Bitte',
     ],
     'remember_me' => 'Angemeldet bleiben',
-    'request' => 'Anfordern',
-    'throttle' => 'Zu viele Loginversuche. Versuchen Sie es bitte in :seconds Sekunden nochmal.',
+    'request' => 'Abrufen',
+    'throttle' => 'Zu viele Anmeldeversuche, bitte versuchen Sie es in :seconds Sekunden erneut.',
     'tos' => 'Nutzungsbedingungen',
 ];

+ 42 - 42
resources/lang/de/common.php

@@ -5,40 +5,40 @@ declare(strict_types=1);
 return [
     'account' => 'Konto',
     'action' => 'Aktion',
-    'active_item' => 'Aktivieren :attribute',
+    'active_item' => ':attribute aktivieren',
     'add' => 'Hinzufügen',
-    'advance' => 'Fortgeschritten',
+    'advance' => 'Erweitert',
     'all' => 'Alle',
-    'applied' => ':attribute angewendet',
+    'applied' => ':attribute wurde angewendet',
     'apply' => 'Anwenden',
-    'available_date' => 'Gültigkeitszeitraum',
+    'available_date' => 'Gültigkeitsdauer',
     'avatar' => 'Avatar',
     'back' => 'Zurück',
     'back_to' => 'Zurück zu :page',
     'bark' => [
-        'custom' => 'Benutzerdefinierte Informationen',
-        'node_status' => 'Knotenzustand',
+        'custom' => 'Anpassen',
+        'node_status' => 'Knoten-Status',
     ],
     'cancel' => 'Abbrechen',
     'change' => 'Ändern',
     'close' => 'Schließen',
-    'close_item' => 'Schließen :attribute',
+    'close_item' => ':attribute schließen',
     'confirm' => 'Bestätigen',
-    'continue' => 'Fortsetzen',
+    'continue' => 'Fortfahren',
     'convert' => 'Konvertieren',
     'copy' => [
         'attribute' => 'Kopieren',
-        'failed' => 'Kopieren fehlgeschlagen, bitte manuell kopieren',
+        'failed' => 'Kopieren fehlgeschlagen, bitte manuell ausführen',
         'success' => 'Erfolgreich kopiert',
     ],
     'create' => 'Erstellen',
-    'created_at' => 'Erstellungsdatum',
+    'created_at' => 'Erstellt am',
     'customize' => 'Anpassen',
     'days' => [
-        'attribute' => '{1} Tag|{2} Tage',
+        'attribute' => '{1} Tage|{2}. Tag',
         'next' => 'Nächster Tag',
         'weekend' => 'Wochenende',
-        'work' => 'Wochentag',
+        'work' => 'Werktag',
     ],
     'default' => 'Standard',
     'delete' => 'Löschen',
@@ -46,15 +46,16 @@ return [
     'deleted_item' => ':attribute gelöscht',
     'developing' => 'In Entwicklung! Bleiben Sie dran',
     'download' => 'Herunterladen',
+    'download_item' => ':attribute herunterladen',
     'edit' => 'Bearbeiten',
     'error' => 'Fehler',
-    'error_action_item' => ':action:attribute-Fehler',
-    'error_item' => ':attribute-Fehler',
-    'exists_error' => 'Unter :attribute gibt es verknüpfte Konten. Bitte trennen Sie diese zuerst!',
+    'error_action_item' => 'Fehler beim :action :attribute',
+    'error_item' => ':attribute Fehler',
+    'exists_error' => ':attribute ist mit anderen Konten verknüpft. Bitte lösen Sie zuerst die Verknüpfung.',
     'expired_at' => 'Ablaufdatum',
     'export' => 'Exportieren',
     'failed' => 'Fehlgeschlagen',
-    'failed_action_item' => ':action:attribute fehlgeschlagen',
+    'failed_action_item' => ':action :attribute fehlgeschlagen',
     'failed_item' => ':attribute fehlgeschlagen',
     'free' => 'Kostenlos',
     'function' => [
@@ -62,14 +63,13 @@ return [
         'menubar' => 'Menüleiste',
         'navigation' => 'Navigation',
     ],
-    'generate' => 'Erzeugen',
-    'generate_item' => 'Erzeugen :attribute',
+    'generate' => 'Generieren',
+    'generate_item' => ':attribute generieren',
     'goto' => 'Gehe zu',
-    'hour' => '{1} Stunde|{2} Uhr',
+    'hour' => '{1} Stunden|{2} Uhr',
     'import' => 'Importieren',
-    'latest_at' => 'Letzte Aktivität',
+    'latest_at' => 'Zuletzt aktualisiert',
     'more' => 'Mehr',
-    'new' => 'Neu',
     'none' => 'Keine',
     'open' => 'Öffnen',
     'or' => 'oder',
@@ -78,8 +78,8 @@ return [
             'canceled' => 'Storniert',
             'completed' => 'Abgeschlossen',
             'ongoing' => 'Laufend',
-            'prepaid' => 'Vorausbezahlt',
-            'review' => 'Zur Überprüfung',
+            'prepaid' => 'Vorauszahlung',
+            'review' => 'Ausstehend',
         ],
     ],
     'payment' => [
@@ -87,12 +87,12 @@ return [
         'credit' => 'Guthaben',
         'crypto' => 'Kryptowährung',
         'manual' => 'Manuelle Zahlung',
-        'qq' => 'QQ Wallet',
+        'qq' => 'QQ-Wallet',
         'wechat' => 'WeChat Pay',
     ],
     'print' => 'Drucken',
     'qrcode' => ':attribute QR-Code',
-    'random_generate' => 'Leer lassen, um zufällig zu generieren',
+    'random_generate' => 'Leer lassen für zufällige Generierung',
     'recommend' => 'Empfehlen',
     'request' => 'Anfrage',
     'request_failed' => 'Anfrage fehlgeschlagen, bitte erneut versuchen',
@@ -102,7 +102,7 @@ return [
     'send' => 'Senden',
     'sorry' => 'Entschuldigung',
     'status' => [
-        'applying' => 'Anwendung läuft',
+        'applying' => 'Beantragung',
         'attribute' => 'Status',
         'available' => 'Verfügbar',
         'banned' => 'Gesperrt',
@@ -115,37 +115,37 @@ return [
         'normal' => 'Normal',
         'paid' => 'Bezahlt',
         'pass' => 'Bestanden',
-        'payment_pending' => 'Zahlung ausstehend',
+        'payment_pending' => 'Zahlung steht aus',
         'pending' => 'Ausstehend',
-        'pending_dispatch' => 'Ausstehende Lieferung',
+        'pending_dispatch' => 'Ausstehende Zustellung',
         'reject' => 'Ablehnen',
         'rejected' => 'Abgelehnt',
         'reply' => 'Beantwortet',
-        'review' => 'Zur Überprüfung',
+        'review' => 'Ausstehende Überprüfung',
         'reviewed' => 'Überprüft',
-        'run_out' => 'Datenverbrauch aufgebraucht',
-        'send_to_credit' => 'Zum Guthaben hinzufügen',
+        'run_out' => 'Aufgebraucht',
+        'send_to_credit' => 'Zu Guthaben hinzufügen',
         'unknown' => 'Unbekannt',
-        'unused' => 'Nicht verwendet',
+        'unused' => 'Unbenutzt',
         'used' => 'Verwendet',
-        'withdrawal_pending' => 'Nicht abgehoben',
-        'withdrawn' => 'Abgehoben',
+        'withdrawal_pending' => 'Auszahlung steht aus',
+        'withdrawn' => 'Ausgezahlt',
     ],
-    'stay_unchanged' => 'Leer lassen, um unverändert zu bleiben',
-    'storage_logo' => 'Logo-Speicher',
+    'stay_unchanged' => 'Leer lassen',
+    'storage_logo' => 'LOGO-Speicher',
     'store' => 'Speicher',
     'submit' => 'Absenden',
     'success' => 'Erfolg',
-    'success_action_item' => ':action:attribute erfolgreich',
+    'success_action_item' => ':action :attribute erfolgreich',
     'success_item' => ':attribute erfolgreich',
-    'to' => 'zu',
-    'to_be_send' => 'Zu senden',
-    'to_safari' => 'Klicken Sie auf das <i class="icon wb-more-horizontal" aria-hidden="true"></i>-Symbol in der oberen rechten Ecke und wählen Sie "In <img class="w-30 h-30 vertical-align-middle m-3" src="https://gw.alicdn.com/tfs/TB1xwiUNpXXXXaIXXXXXXXXXXXX-55-55.png" alt="Safari" /> Safari öffnen", um unsere Website ordnungsgemäß zu besuchen!',
+    'to' => 'bis',
+    'to_be_send' => 'Ausstehend zu senden',
+    'to_safari' => 'Tippen Sie oben rechts auf <i class="icon wb-more-horizontal" aria-hidden="true"></i> und wählen Sie dann <img class="w-30 h-30 vertical-align-middle m-3" src="https://gw.alicdn.com/tfs/TB1xwiUNpXXXXaIXXXXXXXXXXXX-55-55.png" alt="Safari" /> Safari zum Öffnen<br>um die Website ordnungsgemäß zu besuchen!',
     'toggle' => 'Umschalten',
-    'toggle_action' => 'Umschalten :action',
+    'toggle_action' => ':action umschalten',
     'unlimited' => 'Unbegrenzt',
     'update' => 'Aktualisieren',
     'updated_at' => 'Zuletzt aktualisiert',
-    'view' => 'Ansehen',
+    'view' => 'Anzeigen',
     'warning' => 'Warnung',
 ];

+ 20 - 20
resources/lang/de/errors.php

@@ -4,33 +4,33 @@ declare(strict_types=1);
 
 return [
     'forbidden' => [
-        'access' => 'Unbekannte IP oder Proxy-Zugriff erkannt, Zugriff verweigert',
-        'bots' => 'Bot-Zugriff erkannt, Zugriff verweigert',
-        'china' => 'China-IP oder Proxy-Zugriff erkannt, Zugriff verweigert',
-        'oversea' => 'Übersee-IP oder Proxy-Zugriff erkannt, Zugriff verweigert',
-        'redirect' => '(:ip :url) wurde beim Zugriff über einen Abonnement-Link erkannt, erzwungene Weiterleitung.',
-        'unknown' => 'Unbekannter verbotener Zugriffsmodus! Bitte ändern Sie den [Zugriffsbeschränkung] in den Systemeinstellungen!',
+        'access' => 'Der Zugriff wurde aus Sicherheitsgründen verweigert (unbekannte IP oder Proxy erkannt)',
+        'bots' => 'Automatisierter Zugriff erkannt. Der Zugriff wurde aus Sicherheitsgründen verweigert',
+        'china' => 'Chinesische IP oder Proxy erkannt, Zugriff verweigert!',
+        'oversea' => 'Ausländische IP oder Proxy erkannt, Zugriff verweigert!',
+        'redirect' => 'Erkannt (:ip :url) verwendet Abonnement-Link für Zugriff, zwangsweise weitergeleitet',
+        'unknown' => 'Unbekannter Sperrmodus, bitte prüfen Sie die Konfiguration in den Systemeinstellungen!',
     ],
-    'get_ip' => 'Fehler beim Abrufen der IP-Informationen',
+    'get_ip' => 'Abrufen der IP-Informationen fehlgeschlagen',
     'log' => 'Protokoll',
     'refresh' => 'Aktualisieren',
     'refresh_page' => 'Bitte aktualisieren Sie die Seite und versuchen Sie es erneut',
-    'report' => 'Der Fehler trug einen Bericht: ',
+    'report' => 'Fehlerbericht-Inhalt:',
     'safe_code' => 'Bitte geben Sie den Sicherheitscode ein',
-    'safe_enter' => 'Sicherer Eingang',
+    'safe_enter' => 'Sicherer Eingangszugriff',
     'subscribe' => [
-        'banned_until' => 'Konto bis :time gesperrt, bitte warten Sie auf die Freischaltung!',
-        'expired' => 'Konto abgelaufen! Bitte erneuern Sie Ihr Abonnement!',
-        'none' => 'Keine verfügbaren Knoten',
-        'out' => 'KEINE DATEN MEHR! Bitte kaufen Sie mehr oder setzen Sie die Daten zurück!',
-        'question' => 'Konto-Probleme!? Besuchen Sie die Website für Details',
-        'sub_banned' => 'Abonnement gesperrt! Besuchen Sie die Website für Details',
-        'unknown' => 'Ungültiger Abonnementlink! Bitte holen Sie sich einen neuen!',
-        'user' => 'Ungültige URL, Konto existiert nicht!',
-        'user_disabled' => 'Konto deaktiviert! Kontaktieren Sie den Support!',
+        'banned_until' => 'Konto gesperrt bis :time, bitte versuchen Sie es nach der Entsperrung erneut!',
+        'expired' => 'Konto abgelaufen, bitte verlängern Sie vor der Nutzung!',
+        'none' => 'Derzeit keine verfügbaren Knoten',
+        'out' => 'Traffic aufgebraucht, bitte kaufen oder setzen Sie Traffic zurück!',
+        'question' => 'Konto weist Anomalien auf, bitte besuchen Sie die offizielle Website für Details!',
+        'sub_banned' => 'Abonnement-Link wurde gesperrt, bitte besuchen Sie die offizielle Website für Informationen!',
+        'unknown' => 'Abonnement-Link ungültig, bitte neu abrufen!',
+        'user' => 'Link ungültig, Konto existiert nicht, bitte neu abrufen!',
+        'user_disabled' => 'Konto wurde deaktiviert!',
     ],
-    'title' => '⚠️ Fehler ausgelöst',
-    'unsafe_enter' => 'Unsicherer Eingang',
+    'title' => '⚠️ Fehler aufgetreten',
+    'unsafe_enter' => 'Unsicherer Eingangszugriff',
     'visit' => 'Bitte besuchen Sie',
     'whoops' => 'Hoppla!',
 ];

+ 235 - 103
resources/lang/de/model.php

@@ -4,144 +4,142 @@ declare(strict_types=1);
 
 return [
     'aff' => [
-        'amount' => 'Bestellbetrag',
+        'amount' => 'Bestellsumme',
         'commission' => 'Provision',
-        'created_at' => 'Bestellt am',
-        'invitee' => 'Käufer',
+        'created_at' => 'Bestelldatum',
+        'invitee' => 'Eingeladener',
         'updated_at' => 'Bearbeitet am',
     ],
     'article' => [
         'attribute' => 'Artikel',
         'category' => 'Kategorie',
-        'created_at' => 'Veröffentlicht am',
+        'created_at' => 'Veröffentlichungsdatum',
         'language' => 'Sprache',
-        'logo' => 'Cover',
-        'updated_at' => 'Aktualisiert am',
+        'logo' => 'Titelbild',
     ],
     'common' => [
         'description' => 'Beschreibung',
-        'extend' => 'Erweiterte Informationen',
+        'extend' => 'Zusatzinformationen',
         'level' => 'Stufe',
-        'sort' => 'Sortieren',
+        'sort' => 'Sortierreihenfolge',
         'type' => 'Typ',
     ],
     'country' => [
-        'code' => 'Ländercode',
-        'icon' => 'Flagge',
+        'icon' => 'Flaggen-Symbol',
         'name' => 'Ländername',
     ],
     'coupon' => [
         'attribute' => 'Gutschein',
         'groups' => 'Gruppenbeschränkung',
         'levels' => 'Stufenbeschränkung',
-        'logo' => 'Logo',
-        'minimum' => 'Mindestbestellwert',
-        'name' => 'Name',
+        'logo' => 'Bild',
+        'minimum' => 'Mindestausgaben',
+        'name' => 'Gutscheinname',
         'newbie' => 'Nur für neue Benutzer',
-        'num' => 'Anzahl',
-        'priority' => 'Priorität',
-        'services_blacklist' => 'Blacklist-Produkte',
-        'services_whitelist' => 'Whitelist-Produkte',
-        'sn' => 'Code',
-        'usable_times' => 'Verwendungsbeschränkung',
-        'used' => 'Persönliche Begrenzung',
-        'users_blacklist' => 'Blacklist-Benutzer',
-        'users_whitelist' => 'Whitelist-Benutzer',
+        'num' => 'Menge',
+        'priority' => 'Prioritätsstufe',
+        'services_blacklist' => 'Ausgeschlossene Produkte',
+        'services_whitelist' => 'Berechtigte Produkte',
+        'sn' => 'Gutscheincode',
+        'usable_times' => 'Nutzungsgrenze',
+        'used' => 'Limit pro Benutzer',
+        'users_blacklist' => 'Ausgeschlossene Benutzer',
+        'users_whitelist' => 'Berechtigte Benutzer',
         'value' => 'Wert',
     ],
     'goods' => [
         'attribute' => 'Produkt',
-        'available_date' => 'Gültigkeitszeitraum',
+        'available_date' => 'Gültigkeitsdauer',
         'category' => 'Kategorie',
         'color' => 'Farbe',
         'hot' => 'Bestseller',
-        'info' => 'Benutzerdefinierte Informationen',
-        'invite_num' => 'Bonus-Einladungen',
-        'limit_num' => 'Kaufbegrenzung',
-        'logo' => 'Logo',
-        'name' => 'Name',
-        'period' => 'Reset-Zyklus',
+        'info' => 'Benutzerdefinierte Liste',
+        'invite_num' => 'Zusätzliche Einladungsquote',
+        'limit_num' => 'Limit pro Benutzer',
+        'logo' => 'Produktbild',
+        'name' => 'Produktname',
+        'period' => 'Reset-Intervall',
         'price' => 'Preis',
-        'renew' => 'Datenverlängerungspreis',
+        'renew' => 'Verlängerungspreis',
         'traffic' => 'Datenvolumen',
-        'user_limit' => 'Benutzergeschwindigkeitsbegrenzung',
+        'user_limit' => 'Benutzer-Geschwindigkeitslimit',
     ],
     'ip' => [
-        'info' => 'Standort',
+        'info' => 'Geolokation',
         'network_type' => 'Netzwerktyp',
     ],
     'node' => [
         'attribute' => 'Knoten',
-        'client_limit' => 'Gerätebegrenzung',
-        'country' => 'Land',
-        'data_consume' => 'Datenverbrauch',
-        'data_rate' => 'Datenrate',
+        'client_limit' => 'Client-Limit',
+        'country' => 'Standort',
+        'data_consume' => 'Verbrauchter Traffic',
+        'data_rate' => 'Traffic-Multiplikator',
         'ddns' => 'DDNS',
         'detection' => 'Blockierungserkennung',
-        'display' => 'Anzeige & Abonnement',
-        'domain' => 'Domain',
+        'display' => 'Sichtbarkeit & Abonnement',
+        'domain' => 'Domain-Name',
         'id' => 'Knoten-ID',
-        'ipv4' => 'IPv4',
-        'ipv6' => 'IPv6',
-        'label' => 'Label',
-        'method' => 'Verschlüsselung',
-        'name' => 'Name',
+        'ipv4' => 'IPv4-Adresse',
+        'ipv6' => 'IPv6-Adresse',
+        'label' => 'Knoten-Label',
+        'method' => 'Verschlüsselungsmethode',
+        'name' => 'Knotenname',
         'next_renewal_date' => 'Nächstes Verlängerungsdatum',
-        'obfs' => 'Obfs',
-        'obfs_param' => 'Obfs-Parameter',
+        'obfs' => 'Verschleierungsprotokoll',
+        'obfs_param' => 'Verschleierungsparameter',
         'online_user' => 'Online-Benutzer',
-        'protocol' => 'Protokoll',
+        'protocol' => 'Übertragungsprotokoll',
         'protocol_param' => 'Protokollparameter',
         'push_port' => 'Push-Port',
         'relay_port' => 'Relay-Port',
-        'renewal_cost' => 'Rechnungsbetrag',
+        'renewal_cost' => 'Verlängerungsgebühr',
         'service_port' => 'Service-Port',
-        'single' => 'Einzelport',
-        'single_passwd' => '[Einzel] Passwort',
-        'static' => 'Status',
-        'subscription_term' => 'Abonnementdauer',
-        'traffic_limit' => 'Geschwindigkeitsbegrenzung',
-        'transfer' => 'Relay',
-        'udp' => 'UDP',
+        'single' => 'Einzelport-Modus',
+        'single_passwd' => 'Einzelport-Passwort',
+        'static' => 'Online-Status',
+        'subscription_term' => 'Abonnement-Laufzeit',
+        'traffic_limit' => 'Traffic-Obergrenze',
+        'transfer' => 'Relay-Einstellungen',
+        'udp' => 'UDP unterstützt',
         'v2_alter_id' => 'Alter ID',
-        'v2_cover' => 'Cover',
-        'v2_host' => 'Host',
-        'v2_net' => 'Netzwerk',
-        'v2_path' => 'Pfad | Schlüssel',
+        'v2_cover' => 'Traffic-Verschleierung',
+        'v2_host' => 'Host-Header',
+        'v2_net' => 'Transportprotokoll',
+        'v2_path' => 'Pfad oder Schlüssel',
         'v2_sni' => 'SNI',
-        'v2_tls' => 'TLS',
-        'v2_tls_provider' => 'TLS-Konfiguration',
+        'v2_tls' => 'TLS-Verschlüsselung',
+        'v2_tls_provider' => 'TLS-Zertifikat-Anbieter',
     ],
     'node_auth' => [
-        'attribute' => 'Knotenauthentifizierung',
-        'key' => 'Schlüssel <small>für Knoten</small>',
-        'secret' => 'Rückwärtsschlüssel',
+        'attribute' => 'Knoten-Authentifizierung',
+        'key' => 'Kommunikationsschlüssel',
+        'secret' => 'Umkehrschlüssel',
     ],
     'node_cert' => [
         'attribute' => 'Domain-Zertifikat',
-        'domain' => 'Domain',
+        'domain' => 'Domain-Name',
         'expired_date' => 'Ablaufdatum',
         'issuer' => 'Aussteller',
-        'key' => 'Schlüssel',
-        'pem' => 'PEM',
+        'key' => 'Privater Schlüssel',
+        'pem' => 'PEM-Zertifikat',
         'signed_date' => 'Ausstellungsdatum',
     ],
     'notification' => [
         'address' => 'Empfänger',
-        'created_at' => 'Gesendet am',
+        'created_at' => 'Sendedatum',
         'status' => 'Status',
     ],
     'oauth' => [
-        'identifier' => 'Kennung',
-        'type' => 'Kanal',
+        'identifier' => 'Benutzer-Identifikator',
+        'type' => 'Anmeldemethode',
     ],
     'order' => [
         'attribute' => 'Bestellung',
         'id' => 'Bestellnummer',
         'original_price' => 'Originalpreis',
         'pay_way' => 'Zahlungsmethode',
-        'price' => 'Tatsächlicher Preis',
-        'status' => 'Status',
+        'price' => 'Bezahlt',
+        'status' => 'Bestellstatus',
     ],
     'permission' => [
         'attribute' => 'Berechtigung',
@@ -149,44 +147,46 @@ return [
         'name' => 'Routenname',
     ],
     'referral' => [
-        'amount' => 'Betrag',
-        'created_at' => 'Beantragt am',
-        'id' => 'Antragsnummer',
+        'amount' => 'Angeforderte Summe',
+        'created_at' => 'Antragsdatum',
+        'id' => 'Antrags-ID',
         'user' => 'Antragsteller',
     ],
     'role' => [
         'attribute' => 'Rolle',
-        'name' => 'Name',
-        'permissions' => 'Berechtigungen',
+        'name' => 'Rollenname',
+        'permissions' => 'Zugewiesene Berechtigungen',
     ],
     'rule' => [
         'attribute' => 'Regel',
-        'name' => 'Beschreibung',
-        'pattern' => 'Wert',
+        'name' => 'Regelname',
+        'pattern' => 'Übereinstimmungswert',
+        'logs' => 'Auslöseprotokolle',
     ],
     'rule_group' => [
         'attribute' => 'Regelgruppe',
-        'name' => 'Name',
-        'rules' => 'Regeln',
-        'type' => 'Typ',
+        'name' => 'Gruppenname',
+        'rules' => 'Enthaltene Regeln',
+        'type' => 'Gruppentyp',
     ],
     'subscribe' => [
+        'attribute' => 'Abonnement',
         'ban_desc' => 'Sperrgrund',
         'ban_time' => 'Sperrzeit',
-        'code' => 'Abonnementcode',
-        'req_header' => 'Anfrage-Header',
-        'req_ip' => 'Anfrage-IP',
-        'req_times' => 'Anfragenanzahl',
-        'updated_at' => 'Letzte Anfrage',
+        'code' => 'Abonnement-Code',
+        'req_header' => 'Request-Header',
+        'req_ip' => 'Request-IP',
+        'req_times' => 'Anzahl Anfragen',
+        'updated_at' => 'Letzter Zugriff',
     ],
     'user' => [
-        'account_status' => 'Kontostatus',
+        'account_status' => 'Status',
         'attribute' => 'Benutzer',
         'created_date' => 'Registrierungsdatum',
         'credit' => 'Guthaben',
         'expired_date' => 'Ablaufdatum',
         'id' => 'Benutzer-ID',
-        'invite_num' => 'Verfügbare Einladungen',
+        'invite_num' => 'Einladungsquote',
         'inviter' => 'Einlader',
         'nickname' => 'Spitzname',
         'password' => 'Passwort',
@@ -197,37 +197,169 @@ return [
         'proxy_protocol' => 'Protokoll',
         'proxy_status' => 'Proxy-Status',
         'qq' => 'QQ',
-        'remark' => 'Bemerkung',
-        'reset_date' => 'Datenrückstellungsdatum',
-        'role' => 'Rolle',
-        'service' => 'Proxy-Dienst',
+        'remark' => 'Notizen',
+        'reset_date' => 'Daten-Reset-Datum',
+        'role' => 'Benutzerrolle',
+        'service' => 'Proxy-Service',
         'speed_limit' => 'Geschwindigkeitsbegrenzung',
-        'traffic_used' => 'Verwendete Daten',
-        'usable_traffic' => 'Verfügbare Daten',
+        'traffic_used' => 'Verbrauchtes Datenvolumen',
+        'usable_traffic' => 'Verfügbares Datenvolumen',
         'username' => 'Benutzername',
         'uuid' => 'VMess UUID',
         'wechat' => 'WeChat',
     ],
     'user_credit' => [
-        'after' => 'Nachher',
-        'amount' => 'Betrag',
-        'before' => 'Vorher',
-        'created_at' => 'Geändert am',
+        'after' => 'Nach Änderung',
+        'amount' => 'Geänderte Summe',
+        'before' => 'Vor Änderung',
+        'created_at' => 'Protokolliert am',
     ],
     'user_data_modify' => [
-        'after' => 'Nachher',
-        'before' => 'Vorher',
-        'created_at' => 'Geändert am',
+        'after' => 'Nach Änderung',
+        'before' => 'Vor Änderung',
+        'created_at' => 'Protokolliert am',
     ],
     'user_group' => [
         'attribute' => 'Benutzergruppe',
         'name' => 'Gruppenname',
-        'nodes' => 'Knoten',
+        'nodes' => 'Verfügbare Knoten',
     ],
     'user_traffic' => [
         'download' => 'Download',
-        'log_time' => 'Protokolliert am',
+        'log_time' => 'Protokollzeit',
         'total' => 'Gesamt',
         'upload' => 'Upload',
     ],
+    'config' => [
+        'AppStore_id' => 'Apple ID',
+        'AppStore_password' => 'Apple-Passwort',
+        'account_expire_notification' => 'Kontoablauf-Benachrichtigung',
+        'active_times' => 'Maximale Aktivierungen',
+        'admin_invite_days' => 'Admin-Einladungsablauf',
+        'affiliate_link_salt' => 'Empfehlungslink-Salt',
+        'alipay_qrcode' => 'Alipay-QR-Code',
+        'auto_release_port' => 'Port-Recycling',
+        'ban_duration' => 'Sperrdauer',
+        'bark_key' => 'Bark-Geräteschlüssel',
+        'captcha_key' => 'Captcha-Schlüssel',
+        'captcha_secret' => 'Captcha-Secret/ID',
+        'checkin_interval' => 'Check-in-Abklingzeit',
+        'checkin_reward' => 'Check-in-Bonus',
+        'codepay_id' => 'CodePay-Händler-ID',
+        'codepay_key' => 'CodePay-Geheimschlüssel',
+        'codepay_url' => 'CodePay-Zahlungs-URL',
+        'cryptomus_api_key' => 'Cryptomus API-Schlüssel',
+        'cryptomus_merchant_uuid' => 'Cryptomus-Händler-UUID',
+        'data_anomaly_notification' => 'Datenanomalie-Warnung',
+        'data_exhaust_notification' => 'Datenlimit-Warnung',
+        'ddns_key' => 'DDNS-Schlüssel',
+        'ddns_mode' => 'DDNS-Sync-Modus',
+        'ddns_secret' => 'DDNS-Geheimschlüssel',
+        'default_days' => 'Standard-Kontodauer',
+        'default_traffic' => 'Anfangsbandbreite',
+        'detection_check_times' => 'Blockierungserkennungs-Benachrichtigung',
+        'dingTalk_access_token' => 'DingTalk-Access-Token',
+        'dingTalk_secret' => 'DingTalk-Secret',
+        'epay_key' => 'Epay-Geheimschlüssel',
+        'epay_mch_id' => 'Epay-Händler-ID',
+        'epay_url' => 'Epay-Gateway-URL',
+        'expire_days' => 'Ablaufwarnung',
+        'f2fpay_app_id' => 'Alipay-App-ID',
+        'f2fpay_private_key' => 'Alipay-Privatschlüssel',
+        'f2fpay_public_key' => 'Alipay-öffentlicher Schlüssel',
+        'forbid_mode' => 'Zugriffsbeschränkung',
+        'iYuu_token' => 'IYUU-Token',
+        'invite_num' => 'Anfängliche Einladungsquote',
+        'is_AliPay' => 'Alipay',
+        'is_QQPay' => 'QQPay',
+        'is_WeChatPay' => 'WeChatPay',
+        'is_activate_account' => 'Kontoaktivierung',
+        'is_ban_status' => 'Auto-Sperre bei Ablauf',
+        'is_captcha' => 'Captcha',
+        'is_clear_log' => 'Auto-Protokoll-Löschung',
+        'is_custom_subscribe' => 'Benutzerdefinierte Abonnements',
+        'is_email_filtering' => 'E-Mail-Domain-Filter',
+        'is_forbid_robot' => 'Suchmaschinen-Crawler blockieren',
+        'is_free_code' => 'Kostenlose Einladungscodes',
+        'is_invite_register' => 'Nur-Einladungs-Registrierung',
+        'is_otherPay' => 'Benutzerdefinierte Zahlungskanäle',
+        'is_rand_port' => 'Zufällige Port-Zuweisung',
+        'is_register' => 'Registrierung',
+        'maintenance_content' => 'Wartungsankündigung',
+        'maintenance_mode' => 'Wartungsmodus',
+        'maintenance_time' => 'Wartungsende-Zeit',
+        'min_port' => 'Port-Bereich',
+        'node_blocked_notification' => 'Knoten-blockiert-Warnung',
+        'node_daily_notification' => 'Tägliche Knoten-Zusammenfassung',
+        'node_offline_notification' => 'Knoten-offline-Warnung',
+        'node_renewal_notification' => 'Knoten-Verlängerungs-Erinnerung',
+        'oauth_path' => 'OAuth-Login-Callback-Pfad',
+        'offline_check_times' => 'Offline-Prüfungsanzahl',
+        'password_reset_notification' => 'Passwort-Reset-Warnung',
+        'paybeaver_app_id' => 'PayBeaver-App-ID',
+        'paybeaver_app_secret' => 'PayBeaver-Geheimschlüssel',
+        'payjs_key' => 'PayJS-Geheimschlüssel',
+        'payjs_mch_id' => 'PayJS-Händler-ID',
+        'payment_callback_url' => 'Zahlungs-Callback-URL',
+        'payment_confirm_notification' => 'Manuelle Zahlungsbenachrichtigung',
+        'payment_received_notification' => 'Zahlung-erhalten-Warnung',
+        'paypal_app_id' => 'PayPal-App-ID',
+        'paypal_client_id' => 'PayPal-Client-ID',
+        'paypal_client_secret' => 'PayPal-Geheimschlüssel',
+        'pushDeer_key' => 'PushDeer-Schlüssel',
+        'pushplus_token' => 'PushPlus-Token',
+        'rand_subscribe' => 'Abonnement-Link randomisieren',
+        'recently_heartbeat' => 'Aktuelle Knoten-Last-Schwelle',
+        'redirect_url' => 'Blockierte Anfrage-Weiterleitungs-URL',
+        'referral_money' => 'Mindest-Auszahlungsbetrag',
+        'referral_percent' => 'Provisionsrate',
+        'referral_reward_type' => 'Provisions-Berechnungstyp',
+        'referral_status' => 'Affiliate-Programm',
+        'referral_traffic' => 'Empfehlungs-Bonus-Bandbreite',
+        'register_ip_limit' => 'Max. Registrierungen pro IP',
+        'reset_password_times' => 'Passwort-Reset-Limit pro Tag',
+        'reset_traffic' => 'Periodisches Traffic-Reset',
+        'server_chan_key' => 'ServerChan SCKEY',
+        'standard_currency' => 'Standardwährung',
+        'stripe_public_key' => 'Stripe-öffentlicher Schlüssel',
+        'stripe_secret_key' => 'Stripe-Geheimschlüssel',
+        'stripe_signing_secret' => 'Stripe-Webhook-Signatur-Secret',
+        'subject_name' => 'Produkttitel',
+        'subscribe_domain' => 'Abonnement-Domain',
+        'subscribe_max' => 'Max. Abonnement-Knotenanzahl',
+        'subscribe_rate_limit' => 'Abonnement-Rate-Limit',
+        'tasks_chunk' => 'Batch-Verarbeitungsaufgaben',
+        'tasks_clean' => 'Bereinigungsaufgaben',
+        'tasks_close' => 'Schließungsaufgaben',
+        'telegram_token' => 'Telegram-Bot-Token',
+        'tg_chat_token' => 'Telegram-Chat-Token',
+        'theadpay_key' => 'THeadPay-Schlüssel',
+        'theadpay_mchid' => 'THeadPay-Händler-ID',
+        'theadpay_url' => 'THeadPay-Zahlungs-URL',
+        'ticket_closed_notification' => 'Ticket-geschlossen-Warnung',
+        'ticket_created_notification' => 'Ticket-erstellt-Warnung',
+        'ticket_replied_notification' => 'Ticket-Antwort-Warnung',
+        'traffic_abuse_limit' => 'Datenmissbrauchs-Schwelle',
+        'traffic_warning_percent' => 'Datenverbrauchs-Warnung',
+        'trojan_license' => 'Trojan-Backend-Lizenz',
+        'user_invite_days' => 'Benutzer-Einladungscode-Ablauf',
+        'username_type' => 'Konto-Benutzername-Format',
+        'v2ray_license' => 'V2Ray-Backend-Lizenz',
+        'v2ray_tls_provider' => 'V2Ray-TLS-Zertifikat-Anbieter',
+        'web_api_url' => 'Backend-API-URL',
+        'webmaster_email' => 'Administrator-E-Mail',
+        'website_customer_service_code' => 'Kundensupport-Skript',
+        'website_home_logo' => 'Startseiten-Logo',
+        'website_logo' => 'Innenseiten-Logo',
+        'website_name' => 'Website-Name',
+        'website_security_code' => 'Sicherheitscode',
+        'website_statistics_code' => 'Website-Analyse-Skript',
+        'website_url' => 'Primäre Website-Domain',
+        'wechat_aid' => 'WeChat AID',
+        'wechat_cid' => 'WeChat CID',
+        'wechat_encodingAESKey' => 'WeChat-Kodierungs-Schlüssel',
+        'wechat_qrcode' => 'WeChat-Pay-QR-Code',
+        'wechat_secret' => 'WeChat-App-Secret',
+        'wechat_token' => 'WeChat-Token',
+    ],
 ];

+ 37 - 37
resources/lang/de/notification.php

@@ -3,49 +3,49 @@
 declare(strict_types=1);
 
 return [
-    'account_expired' => 'Erinnerung an das Ablaufen des Kontos',
-    'account_expired_blade' => 'Ihr Konto läuft in :days Tagen ab, bitte erneuern Sie es rechtzeitig',
-    'account_expired_content' => 'Ihr Konto läuft in :days Tagen ab. Bitte erneuern Sie es rechtzeitig, um die Dienste weiterhin nutzen zu können.',
-    'active_email' => 'Bitte verifizieren Sie innerhalb von 30 Minuten',
+    'account_expired' => 'Kontoablauf-Erinnerung',
+    'account_expired_blade' => 'Ihr Konto läuft in :days Tagen ab, bitte verlängern Sie rechtzeitig',
+    'account_expired_content' => 'Ihr Konto läuft in :days Tagen ab. Bitte verlängern Sie es rechtzeitig, damit Ihre Dienste nicht unterbrochen werden.',
+    'active_email' => 'Bitte vervollständigen Sie die Verifizierung innerhalb von 30 Minuten',
     'attribute' => 'Benachrichtigung',
-    'block_report' => 'Blockierungsbericht:',
+    'block_report' => 'Detailliertes Blockierungsprotokoll:',
     'close_ticket' => 'Ticket [ID: :id, Titel: :title] wurde geschlossen',
-    'data_anomaly' => 'Warnung: Datenanomalie bei Benutzer',
-    'data_anomaly_content' => 'Benutzer [ID: :id], Datenverbrauch in der letzten Stunde: [Upload: :upload, Download: :download, Gesamt: :total]',
-    'details' => 'Einzelheiten anzeigen',
-    'details_btn' => 'Bitte klicken Sie auf die Schaltfläche unten, um die Einzelheiten anzuzeigen.',
-    'ding_bot_limit' => 'Jeder Bot darf maximal 20 Nachrichten pro Minute senden. Bei Überschreiten wird eine 10-minütige Drosselung angewendet.',
-    'empty' => 'Sie haben keine neuen Nachrichten',
-    'error' => '[:channel] Nachrichtenschub mit Ausnahme: :reason',
-    'get_access_token_failed' => 'Fehler beim Abrufen des Zugriffstokens!\nMit Anforderungsparametern: :body',
-    'into_maintenance' => 'Automatisch in den Wartungsmodus wechseln',
-    'new' => '{1} Sie haben :num neue Nachricht|[1,*] Sie haben :num neue Nachrichten',
-    'new_ticket' => 'Sie haben ein neues Ticket erhalten: [Titel: :title], bitte klicken Sie, um Details anzuzeigen.',
-    'next_check_time' => 'Nächste Knotensperrungserkennung: :time',
+    'data_anomaly' => 'Benutzer-Traffic-Anomalie-Erinnerung',
+    'data_anomaly_content' => 'Benutzer [ID: :id], Traffic-Situation der letzten Stunde (Upload: :upload, Download: :download, Gesamt: :total)',
+    'details' => 'Details anzeigen',
+    'details_btn' => 'Bitte klicken Sie auf die Schaltfläche unten [Details anzeigen]',
+    'ding_bot_limit' => 'Jeder Roboter kann maximal 20 Nachrichten pro Minute senden, bei Überschreitung wird 10 Minuten gedrosselt.',
+    'empty' => 'Sie haben derzeit keine neuen Nachrichten',
+    'error' => '[:channel] Nachrichtenpush-Ausnahme: :reason',
+    'get_access_token_failed' => 'Abrufen des access_token fehlgeschlagen!\nMit Zugriffsparametern: :body',
+    'into_maintenance' => 'Automatisch in Wartungsmodus wechseln',
+    'new' => 'Sie haben :num neue Nachrichten',
+    'new_ticket' => 'Sie haben ein neues Ticket erhalten [Titel: :title], bitte klicken Sie für Details.',
+    'next_check_time' => 'Nächste Knoten-Blockierungserkennungszeit: :time',
     'node' => [
         'download' => 'Download',
         'total' => 'Gesamt',
         'upload' => 'Upload',
     ],
-    'node_block' => 'Warnung: Node-Blockierung',
-    'node_offline' => 'Warnung: Node offline',
-    'node_offline_content' => 'Folgende Nodes sind möglicherweise offline:',
-    'node_renewal' => 'Erinnerung zur Verlängerung des Knotens',
-    'node_renewal_blade' => 'Die folgenden Knoten stehen kurz vor dem Ablauf. Bitte verlängern Sie rechtzeitig: :nodes',
-    'node_renewal_content' => 'Die folgenden Knoten stehen kurz vor dem Ablauf. Bitte verlängern Sie vor Ablauf, um Unterbrechungen des Dienstes zu vermeiden.',
-    'payment_received' => 'Zahlung erhalten, Betrag: :amount. Bestelldetails anzeigen',
-    'reply_ticket' => 'Ticket beantwortet: :title',
-    'reset_failed' => '[Tägliche Aufgabe] Datenrücksetzung für Benutzer [ID: :uid, Benutzername: :username] fehlgeschlagen',
-    'serverChan_exhausted' => 'Das heutige Limit wurde erschöpft!',
-    'serverChan_limit' => 'Frequenz pro Minute zu hoch. Bitte optimieren Sie die Benachrichtigungseinstellungen!',
-    'sign_failed' => 'Die sichere Signaturprüfung ist fehlgeschlagen',
-    'ticket_content' => 'Ticketinhalt:',
-    'traffic_remain' => 'Sie haben :percent% Ihres Datenvolumens verbraucht, bitte den verbleibenden Verbrauch sorgfältig planen.',
-    'traffic_tips' => 'Beachten Sie das Datum der Datenrücksetzung und planen Sie den Verbrauch entsprechend, oder erneuern Sie das Volumen nach dem Verbrauch.',
-    'traffic_warning' => 'Warnung: Datenverbrauch',
+    'node_block' => 'Knoten-Blockierungswarnung',
+    'node_offline' => 'Knoten-Offline-Warnung',
+    'node_offline_content' => 'Folgende Knoten sind anomal, möglicherweise offline:',
+    'node_renewal' => 'Knoten-Verlängerungs-Erinnerung',
+    'node_renewal_blade' => 'Knoten laufen bald ab, bitte verlängern Sie rechtzeitig: :nodes',
+    'node_renewal_content' => 'Folgende Knoten laufen bald ab, bitte verlängern Sie vor Ablauf, um Serviceunterbrechungen zu vermeiden.',
+    'payment_received' => 'Ihre Bestellzahlung war erfolgreich, Betrag: :amount, bitte klicken Sie für Bestelldetails',
+    'reply_ticket' => 'Ticket-Antwort: :title',
+    'reset_failed' => '[Tägliche Aufgabe] Benutzer [ID: :uid, Benutzername: :username] Traffic-Reset fehlgeschlagen',
+    'serverChan_exhausted' => 'Tageslimit erschöpft!',
+    'serverChan_limit' => 'Minutenfrequenz zu hoch, bitte optimieren Sie die Benachrichtigungsszenarien!',
+    'sign_failed' => 'Sicherheitssignatur-Verifizierung fehlgeschlagen',
+    'ticket_content' => 'Ticket-Inhalt:',
+    'traffic_remain' => 'Sie haben bereits :percent% Ihres Datenvolumens verbraucht. Bitte teilen Sie das verbleibende Volumen sinnvoll ein',
+    'traffic_tips' => 'Bitte beachten Sie das Datum der Datenvolumen-Erneuerung und planen Sie Ihre Nutzung entsprechend. Bei Bedarf können Sie zusätzliches Volumen erwerben.',
+    'traffic_warning' => 'Traffic-Nutzungs-Erinnerung',
     'verification' => 'Ihr Verifizierungscode lautet:',
-    'verification_account' => 'Konto-Verifizierung',
-    'verification_limit' => 'Bitte innerhalb von :minutes Minuten verifizieren',
-    'view_ticket' => 'Ticket anzeigen',
-    'view_web' => 'Website anzeigen',
+    'verification_account' => 'Kontoverifizierungs-Benachrichtigung',
+    'verification_limit' => 'Bitte vervollständigen Sie die Verifizierung innerhalb von :minutes Minuten',
+    'view_ticket' => 'Ticket-Fortschritt anzeigen',
+    'view_web' => 'Unsere offizielle Website besuchen',
 ];

+ 176 - 166
resources/lang/de/user.php

@@ -4,273 +4,283 @@ declare(strict_types=1);
 
 return [
     'account' => [
-        'connect_password' => 'Proxy-Verbindungspasswort',
+        'connect_password' => 'Knoten-Verbindungspasswort',
         'credit' => 'Kontoguthaben',
-        'group' => 'Gruppe',
-        'last_login' => 'Letzte Anmeldung',
-        'level' => 'Kontolevel',
+        'group' => 'Zugehörige Gruppe',
+        'last_login' => 'Letzter Login',
+        'level' => 'Kontostufe',
         'reason' => [
-            'expired' => 'Ihr Paket ist abgelaufen',
-            'normal' => 'Konto ist normal',
-            'overused' => 'Sie haben das Limit von <code>:data</code> GB für diesen Zeitraum überschritten<br/> Die Begrenzung wird in <code id="banedTime">:min</code> Minuten aufgehoben',
-            'traffic_exhausted' => 'Datenvolumen ist aufgebraucht',
-            'unknown' => 'Unbekannter Grund, bitte versuchen Sie, den Browser zu aktualisieren! Wenn das Problem weiterhin besteht, kontaktieren Sie den Support.',
+            'expired' => 'Paket ist abgelaufen',
+            'normal' => 'Kontostatus normal',
+            'overused' => 'Überschreitung des <code>:data</code>GB-Limits, Wiederherstellung in <code id="banedTime">:min</code> Minuten',
+            'traffic_exhausted' => 'Paket-Traffic ist aufgebraucht',
+            'unknown' => 'Unbekannter Fehler, bitte aktualisieren und erneut versuchen',
         ],
-        'remain' => 'Verbleibende Daten',
-        'reset' => '{0} Daten werden in <code id="restTime">:days</code> zurückgesetzt|{1} :days Tag bis zur Datenrücksetzung|[2,*] :days Tage bis zur Datenrücksetzung',
+        'remain' => 'Verbleibendes Traffic',
+        'reset' => '{0} Noch <code id="restTime">:days</code> bis Traffic-Reset|[1,*] Noch :days Tage bis Traffic-Reset',
         'speed_limit' => 'Geschwindigkeitsbegrenzung',
         'status' => 'Kontostatus',
-        'time' => 'Paketdauer',
+        'time' => 'Paket-Gültigkeitsdauer',
     ],
     'attribute' => [
-        'address' => 'Standort',
-        'data' => 'Daten',
+        'address' => 'Region',
+        'data' => 'Traffic',
         'ip' => 'IP-Adresse',
-        'isp' => 'ISP',
+        'isp' => 'Anbieter',
         'node' => 'Knoten',
     ],
     'bought_at' => 'Kaufdatum',
     'clients' => 'Clients',
-    'contact' => 'Kontakt',
+    'contact' => 'Kontaktmethode',
     'coupon' => [
         'discount' => 'Rabatt',
         'error' => [
-            'expired' => 'Gutschein abgelaufen',
-            'inactive' => 'Gutschein nicht aktiv',
-            'minimum' => 'Mindestbetrag ist :amount',
-            'overused' => 'Kann nur :times mal verwendet werden',
-            'run_out' => 'Gutschein aufgebraucht',
-            'services' => 'Artikel nicht für Rabatt berechtigt, überprüfen Sie die Aktionsbedingungen',
+            'expired' => 'Gutschein ist abgelaufen',
+            'inactive' => 'Gutschein ist noch nicht aktiv',
+            'minimum' => 'Mindestverwendungsbetrag für diesen Gutschein: :amount',
+            'overused' => 'Dieser Gutschein kann nur :times Mal verwendet werden',
+            'run_out' => 'Gutschein ist ausverkauft',
+            'services' => 'Produkt entspricht nicht den Nutzungsbedingungen, bitte Aktionsbedingungen prüfen',
             'unknown' => 'Ungültiger Gutschein',
-            'unmet' => 'Bedingungen nicht erfüllt',
-            'used' => 'Gutschein bereits verwendet',
-            'users' => 'Konto nicht für die Aktion berechtigt',
-            'wait' => 'Wird um :time aktiv, bitte warten!',
+            'unmet' => 'Nutzungsbedingungen nicht erfüllt',
+            'used' => 'Gutschein wurde bereits verwendet',
+            'users' => 'Konto entspricht nicht den Aktionsbedingungen',
+            'wait' => 'Diese Aktion beginnt um :time, bitte haben Sie Geduld!',
         ],
-        'input' => 'Gutscheincode eingeben',
+        'input' => 'Aufladegutschein-Code eingeben',
     ],
-    'current_role' => 'Aktuelle Rolle als',
-    'error_response' => 'Ein Fehler ist aufgetreten, bitte versuchen Sie es später erneut.',
+    'current_role' => 'Aktuelle Rolle',
+    'error_response' => 'System beschäftigt, bitte später erneut versuchen',
     'home' => [
         'announcement' => 'Ankündigungen',
         'attendance' => [
-            'attribute' => 'Einchecken',
-            'disable' => 'Einchecken deaktiviert',
-            'done' => 'Sie haben bereits eingecheckt. Kommen Sie morgen wieder!',
-            'failed' => 'Systemfehler',
-            'success' => 'Sie haben :data Daten erhalten',
+            'attribute' => 'Check-in',
+            'disable' => 'Check-in-Funktion ist deaktiviert',
+            'done' => 'Heute bereits eingecheckt',
+            'failed' => 'Systemanomalie',
+            'success' => 'Check-in erfolgreich +:data Traffic',
         ],
-        'chat_group' => 'Chat-Gruppe',
+        'chat_group' => 'Offizielle Community',
         'empty_announcement' => 'Keine Ankündigungen',
-        'traffic_logs' => 'Datenprotokolle',
+        'traffic_logs' => 'Traffic-Aufzeichnungen',
         'wechat_push' => 'WeChat-Benachrichtigungen',
     ],
     'invite' => [
         'attribute' => 'Einladungscode',
-        'counts' => 'Insgesamt <code>:num</code> Einladungscodes',
-        'generate_failed' => 'Generierung fehlgeschlagen: Kontingent überschritten',
-        'logs' => 'Einladungsprotokolle',
-        'promotion' => 'Sowohl Sie als auch der Eingeladene erhalten <mark>:traffic</mark> Daten, wenn sie sich mit Ihrem Code registrieren; Sie erhalten <mark>:referral_percent%</mark> Provision, wenn sie einen Kauf tätigen.',
-        'tips' => '<strong>:num</strong> Einladungen verbleiben, Codes verfallen :days Tage nach Erstellung',
+        'counts' => 'Verfügbar: <code>:num</code> Stück',
+        'generate_failed' => 'Generierungsquote unzureichend',
+        'logs' => 'Einladungsaufzeichnungen',
+        'promotion' => [
+            'base' => 'Erfolgreiche Einladungsbelohnung:<br>&nbsp;&nbsp;&nbsp;&nbsp;• Beide Seiten erhalten jeweils <mark>:traffic</mark> Traffic;',
+            'bonus' => [
+                0 => '',
+                1 => '<br>&nbsp;&nbsp;&nbsp;&nbsp;• Beim <strong>ersten</strong> Kauf des Eingeladenen erhalten Sie <mark>:referral_percent%</mark> Rabatt;',
+                2 => '<br>&nbsp;&nbsp;&nbsp;&nbsp;• Bei <strong>jedem</strong> Verbrauch des Eingeladenen erhalten Sie <mark>:referral_percent%</mark> Rabatt;',
+            ],
+        ],
+        'tips' => 'Verbleibende Plätze: <strong>:num</strong>, Gültigkeitsdauer :days Tage',
     ],
-    'invitee' => 'Eingeladener',
-    'inviter' => 'Einladender',
+    'invitee' => 'Eingeladener Benutzer',
+    'inviter' => 'Einlader',
     'invoice' => [
-        'active_prepaid_question' => 'Prepaid-Paket frühzeitig aktivieren?',
-        'active_prepaid_tips' => 'Nach der Aktivierung:<br>Ihr aktueller Plan wird sofort ablaufen<br>Das Ablaufdatum des neuen Plans wird ab heute neu berechnet',
+        'active_prepaid_question' => 'Prepaid-Paket aktivieren?',
+        'active_prepaid_tips' => '<p class="text-left">Frühzeitige Aktivierung bedeutet:</p><ol class="text-left"><li>Aktuelles Paket wird sofort ungültig, verbleibende Gültigkeitsdauer verfällt;</li><li>Neues Paket wird sofort wirksam und beginnt ab der aktuellen Zeit zu zählen;</li></ol>',
         'amount' => 'Betrag',
         'attribute' => 'Bestellung',
-        'detail' => 'Bestelldetails',
-    ],
-    'knowledge' => [
-        'basic' => 'Grundlagen',
-        'title' => 'Wissensdatenbank',
+        'detail' => 'Verbrauchsaufzeichnungen',
     ],
     'menu' => [
-        'admin_dashboard' => 'Dashboard',
-        'help' => 'Hilfe',
+        'admin_dashboard' => 'Verwaltungsbackend',
+        'help' => 'Hilfezentrum',
         'home' => 'Startseite',
-        'invites' => 'Einladen',
-        'invoices' => 'Rechnungen',
-        'nodes' => 'Knoten',
-        'profile' => 'Profil',
-        'promotion' => 'Empfehlung',
-        'shop' => 'Shop',
-        'tickets' => 'Tickets',
+        'invites' => 'Einladungsverwaltung',
+        'invoices' => 'Meine Bestellungen',
+        'nodes' => 'Knotenliste',
+        'profile' => 'Kontoeinstellungen',
+        'promotion' => 'Werbeplan',
+        'shop' => 'Service-Shop',
+        'tickets' => 'Meine Tickets',
     ],
     'node' => [
-        'info' => 'Konfigurationsinfo',
-        'rate' => ':ratio-facher Datenverbrauch',
+        'info' => 'Konfigurationsinformationen',
+        'rate' => 'Traffic-Multiplikator: :ratio',
         'setting' => 'Proxy-Einstellungen',
-        'unstable' => 'Instabil/Wartung',
+        'unstable' => 'Schwankung/In Wartung',
     ],
     'oauth' => [
-        'bind' => 'Binden',
-        'bind_title' => 'Soziales Konto binden',
-        'not_bind' => 'Nicht gebunden',
-        'rebind' => 'Neu binden',
-        'unbind' => 'Entbinden',
+        'bind' => 'Verknüpfen',
+        'bind_title' => 'Social-Media-Konto verknüpfen',
+        'not_bind' => 'Nicht verknüpft',
+        'rebind' => 'Neu verknüpfen',
+        'unbind' => 'Verknüpfung lösen',
     ],
     'pay' => 'Bezahlen',
     'payment' => [
-        'close_tips' => 'Schließen Sie die Zahlung innerhalb von <code>:minutes</code> Minuten ab, sonst wird die Bestellung automatisch geschlossen',
-        'creating' => 'Zahlung wird erstellt...',
-        'error' => 'Ungültiger Aufladebetrag',
-        'insufficient_balance' => 'Ihr Guthaben ist unzureichend. Bitte laden Sie es zuerst auf.',
+        'close_tips' => 'Bitte Zahlung innerhalb von <code>:minutes</code> Minuten abschließen',
+        'creating' => 'Bestellung wird erstellt...',
+        'error' => 'Betrag ist ungültig',
+        'insufficient_balance' => 'Guthaben unzureichend',
         'manual' => [
-            'hint' => 'Nach dem Scannen des QR-Codes zur Zahlung folgen Sie bitte der Reihenfolge der Schritte, bis Sie auf „Abschicken“ klicken, um die Zahlung abzuschließen.',
-            'next' => 'Weiter',
-            'payment_tips' => 'Bitte den genauen Betrag zahlen (keine Rückerstattung bei Überzahlung, Nachzahlung bei Unterzahlung)',
-            'pre' => 'Zurück',
-            'red_packet' => 'Alipay-Rotpaket',
+            'hint' => 'Nach Zahlung Nachweis gemäß Verfahren einreichen',
+            'next' => 'Nächster Schritt',
+            'payment_tips' => 'Bitte exakt bezahlen (weniger muss nachgezahlt werden)',
+            'pre' => 'Vorheriger Schritt',
+            'red_packet' => 'Alipay-Rotes Paket',
             'steps' => [
                 'complete' => [
-                    'description' => 'Warten auf manuelle Zahlungsüberprüfung',
-                    'title' => 'Abschluss',
+                    'description' => 'Warten auf manuelle Prüfung',
+                    'title' => 'Abgeschlossen',
                 ],
                 'notice' => [
-                    'description' => 'Wie man manuell bezahlt',
+                    'description' => 'Anleitung für manuelle Zahlung',
                     'title' => 'Hinweise',
                 ],
                 'payment' => [
-                    'description' => 'QR-Code erhalten und bezahlen',
-                    'title' => 'Bezahlen',
+                    'description' => 'QR-Code scannen, Zahlung durchführen',
+                    'title' => 'Zahlung',
                 ],
                 'remark' => [
-                    'description' => 'Geben Sie Ihr Login-Konto zur manuellen Überprüfung ein',
-                    'title' => 'Kontobemerkung',
+                    'description' => 'Konto angeben für manuelle Überprüfung',
+                    'title' => 'Bemerkung',
                 ],
             ],
         ],
         'method' => 'Zahlungsmethode',
-        'mobile_tips' => '<strong>Mobile Benutzer:</strong> Halten Sie den QR-Code gedrückt -> Bild speichern -> Zahlungs-App öffnen -> Bild scannen, um zu bezahlen',
+        'mobile_tips' => '<strong>Mobilbenutzer</strong>: Lang drücken zum Speichern → Zahlungs-App scannt Album',
         'order_creation' => [
-            'failed' => 'Erstellung der Bestellung fehlgeschlagen. Bitte versuchen Sie eine andere Zahlungsmethode!',
-            'info' => 'Wir werden Ihre Bestellung/Ihre Aufladung innerhalb von [24 Stunden] aktivieren! Bitte haben Sie Geduld.',
-            'order_limit' => 'Dieser Artikel ist auf :limit_num Käufe begrenzt. Sie haben bereits :count Mal gekauft.',
-            'order_timeout' => 'Die Bestellung ist abgelaufen und wurde aufgrund fehlender Zahlung automatisch geschlossen.',
-            'payment_disabled' => 'Bestellerstellung fehlgeschlagen: Die Online-Zahlungsfunktion ist nicht aktiviert.',
-            'pending_order' => 'Bestellerstellung fehlgeschlagen: Es gibt noch unbezahlte Bestellungen. Bitte schließen Sie diese Zahlungen zuerst ab.',
-            'plan_required' => 'Bitte kaufen Sie ein Paket, bevor Sie das Aufladepaket erwerben.',
-            'price_issue' => 'Bestellerstellung fehlgeschlagen: Ungewöhnlicher Gesamtpreis der Bestellung',
-            'price_zero' => 'Bestellerstellung fehlgeschlagen: Der Gesamtpreis der Bestellung beträgt 0; eine Online-Zahlung ist nicht erforderlich.',
-            'product_unavailable' => 'Bestellerstellung fehlgeschlagen: Der Artikel wurde aus dem Verkauf genommen.',
+            'failed' => 'Bestellungserstellung fehlgeschlagen, bitte andere Zahlungsmethode versuchen!',
+            'info' => 'Wir werden Kauf-/Aufladungsbeträge innerhalb von【24 Stunden】aktivieren! Bitte haben Sie Geduld!',
+            'order_limit' => 'Dieses Produkt ist auf :limit_num Käufe begrenzt, Sie haben bereits :count Mal gekauft!',
+            'order_timeout' => 'Bestellung wegen Nichtzahlung automatisch geschlossen!',
+            'payment_disabled' => 'Bestellungserstellung fehlgeschlagen: System hat Online-Zahlungsfunktion nicht aktiviert!',
+            'pending_order' => 'Bestellungserstellung fehlgeschlagen: Sie haben unbezahlte Bestellung, bitte zuerst abschließen oder stornieren!',
+            'plan_required' => 'Vor Kauf von Zusatzpaketen bitte zuerst Paket kaufen!',
+            'price_issue' => 'Bestellungserstellung fehlgeschlagen: Bestellgesamtpreis anomal!',
+            'price_zero' => 'Bestellungserstellung fehlgeschlagen: Bestellgesamtpreis ist 0, keine Online-Zahlung erforderlich!',
+            'product_unavailable' => 'Bestellungserstellung fehlgeschlagen: Produkt ist nicht mehr verfügbar!',
             'success' => 'Bestellung erfolgreich erstellt!',
             'unknown_order' => 'Unbekannte Bestellung',
             'unknown_payment' => 'Unbekannte Zahlungsmethode',
         ],
-        'qrcode_tips' => 'Bitte scannen Sie mit <strong class="red-600">:software</strong>',
-        'redirect_stripe' => 'Weiterleitung zu Stripe',
+        'qrcode_tips' => 'Bitte mit <strong class="red-600">:software</strong> diesen QR-Code scannen',
+        'redirect_stripe' => 'Weiterleitung zu Stripe-Zahlung',
     ],
     'purchase' => [
         'completed' => 'Kauf abgeschlossen!',
-        'promotion' => 'Jetzt Dienst kaufen!',
-        'required' => 'Diese Funktion ist für nicht zahlende Benutzer deaktiviert. Bitte',
-        'to_unlock' => 'Freischalten durch Kauf',
+        'promotion' => 'Service sofort freischalten',
+        'required' => 'Diese Funktion steht nur zahlenden Benutzern zur Verfügung. Bitte',
+        'to_unlock' => 'Kauf zum Freischalten',
     ],
     'recharge' => 'Aufladen',
-    'recharge_credit' => 'Guthaben aufladen',
-    'recharging' => 'Aufladen...',
+    'recharge_credit' => 'Guthaben-Aufladung',
+    'recharging' => 'Wird aufgeladen...',
     'referral' => [
-        'link' => 'Empfehlungslink',
-        'logs' => 'Provisionsprotokolle',
+        'link' => 'Werbelink',
+        'logs' => 'Provisionsaufzeichnungen',
         'msg' => [
-            'account' => 'Konto abgelaufen, bitte zuerst ein Paket kaufen',
-            'applied' => 'Bestehende Anfrage, bitte warten Sie auf die Bearbeitung',
-            'error' => 'Fehler bei der Erstellung der Bestellung, versuchen Sie es später erneut oder kontaktieren Sie den Support',
-            'unfulfilled' => 'Benötigt :amount zur Auszahlung, weiter so!',
-            'wait' => 'Bitte warten Sie auf die Genehmigung des Administrators',
+            'account' => 'Konto ist abgelaufen, bitte zuerst Service kaufen',
+            'applied' => 'Antrag bereits vorhanden, bitte warten bis vorheriger Antrag bearbeitet ist',
+            'error' => 'Antrag fehlgeschlagen, bitte später versuchen oder Ticket erstellen',
+            'unfulfilled' => 'Ab :amount Euro Auszahlung möglich, bitte weiter bemühen!',
+            'wait' => 'Warten auf Prüfung',
         ],
-        'total' => 'Gesamtprovision: :amount (:total Mal), kann ab :money ausgezahlt werden',
+        'total' => 'Kumulierte Rabatte :amount (:total Mal), ab :money auszahlbar',
     ],
     'registered_at' => 'Registrierungsdatum',
     'reset_data' => [
-        'action' => 'Daten zurücksetzen',
-        'cost' => 'Kosten: <code>:amount</code>',
-        'cost_tips' => 'Das Zurücksetzen wird :amount abziehen!',
+        'action' => 'Traffic zurücksetzen',
+        'cost' => 'Erfordert Abzug von <code>:amount</code>',
+        'cost_tips' => 'Für diesen Vorgang werden :amount von Ihrem Guthaben abgezogen',
     ],
-    'scan_qrcode' => 'QR-Code mit Client scannen',
+    'scan_qrcode' => 'Client scannt Code zum Hinzufügen',
     'service' => [
-        'country_count' => 'Deckt <code>:num</code> Länder oder Regionen ab',
+        'country_count' => 'Abdeckung von <code>:num</code> Ländern/Regionen',
         'node_count' => '<code>:num</code> hochwertige Knoten',
-        'unlimited' => 'Unbegrenzte Geschwindigkeit',
+        'unlimited' => 'Keine Geschwindigkeitsbegrenzung',
     ],
     'shop' => [
-        'buy' => 'Kaufen',
-        'call4help' => 'Kontaktieren Sie den Support, wenn Sie Fragen haben',
+        'buy' => 'Sofort kaufen',
+        'support' => 'Fragen? Kundensupport kontaktieren',
         'change_amount' => 'Aufladebetrag',
-        'change_amount_help' => 'Aufladebetrag eingeben',
-        'conflict' => 'Konflikt',
-        'conflict_tips' => '<p>Der aktuelle Kauf wird als <code>Prepaid-Paket</code> festgelegt</p><ol><li>Das Prepaid-Paket wird automatisch aktiviert, nachdem das aktuelle Paket abläuft</li><li>Sie können es nach der Zahlung manuell aktivieren</li></ol>',
-        'description' => 'Beschreibung',
-        'hot' => 'Beliebt',
-        'limited' => 'Begrenzt',
-        'pay_credit' => 'Mit Guthaben bezahlen',
-        'pay_online' => 'Online bezahlen',
+        'change_amount_help' => 'Betrag eingeben',
+        'conflict' => 'Paketkonflikt',
+        'conflict_tips' => '<p>Aktuell gekauftes Paket wird als <code>Prepaid-Paket</code> gesetzt</p><ol class="text-left"><li>Prepaid-Paket wird automatisch wirksam nachdem aktuelles Paket ungültig wird</li><li>Sie können auch nach Zahlung auf der Bestellseite Prepaid-Paket manuell aktivieren</li></ol>',
+        'description' => 'Produktbeschreibung',
+        'hot' => 'Bestseller',
+        'limited' => 'Limitiert',
+        'pay_credit' => 'Guthaben-Zahlung',
+        'pay_online' => 'Online-Zahlung',
         'price' => 'Preis',
         'quantity' => 'Menge',
-        'service' => 'Dienst',
+        'service' => 'Service',
         'subtotal' => 'Zwischensumme',
         'total' => 'Gesamt',
     ],
     'subscribe' => [
         'custom' => 'Benutzerdefiniertes Abonnement',
-        'error' => 'Fehler beim Ändern des Abonnement-Links',
-        'exchange_warning' => 'Das Ändern des Abonnement-Links wird:\n1. Den aktuellen Link sofort ungültig machen\n2. Das Verbindungspasswort ändern',
+        'error' => 'Abonnement-Adresse-Update fehlgeschlagen',
+        'exchange_warning' => '<p>Wechsel der Abonnement-Adresse führt zu</p><ol class="text-left"><li>Sofortige Ungültigkeit der alten Adresse;</li><li>Änderung des Knoten-Verbindungspassworts;</li></ol>',
         'info' => [
-            'download' => 'Download',
-            'title' => 'Kontoübersicht [Nicht in Echtzeit]',
-            'total' => 'Plan-Daten',
-            'upload' => 'Upload',
+            'download' => 'Verwendeter Download',
+            'title' => 'Kontozusammenfassung [Nicht Echtzeit]',
+            'total' => 'Paket-Traffic',
+            'upload' => 'Verwendeter Upload',
         ],
         'link' => 'Abonnement-Link',
-        'ss_only' => 'Nur SS abonnieren',
-        'ssr_only' => 'Nur SSR (inkl. SS) abonnieren',
-        'tips' => 'Warnung: Dieser Link ist nur für den persönlichen Gebrauch. Bitte nicht weitergeben, sonst kann Ihr Konto wegen abnormaler Nutzung gesperrt werden.',
-        'trojan_only' => 'Nur Trojan abonnieren',
-        'v2ray_only' => 'Nur V2Ray abonnieren',
+        'ss_only' => 'Nur SS-Abonnement',
+        'ssr_only' => 'SSR-Abonnement (inkl. SS)',
+        'tips' => 'Warnung: Dieser Link ist nur für den persönlichen Gebrauch, Verbreitung führt zur Kontosperrung',
+        'trojan_only' => 'Nur Trojan-Abonnement',
+        'v2ray_only' => 'Nur V2Ray-Abonnement',
+        'page' => [
+            'get_link' => 'Link abrufen',
+            'connect' => 'Verbinden & Nutzen',
+            'error' => [
+                'no_app' => 'Keine verfügbaren Anwendungen',
+            ],
+        ],
     ],
     'telegram' => [
-        'bind_exists' => 'Dieses Konto ist bereits mit einem Telegram-Konto verknüpft.',
-        'bind_missing' => 'Keine Benutzerinformationen gefunden. Bitte verknüpfen Sie zuerst Ihr Konto.',
+        'bind_exists' => 'Telegram-Konto bereits verknüpft',
+        'bind_missing' => 'Keine Verknüpfungsinformationen gefunden',
         'command' => [
-            'bind' => 'Verknüpfen Sie Ihr :web_name-Konto',
-            'intro' => 'Sie können die folgenden Befehle verwenden',
-            'traffic' => 'Datenverbrauch überprüfen',
-            'unbind' => 'Trennen',
-            'web_url' => 'Holen Sie sich die neueste :web_name-URL',
+            'bind' => ':web_name Konto verknüpfen',
+            'intro' => 'Verfügbare Befehle',
+            'traffic' => 'Traffic-Abfrage',
+            'unbind' => 'Konto-Verknüpfung lösen',
+            'web_url' => 'Neueste :web_name Adresse abrufen',
         ],
-        'get_url' => 'Die neueste URL für :web_name lautet',
-        'params_missing' => 'Ungültige Parameter. Bitte fügen Sie Ihre E-Mail-Adresse bei und senden Sie erneut.',
+        'get_url' => ':web_name neueste Adresse',
+        'params_missing' => 'Parameter fehlerhaft, bitte mit E-Mail-Adresse senden',
         'ticket_missing' => 'Ticket existiert nicht',
-        'ticket_reply' => 'Antwort auf Ticket #`:id` war erfolgreich',
-        'traffic_query' => 'Datenverbrauchsanfrage',
+        'ticket_reply' => 'Ticket #:id wurde beantwortet',
+        'traffic_query' => 'Traffic-Abfrage',
         'user_missing' => 'Benutzer existiert nicht',
     ],
     'ticket' => [
         'attribute' => 'Ticket',
-        'close_msg' => 'Ticket ID :id vom Benutzer geschlossen',
-        'close_tips' => 'Ticket wirklich schließen?',
-        'content_placeholder' => 'Beschreiben Sie Ihr Problem detailliert, damit wir Ihnen besser helfen können',
-        'error' => 'Unbekannter Fehler! Bitte kontaktieren Sie den Support',
-        'new' => 'Neues Ticket erstellen',
+        'close_msg' => 'Ticket #:id wurde geschlossen',
+        'close_tips' => 'Dieses Ticket wirklich schließen?',
+        'content_placeholder' => 'Bitte Problem oder Anfrage so detailliert wie möglich beschreiben, wir werden Ihnen schnellstmöglich helfen',
+        'error' => 'Systemanomalie, bitte Kundensupport kontaktieren',
+        'new' => 'Neues Ticket',
         'online_hour' => 'Online-Zeiten',
         'reply' => 'Antworten',
-        'reply_confirm' => 'Antwort auf Ticket wirklich senden?',
-        'reply_placeholder' => 'Schreiben Sie etwas...',
-        'service_hours' => 'Kundendienstzeiten',
-        'service_tips' => 'Bitte verwenden Sie nur <code>eine</code> Kontaktmethode, um den Support zu erreichen! Mehrfache Anfragen verzögern die Antwortzeit.',
-        'submit_tips' => 'Ticket wirklich einreichen?',
-        'title_placeholder' => 'Beschreiben Sie kurz Ihr Problem',
+        'reply_confirm' => 'Antwort bestätigen?',
+        'reply_placeholder' => 'Etwas sagen?',
+        'service_hours' => 'Kundensupport-Zeiten',
+        'service_tips' => 'Bitte über <code>eine</code> Kontaktmethode Kundensupport kontaktieren, mehrfache Einreichungen vermeiden um Bearbeitungsfortschritt nicht zu beeinträchtigen.',
+        'submit_tips' => 'Ticket-Einreichung bestätigen?',
+        'title_placeholder' => 'Bitte Problem kurz beschreiben',
     ],
     'traffic_logs' => [
         'daily' => 'Datenverbrauch diesen Monat',
-        'hourly' => 'Heutiger Datenverbrauch',
-        'tips' => 'Hinweis: Es gibt eine Verzögerung bei der Aktualisierung der Datenstatistiken.',
+        'hourly' => 'Datenverbrauch heute',
+        'tips' => 'Hinweis: Datenaktualisierung hat Verzögerung',
     ],
-    'tutorials' => 'Anleitungen',
-    'withdraw' => 'Abheben',
-    'withdraw_at' => 'Abhebungsdatum',
-    'withdraw_commission' => 'Provision abheben',
-    'withdraw_logs' => 'Abhebungsprotokolle',
+    'tutorials' => 'Nutzungsanleitungen',
+    'withdraw' => 'Auszahlung',
+    'withdraw_at' => 'Auszahlungsdatum',
+    'withdraw_commission' => 'Provisions-Auszahlung',
+    'withdraw_logs' => 'Auszahlungsaufzeichnungen',
 ];

+ 1 - 1
resources/lang/en.json

@@ -137,5 +137,5 @@
   "Recharge using a recharge voucher.": "Recharge using a recharge voucher.",
   "The user topped up the balance.": "The user topped up the balance.",
   "Purchased an item.": "Purchased an item.",
-  "[:payment] plus the user’s purchased data plan.": "[:payment] plus the user’s purchased data plan."
+  "[:payment] plus the user's purchased data plan.": "[:payment] plus the user's purchased data plan."
 }

+ 8 - 2
resources/lang/en/admin.php

@@ -27,7 +27,7 @@ return [
     'clone' => 'Clone',
     'confirm' => [
         'continues' => 'Are you sure you want to continue?',
-        'delete' => [0 => 'Confirm deletion of :attribute', 1 => '? This action is irreversible!'],
+        'delete' => 'Confirm deletion of :attribute [:name]? This action is irreversible!',
         'export' => 'Confirm export of all data?',
     ],
     'coupon' => [
@@ -698,7 +698,7 @@ return [
         'online_monitor' => 'Online monitor',
         'proxies_config' => '[:username] proxy config',
         'proxy_info' => 'Proxy Information',
-        'reset_confirm' => [0 => '⚠️ Confirm reset traffic for ', 1 => '?'],
+        'reset_confirm' => '⚠️ Confirm resetting traffic for :username?',
         'reset_traffic' => 'Reset traffic',
         'traffic_monitor' => 'Traffic monitor',
         'update_help' => 'Updated! Return to list?',
@@ -707,4 +707,10 @@ return [
     'user_dashboard' => 'User Center',
     'yes' => 'Yes',
     'zero_unlimited_hint' => 'No setting/0 means no restriction',
+    'network_status' => [
+        1 => '✔️ Normal',
+        2 => '🛑 Blocked Overseas',
+        3 => '🛑 Blocked Domestically',
+        4 => '❌ Disconnected',
+    ],
 ];

+ 1 - 0
resources/lang/en/common.php

@@ -46,6 +46,7 @@ return [
     'deleted_item' => ':attribute deleted',
     'developing' => 'Under development! Stay tuned',
     'download' => 'Download',
+    'download_item' => 'Download :attribute',
     'edit' => 'Edit',
     'error' => 'Error',
     'error_action_item' => 'Error during :action :attribute',

+ 106 - 106
resources/lang/fa.json

@@ -1,141 +1,141 @@
 {
   "(and :count more error)": "(و :count خطای دیگر)",
   "(and :count more errors)": "(و :count خطای دیگر)",
-  "----「:job」Completed, Used :time seconds ----": "----「:job」تکمیل شد، مدت زمان: :time ثانیه ----",
+  "----「:job」Completed, Used :time seconds ----": "----「:job」تکمیل شد، زمان صرف شده :time ثانیه ----",
   "[Auto Task] Blocked service: Abnormal traffic within 1 hour": "[وظیفه خودکار] سرویس مسدود شد: ترافیک غیرعادی در 1 ساعت",
-  "[Auto Task] Blocked service: Run out of traffic": "[وظیفه خودکار] سرویس مسدود شد: ترافیک به پایان رسید",
+  "[Auto Task] Blocked service: Run out of traffic": "[وظیفه خودکار] سرویس مسدود شد: ترافیک تمام شده",
   "[Auto Task] Blocked Subscription: Subscription with abnormal requests within 24 hours": "[وظیفه خودکار] اشتراک مسدود شد: درخواست‌های غیرعادی در 24 ساعت",
-  "[Auto Task] Unblocked Service: Account ban expired": "[وظیفه خودکار] سرویس باز شد: ممنوعیت حساب منقضی شد",
-  "[Auto Task] Unblocked Service: Account has available data traffic": "[وظیفه خودکار] سرویس باز شد: حساب دارای ترافیک داده قابل استفاده است",
-  "[Daily Task] Account Expiration: Block Login & Clear Account": "[وظیفه روزانه] انقضای حساب: مسدود کردن ورود و پاکسازی حساب",
+  "[Auto Task] Unblocked Service: Account ban expired": "[وظیفه خودکار] سرویس رفع مسدودی شد: مدت مسدودی حساب به پایان رسید",
+  "[Auto Task] Unblocked Service: Account has available data traffic": "[وظیفه خودکار] سرویس رفع مسدودی شد: حساب دارای ترافیک قابل استفاده است",
+  "[Daily Task] Account Expiration: Block Login & Clear Account": "[وظیفه روزانه] انقضای حساب: مسدود کردن ورود و پاک کردن حساب",
   "[Daily Task] Account Expiration: Stop Service": "[وظیفه روزانه] انقضای حساب: توقف سرویس",
   "[Daily Task] Reset Account Traffic, Next Reset Date: :date": "[وظیفه روزانه] بازنشانی ترافیک حساب، تاریخ بازنشانی بعدی: :date",
   "[Service Timer] Service Expiration": "[تایمر سرویس] انقضای سرویس",
-  "A Timeout Occurred": "مهلت زمانی تمام شد.",
-  "Accepted": "پذیرفته شده",
-  "All rights reserved.": "کلیه حقوق محفوظ است.",
-  "Already Reported": "قبلا گزارش شده است",
-  "Bad Gateway": "درگاه خراب",
-  "Bad Request": "درخواست ناصحیح",
-  "Bandwidth Limit Exceeded": "ظرفیت پهنای‌باند به اتمام رسید",
-  "Client Closed Request": "درخواست توسط کاربر بسته شد",
-  "Conflict": "ناسازگاری",
+  "A Timeout Occurred": "تایم‌اوت رخ داد",
+  "Accepted": "پذیرفته شد",
+  "All rights reserved.": "تمام حقوق محفوظ است.",
+  "Already Reported": "قبلاً گزارش شده",
+  "Bad Gateway": "خطای دروازه",
+  "Bad Request": "درخواست نامعتبر",
+  "Bandwidth Limit Exceeded": "محدودیت پهنای باند تجاوز کرد",
+  "Client Closed Request": "کلاینت درخواست را بست",
+  "Conflict": "تضاد",
   "Connection Closed Without Response": "اتصال بدون پاسخ بسته شد",
-  "Connection Timed Out": "مهلت زمانی اتصال تمام شد",
-  "Continue": "ادامه",
+  "Connection Timed Out": "اتصال تایم‌اوت شد",
+  "Continue": "ادامه درخواست",
   "Created": "ایجاد شد",
-  "Daily Data Usage Report": "گزارش استفاده روزانه از داده",
-  "Expectation Failed": "انتظار با شکست مواجه شده",
-  "Failed Dependency": "وابستگی شکست‌خورده",
-  "Forbidden": "عدم دسترسی",
-  "Found": "انتقال موقتی",
-  "Gateway Timeout": "وقفهٔ در گاه",
-  "Go to page :page": "برو به صفحه :page",
-  "Gone": "رفته",
+  "Daily Data Usage Report": "گزارش استفاده روزانه ترافیک",
+  "Expectation Failed": "انتظار برآورده نشد",
+  "Failed Dependency": "وابستگی ناموفق",
+  "Forbidden": "دسترسی رد شد",
+  "Found": "انتقال موقت",
+  "Gateway Timeout": "تایم‌اوت دروازه",
+  "Go to page :page": "رفتن به صفحه :page",
+  "Gone": "در دسترس نیست",
   "Hello!": "سلام!",
-  "HTTP Version Not Supported": "نگارش HTTP پشتیبانی نمی‌شود",
-  "I'm a teapot": "من یک teapot)قوری( هستم",
-  "If you did not create an account, no further action is required.": "چنانچه شما حساب کاربری ایجاد نکرده اید، نیاز به اقدام خاصی نیست.",
-  "If you did not request a password reset, no further action is required.": "اگر شما درخواست تغییر رمزعبور را نکرده اید، نیاز به اقدام خاصی نیست.",
-  "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "اگر با کلیک کردن روی دکمه \":actionText\" مشکل دارید، لینک زیر را کپی کنید و در مرورگر خود وارد کنید:",
+  "HTTP Version Not Supported": "نسخه HTTP پشتیبانی نمی‌شود",
+  "I'm a teapot": "من یک قوری هستم",
+  "If you did not create an account, no further action is required.": "اگر حساب ایجاد نکرده‌اید، لطفاً این ایمیل را نادیده بگیرید.",
+  "If you did not request a password reset, no further action is required.": "اگر درخواست بازنشانی رمز عبور نکرده‌اید، لطفاً این ایمیل را نادیده بگیرید.",
+  "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "اگر در کلیک کردن دکمه «:actionText» مشکل دارید، لینک زیر را کپی کرده و در مرورگر خود باز کنید:",
   "IM Used": "IM استفاده شده",
-  "Insufficient Storage": "فضای ناکافی",
+  "Insufficient Storage": "فضای ذخیره‌سازی ناکافی",
   "Internal Server Error": "خطای داخلی سرور",
-  "Invalid JSON was returned from the route.": "JSON نامعتبر از مسیر برگردانده شد.",
+  "Invalid JSON was returned from the route.": "JSON نامعتبر از مسیر بازگردانده شد.",
   "Invalid SSL Certificate": "گواهی SSL نامعتبر",
-  "Invoice Detail": "جزئیات فاکتور",
-  "Length Required": "طول مورد نیاز است",
+  "Invoice Detail": "جزئیات سفارش",
+  "Length Required": "طول مورد نیاز",
   "Locked": "قفل شده",
   "Login": "ورود",
   "Logout": "خروج",
-  "Loop Detected": "حلقه شناسایی شد",
+  "Loop Detected": "حلقه تشخیص داده شد",
   "Maintenance Mode": "سرویس در دسترس نیست",
-  "Method Not Allowed": "متود مجاز نیست",
-  "Misdirected Request": "درخواست نادرست",
-  "Moved Permanently": "انتقال دائم",
-  "Multi-Status": "چندین-وضعیت",
+  "Method Not Allowed": "روش مجاز نیست",
+  "Misdirected Request": "درخواست اشتباه",
+  "Moved Permanently": "انتقال دائمی",
+  "Multi-Status": "چند وضعیت",
   "Multiple Choices": "چندین انتخاب",
-  "Network Authentication Required": "احراز هویت درون شبکه لازم است",
-  "Network Connect Timeout Error": "مهلت اتصال شبکه تمام شد",
-  "Network Read Timeout Error": "مهلت خواندن شبکه تمام شد",
-  "No Content": "بدون محتویات",
-  "Non-Authoritative Information": "اطلاعات نامعتبر",
+  "Network Authentication Required": "احراز هویت شبکه مورد نیاز",
+  "Network Connect Timeout Error": "خطای تایم‌اوت اتصال شبکه",
+  "Network Read Timeout Error": "خطای تایم‌اوت خواندن شبکه",
+  "No Content": "محتوایی وجود ندارد",
+  "Non-Authoritative Information": "اطلاعات غیرمعتبر",
   "Not Acceptable": "قابل قبول نیست",
   "Not Extended": "تمدید نشده",
-  "Not Found": "یافت نشد",
-  "Not Implemented": "اجرا نشده",
-  "Not Modified": "اصلاح نشده",
+  "Not Found": "صفحه وجود ندارد",
+  "Not Implemented": "پیاده‌سازی نشده",
+  "Not Modified": "تغییر نکرده",
   "of": "از",
-  "OK": "موفقیت آمیز",
-  "Origin Is Unreachable": "مبدا قابل دستیابی نیست",
-  "Page Expired": "صفحه منقضی شده است",
-  "Pagination Navigation": "راهنمای صفحه بندی",
-  "Partial Content": "محتوی جزئی",
-  "Payload Too Large": "موجودیت درخواست بسیار بزرگ است",
-  "Payment for #:sn has been received! Total amount: :amount.": "پرداخت برای #:sn دریافت شد! مبلغ کل: :amount.",
-  "Payment Received": "پرداخت دریافت شد",
-  "Payment Required": "پرداخت لازم",
-  "Permanent Redirect": "تغییر مسیر دائم",
-  "Please click the button below to verify your email address.": "برای تایید آدرس ایمیل روی دکمه زیر کلیک کنید.",
-  "Precondition Failed": "پیش شرط با شکست مواجه شده",
-  "Precondition Required": "پیش شرط لازم است",
-  "Processing": "پردازش درخواست",
-  "Proxy Authentication Required": "نیاز به تصدیق پراکسی می‌باشد",
-  "Railgun Error": "خطای Railgun",
-  "Range Not Satisfiable": "محدودهٔ درخواست شده رضایت پذیر نیست",
+  "OK": "درخواست موفق",
+  "Origin Is Unreachable": "مبدأ در دسترس نیست",
+  "Page Expired": "جلسه صفحه منقضی شده",
+  "Pagination Navigation": "ناوبری صفحه‌بندی",
+  "Partial Content": "محتوای جزئی",
+  "Payload Too Large": "بار درخواست بیش از حد بزرگ",
+  "Payment for #:sn has been received! Total amount: :amount.": "پرداخت سفارش #:sn دریافت شد! مبلغ کل: :amount.",
+  "Payment Received": "پرداخت موفق",
+  "Payment Required": "پرداخت مورد نیاز",
+  "Permanent Redirect": "تغییر مسیر دائمی",
+  "Please click the button below to verify your email address.": "لطفاً روی دکمه زیر کلیک کنید تا آدرس ایمیل خود را تأیید کنید:",
+  "Precondition Failed": "پیش‌شرط برآورده نشد",
+  "Precondition Required": "پیش‌شرط مورد نیاز",
+  "Processing": "در حال پردازش",
+  "Proxy Authentication Required": "احراز هویت پروکسی مورد نیاز",
+  "Railgun Error": "خطای ریل‌گان",
+  "Range Not Satisfiable": "محدوده درخواست مطابقت ندارد",
   "Regards": "با احترام",
   "Register": "ثبت نام",
-  "Request Header Fields Too Large": "فیلد های هدر درخواست بیش اندازه بزرگ هستند",
-  "Request Timeout": "وقفهٔ درخواست",
-  "Reset Content": "تنظیم مجدد محتوی",
-  "Reset Password": "فراموشی رمزعبور",
-  "Reset Password Notification": "پیام فراموشی رمزعبور",
+  "Request Header Fields Too Large": "فیلدهای هدر درخواست بیش از حد بزرگ",
+  "Request Timeout": "تایم‌اوت درخواست",
+  "Reset Content": "بازنشانی محتوا",
+  "Reset Password": "بازنشانی رمز عبور",
+  "Reset Password Notification": "اعلان بازنشانی رمز عبور",
   "results": "نتایج",
   "Retry With": "تلاش مجدد با",
-  "See Other": "به محل دیگری مراجعه کنید",
+  "See Other": "مشاهده سایر",
   "Server Error": "خطای سرور",
-  "Service Unavailable": "عدم دسترسی به سرویس",
-  "Session Has Expired": "نشست منقضی شده",
-  "Showing": "در حال نمایش",
-  "SSL Handshake Failed": "SSL Handshake با شکست مواجه شد",
+  "Service Unavailable": "سرویس در دسترس نیست",
+  "Session Has Expired": "جلسه منقضی شده",
+  "Showing": "نمایش",
+  "SSL Handshake Failed": "دست‌دهی SSL ناموفق",
   "Subscription link receive abnormal access and banned by the system": "لینک اشتراک دسترسی غیرعادی دریافت کرد و توسط سیستم مسدود شد",
-  "Switching Protocols": "درحال تعویض پروتکل ها",
+  "Switching Protocols": "تغییر پروتکل‌ها",
   "Temporary Redirect": "تغییر مسیر موقت",
-  "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "از ثبت‌نام شما متشکریم! قبل از شروع، باید با کلیک بر روی لینکی که به تازگی به ایمیل شما ارسال کرده‌ایم، ایمیل خود را تأیید کنید! اگر ایمیلی دریافت نکرده‌اید، خوشحال می‌شویم یکی دیگر را ارسال کنیم.",
-  "The given data was invalid.": "داده دریافتی معتبر نمی‌باشد.",
+  "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "از ثبت نام شما متشکریم! قبل از شروع، باید ایمیل خود را با کلیک روی لینکی که به ایمیل شما فرستادیم تأیید کنید! اگر ایمیلی دریافت نکرده‌اید، خوشحال می‌شویم یکی دیگر برای شما ارسال کنیم.",
+  "The given data was invalid.": "داده‌های ارائه شده نامعتبر بود.",
   "The response is not a streamed response.": "پاسخ یک پاسخ جریانی نیست.",
-  "The response is not a view.": "پاسخ یک دیدگاه نیست.",
-  "This password reset link will expire in :count minutes.": "لینک فراموشی رمزعبور برای :count دقیقه معتبر است.",
-  "to": "به",
+  "The response is not a view.": "پاسخ یک نما نیست.",
+  "This password reset link will expire in :count minutes.": "این لینک بازنشانی رمز عبور در :count دقیقه منقضی خواهد شد.",
+  "to": "تا",
   "Toggle navigation": "تغییر ناوبری",
   "Too Early": "خیلی زود",
-  "Too Many Requests": "تعداد درخواست های ارسال شده زیاد است",
-  "Unauthorized": "دسترسی غیر مجاز",
+  "Too Many Requests": "درخواست‌های بیش از حد.",
+  "Unauthorized": "غیرمجاز",
   "Unavailable For Legal Reasons": "به دلایل قانونی در دسترس نیست",
-  "Unknown Error": "خطای ناشناخته",
-  "Unprocessable Entity": "موجودیت غیر قابل پردازش",
+  "Unknown Error": "خطای نامشخص",
+  "Unprocessable Entity": "موجودیت قابل پردازش نیست",
   "Unsupported Media Type": "نوع رسانه پشتیبانی نمی‌شود",
-  "Upgrade Required": "ارتقاء لازم است",
-  "URI Too Long": "یوآرال در خواست شده بسیار طولانی است",
-  "Use Proxy": "استفاده از پراکسی",
-  "Variant Also Negotiates": "گونه‌ها همچنین مذاکره می‌کنند",
-  "Verify Email Address": "تایید آدرس ایمیل",
-  "Verify Your Email Address": "ایمیل خود را تأیید کنید",
-  "Web Server is Down": "وب سرور از کار افتاده است",
-  "Whoops!": "وای!",
-  "You are receiving this email because we received a password reset request for your account.": "شما این ایمیل را به دلیل درخواست رمزعبور جدید دریافت کرده‌اید.",
-  "You have not responded this ticket in :num hours, System has closed your ticket.": "شما در :num ساعت به این تیکت پاسخ نداده‌اید، سیستم تیکت شما را بسته است.",
-  "You must have a valid subscription to view the content in this area!": "برای مشاهده محتوای این منطقه باید اشتراک معتبر داشته باشید!",
-  "Your subscription has been disabled by the administrator, please contact the administrator to restore it": "اشتراک شما توسط مدیر غیرفعال شده است، لطفاً برای بازیابی آن با مدیر تماس بگیرید.",
-  "Manually add in dashboard.": "به صورت دستی در داشبورد اضافه کنید",
-  "Manually edit in dashboard.": "به صورت دستی در داشبورد ویرایش کنید",
-  "Batch generate user accounts in dashboard.": "ایجاد حساب‌های کاربری به صورت گروهی در پس‌زمینه",
-  "Coupon used in order.": "کوپن در سفارش استفاده شده است",
-  "Order canceled, coupon reinstated.": "سفارش لغو شد و کوپن بازگردانی شد",
-  "Used for credit recharge.": "برای شارژ اعتبار استفاده می‌شود",
-  "The user manually reset the data.": "کاربر داده را بازنشانی کرد",
-  "Recharge using a recharge voucher.": "با استفاده از کوپن شارژ، اعتبار را شارژ کنید",
-  "The user topped up the balance.": "کاربر اعتبار را شارژ کرد",
-  "Purchased an item.": "کالایی خریداری شد",
-  "[:payment] plus the user’s purchased data plan.": "[:payment] به همراه بسته داده‌ای که کاربر خریداری کرده است"
+  "Upgrade Required": "ارتقا مورد نیاز",
+  "URI Too Long": "URI بیش از حد طولانی",
+  "Use Proxy": "استفاده از پروکسی",
+  "Variant Also Negotiates": "متغیر نیز مذاکره می‌کند",
+  "Verify Email Address": "تأیید آدرس ایمیل",
+  "Verify Your Email Address": "آدرس ایمیل خود را تأیید کنید",
+  "Web Server is Down": "وب سرور خاموش است",
+  "Whoops!": "اوه!",
+  "You are receiving this email because we received a password reset request for your account.": "این ایمیل را دریافت می‌کنید زیرا درخواست بازنشانی رمز عبور برای حساب شما دریافت کردیم.",
+  "You have not responded this ticket in :num hours, System has closed your ticket.": "در :num ساعت به این تیکت پاسخ نداده‌اید، سیستم تیکت شما را بسته است.",
+  "You must have a valid subscription to view the content in this area!": "برای مشاهده محتوای این بخش باید اشتراک معتبری داشته باشید!",
+  "Your subscription has been disabled by the administrator, please contact the administrator to restore it": "اشتراک شما توسط مدیر غیرفعال شده، لطفاً با مدیر تماس بگیرید تا آن را بازیابی کند.",
+  "Manually add in dashboard.": "افزودن دستی در پنل مدیریت.",
+  "Manually edit in dashboard.": "ویرایش دستی در پنل مدیریت.",
+  "Batch generate user accounts in dashboard.": "تولید انبوه حساب‌های کاربری در پنل مدیریت.",
+  "Coupon used in order.": "کوپن در سفارش استفاده شد.",
+  "Order canceled, coupon reinstated.": "سفارش لغو شد، کوپن بازیابی شد.",
+  "Used for credit recharge.": "برای شارژ موجودی استفاده شد.",
+  "The user manually reset the data.": "کاربر به صورت دستی ترافیک را بازنشانی کرد.",
+  "Recharge using a recharge voucher.": "شارژ با استفاده از کوپن شارژ.",
+  "The user topped up the balance.": "کاربر موجودی را شارژ کرد.",
+  "Purchased an item.": "کالایی خریداری شد.",
+  "[:payment] plus the user's purchased data plan.": "[:payment] به علاوه بسته ترافیک خریداری شده توسط کاربر."
 }

+ 465 - 582
resources/lang/fa/admin.php

@@ -9,261 +9,251 @@ return [
     ],
     'aff' => [
         'apply_counts' => 'مجموع <code>:num</code> درخواست برداشت',
-        'commission_counts' => 'این درخواست شامل مجموع <code>:num</code> سفارش است',
-        'commission_title' => 'جزئیات درخواست',
-        'counts' => 'مجموع <code>:num</code> رکورد بازگشت وجه',
-        'rebate_title' => 'تاریخچه بازگشت وجه',
-        'referral' => 'بازگشت وجه معرفی',
-        'title' => 'لیست درخواست‌های برداشت',
+        'commission_counts' => 'این درخواست شامل <code>:num</code> سفارش می‌باشد',
+        'commission_title' => 'جزئیات درخواست برداشت',
+        'counts' => 'مجموع <code>:num</code> رکورد کمیسیون',
+        'referral' => 'کمیسیون‌های معرفی',
     ],
     'article' => [
-        'category_hint' => 'همه مقالات در یک دسته‌بندی در یک پوشه قرار می‌گیرند',
+        'category_hint' => 'مقالات در همان دسته به صورت خودکار بایگانی می‌شوند',
         'counts' => 'مجموع <code>:num</code> مقاله',
-        'logo_placeholder' => 'یا آدرس URL لوگو را وارد کنید',
-        'title' => 'مقالات',
+        'logo_placeholder' => 'یا آدرس تصویر لوگو را وارد کنید',
         'type' => [
-            'announcement' => 'اعلان',
-            'knowledge' => 'مقاله',
+            'announcement' => 'اعلامیه',
+            'knowledge' => 'پایگاه دانش',
         ],
     ],
     'clear' => 'پاک کردن',
-    'clone' => 'کلون',
+    'clone' => 'کپی',
     'confirm' => [
-        'continues' => 'آیا می‌خواهید این عملیات را ادامه دهید؟',
-        'delete' => [0 => 'آیا می‌خواهید :attribute【', 1 => '】را حذف کنید؟'],
-        'export' => 'آیا می‌خواهید همه را صادر کنید؟',
+        'continues' => 'آیا از ادامه عملیات اطمینان دارید؟',
+        'delete' => 'حذف :attribute [:name] تأیید می‌شود؟ این عملیات غیرقابل بازگشت است!',
+        'export' => 'تأیید صادرات تمام داده‌ها؟',
     ],
     'coupon' => [
         'counts' => 'مجموع <code>:num</code> کوپن',
-        'created_days_hint' => '<code>:days</code> روز پس از ثبت‌نام',
+        'created_days_hint' => 'ثبت نام حساب ≥ :days روز',
         'discount' => 'تخفیف',
-        'export_title' => 'صادرات',
-        'groups_hint' => 'فقط برای گروه‌های کاربری انتخاب شده قابل استفاده است',
-        'info_title' => 'اطلاعات',
-        'levels_hint' => 'فقط برای سطوح کاربری انتخاب شده قابل استفاده است',
-        'limit_hint' => 'قوانین دارای رابطه <strong>و</strong> هستند، به‌درستی استفاده کنید',
-        'minimum_hint' => 'فقط زمانی قابل استفاده است که مبلغ پرداختی بیش از <strong>:num</strong> باشد',
-        'name_hint' => 'برای نمایش',
+        'export_title' => 'صادرات انبوه کوپن',
+        'groups_hint' => 'فقط برای گروه‌های کاربری انتخاب شده مؤثر است',
+        'info_title' => 'جزئیات کوپن',
+        'levels_hint' => 'فقط برای سطوح کاربری انتخاب شده مؤثر است',
+        'limit_hint' => 'شرایط رابطه <strong>AND</strong> دارند',
+        'minimum_hint' => 'حداقل خرید مورد نیاز: <code>:num</code>',
+        'name_hint' => 'نام نمایش داده شده به کاربران',
         'newbie' => [
-            'created_days' => 'عمر حساب',
-            'first_discount' => 'تخفیف اولین بار',
-            'first_order' => 'اولین سفارش',
-        ],
-        'priority_hint' => 'کوپن با اولویت بالاتر ابتدا استفاده می‌شود. حداکثر 255',
-        'services_blacklist_hint' => 'برای محصولات سیاه لیست قابل استفاده نیست، خالی بگذارید اگر استفاده نمی‌شود',
-        'services_placeholder' => 'شناسه محصول را وارد کنید، سپس Enter را فشار دهید',
-        'services_whitelist_hint' => 'فقط برای محصولات سفید لیست قابل استفاده است، خالی بگذارید اگر استفاده نمی‌شود',
-        'single_use' => 'استفاده یک‌باره',
-        'sn_hint' => 'برای استفاده کاربران از کوپن، خالی بگذارید تا به صورت تصادفی یک کد 8 رقمی تولید شود',
-        'title' => 'کوپن‌ها',
+            'created_days' => 'سن حساب',
+            'first_discount' => 'اولین استفاده از کوپن',
+            'first_order' => 'تخفیف اولین سفارش',
+        ],
+        'priority_hint' => 'اولویت (0-255)، مقادیر بالاتر اولویت دارند',
+        'services_blacklist_hint' => 'محصولات مستثنی',
+        'services_placeholder' => 'شناسه محصول را وارد کرده و Enter بزنید',
+        'services_whitelist_hint' => 'محصولات واجد شرایط',
+        'single_use' => 'یک بار مصرف',
+        'sn_hint' => 'کد کوپن (خالی بگذارید تا کد 8 کاراکتری خودکار تولید شود)',
         'type' => [
-            'charge' => 'شارژ',
-            'discount' => 'تخفیف',
-            'voucher' => 'کوپن',
-        ],
-        'type_hint' => 'کاهش: کسر مبلغ؛ تخفیف: درصد تخفیف؛ شارژ: افزودن مبلغ به موجودی',
-        'used_hint' => 'هر کاربر می‌تواند این کوپن را حداکثر <strong>:num</strong> بار استفاده کند',
-        'user_whitelist_hint' => 'کاربران سفید لیست می‌توانند استفاده کنند، خالی بگذارید اگر استفاده نمی‌شود',
-        'users_blacklist_hint' => 'کاربران سیاه لیست نمی‌توانند استفاده کنند، خالی بگذارید اگر استفاده نمی‌شود',
-        'users_placeholder' => 'شناسه کاربر را وارد کنید، سپس Enter را فشار دهید',
-        'value' => '{1} ➖ :num|{2} :num% تخفیف|{3} ➕ :num',
-        'value_hint' => 'محدوده 1٪ تا 99٪',
+            'charge' => 'کوپن شارژ',
+            'discount' => 'کوپن تخفیف',
+            'voucher' => 'کوپن نقدی',
+        ],
+        'type_hint' => '• کوپن نقدی: کسر مبلغ ثابت | • تخفیف: اعمال تخفیف درصدی | • شارژ: شارژ موجودی حساب',
+        'used_hint' => 'هر کاربر می‌تواند <strong>:num</strong> بار استفاده کند',
+        'user_whitelist_hint' => 'کاربران واجد شرایط مشخص شده',
+        'users_blacklist_hint' => 'کاربران مستثنی',
+        'users_placeholder' => 'شناسه کاربر را وارد کرده و Enter بزنید',
+        'value' => '{1} کسر :num|{2} :num% تخفیف|{3} افزودن :num به موجودی',
+        'value_hint' => 'محدوده کوپن تخفیف: 1%-99%',
     ],
-    'creating' => 'در حال افزودن...',
+    'creating' => 'در حال ایجاد...',
     'dashboard' => [
-        'abnormal_users' => 'کاربرانی که در یک ساعت گذشته ترافیک غیرعادی داشته‌اند',
-        'active_days_users' => 'کاربران فعال در :days روز گذشته',
-        'available_users' => 'تعداد کاربران فعال',
-        'credit' => 'کل موجودی',
+        'abnormal_users' => 'کاربران ترافیک غیرعادی (1ساعت)',
+        'active_days_users' => 'کاربران فعال :days روزه',
+        'available_users' => 'کاربران فعال',
+        'credit' => 'موجودی حساب',
         'current_month_traffic_consumed' => 'ترافیک مصرفی این ماه',
-        'days_traffic_consumed' => 'ترافیک مصرفی در :days روز گذشته',
+        'days_traffic_consumed' => 'مصرف ترافیک :days روزه',
         'expiring_users' => 'کاربران در حال انقضا',
-        'inactive_days_users' => 'کاربران غیرفعال برای بیش از :days روز',
-        'maintaining_nodes' => 'گره‌های در حالت نگهداری',
-        'nodes' => 'تعداد گره‌ها',
-        'online_orders' => 'تعداد سفارش‌های پرداخت آنلاین',
-        'online_users' => 'کاربران آنلاین کنونی',
-        'orders' => 'تعداد کل سفارش‌ها',
-        'overuse_users' => 'کاربرانی که بیش از 90٪ از داده استفاده کرده‌اند',
-        'paid_users' => 'تعداد کاربران پرداخت کننده',
-        'succeed_orders' => 'تعداد سفارش‌های پرداخت شده',
-        'users' => 'تعداد کل کاربران',
-        'withdrawing_commissions' => 'کمیسیون‌های در حال برداشت',
+        'inactive_days_users' => 'کاربران غیرفعال :days روزه',
+        'maintaining_nodes' => 'نودهای در حال تعمیر',
+        'nodes' => 'نودها',
+        'online_orders' => 'سفارشات پرداخت آنلاین',
+        'online_users' => 'آنلاین در حال حاضر',
+        'orders' => 'سفارشات',
+        'overuse_users' => 'کاربران با >90% ترافیک مصرفی',
+        'paid_users' => 'کاربران پرداخت کننده',
+        'succeed_orders' => 'سفارشات پرداخت شده',
+        'users' => 'کاربران',
+        'withdrawing_commissions' => 'کمیسیون‌های در انتظار',
         'withdrawn_commissions' => 'کمیسیون‌های برداشت شده',
     ],
-    'end_time' => 'پایان',
+    'filter' => [
+        'start_time' => 'زمان شروع',
+        'end_time' => 'زمان پایان',
+    ],
     'goods' => [
         'counts' => 'مجموع <code>:num</code> محصول',
         'info' => [
-            'available_date_hint' => 'هنگام انقضا به‌طور خودکار از کل داده کسر می‌شود',
-            'desc_placeholder' => 'توضیحات مختصر',
-            'limit_num_hint' => 'حداکثر تعداد خریدها برای هر کاربر، 0 برای نامحدود',
-            'list_hint' => 'هر خط را با <code><li></code> شروع کنید و با <code></li></code> پایان دهید',
-            'list_placeholder' => 'افزودن محتوای سفارشی',
-            'period_hint' => 'مقدار داده هر N روز برای طرح‌ها بازنشانی می‌شود',
-            'type_hint' => 'طرح‌ها بر انقضای حساب تأثیر می‌گذارند، بسته‌ها فقط از داده کسر می‌کنند و بر انقضا تأثیر نمی‌گذارند',
-        ],
-        'sell_and_used' => 'استفاده شده / فروخته شده',
+            'available_date_hint' => 'به صورت خودکار ترافیک را پس از انقضا کسر می‌کند',
+            'desc_placeholder' => 'توضیحات محصول (قابل مشاهده برای کاربران)',
+            'limit_num_hint' => 'هر کاربر می‌تواند این آیتم را حداکثر N بار خریداری کند. 0 یعنی بدون محدودیت.',
+            'list_hint' => 'هر خط را به صورت <li>محتوا</li> فرمت کنید',
+            'list_placeholder' => 'اطلاعات سفارشی (HTML پشتیبانی می‌شود)',
+            'period_hint' => 'هر N روز ترافیک را به صورت خودکار بازنشانی کند',
+            'type_hint' => 'پلن‌ها شامل دوره اعتبار هستند؛ بسته‌های ترافیک فقط ترافیک کسر می‌کنند',
+        ],
+        'sell_and_used' => 'استفاده/فروش',
         'status' => [
             'no' => 'خارج از فروش',
             'yes' => 'در حال فروش',
         ],
-        'title' => 'محصولات',
         'type' => [
-            'package' => 'بسته داده',
-            'plan' => 'طرح اشتراک',
-            'top_up' => 'شارژ',
+            'package' => 'بسته ترافیک',
+            'plan' => 'پلن اشتراک',
+            'top_up' => 'شارژ موجودی',
         ],
     ],
-    'hint' => 'نکته',
+    'hint' => 'راهنمای مدیریت',
     'logs' => [
         'ban' => [
-            'ban_time' => 'زمان مسدودسازی',
-            'last_connect_at' => 'آخرین زمان ورود',
-            'reason' => 'دلیل',
-            'time' => 'مدت زمان',
-            'title' => 'سوابق مسدودسازی کاربران',
+            'ban_time' => 'زمان مسدودی',
+            'last_connect_at' => 'آخرین اتصال',
+            'reason' => 'دلیل مسدودی',
+            'time' => 'مدت مسدودی',
         ],
-        'callback' => 'لاگ‌های بازگشت <small>(پرداخت)</small>',
         'counts' => 'مجموع <code>:num</code> رکورد',
-        'credit_title' => 'سوابق تغییر موجودی',
-        'ip_monitor' => 'IP‌های آنلاین <small>داده‌های زمان واقعی 2 دقیقه‌ای</small>',
-        'notification' => 'لاگ‌های ایمیل',
         'order' => [
             'is_coupon' => 'کوپن استفاده شده',
             'is_expired' => 'منقضی شده',
-            'title' => 'سفارش‌ها',
-            'update_conflict' => 'به‌روزرسانی ناموفق: تعارض سفارش',
+            'update_conflict' => 'به‌روزرسانی ناموفق، تضاد سفارش',
         ],
         'rule' => [
             'clear_all' => 'پاک کردن همه رکوردها',
-            'clear_confirm' => 'آیا می‌خواهید همه سوابق فعال‌سازی قوانین را پاک کنید؟',
+            'clear_confirm' => '⚠️ تأیید پاک کردن همه رکوردهای فعال‌سازی؟',
             'created_at' => 'زمان فعال‌سازی',
-            'name' => 'نام قانون فعال شده',
+            'name' => 'نام قانون',
             'reason' => 'دلیل فعال‌سازی',
-            'tag' => '✅ دسترسی به محتوای غیرمجاز',
-            'title' => 'سوابق فعال‌سازی قوانین',
+            'tag' => '✅ دسترسی به محتوای غیرقانونی',
+        ],
+        'monitor' => [
+            'sub_title' => 'بلادرنگ (در 2 دقیقه)',
         ],
-        'subscribe' => 'اشتراک‌ها',
-        'user_data_modify_title' => 'سوابق تغییر داده‌ها',
         'user_ip' => [
-            'connect' => 'IP متصل شده',
-            'title' => 'IP‌های آنلاین <small>10 دقیقه گذشته</small>',
+            'connect' => 'IP اتصال',
+            'sub_title' => '10 دقیقه گذشته',
         ],
         'user_traffic' => [
-            'choose_node' => 'انتخاب گره',
-            'title' => 'سوابق استفاده از داده‌ها',
+            'choose_node' => 'انتخاب نود',
         ],
     ],
     'marketing' => [
-        'counts' => 'مجموع <code>:num</code> ایمیل',
+        'counts' => 'مجموع <code>:num</code> پیام',
         'email' => [
-            'ever_paid' => 'پرداخت شده',
-            'expired_date' => 'تاریخ انقضا',
+            'ever_paid' => 'تاریخچه پرداخت',
+            'expire_start' => 'منقضی شده پس از',
+            'expire_end' => 'منقضی شده قبل از',
             'filters' => 'فیلترها',
-            'loading_statistics' => 'در حال بارگذاری آمار...',
-            'never_paid' => 'هرگز پرداخت نشده',
-            'paid_servicing' => 'خدمات پرداختی',
-            'previously_paid' => 'قبلاً پرداخت شده',
-            'recent_traffic_abnormal' => 'ناهنجاری ترافیک در ساعت اخیر',
-            'recently_active' => 'فعالیت اخیر',
+            'loading_statistics' => 'بارگیری آمار...',
+            'never_paid' => 'هرگز پرداخت نکرده',
+            'paid_servicing' => 'سرویس فعال',
+            'previously_paid' => 'قبلاً پرداخت کرده بدون سرویس فعال فعلی',
+            'recent_traffic_abnormal' => 'ترافیک غیرعادی اخیر',
+            'recently_active' => 'اخیراً فعال',
             'targeted_users_count' => 'تعداد کاربران هدف',
-            'traffic_usage_over' => 'استفاده از ترافیک بیش از N%',
-            'will_expire_date' => 'تاریخ انقضا آینده',
+            'traffic_usage_over' => 'استفاده ترافیک >N%',
         ],
-        'email_send' => 'ارسال ایمیل گروهی',
+        'email_send' => 'ارسال ایمیل انبوه',
         'error_message' => 'پیام‌های خطا',
-        'processed' => 'درخواست پردازش شده',
-        'push_send' => 'ارسال پیام فشاری',
-        'send_status' => 'وضعیت ارسال',
-        'send_time' => 'زمان ارسال',
-        'targeted_users_not_found' => 'کاربران هدف یافت نشد',
-        'unknown_sending_type' => 'نوع ارسال ناشناخته',
+        'processed' => 'درخواست پذیرفته شد',
+        'push_send' => 'اعلان‌های پوش',
+        'send_status' => 'وضعیت تحویل',
+        'send_time' => 'ارسال شده در',
+        'targeted_users_not_found' => 'کاربران هدف یافت نشدند',
+        'unknown_sending_type' => 'نوع تحویل ناشناخته',
     ],
-    'massive_export' => 'صادرات گروهی',
+    'massive_export' => 'صادرات انبوه',
     'menu' => [
         'analysis' => [
-            'accounting' => 'حسابداری',
-            'attribute' => 'تحلیل داده‌ها',
-            'node_flow' => 'تحلیل ترافیک گره',
-            'site_flow' => 'تحلیل ترافیک سایت',
-            'user_flow' => 'تحلیل ترافیک کاربران',
+            'accounting' => 'سوابق مالی',
+            'attribute' => 'تحلیل‌ها',
+            'node_flow' => 'ترافیک نود',
+            'site_flow' => 'ترافیک سیستم',
+            'user_flow' => 'ترافیک کاربر',
         ],
         'customer_service' => [
-            'article' => 'مدیریت مقالات',
-            'attribute' => 'سیستم پشتیبانی',
-            'marketing' => 'پخش پیام‌ها',
-            'ticket' => 'تیکت‌های پشتیبانی',
+            'article' => 'مدیریت مقاله',
+            'attribute' => 'پشتیبانی',
+            'marketing' => 'پیام‌های پخش',
+            'ticket' => 'مرکز تیکت',
         ],
-        'dashboard' => 'مدیریت',
+        'dashboard' => 'داشبورد',
         'log' => [
-            'attribute' => 'سیستم لاگ‌ها',
-            'notify' => 'سوابق اعلان‌ها',
+            'attribute' => 'لاگ‌ها',
+            'notify' => 'تاریخچه اعلان‌ها',
             'online_logs' => 'لاگ‌های آنلاین',
-            'online_monitor' => 'نظارت آنلاین',
+            'online_monitor' => 'مانیتور آنلاین',
             'payment_callback' => 'بازگشت پرداخت',
-            'service_ban' => 'سوابق مسدودسازی',
+            'service_ban' => 'سوابق مسدودی',
             'system' => 'لاگ‌های سیستم',
-            'traffic' => 'استفاده از ترافیک',
-            'traffic_flow' => 'تغییرات ترافیک',
+            'traffic' => 'لاگ‌های ترافیک',
+            'traffic_flow' => 'جزئیات ترافیک',
         ],
         'node' => [
-            'attribute' => 'سیستم گره‌ها',
-            'auth' => 'مجوزهای گره',
-            'cert' => 'لیست گواهینامه‌ها',
-            'list' => 'مدیریت گره‌ها',
+            'attribute' => 'نودها',
+            'auth' => 'مجوز',
+            'cert' => 'مدیریت گواهی',
+            'list' => 'فهرست نود',
         ],
         'promotion' => [
             'attribute' => 'بازاریابی',
-            'invite' => 'مدیریت دعوت‌ها',
-            'rebate_flow' => 'تاریخچه تخفیف‌ها',
-            'withdraw' => 'مدیریت برداشت‌ها',
+            'invite' => 'مدیریت دعوت',
+            'rebate_flow' => 'تاریخچه تخفیف',
+            'withdraw' => 'برداشت‌ها',
         ],
         'rbac' => [
-            'attribute' => 'سیستم دسترسی',
-            'permission' => 'مدیریت دسترسی‌ها',
-            'role' => 'لیست نقش‌ها',
+            'attribute' => 'دسترسی',
+            'permission' => 'تنظیمات مجوز',
+            'role' => 'مدیریت نقش',
         ],
         'rule' => [
-            'attribute' => 'قوانین بررسی',
-            'group' => 'گروه‌های قوانین',
-            'list' => 'لیست قوانین',
-            'trigger' => 'سوابق فعال‌سازی',
+            'attribute' => 'حسابرسی',
+            'group' => 'گروه‌های قانون',
+            'list' => 'مدیریت قوانین',
+            'trigger' => 'لاگ‌های فعال‌سازی',
         ],
         'setting' => [
-            'attribute' => 'تنظیمات سیستم',
-            'email_suffix' => 'مدیریت پسوندهای ایمیل',
-            'system' => 'پارامترهای سیستم',
-            'universal' => 'تنظیمات عمومی',
+            'attribute' => 'تنظیمات',
+            'email_suffix' => 'پسوندهای ایمیل',
+            'system' => 'تنظیمات سیستم',
+            'universal' => 'پیکربندی عمومی',
         ],
         'shop' => [
-            'attribute' => 'سیستم فروشگاه',
-            'coupon' => 'مدیریت کوپن‌ها',
-            'goods' => 'مدیریت محصولات',
-            'order' => 'سفارش‌های محصول',
+            'attribute' => 'فروشگاه',
+            'coupon' => 'مرکز کوپن',
+            'goods' => 'کاتالوگ محصول',
+            'order' => 'مرکز سفارش',
         ],
         'tools' => [
-            'analysis' => 'تحلیل لاگ‌ها',
+            'analysis' => 'تحلیلگر لاگ',
             'attribute' => 'ابزارها',
-            'convert' => 'تبدیل فرمت',
-            'decompile' => 'دی‌کامپایل',
-            'import' => 'وارد کردن داده‌ها',
+            'convert' => 'مبدل فرمت',
+            'decompile' => 'رمزگشای پیکربندی',
+            'import' => 'واردات داده',
         ],
         'user' => [
-            'attribute' => 'سیستم کاربران',
-            'credit_log' => 'تاریخچه موجودی',
+            'attribute' => 'کاربران',
+            'credit_log' => 'لاگ‌های موجودی',
             'group' => 'گروه‌های کاربری',
-            'list' => 'مدیریت کاربران',
-            'oauth' => 'مجوزهای شخص ثالث',
-            'subscribe' => 'مدیریت اشتراک‌ها',
+            'list' => 'فهرست کاربران',
+            'oauth' => 'ورودهای OAuth',
+            'subscribe' => 'مرکز اشتراک',
         ],
     ],
-    'minute' => 'دقیقه',
     'monitor' => [
-        'daily_chart' => 'نمودار مصرف روزانه ترافیک',
-        'hint' => '<strong>نکته:</strong> اگر داده‌ای وجود ندارد، بررسی کنید که وظایف زمان‌بندی شده به درستی اجرا می‌شوند.',
-        'monthly_chart' => 'نمودار مصرف ماهانه ترافیک',
-        'node' => 'ترافیک گره',
+        'daily_chart' => 'نمودار ترافیک روزانه',
+        'hint' => '<strong>نکته:</strong> اگر داده‌ای وجود ندارد، وضعیت وظایف زمان‌بندی شده را بررسی کنید',
+        'monthly_chart' => 'نمودار ترافیک ماهانه',
+        'node' => 'ترافیک نود',
         'user' => 'ترافیک کاربر',
     ],
     'no' => 'خیر',
@@ -272,356 +262,305 @@ return [
             'counts' => 'مجموع <code>:num</code> مجوز',
             'deploy' => [
                 'attribute' => 'استقرار بک‌اند',
-                'command' => 'دستورات',
-                'real_time_logs' => 'لاگ‌های زمان واقعی',
+                'command' => 'اجرای دستور',
+                'real_time_logs' => 'لاگ‌های بلادرنگ',
                 'recent_logs' => 'لاگ‌های اخیر',
-                'restart' => 'راه‌اندازی مجدد',
+                'restart' => 'راه‌اندازی مجدد سرویس',
                 'same' => 'همان بالا',
-                'start' => 'شروع',
-                'status' => 'وضعیت',
-                'stop' => 'توقف',
-                'title' => 'استقرار بک‌اند :type_label',
-                'trojan_hint' => 'لطفاً <a href=":url" target="_blank">نام گره را وارد کنید</a> و به IP مربوطه تجزیه کنید',
-                'uninstall' => 'حذف',
-                'update' => 'به‌روزرسانی',
+                'start' => 'شروع سرویس',
+                'status' => 'وضعیت اجرا',
+                'stop' => 'توقف سرویس',
+                'title' => 'استقرار :type_label',
+                'trojan_hint' => 'ابتدا <a href=":url" target="_blank">دامنه نود را پیکربندی کنید</a>',
+                'uninstall' => 'حذف سرویس',
+                'update' => 'به‌روزرسانی سرویس',
             ],
-            'empty' => 'هیچ گره‌ای نیاز به تولید مجوز ندارد',
-            'generating_all' => 'آیا می‌خواهید کلیدهای مجوز را برای همه گره‌ها تولید کنید؟',
+            'empty' => 'نود در انتظار وجود ندارد',
+            'generating_all' => 'تولید کلید برای همه نودها؟',
             'reset_auth' => 'بازنشانی کلید مجوز',
-            'title' => 'مجوزهای API <small>WEBAPI</small>',
         ],
         'cert' => [
-            'counts' => 'مجموع <code>:num</code> گواهینامه دامنه',
-            'key_placeholder' => 'مقدار KEY گواهینامه، می‌تواند خالی بماند. بک‌اند VNET-V2Ray از صدور خودکار پشتیبانی می‌کند',
-            'pem_placeholder' => 'بک‌اند VNET-V2Ray از صدور خودکار پشتیبانی می‌کند',
-            'title' => 'گواهینامه‌های دامنه <small>(برای پنهان‌سازی دامنه V2Ray)</small>',
+            'counts' => 'مجموع <code>:num</code> گواهی SSL',
+            'key_placeholder' => 'کلید خصوصی (صدور خودکار برای VNET-V2Ray)',
+            'pem_placeholder' => 'گواهی (صدور خودکار برای VNET-V2Ray)',
         ],
         'connection_test' => 'تست اتصال',
-        'counts' => 'مجموع <code>:num</code> گره',
+        'counts' => 'مجموع <code>:num</code> نود',
         'info' => [
-            'additional_ports_hint' => 'اگر فعال است، لطفاً پیکربندی سرور <span class="red-700"><a href="javascript:showTnc();">additional_ports</a></span> را انجام دهید',
-            'basic' => 'اطلاعات پایه',
-            'data_rate_hint' => 'مثال: 0.1 به معنای 100M به عنوان 10M محاسبه می‌شود؛ 5 به معنای 100M به عنوان 500M محاسبه می‌شود',
-            'ddns_hint' => 'گره‌های داینامیک IP نیاز به <a href="https://github.com/NewFuture/DDNS" target="_blank">پیکربندی DDNS</a> دارند. برای این نوع گره، تست اتصال از طریق نام دامنه انجام می‌شود.',
+            'additional_ports_hint' => 'اگر فعال شود، لطفاً تنظیمات <span class="red-700"><a href="javascript:showTnc();">additional_ports</a></span> سمت سرور را پیکربندی کنید.',
+            'basic' => 'پیکربندی پایه',
+            'data_rate_hint' => 'مثال: 0.1 یعنی 100MB به عنوان 10MB محاسبه می‌شود',
+            'ddns_hint' => 'برای IP های پویا: <a href="https://github.com/NewFuture/DDNS" target="_blank">پیکربندی DDNS</a>',
             'detection' => [
-                'all' => 'هر دو',
-                'hint' => 'بررسی تصادفی هر 30-60 دقیقه',
+                'all' => 'تشخیص پروتکل کامل',
+                'hint' => 'بررسی خودکار با فاصله 30-60 دقیقه',
                 'icmp' => 'فقط ICMP',
                 'tcp' => 'فقط TCP',
             ],
             'display' => [
                 'all' => 'کاملاً قابل مشاهده',
-                'hint' => 'آیا در لیست اشتراک/گره قابل مشاهده است',
-                'invisible' => 'کاملاً نامرئی',
-                'node' => 'فقط در صفحه گره‌ها قابل مشاهده است',
-                'sub' => 'فقط در اشتراک‌ها قابل مشاهده است',
+                'hint' => 'محدوده قابلیت مشاهده کاربر',
+                'invisible' => 'کاملاً مخفی',
+                'node' => 'فقط فهرست نود',
+                'sub' => 'فقط اشتراک',
             ],
-            'domain_hint' => 'پس از فعال‌سازی DDNS در تنظیمات سیستم، نام دامنه و IPها به‌طور خودکار به‌روزرسانی می‌شوند! دیگر نیازی به ویرایش این اطلاعات در وب‌سایت ثبت دامنه نیست.',
-            'domain_placeholder' => 'دامنه سرور، در صورت پر شدن ابتدا استفاده می‌شود',
-            'extend' => 'اطلاعات اضافی',
-            'hint' => '<strong>توجه:</strong> <code>ID</code> به‌طور خودکار تولید شده، <code>node_id</code> برای بک‌اند ShadowsocksR و <code>nodeId</code> برای بک‌اند V2Ray است',
-            'ipv4_hint' => 'چندین IP باید با کاما انگلیسی جدا شوند، برای مثال: 1.1.1.1,8.8.8.8',
-            'ipv4_placeholder' => 'آدرس IPv4 سرور',
-            'ipv6_hint' => 'چندین IP باید با کاما انگلیسی جدا شوند، برای مثال: 1.1.1.1,8.8.8.8',
-            'ipv6_placeholder' => 'آدرس IPv6 سرور',
-            'level_hint' => 'سطح: 0 - بدون محدودیت سطح، همه قابل مشاهده هستند.',
-            'obfs_param_hint' => 'اگر obfs [plain] نیست، پارامترها را برای پنهان‌سازی ترافیک وارد کنید؛ &#13;&#10;پورت 80 را پیشنهاد می‌کنیم اگر obfs [http_simple] است؛ &#13;&#10;پورت 443 را پیشنهاد می‌کنیم اگر obfs [tls] است؛',
-            'push_port_hint' => 'الزامی است. مطمئن شوید که این پورت در فایروال سرور باز است، در غیر این صورت ارسال پیام‌ها غیرعادی خواهد بود.',
-            'single_hint' => 'پورت 80/443 را پیشنهاد می‌کنیم. بک‌اند نیاز به پیکربندی حالت سختگیرانه دارد: فقط از طریق پورت‌های مشخص شده متصل شوید. (<a href="javascript:showPortsOnlyConfig();">چگونه پیکربندی کنیم</a>)',
+            'domain_hint' => 'اتصال خودکار IP در حالت DDNS',
+            'domain_placeholder' => 'دامنه (اولویت بر IP)',
+            'extend' => 'پیکربندی پیشرفته',
+            'hint' => '<strong>توجه:</strong> شناسه نود برای پیکربندی سرور استفاده می‌شود',
+            'ipv4_hint' => 'چندین IP با کاما جدا شوند',
+            'ipv4_placeholder' => 'آدرس IPv4',
+            'ipv6_hint' => 'چندین IP با کاما جدا شوند',
+            'ipv6_placeholder' => 'آدرس IPv6',
+            'level_hint' => '0 = بدون محدودیت سطح',
+            'obfs_param_hint' => 'تنظیمات obfuscation پیشرفته:<br>• http_simple: پورت 80 پیشنهاد می‌شود<br>• tls: پورت 443 پیشنهاد می‌شود',
+            'push_port_hint' => 'اطمینان حاصل کنید که این پورت در فایروال سرور باز است تا پیام‌ها به درستی ارسال شوند.',
+            'single_hint' => 'پورت 80/443 توصیه می‌شود<br><a href="javascript:showPortsOnlyConfig();">مشاهده پیکربندی حالت سخت</a>',
             'v2_cover' => [
                 'dtls' => 'DTLS 1.2',
                 'http' => 'HTTP',
-                'none' => 'بدون پنهان‌سازی',
+                'none' => 'هیچ',
                 'srtp' => 'SRTP',
                 'utp' => 'uTP',
-                'wechat' => 'تماس ویدیویی WeChat',
+                'wechat' => 'ویدیو WeChat',
                 'wireguard' => 'WireGuard',
             ],
-            'v2_host_hint' => 'هنگام استفاده از پنهان‌سازی HTTP، چندین دامنه باید با کاما جدا شوند، در حالی که WebSocket فقط یک دامنه را مجاز می‌داند.',
-            'v2_method_hint' => 'پروتکل انتقال WebSocket نباید از روش رمزنگاری \'none\' استفاده کند.',
-            'v2_net_hint' => 'لطفاً TLS را برای WebSocket فعال کنید',
-            'v2_tls_provider_hint' => 'پیکربندی‌های مختلف برای بک‌اندهای مختلف:',
-        ],
-        'proxy_info' => '*سازگار با Shadowsocks',
-        'proxy_info_hint' => 'برای سازگاری، لطفاً <span class="red-700">_compatible</span> را به پروتکل و obfuscation در پیکربندی سرور اضافه کنید',
-        'refresh_geo' => 'تازه‌سازی جغرافیا',
-        'refresh_geo_all' => 'تازه‌سازی داده‌های جغرافیا',
-        'reload' => 'بارگذاری مجدد بک‌اند',
-        'reload_all' => 'بارگذاری مجدد همه سرورها',
-        'reload_confirm' => 'آیا می‌خواهید سرور را بارگذاری مجدد کنید؟',
+            'v2_host_hint' => 'برای obfuscation HTTP، چندین دامنه را با کاما جدا کنید. WebSocket فقط یک دامنه مجاز است.',
+            'v2_method_hint' => '⚠️ از رمزگذاری "none" برای WebSocket استفاده نکنید',
+            'v2_net_hint' => '⚠️ WebSocket نیازمند رمزگذاری TLS است',
+            'v2_tls_provider_hint' => 'بک‌اندهای مختلف پیکربندی‌های متفاوتی دارند:',
+        ],
+        'proxy_info' => '*سازگاری پروتکل SS',
+        'proxy_info_hint' => 'حالت سازگاری نیاز به افزودن <span class="red-700">_compatible</span> به پیکربندی بک‌اند دارد',
+        'refresh_geo' => 'تازه‌سازی موقعیت جغرافیایی',
+        'refresh_geo_all' => 'تازه‌سازی همه موقعیت‌های جغرافیایی',
+        'reload' => 'بارگیری مجدد سرویس',
+        'reload_all' => 'بارگیری مجدد همه سرویس‌ها',
+        'reload_confirm' => 'تأیید بارگیری مجدد سرویس نود؟',
         'traffic_monitor' => 'آمار ترافیک',
     ],
     'oauth' => [
-        'counts' => 'مجموع <code>:num</code> رکورد مجوز',
-        'title' => 'مجوزهای شخص ثالث',
+        'counts' => 'مجموع <code>:num</code> مجوز',
     ],
     'optional' => 'اختیاری',
     'permission' => [
-        'counts' => 'مجموع <code>:num</code> دسترسی',
-        'description_hint' => 'توضیحات، برای مثال: [سیستم X] ویرایش A',
-        'name_hint' => 'نام مسیر، برای مثال: admin.user.update',
-        'title' => 'دسترسی‌ها',
+        'counts' => 'مجموع <code>:num</code> مجوز',
+        'description_hint' => 'مثال: مجوز حذف سفارش',
+        'name_hint' => 'نام مسیر، مثلاً admin.user.edit',
     ],
-    'query' => 'جستجو',
+    'query' => 'پرس‌وجو',
     'report' => [
         'annually_accounting' => 'تراکنش‌های سالانه',
-        'annually_site_flow' => 'مصرف سالانه ترافیک',
-        'avg_traffic_30d' => 'میانگین ترافیک روزانه در 30 روز',
-        'current_month' => 'این ماه',
-        'current_year' => 'امسال',
+        'annually_site_flow' => 'ترافیک سالانه',
+        'avg_traffic_30d' => 'میانگین 30 روزه',
+        'current_month' => 'ماه جاری',
+        'current_year' => 'سال جاری',
         'daily_accounting' => 'تراکنش‌های روزانه',
-        'daily_distribution' => 'توزیع روزانه',
-        'daily_site_flow' => 'مصرف روزانه ترافیک',
+        'daily_distribution' => 'توزیع زمانی',
+        'daily_site_flow' => 'ترافیک روزانه سیستم',
         'daily_traffic' => 'ترافیک روزانه',
         'hourly_traffic' => 'ترافیک ساعتی',
         'last_month' => 'ماه گذشته',
         'last_year' => 'سال گذشته',
         'monthly_accounting' => 'تراکنش‌های ماهانه',
-        'monthly_site_flow' => 'مصرف ماهانه ترافیک',
-        'select_hourly_date' => 'انتخاب تاریخ ساعتی',
-        'sum_traffic_30d' => 'نسبت ترافیک 30 روزه',
+        'monthly_site_flow' => 'ترافیک ماهانه سیستم',
+        'select_hourly_date' => 'انتخاب تاریخ',
+        'sum_traffic_30d' => 'مجموع 30 روزه',
         'today' => 'امروز',
     ],
-    'require' => 'ضروری',
+    'require' => 'الزامی',
     'role' => [
         'counts' => 'مجموع <code>:num</code> نقش',
-        'description_hint' => 'نام نمایشی، برای مثال: مدیر',
-        'modify_admin_error' => 'لطفاً سرپرست ارشد را تغییر ندهید!',
-        'name_hint' => 'شناسه منحصر به فرد، برای مثال: admin',
-        'permissions_all' => 'همه دسترسی‌ها',
-        'title' => 'نقش‌ها',
+        'description_hint' => 'نام نمایشی، مثلاً متخصص پشتیبانی',
+        'modify_admin_error' => '⚠️ نمی‌توان سوپر ادمین را تغییر داد',
+        'name_hint' => 'شناسه منحصر به فرد، مثلاً SupportAdmin',
+        'permissions_all' => 'همه مجوزها',
     ],
     'rule' => [
         'counts' => 'مجموع <code>:num</code> قانون',
         'group' => [
             'counts' => 'مجموع <code>:num</code> گروه',
-            'title' => 'گروه‌های قوانین',
             'type' => [
                 'off' => 'مسدود',
                 'on' => 'مجاز',
             ],
         ],
-        'title' => 'قوانین',
         'type' => [
-            'domain' => 'دامنه',
-            'ip' => 'IP',
-            'protocol' => 'پروتکل',
-            'reg' => 'عبارت با قاعده',
+            'domain' => 'قانون دامنه',
+            'ip' => 'قانون IP',
+            'protocol' => 'قانون پروتکل',
+            'reg' => 'قانون Regex',
         ],
     ],
     'select_all' => 'انتخاب همه',
-    'selected_hint' => 'قوانین تخصیص داده شده، اینجا قابل جستجو هستند',
-    'set_to' => 'تنظیم به :attribute',
+    'selected_hint' => 'قوانین اختصاص یافته (قابل جستجو)',
+    'set_to' => 'تنظیم به عنوان :attribute',
     'setting' => [
         'common' => [
-            'connect_nodes' => 'تعداد گره‌های متصل',
+            'connect_nodes' => 'اتصالات نود',
             'set_default' => 'تنظیم به عنوان پیش‌فرض',
-            'title' => 'تنظیمات عمومی',
         ],
         'email' => [
-            'black' => 'لیست سیاه',
-            'rule' => 'قانون',
+            'black' => 'فهرست سیاه',
+            'rule' => 'قوانین فیلتر',
             'tail' => 'پسوند ایمیل',
-            'tail_placeholder' => 'لطفاً پسوند ایمیل را وارد کنید',
-            'title' => 'لیست فیلتر ایمیل <small>(برای جلوگیری از ثبت‌نام با پسوندهای خاص ایمیل)</small>',
-            'white' => 'لیست سفید',
+            'tail_placeholder' => 'پسوند را وارد کنید (بدون @)',
+            'sub_title' => 'با تنظیمات سیستم کار می‌کند تا ایمیل‌های خاص را مسدود کند',
+            'white' => 'فهرست سفید',
         ],
-        'no_permission' => 'شما اجازه تغییر پارامترها را ندارید!',
+        'no_permission' => '⚠️ مجوز تغییر وجود ندارد',
         'system' => [
-            'account' => 'تنظیمات حساب',
-            'auto_job' => 'وظایف خودکار',
-            'check_in' => 'سیستم چک‌این',
-            'extend' => 'ویژگی‌های اضافی',
+            'web' => 'وب‌سایت',
+            'account' => 'حساب',
+            'node' => 'نود',
+            'security' => 'امنیت',
+            'payment' => 'پرداخت',
+            'notify' => 'اعلان‌ها',
+            'auto_job' => 'وظایف Cron',
             'menu' => 'منو',
-            'node' => 'تنظیمات گره',
-            'notify' => 'سیستم اطلاع‌رسانی',
-            'other' => 'لوگو | خدمات مشتری | آمار',
-            'payment' => 'سیستم پرداخت',
-            'promotion' => 'سیستم معرفی',
-            'title' => 'تنظیمات سیستم',
-            'web' => 'تنظیمات سایت',
         ],
     ],
-    'sort_asc' => 'مقدار مرتب‌سازی بزرگتر اولویت بالاتری دارد',
-    'start_time' => 'شروع',
+    'sort_asc' => 'مقادیر بالاتر ابتدا مرتب می‌شوند',
     'system' => [
-        'AppStore_id' => 'حساب اپل',
-        'AppStore_password' => 'رمز عبور اپل',
-        'account_expire_notification' => 'اطلاع‌رسانی انقضای حساب',
         'active_account' => [
-            'after' => 'فعال‌سازی پس از ثبت‌نام',
-            'before' => 'فعال‌سازی قبل از ثبت‌نام',
-        ],
-        'active_times' => 'تعداد فعال‌سازی حساب',
-        'admin_invite_days' => 'مدت اعتبار دعوت‌نامه مدیر',
-        'aff_salt' => 'رمزنگاری اطلاعات کاربر در لینک معرفی',
-        'alipay_qrcode' => 'کد QR علی‌پی',
-        'auto_release_port' => 'مکانیزم بازیابی پورت',
-        'bark_key' => 'کلید دستگاه Bark',
+            'after' => 'فعال‌سازی پس از ثبت نام',
+            'before' => 'فعال‌سازی قبل از ثبت نام',
+        ],
         'captcha' => [
             'geetest' => 'Geetest',
             'hcaptcha' => 'hCaptcha',
-            'recaptcha' => 'Google reCaptcha',
-            'standard' => 'کد امنیتی استاندارد',
+            'recaptcha' => 'Google reCAPTCHA',
+            'standard' => 'CAPTCHA استاندارد',
             'turnstile' => 'Turnstile',
         ],
-        'captcha_key' => 'کلید کد امنیتی',
-        'captcha_secret' => 'رمز/شناسه کد امنیتی',
-        'codepay_id' => 'شناسه CodePay',
-        'codepay_key' => 'کلید ارتباطی',
-        'codepay_url' => 'URL درخواست',
-        'data_anomaly_notification' => 'اطلاع‌رسانی ناهنجاری داده',
-        'data_exhaust_notification' => 'اطلاع‌رسانی اتمام داده',
-        'ddns_key' => 'کلید DNS',
-        'ddns_mode' => 'حالت DDNS',
-        'ddns_secret' => 'رمز DNS',
-        'default_days' => 'مدت اعتبار پیش‌فرض',
-        'default_traffic' => 'داده اولیه پیش‌فرض',
-        'demo_restriction' => 'در محیط نمایشی، تغییر این پیکربندی ممنوع است!',
-        'detection_check_times' => 'اطلاع‌رسانی تشخیص انسداد',
-        'dingTalk_access_token' => 'توکن دسترسی DingTalk',
-        'dingTalk_secret' => 'رمز DingTalk',
-        'epay_key' => 'کلید ePay',
-        'epay_mch_id' => 'شناسه بازرگان ePay',
-        'epay_url' => 'URL ePay',
-        'expire_days' => 'هشدار انقضا',
-        'f2fpay_app_id' => 'شناسه برنامه Alipay',
-        'f2fpay_private_key' => 'کلید خصوصی Alipay',
-        'f2fpay_public_key' => 'کلید عمومی Alipay',
+        'tasks' => [
+            'clean' => [
+                'notification_logs' => 'لاگ‌های اعلان',
+                'node_daily_logs' => 'ترافیک روزانه نود',
+                'node_hourly_logs' => 'ترافیک ساعتی نود',
+                'node_heartbeats' => 'ضربان نود',
+                'node_online_logs' => 'کاربران آنلاین نود',
+                'payments' => 'داده‌های پرداخت',
+                'rule_logs' => 'فعال‌سازی قوانین',
+                'node_online_ips' => 'IP های اتصال کاربر',
+                'user_baned_logs' => 'مسدودی کاربران',
+                'user_daily_logs_nodes' => 'ترافیک روزانه نود کاربر',
+                'user_daily_logs_total' => 'کل ترافیک روزانه کاربر',
+                'user_hourly_logs' => 'ترافیک ساعتی نود کاربر',
+                'login_logs' => 'لاگ‌های ورود',
+                'subscribe_logs' => 'لاگ‌های اشتراک',
+                'traffic_logs' => 'جزئیات ترافیک',
+                'unpaid_orders' => 'سفارشات پرداخت نشده',
+            ],
+            'close' => [
+                'tickets' => 'تیکت‌ها',
+                'confirmation_orders' => 'سفارشات دستی',
+                'orders' => 'سفارشات پرداخت',
+                'verify' => 'کد تأیید',
+            ],
+        ],
         'forbid' => [
-            'china' => 'مسدودسازی دسترسی از چین',
-            'mainland' => 'مسدودسازی دسترسی از سرزمین اصلی چین',
-            'oversea' => 'مسدودسازی دسترسی از خارج از کشور',
+            'china' => 'منع دسترسی چین',
+            'mainland' => 'منع دسترسی سرزمین اصلی چین',
+            'oversea' => 'منع دسترسی خارج از کشور',
         ],
-        'forbid_mode' => 'حالت مسدودسازی',
         'hint' => [
-            'AppStore_id' => 'حساب اپل استفاده شده در آموزش‌ها',
-            'AppStore_password' => 'رمز عبور اپل استفاده شده در آموزش‌ها',
-            'account_expire_notification' => 'اطلاع‌رسانی انقضای حساب',
-            'active_times' => 'تعداد فعال‌سازی حساب از طریق ایمیل در 24 ساعت',
-            'admin_invite_days' => 'مدت اعتبار کدهای دعوت ایجاد شده توسط مدیر',
-            'aff_salt' => 'رمزنگاری لینک دعوت با استفاده از متن مخفی',
-            'auto_release_port' => 'پورت‌های مسدود شده/منقضی شده پس از <code>'.sysConfig('tasks.release_port').'</code> روز به‌صورت خودکار آزاد می‌شوند',
-            'bark_key' => 'کلید دستگاه برای ارسال پیام به iOS',
-            'captcha_key' => 'راهنمای تنظیمات <a href="https://proxypanel.gitbook.io/wiki/captcha" target="_blank">اینجا</a>',
-            'data_anomaly_notification' => 'اطلاع‌رسانی به مدیر در صورت استفاده غیرعادی از داده در یک ساعت',
-            'data_exhaust_notification' => 'اطلاع‌رسانی در صورت اتمام داده',
-            'ddns_key' => 'راهنمای تنظیمات <a href="https://proxypanel.gitbook.io/wiki/ddns" target="_blank">اینجا</a>',
-            'ddns_mode' => 'به‌روزرسانی خودکار تغییرات دامنه و IP به DNS',
-            'default_days' => 'مدت اعتبار پیش‌فرض حساب‌های جدید، 0 به‌معنای انقضا در همان روز',
-            'default_traffic' => 'داده پیش‌فرض برای حساب‌های جدید',
-            'detection_check_times' => 'تعداد اطلاع‌رسانی‌های انسداد گره، 0 برای نامحدود، حداکثر 12',
-            'dingTalk_access_token' => 'توکن دسترسی ربات سفارشی <a href=https://open.dingtalk.com/document/group/custom-robot-access#title-jfe-yo9-jl2 target=_blank>اینجا</a>',
-            'dingTalk_secret' => 'رمز ربات سفارشی در صورت فعال‌سازی امضا',
-            'expire_days' => 'شروع اطلاع‌رسانی انقضای حساب',
-            'f2fpay_app_id' => 'شناسه برنامه Alipay',
-            'f2fpay_private_key' => 'کلید خصوصی Alipay از ابزار تولید کلید',
-            'f2fpay_public_key' => 'توجه: این کلید عمومی برنامه نیست!',
-            'forbid_mode' => 'مسدودسازی دسترسی از مناطق مشخص',
-            'iYuu_token' => 'پر کردن <a href=https://iyuu.cn target=_blank>توکن IYUU</a> قبل از فعال‌سازی',
-            'invite_num' => 'تعداد دعوت‌نامه‌های پیش‌فرض برای هر کاربر',
-            'is_activate_account' => 'نیاز به فعال‌سازی حساب از طریق ایمیل',
-            'is_ban_status' => '(احتیاط) مسدودسازی کل حساب باعث بازنشانی تمام داده‌های کاربر می‌شود',
-            'is_captcha' => 'نیاز به کد امنیتی برای ورود/ثبت‌نام در صورت فعال‌سازی',
-            'is_checkin' => 'پاداش تصادفی در زمان چک‌این',
-            'is_clear_log' => '(توصیه می‌شود) پاکسازی خودکار لاگ‌های بی‌استفاده/قدیمی در صورت فعال‌سازی',
-            'is_custom_subscribe' => 'نمایش تاریخ انقضا و داده باقی‌مانده در لیست اشتراک در صورت فعال‌سازی',
-            'is_email_filtering' => 'لیست سیاه: کاربران می‌توانند با پسوندهای ایمیلی که در لیست سیاه نیستند ثبت‌نام کنند؛ لیست سفید: کاربران باید با پسوندهای ایمیلی که در لیست سفید هستند ثبت‌نام کنند',
-            'is_forbid_robot' => 'بازگشت خطای 404 در صورت دسترسی ربات‌ها/پراکسی‌ها',
-            'is_free_code' => 'پنهان کردن کدهای دعوت رایگان در صورت غیرفعال‌سازی',
-            'is_rand_port' => 'پورت تصادفی در زمان افزودن/ثبت‌نام کاربر',
-            'is_register' => 'غیرفعال‌سازی ثبت‌نام در صورت عدم انتخاب',
-            'is_subscribe_ban' => 'مسدودسازی خودکار در صورت درخواست‌های اشتراک بیش از حد',
-            'is_traffic_ban' => 'غیرفعال‌سازی خودکار سرویس در صورت استفاده غیرعادی از داده در یک ساعت',
-            'maintenance_content' => 'اطلاع‌رسانی سفارشی نگهداری',
-            'maintenance_mode' => 'انتقال کاربران عادی به صفحه نگهداری در صورت فعال‌سازی | مدیر می‌تواند از طریق <a href=\'javascript:(0)\'>:url</a> وارد شود',
-            'maintenance_time' => 'برای شمارش معکوس صفحه نگهداری',
-            'min_port' => 'محدوده پورت 1000 - 65535',
-            'node_blocked_notification' => 'تشخیص انسداد گره ساعتی و اطلاع‌رسانی به مدیران',
-            'node_daily_notification' => 'گزارش روزانه استفاده از گره',
-            'node_offline_notification' => 'تشخیص آفلاین شدن هر 10 دقیقه و اطلاع‌رسانی در صورت آفلاین بودن گره',
-            'node_renewal_notification' => '7 روز، 3 روز و 1 روز قبل از انقضا به مدیر یادآوری کنید که گره را تمدید کند.',
-            'oauth_path' => 'لطفاً ابتدا پلتفرم‌ها را در .ENV فعال کنید',
-            'offline_check_times' => 'توقف اطلاع‌رسانی پس از N هشدار در 24 ساعت',
-            'password_reset_notification' => 'اجازه بازنشانی رمز عبور از طریق ایمیل در صورت فعال‌سازی',
-            'paybeaver_app_id' => '<a href="https://merchant.paybeaver.com/" target="_blank">مرکز بازرگان</a> -> توسعه‌دهنده -> شناسه برنامه',
-            'paybeaver_app_secret' => '<a href="https://merchant.paybeaver.com/" target="_blank">مرکز بازرگان</a> -> توسعه‌دهنده -> رمز برنامه',
-            'payjs_mch_id' => 'دریافت از <a href="https://payjs.cn/dashboard/member" target="_blank">صفحه عضو</a>',
-            'payment_confirm_notification' => 'اطلاع‌رسانی به مدیر برای پردازش سفارش‌های پرداخت دستی',
-            'payment_received_notification' => 'اطلاع‌رسانی به کاربر در صورت دریافت پرداخت',
-            'pushDeer_key' => 'پر کردن <a href=https://www.pushdeer.com/official.html target=_blank>کلید PushDeer</a> قبل از فعال‌سازی',
-            'pushplus_token' => 'پر کردن <a href=https://www.pushplus.plus/push1.html target=_blank>توکن PushPlus</a> قبل از فعال‌سازی',
-            'rand_subscribe' => 'ترتیب تصادفی در صورت فعال‌سازی، در غیر این صورت بر اساس ترتیب لیست گره‌ها',
-            'redirect_url' => 'تغییر مسیر درخواست‌های مسدود شده به این URL در صورت فعال‌سازی قوانین',
-            'referral_money' => 'حداقل مبلغ برداشت',
-            'referral_percent' => 'درصد بازگشت وجه از مبلغ سفارش',
-            'referral_status' => 'بستن سیستم معرفی بدون تأثیر بر داده‌های موجود',
-            'referral_traffic' => 'هدیه داده برای ثبت‌نام با لینک معرفی',
-            'referral_type' => 'محاسبه بازگشت وجه جدید بر اساس حالت جدید پس از تغییر',
-            'register_ip_limit' => 'تعداد ثبت‌نام‌های مجاز در هر IP در 24 ساعت، 0 برای نامحدود',
-            'reset_password_times' => 'تعداد مجاز بازنشانی رمز عبور از طریق ایمیل در 24 ساعت',
-            'reset_traffic' => 'بازنشانی خودکار داده بر اساس چرخه طرح کاربر',
-            'server_chan_key' => 'پر کردن <a href="https://sct.ftqq.com/r/2626" target="_blank">کلید ServerChan</a> قبل از فعال‌سازی',
-            'standard_currency' => 'واحد پول اصلی مورد استفاده در پنل',
-            'subject_name' => 'نام محصول سفارشی در درگاه‌های پرداخت',
-            'subscribe_ban_times' => 'حداکثر تعداد درخواست‌های اشتراک در 24 ساعت',
-            'subscribe_domain' => 'شروع با http:// یا https:// برای جلوگیری از شکست DNS poisoning',
-            'subscribe_max' => 'حداکثر تعداد گره‌های بازگشتی در لیست اشتراک، 0 برای همه',
-            'telegram_token' => 'دریافت <a href=https://t.me/BotFather target=_blank>توکن ربات</a> از @BotFather',
-            'tg_chat_token' => 'پر کردن <a href=https://t.me/realtgchat_bot target=_blank>توکن TG Chat</a> قبل از فعال‌سازی',
+            'AppStore_id' => 'Apple ID برای راهنمای تنظیم iOS',
+            'AppStore_password' => 'رمز عبور Apple برای راهنمای تنظیم iOS',
+            'account_expire_notification' => 'اطلاع‌رسانی به کاربران قبل از انقضای حساب',
+            'active_times' => 'حداکثر فعال‌سازی ایمیل مجاز در 24 ساعت',
+            'admin_invite_days' => 'اعتبار کدهای دعوت ادمین',
+            'affiliate_link_salt' => 'خالی = نمایش شناسه کاربر؛ تنظیم برای رمزگذاری لینک معرفی',
+            'auto_release_port' => 'آزادسازی پورت‌ها N روز پس از مسدودی/انقضا',
+            'bark_key' => 'کلید دستگاه برای Bark (اپ iOS مورد نیاز)',
+            'captcha_key' => '<a href="https://proxypanel.gitbook.io/wiki/captcha" target="_blank">راهنمای پیکربندی CAPTCHA</a>',
+            'data_anomaly_notification' => 'هشدار به ادمین اگر ترافیک کاربر از آستانه در 1 ساعت تجاوز کند',
+            'data_exhaust_notification' => 'اطلاع‌رسانی به کاربران هنگام تمام شدن ترافیک',
+            'ddns_key' => '<a href="https://proxypanel.gitbook.io/wiki/ddns" target="_blank">راهنمای پیکربندی DDNS</a>',
+            'ddns_mode' => 'همگام‌سازی خودکار دامنه/IP نود با ارائه‌دهنده DNS',
+            'default_days' => 'اعتبار پیش‌فرض حساب‌های جدید (0 = همان روز)',
+            'default_traffic' => 'ترافیک اولیه برای کاربران جدید',
+            'detection_check_times' => 'حداکثر هشدارهای مسدودی (0/خالی = نامحدود، ≤12)',
+            'dingTalk_access_token' => '<a href="https://open.dingtalk.com/document/group/custom-robot-access#title-jfe-yo9-jl2" target="_blank">راهنمای توکن DingTalk</a>',
+            'dingTalk_secret' => 'در صورت فعال بودن امضای ربات الزامی است',
+            'expire_days' => 'روزهای قبل از انقضا برای ارسال هشدار',
+            'f2fpay_app_id' => 'App ID علی‌پی',
+            'f2fpay_private_key' => 'کلید خصوصی علی‌پی (امن نگه دارید)',
+            'f2fpay_public_key' => 'کلید عمومی علی‌پی (نه کلید عمومی اپ)',
+            'forbid_mode' => 'مسدود کردن دسترسی بر اساس منطقه IP',
+            'iYuu_token' => '<a href="https://iyuu.cn" target="_blank">دریافت توکن IYUU</a>',
+            'invite_num' => 'سهمیه دعوت پیش‌فرض برای کاربران جدید',
+            'is_activate_account' => 'نیاز به فعال‌سازی ایمیل برای حساب‌های جدید',
+            'is_ban_status' => '⚠️ مسدودی همه داده‌های حساب را بازنشانی می‌کند',
+            'is_captcha' => 'فعال‌سازی CAPTCHA در ورود/ثبت نام',
+            'is_clear_log' => 'پاک‌سازی خودکار لاگ‌ها (توصیه می‌شود)',
+            'is_custom_subscribe' => 'نمایش اطلاعات استفاده در صفحه اشتراک',
+            'is_email_filtering' => 'فهرست سیاه یا سفید پسوندهای ایمیل',
+            'is_forbid_robot' => 'مسدود کردن ربات‌ها، خزنده‌ها و پروکسی‌ها',
+            'is_free_code' => 'مخفی کردن کدهای رایگان در صورت غیرفعال بودن',
+            'is_rand_port' => 'پورت تصادفی برای کاربران جدید',
+            'is_register' => 'غیرفعال کردن ثبت نام',
+            'maintenance_content' => 'محتوای HTML برای صفحه تعمیر',
+            'maintenance_mode' => 'هدایت به صفحه تعمیر؛ ورود ادمین از طریق <a href="javascript:(0)">:url</a>',
+            'maintenance_time' => 'تایمر شمارش معکوس تعمیر',
+            'min_port' => 'محدوده پورت معتبر: 1000–65535',
+            'checkin_reward' => 'محدوده ترافیک پاداش داده شده در هر چک‌این',
+            'node_blocked_notification' => 'بررسی و اطلاع‌رسانی مسدودی نود هر ساعت',
+            'node_daily_notification' => 'گزارش ترافیک روزانه برای نودها',
+            'node_offline_notification' => 'بررسی وضعیت نود هر 10 دقیقه',
+            'node_renewal_notification' => 'یادآوری تمدید 7/3/1 روز قبل از انقضا',
+            'oauth_path' => 'در .ENV تنظیم کنید قبل از فعال‌سازی اینجا',
+            'offline_check_times' => 'حداکثر هشدارهای آفلاین در 24 ساعت',
+            'password_reset_notification' => 'اجازه بازنشانی رمز عبور به کاربران',
+            'paybeaver_app_id' => '<a href="https://merchant.paybeaver.com/" target="_blank">App ID از مرکز بازرگان</a>',
+            'paybeaver_app_secret' => '<a href="https://merchant.paybeaver.com/" target="_blank">App Secret از مرکز بازرگان</a>',
+            'payjs_mch_id' => '<a href="https://payjs.cn/dashboard/member" target="_blank">دریافت اطلاعات بازرگان</a>',
+            'payment_confirm_notification' => 'اطلاع‌رسانی به ادمین پس از پرداخت دستی',
+            'payment_received_notification' => 'اطلاع‌رسانی به کاربر پس از موفقیت پرداخت',
+            'pushDeer_key' => '<a href="https://www.pushdeer.com/official.html" target="_blank">دریافت کلید PushDeer</a>',
+            'pushplus_token' => '<a href="https://www.pushplus.plus/push1.html" target="_blank">دریافت توکن PushPlus</a>',
+            'rand_subscribe' => 'بازگرداندن نودها به صورت تصادفی یا بر اساس شناسه در اشتراک',
+            'redirect_url' => 'هدایت هنگام فعال شدن قانون حسابرسی',
+            'referral_money' => 'حداقل مبلغ برای برداشت',
+            'referral_percent' => 'نرخ کمیسیون (0–100%)',
+            'referral_status' => 'مخفی کردن ویژگی‌های معرفی (تأثیری بر پاداش‌های موجود ندارد)',
+            'referral_traffic' => 'دریافت ترافیک پاداش هنگام ثبت نام با لینک معرفی یا کد دعوت',
+            'referral_reward_type' => 'تغییر نوع بر سوابق گذشته تأثیر نمی‌گذارد',
+            'register_ip_limit' => 'حداکثر ثبت نام در هر IP در 24 ساعت (0 = نامحدود)',
+            'reset_password_times' => 'حداکثر بازنشانی رمز عبور در 24 ساعت',
+            'reset_traffic' => 'بازنشانی ترافیک در تاریخ تمدید پلن',
+            'server_chan_key' => '<a href="https://sct.ftqq.com/r/2626" target="_blank">دریافت SCKEY ServerChan</a>',
+            'standard_currency' => 'ارز پیش‌فرض سیستم',
+            'subject_name' => 'نام محصول در درگاه‌های پرداخت',
+            'subscribe_rate_limit' => 'حداکثر درخواست‌های اشتراک در 24 ساعت (0 = غیرفعال)',
+            'subscribe_domain' => 'دامنه اشتراک با http(s):// (ضد آلودگی DNS)',
+            'subscribe_max' => 'حداکثر نودها در هر اشتراک (0 = همه)',
+            'telegram_token' => '<a href="https://t.me/BotFather" target="_blank">دریافت توکن ربات تلگرام</a>',
+            'tg_chat_token' => '<a href="https://t.me/realtgchat_bot" target="_blank">دریافت توکن TGChat</a>',
             'ticket_closed_notification' => 'اطلاع‌رسانی به کاربر هنگام بسته شدن تیکت',
-            'ticket_created_notification' => 'اطلاع‌رسانی به مدیر یا کاربر بسته به ایجادکننده تیکت',
-            'ticket_replied_notification' => 'اطلاع‌رسانی به طرف دیگر هنگام پاسخ به تیکت',
-            'traffic_ban_time' => 'مدت زمان مسدودسازی خودکار برای موارد استثنا',
-            'traffic_ban_value' => 'مسدودسازی خودکار حساب در صورت تجاوز از این مقدار در یک ساعت',
-            'traffic_limit_time' => 'فاصله زمانی بین چک‌این‌ها',
-            'traffic_warning_percent' => 'ارسال اطلاع‌رسانی اتمام داده هنگامی که استفاده روزانه به این درصد برسد',
-            'user_invite_days' => 'مدت اعتبار کدهای دعوت ایجاد شده توسط کاربر',
-            'username_type' => 'نوع نام کاربری پیش‌فرض برای کاربران',
-            'v2ray_tls_provider' => 'تنظیمات گره این پیکربندی TLS را نادیده می‌گیرد',
-            'web_api_url' => 'مثال: '.config('app.url'),
-            'webmaster_email' => 'ایمیل تماس نمایش داده شده در برخی از پیام‌های خطا',
-            'website_analytics' => 'کد جاوا اسکریپت تحلیل وب‌سایت',
-            'website_callback_url' => 'جلوگیری از شکست بازگشت پرداخت به دلیل DNS poisoning',
-            'website_customer_service' => 'کد جاوا اسکریپت خدمات مشتری',
-            'website_name' => 'نام وب‌سایت در ایمیل‌ها',
-            'website_security_code' => 'نیاز به کد امنیتی برای دسترسی به سایت در صورت تنظیم',
-            'website_url' => 'دامنه اصلی مورد استفاده برای لینک‌ها',
-            'wechat_aid' => '<a href="https://work.weixin.qq.com/wework_admin/frame#apps" target="_blank">مدیریت برنامه</a> -> AgentId',
-            'wechat_cid' => 'دریافت از <a href="https://work.weixin.qq.com/wework_admin/frame#profile" target="_blank">اطلاعات شرکت</a>',
-            'wechat_encodingAESKey' => 'مدیریت برنامه -> تنظیمات برنامه -> EncodingAESKey',
-            'wechat_secret' => 'رمز برنامه (ممکن است نیاز به دانلود WeChat Enterprise باشد)',
-            'wechat_token' => 'تنظیمات برنامه -> TOKEN، آدرس بازگشت: :url',
-        ],
-        'iYuu_token' => 'توکن IYUU',
-        'invite_num' => 'تعداد دعوت‌نامه‌های پیش‌فرض',
-        'is_AliPay' => 'پرداخت علی‌پی',
-        'is_QQPay' => 'پرداخت QQ',
-        'is_WeChatPay' => 'پرداخت WeChat',
-        'is_activate_account' => 'فعال‌سازی حساب',
-        'is_ban_status' => 'مسدودسازی خودکار در صورت انقضا',
-        'is_captcha' => 'حالت کد امنیتی',
-        'is_checkin' => 'افزایش داده با چک‌این',
-        'is_clear_log' => 'پاکسازی خودکار لاگ‌ها',
-        'is_custom_subscribe' => 'اشتراک پیشرفته',
-        'is_email_filtering' => 'فیلتر ایمیل',
-        'is_forbid_robot' => 'مسدودسازی ربات‌ها',
-        'is_free_code' => 'کد دعوت رایگان',
-        'is_invite_register' => 'ثبت‌نام با دعوت',
-        'is_otherPay' => 'پرداخت خاص',
-        'is_rand_port' => 'پورت تصادفی',
-        'is_register' => 'ثبت‌نام کاربران',
-        'is_subscribe_ban' => 'مسدودسازی خودکار درخواست‌های اشتراک غیرعادی',
-        'is_traffic_ban' => 'مسدودسازی خودکار در صورت استفاده غیرعادی از داده',
-        'maintenance_content' => 'محتوای معرفی نگهداری',
-        'maintenance_mode' => 'حالت نگهداری',
-        'maintenance_time' => 'زمان پایان نگهداری',
-        'min_port' => 'محدوده پورت',
-        'min_rand_traffic' => 'محدوده داده',
-        'node_blocked_notification' => 'اطلاع‌رسانی انسداد گره',
-        'node_daily_notification' => 'گزارش روزانه مصرف گره',
-        'node_offline_notification' => 'اطلاع‌رسانی آفلاین شدن گره',
-        'node_renewal_notification' => 'اطلاعیه تمدید گره',
+            'ticket_created_notification' => 'اطلاع‌رسانی به مدیر هنگام ایجاد تیکت',
+            'ticket_replied_notification' => 'اطلاع‌رسانی در پاسخ‌های تیکت',
+            'ban_duration' => 'مدت رفع مسدودی خودکار پس از تعلیق',
+            'traffic_abuse_limit' => '⚠️ مسدودی اگر ترافیک از حد در 1 ساعت تجاوز کند',
+            'checkin_interval' => 'حداقل ساعت بین چک‌این‌ها (0 = غیرفعال)',
+            'traffic_warning_percent' => 'اطلاع‌رسانی هنگام ترافیک زیر آستانه (%)',
+            'user_invite_days' => 'اعتبار کد دعوت کاربر',
+            'username_type' => 'نوع شناسه کاربر (پیش‌فرض: ایمیل)',
+            'v2ray_tls_provider' => 'ارائه‌دهنده TLS برای بک‌اند V2Ray',
+            'web_api_url' => 'دامنه برای استفاده بک‌اندهای نود. مثلاً '.config('app.url'),
+            'webmaster_email' => 'ایمیل تماس ادمین برای خطاها',
+            'website_statistics_code' => 'کد JS تحلیل‌ها',
+            'payment_callback_url' => 'URL بازگشت با http(s):// (ضد آلودگی DNS)',
+            'website_customer_service_code' => 'کد JS چت زنده',
+            'website_name' => 'نام در اعلان‌ها/ایمیل‌ها',
+            'website_security_code' => 'تنظیم <a href=":url" target="_blank">ورودی امن</a> اگر خالی نباشد',
+            'website_url' => 'URL پایه (برای بازنشانی/پرداخت لازم)',
+            'wechat_aid' => '<a href="https://work.weixin.qq.com/wework_admin/frame#apps" target="_blank">AgentId اپ WeCom</a>',
+            'wechat_cid' => '<a href="https://work.weixin.qq.com/wework_admin/frame#profile" target="_blank">Corp ID WeCom</a>',
+            'wechat_encodingAESKey' => 'از API → EncodingAESKey',
+            'wechat_secret' => 'Secret اپ',
+            'wechat_token' => 'از API → Token، URL: :url',
+        ],
         'notification' => [
             'channel' => [
                 'bark' => 'Bark',
@@ -631,203 +570,147 @@ return [
                 'pushdeer' => 'PushDeer',
                 'pushplus' => 'PushPlus',
                 'serverchan' => 'ServerChan',
-                'site' => 'اعلان درون سایت',
+                'site' => 'درون‌سایت',
                 'telegram' => 'تلگرام',
-                'tg_chat' => 'TG Chat',
-                'wechat' => 'WeChat Enterprise',
+                'tg_chat' => 'TG-Chat',
+                'wechat' => 'WeCom',
             ],
-            'send_test' => 'ارسال پیام آزمایشی',
+            'send_test' => 'ارسال تست',
             'test' => [
-                'content' => 'محتوای آزمون',
-                'success' => 'ارسال با موفقیت انجام شد. لطفاً تلفن خود را برای دریافت اعلان پوش بررسی کنید.',
-                'title' => 'این یک عنوان آزمایشی است',
+                'content' => 'محتوای اعلان تست',
+                'success' => 'اعلان تست ارسال شد',
+                'title' => 'عنوان اعلان تست',
                 'unknown_channel' => 'کانال ناشناخته',
             ],
         ],
-        'oauth_path' => 'پلتفرم‌های ورود شخص ثالث',
-        'offline_check_times' => 'تعداد اطلاع‌رسانی‌های آفلاین',
-        'params_required' => 'لطفاً ابتدا پارامترهای ضروری برای این :attribute را کامل کنید!',
-        'password_reset_notification' => 'اطلاع‌رسانی بازنشانی رمز عبور',
-        'paybeaver_app_id' => 'شناسه برنامه PayBeaver',
-        'paybeaver_app_secret' => 'رمز برنامه PayBeaver',
-        'payjs_key' => 'کلید PayJs',
-        'payjs_mch_id' => 'شناسه بازرگان PayJs',
         'payment' => [
-            'attribute' => 'درگاه پرداخت',
+            'attribute' => 'تنظیمات پرداخت',
             'channel' => [
-                'alipay' => 'Alipay F2F',
+                'f2fpay' => 'علی‌پی F2F',
                 'codepay' => 'CodePay',
+                'credit' => 'اعتبار',
                 'epay' => 'ePay',
-                'manual' => 'پرداخت دستی',
+                'manual' => 'دستی',
                 'paybeaver' => 'PayBeaver',
                 'payjs' => 'PayJs',
                 'paypal' => 'PayPal',
                 'stripe' => 'Stripe',
                 'theadpay' => 'THeadPay',
+                'cryptomus' => 'Cryptomus',
+                'youzan' => 'YouZan',
+                'bitpayx' => 'BitPayX',
             ],
             'hint' => [
-                'alipay' => 'این ویژگی نیاز به رفتن به <a href="https://open.alipay.com/platform/appManage.htm?#/create/" target="_blank">پلتفرم باز خدمات مالی Ant</a> برای درخواست مجوز و برنامه دارد',
-                'codepay' => 'لطفاً به <a href="https://codepay.fateqq.com/i/377289" target="_blank">CodePay</a> بروید و یک حساب کاربری درخواست دهید، سپس نرم‌افزار آن را دانلود و تنظیم کنید',
-                'manual' => 'پس از تنظیم و انتخاب درگاه پرداخت، در سمت کاربر نمایش داده می‌شود',
-                'paybeaver' => 'لطفاً به <a href="https://merchant.paybeaver.com/?aff_code=iK4GNuX8" target="_blank">PayBeaver</a> بروید و یک حساب کاربری درخواست دهید',
-                'payjs' => 'لطفاً به <a href="https://payjs.cn/ref/zgxjnb" target="_blank">PayJs</a> بروید و یک حساب کاربری درخواست دهید',
-                'paypal' => 'با حساب بازرگانی خود وارد <a href="https://www.paypal.com/businessprofile/mytools/apiaccess/firstparty" target="_blank">صفحه درخواست اعتبار API</a> شوید، موافقت کنید و اطلاعات تنظیمات را دریافت کنید',
-                'theadpay' => 'لطفاً به <a href="https://theadpay.com/" target="_blank">THeadPay</a> بروید و یک حساب کاربری درخواست دهید',
+                'f2fpay' => 'درخواست در <a href="https://open.alipay.com/platform/appManage.htm?#/create/" target="_blank">پلتفرم باز علی‌پی</a>',
+                'codepay' => 'ثبت نام در <a href="https://codepay.fateqq.com/i/377289" target="_blank">CodePay</a> و دانلود نرم‌افزار',
+                'manual' => 'به صورت خودکار هنگام پیکربندی نمایش داده می‌شود',
+                'paybeaver' => 'ثبت نام در <a href="https://merchant.paybeaver.com/?aff_code=iK4GNuX8" target="_blank">PayBeaver</a>',
+                'payjs' => 'ثبت نام در <a href="https://payjs.cn/ref/zgxjnb" target="_blank">PayJs</a>',
+                'paypal' => 'دریافت اعتبارنامه در <a href="https://www.paypal.com/businessprofile/mytools/apiaccess/firstparty" target="_blank">API PayPal</a>',
+                'theadpay' => 'ثبت نام در <a href="https://theadpay.com/" target="_blank">THeadPay</a>',
+                'cryptomus' => 'ثبت نام در <a href="https://app.cryptomus.com/signup" target="_blank">Cryptomus</a> و دریافت کلید API',
             ],
         ],
-        'payment_confirm_notification' => 'اطلاع‌رسانی تأیید پرداخت دستی',
-        'payment_received_notification' => 'اطلاع‌رسانی موفقیت پرداخت',
-        'paypal_app_id' => 'شناسه برنامه PayPal',
-        'paypal_client_id' => 'شناسه مشتری PayPal',
-        'paypal_client_secret' => 'کلید مخفی PayPal',
         'placeholder' => [
-            'bark_key' => 'پر کردن کلید دستگاه Bark سپس کلیک بر روی به‌روزرسانی',
-            'codepay_url' => 'https://codepay.fatq.com/create_order/?',
-            'default_url' => 'پیش‌فرض به عنوان :url',
-            'dingTalk_access_token' => 'توکن دسترسی ربات سفارشی',
-            'dingTalk_secret' => 'رمز ربات سفارشی پس از امضا',
-            'iYuu_token' => 'پر کردن توکن IYUU سپس کلیک بر روی به‌روزرسانی',
-            'pushDeer_key' => 'پر کردن کلید PushDeer سپس کلیک بر روی به‌روزرسانی',
-            'pushplus_token' => 'لطفاً در ServerChan درخواست دهید',
-            'server_chan_key' => 'پر کردن SCKEY ServerChan سپس کلیک بر روی به‌روزرسانی',
-            'telegram_token' => 'پر کردن توکن Telegram سپس کلیک بر روی به‌روزرسانی',
-            'tg_chat_token' => 'لطفاً در Telegram درخواست دهید',
-            'wechat_aid' => 'شناسه برنامه WeChat Enterprise',
-            'wechat_cid' => 'پر کردن CID WeChat سپس کلیک بر روی به‌روزرسانی',
-            'wechat_secret' => 'رمز برنامه WeChat Enterprise',
-        ],
-        'pushDeer_key' => 'کلید PushDeer',
-        'pushplus_token' => 'توکن PushPlus',
-        'rand_subscribe' => 'اشتراک تصادفی',
-        'redirect_url' => 'URL تغییر مسیر',
+            'bark_key' => 'کلید Bark را وارد کنید → به‌روزرسانی',
+            'codepay_url' => 'https://codepay.fateqq.com/creat_order/?',
+            'default_url' => 'پیش‌فرض: :url',
+            'dingTalk_access_token' => 'access_token WebHook ربات',
+            'dingTalk_secret' => 'رمز امضای ربات',
+            'iYuu_token' => 'توکن IYUU را وارد کنید → به‌روزرسانی',
+            'pushDeer_key' => 'کلید PushDeer را وارد کنید → به‌روزرسانی',
+            'pushplus_token' => 'درخواست در PushPlus',
+            'server_chan_key' => 'SCKEY ServerChan را وارد کنید → به‌روزرسانی',
+            'telegram_token' => 'توکن تلگرام را وارد کنید → به‌روزرسانی',
+            'tg_chat_token' => 'درخواست در تلگرام',
+            'wechat_aid' => 'AgentId اپ WeChat را وارد کنید',
+            'wechat_cid' => 'Corp ID WeChat را وارد کنید → به‌روزرسانی',
+            'wechat_secret' => 'Secret اپ WeChat را وارد کنید',
+        ],
         'referral' => [
-            'loop' => 'بازگشت وجه دائمی',
-            'once' => 'بازگشت وجه اولین خرید',
-        ],
-        'referral_money' => 'حداقل مبلغ برداشت',
-        'referral_percent' => 'درصد بازگشت وجه',
-        'referral_status' => 'وضعیت معرفی',
-        'referral_traffic' => 'هدیه داده برای ثبت‌نام با لینک معرفی',
-        'referral_type' => 'نوع بازگشت وجه',
-        'register_ip_limit' => 'محدودیت ثبت‌نام با IP مشابه',
-        'reset_password_times' => 'تعداد بازنشانی رمز عبور',
-        'reset_traffic' => 'بازنشانی خودکار داده',
-        'server_chan_key' => 'کلید ServerChan',
-        'standard_currency' => 'واحد پول اصلی',
-        'stripe_public_key' => 'کلید عمومی Stripe',
-        'stripe_secret_key' => 'کلید مخفی Stripe',
-        'stripe_signing_secret' => 'رمز امضای WebHook',
-        'subject_name' => 'نام محصول سفارشی',
-        'subscribe_ban_times' => 'حداکثر تعداد درخواست‌های اشتراک',
-        'subscribe_domain' => 'آدرس اشتراک',
-        'subscribe_max' => 'حداکثر تعداد گره‌های اشتراک',
-        'telegram_token' => 'توکن Telegram',
-        'tg_chat_token' => 'توکن TG Chat',
-        'theadpay_key' => 'کلید THeadPay',
-        'theadpay_mchid' => 'شناسه بازرگان THeadPay',
-        'theadpay_url' => 'URL THeadPay',
-        'ticket_closed_notification' => 'اطلاع‌رسانی بسته شدن تیکت',
-        'ticket_created_notification' => 'اطلاع‌رسانی ایجاد تیکت جدید',
-        'ticket_replied_notification' => 'اطلاع‌رسانی پاسخ به تیکت',
-        'traffic_ban_time' => 'مدت زمان مسدودسازی',
-        'traffic_ban_value' => 'آستانه استفاده غیرعادی از داده',
-        'traffic_limit_time' => 'فاصله زمانی بین چک‌این‌ها',
-        'traffic_warning_percent' => 'آستانه هشدار استفاده از داده',
-        'trojan_license' => 'مجوز Trojan',
-        'user_invite_days' => 'مدت اعتبار دعوت‌نامه کاربر',
+            'loop' => 'کمیسیون تکراری',
+            'once' => 'کمیسیون اولین خرید',
+        ],
         'username' => [
-            'any' => 'هر نام کاربری',
+            'any' => 'هر',
             'email' => 'ایمیل',
-            'mobile' => 'شماره تلفن',
-        ],
-        'username_type' => 'نوع نام کاربری',
-        'v2ray_license' => 'مجوز V2Ray',
-        'v2ray_tls_provider' => 'تنظیمات TLS برای V2Ray',
-        'web_api_url' => 'آدرس API',
-        'webmaster_email' => 'ایمیل مدیر سایت',
-        'website_analytics' => 'کد آمار وب‌سایت',
-        'website_callback_url' => 'آدرس بازگشت پرداخت',
-        'website_customer_service' => 'کد خدمات مشتری',
-        'website_home_logo' => 'لوگو صفحه اصلی',
-        'website_logo' => 'لوگو سایت',
-        'website_name' => 'نام وب‌سایت',
-        'website_security_code' => 'کد امنیتی وب‌سایت',
-        'website_url' => 'آدرس وب‌سایت',
-        'wechat_aid' => 'شناسه برنامه WeChat',
-        'wechat_cid' => 'شناسه شرکت WeChat',
-        'wechat_encodingAESKey' => 'کلید رمزنگاری AES WeChat',
-        'wechat_qrcode' => 'کد QR WeChat',
-        'wechat_secret' => 'رمز WeChat',
-        'wechat_token' => 'توکن WeChat',
+            'mobile' => 'موبایل',
+        ],
+        'demo_restriction' => '⚠️ تغییر در نسخه نمایشی غیرفعال است',
+        'params_required' => 'پارامترهای :attribute را کامل کنید',
     ],
     'system_generate' => 'تولید شده توسط سیستم',
     'ticket' => [
-        'close_confirm' => 'آیا می‌خواهید این تیکت را ببندید؟',
+        'close_confirm' => '⚠️ تأیید بستن تیکت؟ این غیرقابل بازگشت است',
         'counts' => 'مجموع <code>:num</code> تیکت',
-        'error' => 'خطای ناشناخته! لطفاً لاگ‌ها را بررسی کنید',
+        'error' => 'خطای ناشناخته! لطفاً لاگ‌ها را بررسی کنید.',
         'inviter_info' => 'اطلاعات دعوت‌کننده',
         'self_send' => 'نمی‌توانید برای خودتان تیکت ایجاد کنید!',
-        'send_to' => 'لطفاً جزئیات کاربر هدف را وارد کنید',
-        'title' => 'تیکت‌ها',
+        'send_to' => 'لطفاً جزئیات کاربر هدف را ارائه دهید',
         'user_info' => 'اطلاعات کاربر',
     ],
     'times' => 'بار',
     'tools' => [
         'analysis' => [
             'file_missing' => ':file_name وجود ندارد. لطفاً ابتدا فایل را ایجاد کنید.',
-            'not_enough' => 'کمتر از 15000 رکورد، قادر به تحلیل نیست',
-            'req_url' => 'سوابق URL درخواست‌های اخیر',
-            'title' => 'تحلیل لاگ SSR <small>فقط برای یک گره</small>',
+            'not_enough' => 'رکوردهای ناکافی (≥15,000 مورد نیاز)',
+            'req_url' => 'URL های درخواست اخیر',
+            'sub_title' => 'فقط برای استقرارهای تک نود',
         ],
         'convert' => [
-            'content_placeholder' => 'لطفاً اطلاعات پیکربندی که نیاز به تبدیل دارند را وارد کنید.',
-            'file_missing' => 'فایل پیدا نشد. لطفاً مجوزهای دایرکتوری را بررسی کنید.',
-            'missing_error' => 'تبدیل ناموفق: اطلاعات پیکربندی فاقد فیلد [port_password] است یا این فیلد خالی است.',
-            'params_unknown' => 'استثنای پارامتر',
-            'title' => 'تبدیل فرمت <small>SS به SSR</small>',
+            'content_placeholder' => 'JSON Shadowsocks را وارد کنید',
+            'file_missing' => 'فایل وجود ندارد. لطفاً مجوزهای دایرکتوری را بررسی کنید.',
+            'missing_error' => 'تبدیل ناموفق: فیلد [port_password] در پیکربندی وجود ندارد یا خالی است.',
+            'params_unknown' => 'خطای پارامتر',
+            'sub_title' => 'تبدیل Shadowsocks به ShadowsocksR',
         ],
         'decompile' => [
-            'attribute' => 'لینک پیکربندی دی‌کامپایل',
-            'content_placeholder' => 'لطفاً لینک‌های ShadowsocksR که نیاز به دی‌کامپایل دارند را وارد کنید، با خط جدا شوند.',
-            'title' => 'دی‌کامپایل <small>اطلاعات پیکربندی</small>',
+            'attribute' => 'تجزیه پیکربندی',
+            'content_placeholder' => 'لینک‌های ShadowsocksR را وارد کنید (یکی در هر خط)',
         ],
         'import' => [
-            'file_error' => 'خطای ناشناخته‌ای رخ داده است. لطفاً دوباره بارگذاری کنید.',
-            'file_required' => 'لطفاً فایلی را برای بارگذاری انتخاب کنید',
-            'file_type_error' => 'فقط فایل‌های نوع :type برای بارگذاری مجاز هستند.',
-            'format_error' => 'خطای تجزیه فرمت محتوا. لطفاً فایلی از نوع :type که با فرمت مشخص شده مطابقت دارد بارگذاری کنید.',
+            'file_error' => 'خطای ناشناخته رخ داد. لطفاً مجدداً آپلود کنید.',
+            'file_required' => 'لطفاً فایلی برای آپلود انتخاب کنید.',
+            'file_type_error' => 'فقط فرمت :type پشتیبانی می‌شود',
+            'format_error' => 'خطای تجزیه فرمت محتوا. لطفاً فایل :type با پیکربندی مطابق آپلود کنید.',
         ],
     ],
-    'unselected_hint' => 'قوانینی که باید تخصیص داده شوند، اینجا قابل جستجو هستند',
+    'unselected_hint' => 'قوانین تخصیص نیافته (قابل جستجو)',
     'user' => [
-        'admin_deletion' => 'مدیران سیستم را نمی‌توان حذف کرد',
-        'bulk_account_quantity' => 'تعداد حساب‌های تولید شده به‌صورت عمده',
+        'admin_deletion' => '⚠️ حساب‌های ادمین قابل حذف نیستند',
+        'bulk_account_quantity' => 'تعداد تولید',
         'connection_test' => 'تست اتصال',
         'counts' => 'مجموع <code>:num</code> حساب',
         'group' => [
             'counts' => 'مجموع <code>:num</code> گروه',
             'name' => 'نام گروه',
-            'title' => 'کنترل گروه‌های کاربری<small> (یک گره می‌تواند در چندین گروه باشد، اما کاربر فقط می‌تواند در یک گروه باشد؛ برای گره‌های قابل مشاهده/قابل استفاده برای کاربران، گروه اولویت بیشتری نسبت به سطح دارد)</small>',
+            'sub_title' => 'نودها می‌توانند در چندین گروه باشند؛ کاربران فقط در یک گروه. گروه بر سطح برای قابلیت مشاهده نود اولویت دارد.',
         ],
         'info' => [
             'account' => 'اطلاعات حساب',
-            'expired_date_hint' => 'اگر خالی بماند، پیش‌فرض یک سال اعتبار دارد',
+            'expired_date_hint' => 'خالی پیش‌فرض 365 روز',
             'proxy' => 'اطلاعات پروکسی',
-            'recharge_placeholder' => 'اگر منفی باشد، موجودی کسر می‌شود',
-            'reset_date_hint' => 'تاریخ بازنشانی بعدی ترافیک',
-            'switch' => 'تغییر هویت',
+            'recharge_placeholder' => 'منفی = کسر',
+            'reset_date_hint' => 'تاریخ بازنشانی ترافیک بعدی',
+            'switch' => 'تغییر نقش',
             'uuid_hint' => 'UUID برای V2Ray',
         ],
-        'online_monitor' => 'نظارت آنلاین',
-        'proxies_config' => 'اطلاعات اتصال برای :username',
-        'proxy_info' => 'اطلاعات پیکربندی',
-        'reset_confirm' => [0 => 'آیا می‌خواهید ترافیک [', 1 => '] را بازنشانی کنید؟'],
+        'online_monitor' => 'مانیتور آنلاین',
+        'proxies_config' => 'پیکربندی پروکسی [:username]',
+        'proxy_info' => 'اطلاعات پروکسی',
+        'reset_confirm' => '⚠️ آیا بازنشانی ترافیک برای【:username】را تأیید می‌کنید؟',
         'reset_traffic' => 'بازنشانی ترافیک',
-        'traffic_monitor' => 'آمار ترافیک',
-        'update_help' => 'به‌روزرسانی موفقیت‌آمیز بود، بازگردید؟',
-        'user_view' => 'تغییر به نمای کاربر',
+        'traffic_monitor' => 'مانیتور ترافیک',
+        'update_help' => 'به‌روزرسانی شد! بازگشت به فهرست؟',
+        'user_view' => 'نمای کاربر',
     ],
-    'user_dashboard' => 'مرکز کاربری',
+    'user_dashboard' => 'مرکز کاربر',
     'yes' => 'بله',
-    'zero_unlimited_hint' => '0 یا خالی برای نامحدود',
+    'zero_unlimited_hint' => 'عدم تنظیم/0 یعنی بدون محدودیت',
+    'network_status' => [
+        1 => '✔️ عادی',
+        2 => '🛑 مسدود خارج از کشور',
+        3 => '🛑 مسدود داخل کشور',
+        4 => '❌ قطع شده',
+    ],
 ];

+ 43 - 43
resources/lang/fa/auth.php

@@ -3,87 +3,87 @@
 declare(strict_types=1);
 
 return [
-    'accept_term' => 'من شرایط را خوانده و پذیرفته‌ام',
+    'accept_term' => 'من خوانده‌ام و موافقم',
     'active' => [
         'attribute' => 'فعال‌سازی',
         'error' => [
-            'activated' => 'حساب کاربری قبلاً فعال شده است، نیازی به فعال‌سازی مجدد نیست',
-            'disable' => 'فعال‌سازی حساب کاربری غیر فعال شده است، می‌توانید مستقیماً وارد شوید!',
-            'throttle' => 'شما به محدودیت درخواست‌های فعال‌سازی رسیده‌اید، لطفاً بعداً دوباره تلاش کنید! اگر سوالی دارید، با :email تماس بگیرید.',
+            'activated' => 'حساب قبلاً فعال شده، لطفاً مستقیماً وارد شوید!',
+            'disable' => 'این سایت عملکرد فعال‌سازی حساب را غیرفعال کرده، می‌توانید مستقیماً وارد شوید!',
+            'throttle' => 'شما محدودیت درخواست فعال‌سازی را فعال کرده‌اید، لطفاً بعداً دوباره تلاش کنید!',
         ],
-        'promotion' => 'حساب کاربری هنوز فعال نشده است، لطفاً ابتدا [:action] کنید!',
-        'sent' => 'ایمیل فعال‌سازی به صندوق پستی شما ارسال شده است، لطفاً آن را بررسی کنید (شامل پوشه اسپم).',
+        'promotion' => 'حساب هنوز فعال نشده، لطفاً ابتدا «:action» کنید!',
+        'sent' => 'لینک فعال‌سازی به ایمیل شما ارسال شد، لطفاً منتظر بمانید یا صندوق اسپم را بررسی کنید',
     ],
-    'aup' => 'سیاست استفاده قابل قبول',
+    'aup' => 'شرایط استفاده قابل قبول',
     'captcha' => [
-        'attribute' => 'کپچا',
+        'attribute' => 'کد تأیید',
         'error' => [
-            'failed' => 'تأیید کپچا ناموفق بود، لطفاً دوباره تلاش کنید',
-            'timeout' => 'کپچا منقضی شده است، لطفاً صفحه را تازه‌سازی کرده و دوباره تلاش کنید.',
+            'failed' => 'کد تأیید اشتباه وارد شده، لطفاً دوباره وارد کنید!',
+            'timeout' => 'کد تأیید منقضی شده، لطفاً صفحه را تازه‌سازی کرده و دوباره تلاش کنید!',
         ],
-        'required' => 'لطفاً کپچا را کامل کنید!',
-        'sent' => 'کپچا به ایمیل شما ارسال شده است، لطفاً آن را بررسی کنید (شامل پوشه اسپم).',
+        'required' => 'لطفاً عملیات کد تأیید را به درستی تکمیل کنید',
+        'sent' => 'کد تأیید به ایمیل شما ارسال شد، لطفاً منتظر بمانید یا صندوق اسپم را بررسی کنید',
     ],
     'email' => [
         'error' => [
-            'banned' => 'ارائه دهنده ایمیل شما مسدود شده است، لطفاً از یک ایمیل دیگر استفاده کنید.',
-            'invalid' => 'ایمیل شما پشتیبانی نمی‌شود.',
+            'banned' => 'این سایت از ارائه‌دهنده ایمیل شما پشتیبانی نمی‌کند، لطفاً ایمیل را تغییر دهید!',
+            'invalid' => 'ایمیلی که وارد کرده‌اید در فهرست ایمیل‌های پشتیبانی شده این سایت نیست!',
         ],
     ],
     'error' => [
-        'account_baned' => 'حساب کاربری شما مسدود شده است!',
-        'login_error' => 'خطای ورود، لطفاً بعداً دوباره تلاش کنید!',
-        'login_failed' => 'ورود ناموفق بود، لطفاً نام کاربری و رمز عبور خود را بررسی کنید!',
-        'not_found_user' => 'حسابی یافت نشد، لطفاً از روش‌های ورود دیگر استفاده کنید.',
-        'repeat_request' => 'لطفاً درخواست‌های تکراری ارسال نکنید، صفحه را تازه‌سازی کرده و دوباره تلاش کنید.',
-        'url_timeout' => 'لینک منقضی شده است، لطفاً دوباره درخواست کنید.',
+        'account_baned' => 'حساب شما موقتاً مسدود شده است. برای اطلاعات بیشتر با پشتیبانی تماس بگیرید.',
+        'login_error' => 'در فرآیند ورود خطایی رخ داد، لطفاً بعداً دوباره تلاش کنید!',
+        'login_failed' => 'ورود ناموفق. لطفاً نام کاربری و رمز عبور خود را بررسی کنید.',
+        'not_found_user' => 'حساب مرتبطی یافت نشد، لطفاً از روش‌های دیگر برای ورود استفاده کنید!',
+        'repeat_request' => 'لطفاً درخواست تکراری نکنید، صفحه را تازه‌سازی کرده و دوباره تلاش کنید!',
+        'url_timeout' => 'لینک منقضی شده، لطفاً دوباره عمل کنید!',
     ],
-    'failed' => 'مشخصات وارد شده با اطلاعات ما سازگار نیست.',
+    'failed' => 'نام کاربری یا رمز عبور اشتباه است.',
     'invite' => [
-        'get' => 'دریافت کد دعوت',
+        'get' => 'کلیک برای دریافت کد دعوت',
         'not_required' => 'نیازی به کد دعوت نیست، می‌توانید مستقیماً ثبت نام کنید!',
-        'unavailable' => 'کد دعوت نامعتبر است، لطفاً دوباره تلاش کنید.',
+        'unavailable' => 'کد دعوت نامعتبر است، لطفاً دوباره تلاش کنید!',
     ],
     'login' => 'ورود',
     'logout' => 'خروج',
-    'maintenance' => 'نگهداری',
-    'maintenance_tip' => 'در حال نگهداری',
+    'maintenance' => 'تعمیر سیستم',
+    'maintenance_tip' => 'سیستم در حال تعمیر است، لطفاً بعداً مراجعه کنید!',
     'oauth' => [
-        'login_failed' => 'ورود با شخص ثالث ناموفق بود!',
+        'login_failed' => 'ورود از طریق شخص ثالث ناموفق!',
         'register' => 'ثبت نام سریع',
-        'registered' => 'قبلاً ثبت نام کرده‌اید، لطفاً مستقیماً وارد شوید.',
+        'registered' => 'قبلاً ثبت نام کرده‌اید، لطفاً مستقیماً وارد شوید',
     ],
-    'one-click_login' => 'ورود با یک کلیک',
+    'one-click_login' => 'ورود یک کلیکه',
     'optional' => 'اختیاری',
     'password' => [
         'forget' => 'رمز عبور را فراموش کرده‌اید؟',
         'new' => 'رمز عبور جدید را وارد کنید',
-        'original' => 'رمز عبور فعلی',
+        'original' => 'رمز عبور اصلی',
         'reset' => [
             'attribute' => 'بازنشانی رمز عبور',
             'error' => [
-                'demo' => 'در حالت دمو نمی‌توان رمز عبور ادمین را تغییر داد.',
-                'disabled' => 'بازنشانی رمز عبور غیر فعال شده است، لطفاً برای کمک با :email تماس بگیرید.',
-                'same' => 'رمز عبور جدید نمی‌تواند با رمز عبور قدیمی یکسان باشد، لطفاً دوباره وارد کنید.',
-                'throttle' => 'شما می‌توانید فقط :time بار در 24 ساعت رمز عبور را بازنشانی کنید، لطفاً عملیات را تکرار نکنید.',
-                'wrong' => 'رمز عبور اشتباه است، لطفاً دوباره تلاش کنید.',
+                'demo' => 'محیط نمایشی تغییر رمز عبور مدیر را ممنوع کرده!',
+                'disabled' => 'این سایت عملکرد بازنشانی رمز عبور را غیرفعال کرده!',
+                'same' => 'رمز عبور جدید نمی‌تواند با رمز عبور قدیمی یکسان باشد، لطفاً دوباره تنظیم کنید!',
+                'throttle' => 'هر 24 ساعت فقط می‌توان :time بار رمز عبور را بازنشانی کرد، لطفاً مکرراً عمل نکنید!',
+                'wrong' => 'رمز عبور قدیمی اشتباه است، لطفاً دوباره وارد کنید!',
             ],
-            'sent' => 'لینک بازنشانی به صندوق پستی شما ارسال شده است، لطفاً آن را بررسی کنید (شامل پوشه اسپم).',
-            'success' => 'رمز عبور جدید با موفقیت بازنشانی شد، اکنون می‌توانید وارد شوید.',
+            'sent' => 'بازنشانی موفق، لطفاً ایمیل خود را بررسی کنید (ممکن است در صندوق اسپم باشد)',
+            'success' => 'رمز عبور جدید با موفقیت تنظیم شد، لطفاً به صفحه ورود بروید و وارد شوید.',
         ],
     ],
     'register' => [
         'attribute' => 'ثبت نام',
-        'code' => 'کد ثبت نام',
+        'code' => 'کد تأیید ثبت نام',
         'error' => [
-            'disable' => 'متأسفیم، ما به طور موقت پذیرش کاربران جدید را متوقف کرده‌ایم.',
-            'throttle' => 'سیستم ضد ربات فعال شده است! لطفاً از ارسال مکرر خودداری کنید!',
+            'disable' => 'متأسفیم، این سایت موقتاً کانال ثبت نام را بسته است',
+            'throttle' => 'مکانیزم ضد اسپم فعال شده، لطفاً مکرراً ثبت نام نکنید',
         ],
-        'failed' => 'ثبت نام ناموفق بود، لطفاً بعداً دوباره تلاش کنید.',
-        'promotion' => 'هنوز حساب کاربری ندارید؟ لطفاً به ',
+        'failed' => 'ثبت نام ناموفق، لطفاً بعداً دوباره تلاش کنید',
+        'promotion' => 'هنوز حساب ندارید؟ لطفاً ابتدا',
     ],
     'remember_me' => 'مرا به خاطر بسپار',
-    'request' => 'درخواست',
-    'throttle' => 'دفعات تلاش شما برای ورود بیش از حد مجاز است. لطفا پس از :seconds ثانیه مجددا تلاش فرمایید.',
+    'request' => 'دریافت',
+    'throttle' => 'تعداد تلاش‌های ورود شما زیاد است، لطفاً :seconds ثانیه بعد دوباره تلاش کنید.',
     'tos' => 'شرایط خدمات',
 ];

+ 50 - 50
resources/lang/fa/common.php

@@ -5,19 +5,19 @@ declare(strict_types=1);
 return [
     'account' => 'حساب',
     'action' => 'عملیات',
-    'active_item' => 'فعال کردن :attribute',
-    'add' => 'اضافه کردن',
+    'active_item' => 'فعال‌سازی :attribute',
+    'add' => 'افزودن',
     'advance' => 'پیشرفته',
     'all' => 'همه',
     'applied' => ':attribute اعمال شد',
     'apply' => 'اعمال',
-    'available_date' => 'دوره اعتبار',
+    'available_date' => 'تاریخ اعتبار',
     'avatar' => 'آواتار',
     'back' => 'بازگشت',
     'back_to' => 'بازگشت به :page',
     'bark' => [
-        'custom' => 'اطلاعات سفارشی',
-        'node_status' => 'وضعیت گره',
+        'custom' => 'سفارشی',
+        'node_status' => 'وضعیت نود',
     ],
     'cancel' => 'لغو',
     'change' => 'تغییر',
@@ -28,48 +28,48 @@ return [
     'convert' => 'تبدیل',
     'copy' => [
         'attribute' => 'کپی',
-        'failed' => 'کپی ناموفق بود، لطفاً دستی کپی کنید',
-        'success' => 'کپی با موفقیت انجام شد',
+        'failed' => 'کپی ناموفق، لطفاً به صورت دستی انجام دهید',
+        'success' => 'کپی موفق',
     ],
-    'create' => 'ایجاد کردن',
+    'create' => 'ایجاد',
     'created_at' => 'تاریخ ایجاد',
-    'customize' => 'شخصی‌سازی',
+    'customize' => 'سفارشی‌سازی',
     'days' => [
-        'attribute' => '{1} روز|{2} روز',
+        'attribute' => '{1}روز|{2}روز',
         'next' => 'روز بعد',
         'weekend' => 'آخر هفته',
         'work' => 'روز کاری',
     ],
     'default' => 'پیش‌فرض',
     'delete' => 'حذف',
-    'deleted' => 'حذف شد',
+    'deleted' => 'حذف شده',
     'deleted_item' => ':attribute حذف شد',
-    'developing' => 'در حال توسعه! منتظر بمانید',
+    'developing' => 'عملکرد در حال توسعه، منتظر بمانید!',
     'download' => 'دانلود',
+    'download_item' => 'دانلود :attribute',
     'edit' => 'ویرایش',
     'error' => 'خطا',
-    'error_action_item' => 'خطای:action:attribute',
-    'error_item' => 'خطای :attribute',
-    'exists_error' => 'حساب‌های مرتبطی تحت :attribute وجود دارد. لطفاً ابتدا ارتباطات را لغو کنید!',
-    'expired_at' => 'تاریخ انقضا',
-    'export' => 'صادر کردن',
+    'error_action_item' => ':action :attribute خطا',
+    'error_item' => ':attribute خطا',
+    'exists_error' => ':attribute با حساب‌های دیگر مرتبط است، لطفاً ابتدا ارتباط را قطع کنید!',
+    'expired_at' => 'زمان انقضا',
+    'export' => 'صادرات',
     'failed' => 'ناموفق',
-    'failed_action_item' => ':action :attribute ناموفق بود',
-    'failed_item' => ':attribute ناموفق بود',
+    'failed_action_item' => ':action :attribute ناموفق',
+    'failed_item' => ':attribute ناموفق',
     'free' => 'رایگان',
     'function' => [
         'fullscreen' => 'تمام صفحه',
-        'menubar' => 'نوار منو',
+        'menubar' => 'منو',
         'navigation' => 'ناوبری',
     ],
     'generate' => 'تولید',
     'generate_item' => 'تولید :attribute',
-    'goto' => 'برو به',
-    'hour' => '{1} ساعت|{2} ساعت',
+    'goto' => 'رفتن به',
+    'hour' => '{1}ساعت|{2}ساعت',
     'import' => 'وارد کردن',
-    'latest_at' => 'آخرین فعالیت',
+    'latest_at' => 'آخرین به‌روزرسانی',
     'more' => 'بیشتر',
-    'new' => 'جدید',
     'none' => 'هیچ',
     'open' => 'باز کردن',
     'or' => 'یا',
@@ -79,23 +79,23 @@ return [
             'completed' => 'تکمیل شده',
             'ongoing' => 'در حال استفاده',
             'prepaid' => 'پیش‌پرداخت',
-            'review' => 'در انتظار بررسی',
+            'review' => 'در انتظار تأیید',
         ],
     ],
     'payment' => [
         'alipay' => 'علی‌پی',
-        'credit' => 'اعتبار',
+        'credit' => 'موجودی',
         'crypto' => 'ارز دیجیتال',
         'manual' => 'پرداخت دستی',
         'qq' => 'کیف پول QQ',
-        'wechat' => 'پرداخت وی‌چت',
+        'wechat' => 'پرداخت WeChat',
     ],
     'print' => 'چاپ',
     'qrcode' => 'کد QR :attribute',
-    'random_generate' => 'خالی بگذارید تا به صورت تصادفی تولید شود',
+    'random_generate' => 'خالی بگذارید تا تصادفی تولید شود',
     'recommend' => 'توصیه',
     'request' => 'درخواست',
-    'request_failed' => 'درخواست ناموفق بود، لطفاً دوباره تلاش کنید',
+    'request_failed' => 'درخواست ناموفق، لطفاً مجدداً تلاش کنید',
     'request_url' => 'آدرس درخواست',
     'reset' => 'بازنشانی',
     'search' => 'جستجو',
@@ -104,19 +104,19 @@ return [
     'status' => [
         'applying' => 'در حال درخواست',
         'attribute' => 'وضعیت',
-        'available' => 'در دسترس',
-        'banned' => 'ممنوع',
-        'closed' => 'بسته شده',
-        'disabled' => 'غیرفعال',
+        'available' => 'در حال اجرا',
+        'banned' => 'غیرفعال',
+        'closed' => 'بسته',
+        'disabled' => 'متوقف',
         'enabled' => 'فعال',
-        'expire' => 'منقضی شده',
-        'inactive' => 'غیرفعال',
+        'expire' => 'منقضی',
+        'inactive' => 'فعال نشده',
         'limited' => 'محدود',
         'normal' => 'عادی',
         'paid' => 'پرداخت شده',
-        'pass' => 'قبول',
+        'pass' => 'تأیید',
         'payment_pending' => 'در انتظار پرداخت',
-        'pending' => 'در انتظار',
+        'pending' => 'در انتظار پردازش',
         'pending_dispatch' => 'در انتظار تحویل',
         'reject' => 'رد',
         'rejected' => 'رد شده',
@@ -124,28 +124,28 @@ return [
         'review' => 'در انتظار بررسی',
         'reviewed' => 'بررسی شده',
         'run_out' => 'تمام شده',
-        'send_to_credit' => 'اضافه به اعتبار',
-        'unknown' => 'ناشناخته',
+        'send_to_credit' => 'انتقال به موجودی',
+        'unknown' => 'نامشخص',
         'unused' => 'استفاده نشده',
         'used' => 'استفاده شده',
-        'withdrawal_pending' => 'برداشت نشده',
+        'withdrawal_pending' => 'در انتظار برداشت',
         'withdrawn' => 'برداشت شده',
     ],
     'stay_unchanged' => 'خالی بگذارید تا تغییر نکند',
     'storage_logo' => 'ذخیره‌سازی لوگو',
-    'store' => 'ذخیره‌سازی',
+    'store' => 'ذخیره',
     'submit' => 'ارسال',
-    'success' => 'موفقیت',
-    'success_action_item' => ':action :attribute با موفقیت',
-    'success_item' => ':attribute موفق شد',
-    'to' => 'به',
+    'success' => 'موفق',
+    'success_action_item' => ':action :attribute موفق',
+    'success_item' => ':attribute موفق',
+    'to' => 'تا',
     'to_be_send' => 'در انتظار ارسال',
-    'to_safari' => 'روی آیکون <i class="icon wb-more-horizontal" aria-hidden="true"></i> در بالای سمت راست کلیک کنید، سپس انتخاب کنید "باز کردن در <img class="w-30 h-30 vertical-align-middle m-3" src="https://gw.alicdn.com/tfs/TB1xwiUNpXXXXaIXXXXXXXXXXXX-55-55.png" alt="Safari" /> Safari" تا به درستی به سایت ما دسترسی پیدا کنید!',
-    'toggle' => 'تغییر وضعیت',
-    'toggle_action' => 'تغییر وضعیت :action',
+    'to_safari' => 'روی <i class="icon wb-more-horizontal" aria-hidden="true"></i> در بالا سمت راست کلیک کنید، سپس <img class="w-30 h-30 vertical-align-middle m-3" src="https://gw.alicdn.com/tfs/TB1xwiUNpXXXXaIXXXXXXXXXXXX-55-55.png" alt="Safari" /> Safari را انتخاب کنید تا باز شود<br>تا بتوانید به طور عادی به این وب‌سایت دسترسی داشته باشید!',
+    'toggle' => 'تغییر حالت',
+    'toggle_action' => 'تغییر حالت :action',
     'unlimited' => 'نامحدود',
-    'update' => 'بروزرسانی',
-    'updated_at' => 'آخرین بروزرسانی',
+    'update' => 'به‌روزرسانی',
+    'updated_at' => 'زمان به‌روزرسانی',
     'view' => 'مشاهده',
     'warning' => 'هشدار',
 ];

+ 23 - 23
resources/lang/fa/errors.php

@@ -4,33 +4,33 @@ declare(strict_types=1);
 
 return [
     'forbidden' => [
-        'access' => 'دسترسی IP یا پروکسی ناشناخته شناسایی شد، دسترسی ممنوع',
-        'bots' => 'دسترسی ربات شناسایی شد، دسترسی ممنوع',
-        'china' => 'دسترسی IP یا پروکسی چین شناسایی شد، دسترسی ممنوع',
-        'oversea' => 'دسترسی IP یا پروکسی خارج از کشور شناسایی شد، دسترسی ممنوع',
-        'redirect' => '(:ip :url) از طریق پیوند اشتراک دسترسی شناسایی شد، تغییر مسیر اجباری.',
-        'unknown' => 'حالت دسترسی ممنوع ناشناخته! لطفاً [حالت مسدودسازی] را در تنظیمات سیستم تغییر دهید!',
+        'access' => 'IP یا پروکسی ناشناخته تشخیص داده شد، دسترسی ممنوع!',
+        'bots' => 'دسترسی ربات تشخیص داده شد، دسترسی ممنوع!',
+        'china' => 'IP یا پروکسی چینی تشخیص داده شد، دسترسی ممنوع!',
+        'oversea' => 'IP یا پروکسی خارجی تشخیص داده شد، دسترسی ممنوع!',
+        'redirect' => 'تشخیص داده شد (:ip :url) از لینک اشتراک دسترسی دارد، اجباراً تغییر مسیر داده شد',
+        'unknown' => 'حالت رهگیری ناشناخته، لطفاً در تنظیمات سیستم پیکربندی را بررسی کنید!',
     ],
-    'get_ip' => 'دریافت اطلاعات IP ناموفق بود',
+    'get_ip' => 'دریافت اطلاعات IP ناموفق',
     'log' => 'لاگ',
     'refresh' => 'تازه‌سازی',
-    'refresh_page' => 'لطفاً صفحه را تازه‌سازی کنید و دوباره تلاش کنید',
-    'report' => 'خطا حاوی گزارشی بود: ',
-    'safe_code' => 'لطفاً کد ایمنی را وارد کنید',
-    'safe_enter' => 'ورود امن',
+    'refresh_page' => 'لطفاً صفحه را تازه‌سازی کرده و دوباره تلاش کنید',
+    'report' => 'محتوای گزارش خطا:',
+    'safe_code' => 'لطفاً کد امنیتی را وارد کنید',
+    'safe_enter' => 'دسترسی از ورودی امن',
     'subscribe' => [
-        'banned_until' => 'حساب تا :time ممنوع شده، لطفاً برای باز شدن صبر کنید!',
-        'expired' => 'حساب منقضی شده! لطفاً اشتراک خود را تمدید کنید!',
-        'none' => 'هیچ گره‌ای در دسترس نیست',
-        'out' => 'داده تمام شده! لطفاً بیشتر خریداری کنید یا داده را بازنشانی کنید!',
-        'question' => 'مشکلات حساب؟! برای جزئیات به وب‌سایت مراجعه کنید',
-        'sub_banned' => 'اشتراک ممنوع شده! برای جزئیات به وب‌سایت مراجعه کنید',
-        'unknown' => 'لینک اشتراک نامعتبر است! لطفاً لینک جدیدی دریافت کنید!',
-        'user' => 'URL نامعتبر است، حساب وجود ندارد!',
-        'user_disabled' => 'حساب غیرفعال شده است! با پشتیبانی تماس بگیرید!',
+        'banned_until' => 'حساب تا :time مسدود شده، لطفاً پس از رفع مسدودی دوباره تلاش کنید!',
+        'expired' => 'حساب منقضی شده، لطفاً پس از تمدید استفاده کنید!',
+        'none' => 'موقتاً نود قابل استفاده‌ای وجود ندارد',
+        'out' => 'ترافیک تمام شده، لطفاً خریداری کنید یا ترافیک را بازنشانی کنید!',
+        'question' => 'حساب دارای ناهنجاری است، لطفاً به وب‌سایت رسمی بروید و جزئیات را بررسی کنید!',
+        'sub_banned' => 'لینک اشتراک مسدود شده، لطفاً به وب‌سایت رسمی بروید و دلیل را بفهمید!',
+        'unknown' => 'لینک اشتراک نامعتبر، لطفاً دوباره دریافت کنید!',
+        'user' => 'لینک نامعتبر، حساب وجود ندارد، لطفاً دوباره دریافت کنید!',
+        'user_disabled' => 'حساب غیرفعال شده!',
     ],
     'title' => '⚠️ خطا رخ داد',
-    'unsafe_enter' => 'ورود ناامن',
-    'visit' => 'لطفاً مراجعه کنید به',
-    'whoops' => 'اوپس!',
+    'unsafe_enter' => 'دسترسی از ورودی غیرامن',
+    'visit' => 'لطفاً مراجعه کنید',
+    'whoops' => 'اوه!',
 ];

+ 240 - 108
resources/lang/fa/model.php

@@ -4,131 +4,129 @@ declare(strict_types=1);
 
 return [
     'aff' => [
-        'amount' => 'مقدار سفارش',
+        'amount' => 'مبلغ مصرف',
         'commission' => 'کمیسیون',
-        'created_at' => 'سفارش شده در',
-        'invitee' => 'خریدار',
-        'updated_at' => 'پردازش شده در',
+        'created_at' => 'زمان سفارش',
+        'invitee' => 'مصرف‌کننده',
+        'updated_at' => 'زمان پردازش',
     ],
     'article' => [
         'attribute' => 'مقاله',
         'category' => 'دسته‌بندی',
-        'created_at' => 'منتشر شده در',
+        'created_at' => 'زمان انتشار',
         'language' => 'زبان',
-        'logo' => 'کاور',
-        'updated_at' => 'بروزرسانی شده در',
+        'logo' => 'تصویر جلد',
     ],
     'common' => [
         'description' => 'توضیحات',
-        'extend' => 'اطلاعات گسترش یافته',
+        'extend' => 'اطلاعات تکمیلی',
         'level' => 'سطح',
-        'sort' => 'مرتب‌سازی',
+        'sort' => 'ترتیب',
         'type' => 'نوع',
     ],
     'country' => [
-        'code' => 'کد کشور',
-        'icon' => 'پرچم',
+        'icon' => 'پرچم کشور',
         'name' => 'نام کشور',
     ],
     'coupon' => [
         'attribute' => 'کوپن',
         'groups' => 'محدودیت گروه',
         'levels' => 'محدودیت سطح',
-        'logo' => 'لوگو',
-        'minimum' => 'حداقل هزینه',
+        'logo' => 'تصویر',
+        'minimum' => 'حداقل خرید',
         'name' => 'نام',
-        'newbie' => 'فقط برای کاربران جدید',
+        'newbie' => 'ویژه کاربران جدید',
         'num' => 'تعداد',
         'priority' => 'اولویت',
-        'services_blacklist' => 'کالاهای ممنوع',
-        'services_whitelist' => 'کالاهای مجاز',
-        'sn' => 'کد',
+        'services_blacklist' => 'محصولات غیرمجاز',
+        'services_whitelist' => 'محصولات مجاز',
+        'sn' => 'کد کوپن',
         'usable_times' => 'تعداد استفاده',
-        'used' => 'محدودیت شخصی',
-        'users_blacklist' => 'کاربران ممنوع',
+        'used' => 'محدودیت هر کاربر',
+        'users_blacklist' => 'کاربران غیرمجاز',
         'users_whitelist' => 'کاربران مجاز',
-        'value' => 'مقدار',
+        'value' => 'ارزش',
     ],
     'goods' => [
-        'attribute' => 'کالا',
-        'available_date' => 'دوره اعتبار',
+        'attribute' => 'محصول',
+        'available_date' => 'مدت اعتبار',
         'category' => 'دسته‌بندی',
         'color' => 'رنگ',
         'hot' => 'پرفروش',
         'info' => 'اطلاعات سفارشی',
-        'invite_num' => 'دعوت‌نامه‌های اضافی',
-        'limit_num' => 'محدودیت خرید',
-        'logo' => 'لوگو',
+        'invite_num' => 'سهمیه دعوت اضافی',
+        'limit_num' => 'محدودیت خرید هر کاربر',
+        'logo' => 'تصویر محصول',
         'name' => 'نام',
         'period' => 'دوره بازنشانی',
-        'price' => 'قیمت',
-        'renew' => 'قیمت بازنشانی داده',
-        'traffic' => 'مقدار داده مجاز',
+        'price' => 'قیمت فروش',
+        'renew' => 'قیمت بازنشانی ترافیک',
+        'traffic' => 'ترافیک',
         'user_limit' => 'محدودیت سرعت کاربر',
     ],
     'ip' => [
-        'info' => 'مکان',
+        'info' => 'موقعیت جغرافیایی',
         'network_type' => 'نوع شبکه',
     ],
     'node' => [
-        'attribute' => 'گره',
-        'client_limit' => 'محدودیت کاربر',
-        'country' => 'کشور',
-        'data_consume' => 'مصرف داده',
-        'data_rate' => 'نسبت داده',
+        'attribute' => 'نود',
+        'client_limit' => 'محدودیت دستگاه',
+        'country' => 'کشور/منطقه',
+        'data_consume' => 'ترافیک مصرفی',
+        'data_rate' => 'ضریب ترافیک',
         'ddns' => 'DDNS',
-        'detection' => 'تشخیص مسدود شدن',
+        'detection' => 'تشخیص مسدودی',
         'display' => 'نمایش و اشتراک',
         'domain' => 'دامنه',
-        'id' => 'شناسه گره',
-        'ipv4' => 'IPv4',
-        'ipv6' => 'IPv6',
+        'id' => 'شناسه نود',
+        'ipv4' => 'آدرس IPv4',
+        'ipv6' => 'آدرس IPv6',
         'label' => 'برچسب',
-        'method' => 'رمزنگاری',
-        'name' => 'نام',
+        'method' => 'روش رمزگذاری',
+        'name' => 'نام نود',
         'next_renewal_date' => 'تاریخ تمدید بعدی',
-        'obfs' => 'پنهان‌سازی',
-        'obfs_param' => 'پارامتر پنهان‌سازی',
+        'obfs' => 'پروتکل obfuscation',
+        'obfs_param' => 'پارامترهای obfuscation',
         'online_user' => 'کاربران آنلاین',
-        'protocol' => 'پروتکل',
-        'protocol_param' => 'پارامتر پروتکل',
-        'push_port' => 'پورت ارسال',
-        'relay_port' => 'پورت رله',
-        'renewal_cost' => 'مبلغ صورتحساب',
+        'protocol' => 'پروتکل انتقال',
+        'protocol_param' => 'پارامترهای پروتکل',
+        'push_port' => 'پورت push',
+        'relay_port' => 'پورت relay',
+        'renewal_cost' => 'هزینه تمدید',
         'service_port' => 'پورت سرویس',
-        'single' => 'پورت تکی',
-        'single_passwd' => '[تکی] رمز عبور',
-        'static' => 'وضعیت زنده بودن',
-        'subscription_term' => 'مدت اشتراک',
-        'traffic_limit' => 'محدودیت سرعت',
-        'transfer' => 'رله',
-        'udp' => 'UDP',
-        'v2_alter_id' => 'شناسه جایگزین',
-        'v2_cover' => 'پوشش',
-        'v2_host' => 'میزبان',
-        'v2_net' => 'شبکه',
-        'v2_path' => 'مسیر | کلید',
+        'single' => 'تک پورت',
+        'single_passwd' => 'رمز عبور تک پورت',
+        'static' => 'وضعیت اجرا',
+        'subscription_term' => 'دوره اشتراک',
+        'traffic_limit' => 'محدودیت ترافیک',
+        'transfer' => 'تنظیمات relay',
+        'udp' => 'پشتیبانی UDP',
+        'v2_alter_id' => 'شناسه اضافی',
+        'v2_cover' => 'پوشش ترافیک',
+        'v2_host' => 'دامنه پوشش',
+        'v2_net' => 'پروتکل انتقال',
+        'v2_path' => 'مسیر/کلید',
         'v2_sni' => 'SNI',
-        'v2_tls' => 'TLS',
-        'v2_tls_provider' => 'پیکربندی TLS',
+        'v2_tls' => 'رمزگذاری TLS',
+        'v2_tls_provider' => 'پیکربندی گواهی TLS',
     ],
     'node_auth' => [
-        'attribute' => 'احراز هویت گره',
-        'key' => 'کلید <small>برای گره</small>',
+        'attribute' => 'احراز هویت نود',
+        'key' => 'کلید ارتباط',
         'secret' => 'کلید معکوس',
     ],
     'node_cert' => [
-        'attribute' => 'گواهینامه دامنه',
+        'attribute' => 'گواهی',
         'domain' => 'دامنه',
-        'expired_date' => 'تاریخ انقضا',
-        'issuer' => 'صادر کننده',
-        'key' => 'کلید',
-        'pem' => 'PEM',
-        'signed_date' => 'تاریخ امضا',
+        'expired_date' => 'زمان انقضا',
+        'issuer' => 'مرجع صدور',
+        'key' => 'کلید خصوصی',
+        'pem' => 'گواهی',
+        'signed_date' => 'تاریخ صدور',
     ],
     'notification' => [
-        'address' => 'گیرنده',
-        'created_at' => 'ارسال شده در',
+        'address' => 'آدرس گیرنده',
+        'created_at' => 'زمان تحویل',
         'status' => 'وضعیت',
     ],
     'oauth' => [
@@ -137,10 +135,10 @@ return [
     ],
     'order' => [
         'attribute' => 'سفارش',
-        'id' => 'شناسه سفارش',
-        'original_price' => 'قیمت اولیه',
+        'id' => 'شماره سفارش',
+        'original_price' => 'قیمت اصلی',
         'pay_way' => 'روش پرداخت',
-        'price' => 'قیمت واقعی',
+        'price' => 'مبلغ پرداختی',
         'status' => 'وضعیت',
     ],
     'permission' => [
@@ -149,10 +147,10 @@ return [
         'name' => 'نام مسیر',
     ],
     'referral' => [
-        'amount' => 'مقدار',
-        'created_at' => 'درخواست شده در',
-        'id' => 'شناسه درخواست',
-        'user' => 'درخواست کننده',
+        'amount' => 'مبلغ درخواست',
+        'created_at' => 'زمان درخواست',
+        'id' => 'شماره درخواست',
+        'user' => 'حساب درخواست‌کننده',
     ],
     'role' => [
         'attribute' => 'نقش',
@@ -162,72 +160,206 @@ return [
     'rule' => [
         'attribute' => 'قانون',
         'name' => 'توضیحات',
-        'pattern' => 'مقدار',
+        'pattern' => 'مقدار تطبیق',
+        'logs' => 'رکوردهای فعال‌سازی',
     ],
     'rule_group' => [
         'attribute' => 'گروه قوانین',
-        'name' => 'نام',
+        'name' => 'نام گروه',
         'rules' => 'قوانین',
-        'type' => 'نوع',
+        'type' => 'حالت',
     ],
     'subscribe' => [
-        'ban_desc' => 'دلیل ممنوعیت',
-        'ban_time' => 'زمان ممنوعیت',
+        'attribute' => 'اشتراک',
+        'ban_desc' => 'دلیل مسدودی',
+        'ban_time' => 'زمان مسدودی',
         'code' => 'کد اشتراک',
-        'req_header' => 'هدر دسترسی',
+        'req_header' => 'هدر درخواست',
         'req_ip' => 'IP درخواست',
-        'req_times' => 'تعداد درخواست‌ها',
+        'req_times' => 'تعداد درخواست',
         'updated_at' => 'آخرین درخواست',
     ],
     'user' => [
         'account_status' => 'وضعیت حساب',
         'attribute' => 'کاربر',
-        'created_date' => 'تاریخ ثبت‌نام',
-        'credit' => 'اعتبار',
-        'expired_date' => 'تاریخ انقضا',
+        'created_date' => 'زمان ثبت نام',
+        'credit' => 'موجودی',
+        'expired_date' => 'زمان انقضا',
         'id' => 'شناسه کاربر',
-        'invite_num' => 'تعداد دعوت‌های موجود',
+        'invite_num' => 'سهمیه دعوت',
         'inviter' => 'دعوت‌کننده',
         'nickname' => 'نام مستعار',
         'password' => 'رمز عبور',
         'port' => 'پورت',
-        'proxy_method' => 'رمزنگاری',
-        'proxy_obfs' => 'پنهان‌سازی',
-        'proxy_passwd' => 'رمز پروکسی',
-        'proxy_protocol' => 'پروتکل',
+        'proxy_method' => 'رمزگذاری انتقال',
+        'proxy_obfs' => 'پوشش ترافیک',
+        'proxy_passwd' => 'رمز عبور پروکسی',
+        'proxy_protocol' => 'پروتکل پروکسی',
         'proxy_status' => 'وضعیت پروکسی',
         'qq' => 'QQ',
         'remark' => 'یادداشت',
-        'reset_date' => 'تاریخ بازنشانی داده',
-        'role' => 'نقش',
-        'service' => 'پروکسی',
+        'reset_date' => 'تاریخ بازنشانی ترافیک',
+        'role' => 'نقش کاربر',
+        'service' => 'سرویس پروکسی',
         'speed_limit' => 'محدودیت سرعت',
-        'traffic_used' => 'داده استفاده شده',
-        'usable_traffic' => 'مقدار داده مجاز',
+        'traffic_used' => 'ترافیک مصرفی',
+        'usable_traffic' => 'ترافیک قابل استفاده',
         'username' => 'نام کاربری',
         'uuid' => 'VMess UUID',
-        'wechat' => 'وی‌چت',
+        'wechat' => 'WeChat',
     ],
     'user_credit' => [
-        'after' => 'بعد از تغییر',
+        'after' => 'پس از تغییر',
         'amount' => 'مقدار تغییر',
         'before' => 'قبل از تغییر',
-        'created_at' => 'زمان تغییر',
+        'created_at' => 'زمان ثبت',
     ],
     'user_data_modify' => [
-        'after' => 'بعد از تغییر',
+        'after' => 'پس از تغییر',
         'before' => 'قبل از تغییر',
-        'created_at' => 'زمان تغییر',
+        'created_at' => 'زمان ثبت',
     ],
     'user_group' => [
         'attribute' => 'گروه کاربری',
-        'name' => 'نام گروه',
-        'nodes' => 'گره‌ها',
+        'name' => 'نام',
+        'nodes' => 'نودها',
     ],
     'user_traffic' => [
-        'download' => 'دانلود',
+        'download' => 'ترافیک دانلود',
         'log_time' => 'زمان ثبت',
-        'total' => 'کل',
-        'upload' => 'آپلود',
+        'total' => 'کل ترافیک',
+        'upload' => 'ترافیک آپلود',
+    ],
+    'config' => [
+        'AppStore_id' => 'Apple ID',
+        'AppStore_password' => 'رمز عبور Apple',
+        'account_expire_notification' => 'اعلان انقضای حساب',
+        'active_times' => 'محدودیت تعداد فعال‌سازی',
+        'admin_invite_days' => 'اعتبار کد دعوت مدیر',
+        'affiliate_link_salt' => 'رمزگذاری لینک دعوت',
+        'alipay_qrcode' => 'کد QR علی‌پی',
+        'auto_release_port' => 'بازیافت پورت',
+        'ban_duration' => 'مدت مسدودی',
+        'bark_key' => 'شماره دستگاه Bark',
+        'captcha_key' => 'کلید کپچا',
+        'captcha_secret' => 'Secret/ID کپچا',
+        'checkin_interval' => 'فاصله چک‌این',
+        'checkin_reward' => 'پاداش چک‌این',
+        'codepay_id' => 'شناسه CodePay',
+        'codepay_key' => 'کلید ارتباط',
+        'codepay_url' => 'URL درخواست',
+        'cryptomus_api_key' => 'کلید API',
+        'cryptomus_merchant_uuid' => 'شناسه بازرگان',
+        'data_anomaly_notification' => 'اعلان ناهنجاری ترافیک',
+        'data_exhaust_notification' => 'اعلان تمام شدن ترافیک',
+        'ddns_key' => 'کلید DNS',
+        'ddns_mode' => 'حالت DDNS',
+        'ddns_secret' => 'Secret DNS',
+        'default_days' => 'مدت اعتبار اولیه',
+        'default_traffic' => 'ترافیک اولیه',
+        'detection_check_times' => 'اعلان تشخیص مسدودی',
+        'dingTalk_access_token' => 'Access Token دینگ‌تاک',
+        'dingTalk_secret' => 'کلید دینگ‌تاک',
+        'epay_key' => 'کلید بازرگان',
+        'epay_mch_id' => 'شناسه بازرگان',
+        'epay_url' => 'آدرس رابط',
+        'expire_days' => 'آستانه هشدار انقضا',
+        'f2fpay_app_id' => 'شناسه اپلیکیشن',
+        'f2fpay_private_key' => 'کلید خصوصی اپلیکیشن',
+        'f2fpay_public_key' => 'کلید عمومی علی‌پی',
+        'forbid_mode' => 'حالت مسدودسازی دسترسی',
+        'iYuu_token' => 'توکن IYUU',
+        'invite_num' => 'سهمیه دعوت اولیه',
+        'is_AliPay' => 'علی‌پی',
+        'is_QQPay' => 'کیف پول QQ',
+        'is_WeChatPay' => 'پرداخت WeChat',
+        'is_activate_account' => 'فعال‌سازی حساب',
+        'is_ban_status' => 'مسدودی خودکار پس از انقضا',
+        'is_captcha' => 'کپچا',
+        'is_clear_log' => 'پاک‌سازی خودکار لاگ',
+        'is_custom_subscribe' => 'اشتراک پیشرفته',
+        'is_email_filtering' => 'فیلتر ایمیل',
+        'is_forbid_robot' => 'مسدودسازی ربات',
+        'is_free_code' => 'کد دعوت رایگان',
+        'is_invite_register' => 'ثبت نام با دعوت',
+        'is_otherPay' => 'پرداخت ویژه',
+        'is_rand_port' => 'پورت تصادفی',
+        'is_register' => 'ثبت نام آزاد',
+        'maintenance_content' => 'اعلامیه تعمیر',
+        'maintenance_mode' => 'حالت تعمیر',
+        'maintenance_time' => 'زمان پایان تعمیر',
+        'min_port' => 'محدوده پورت',
+        'node_blocked_notification' => 'اعلان مسدودی نود',
+        'node_daily_notification' => 'گزارش روزانه نود',
+        'node_offline_notification' => 'اعلان آفلاین نود',
+        'node_renewal_notification' => 'یادآوری تمدید نود',
+        'oauth_path' => 'ورود شخص ثالث',
+        'offline_check_times' => 'تعداد اعلان آفلاین',
+        'password_reset_notification' => 'بازنشانی رمز عبور',
+        'paybeaver_app_id' => 'App ID',
+        'paybeaver_app_secret' => 'App Secret',
+        'payjs_key' => 'کلید ارتباط',
+        'payjs_mch_id' => 'شماره بازرگان',
+        'payment_callback_url' => 'آدرس بازگشت پرداخت',
+        'payment_confirm_notification' => 'اعلان پرداخت دستی',
+        'payment_received_notification' => 'اعلان موفقیت پرداخت',
+        'paypal_app_id' => 'APP ID',
+        'paypal_client_id' => 'Client ID',
+        'paypal_client_secret' => 'Client Secret Key',
+        'pushDeer_key' => 'کلید PushDeer',
+        'pushplus_token' => 'توکن PushPlus',
+        'rand_subscribe' => 'اشتراک تصادفی',
+        'recently_heartbeat' => 'آستانه بار اخیر نود',
+        'redirect_url' => 'آدرس تغییر مسیر',
+        'referral_money' => 'حداقل مبلغ برداشت',
+        'referral_percent' => 'نسبت کمیسیون',
+        'referral_reward_type' => 'حالت کمیسیون',
+        'referral_status' => 'عملکرد تبلیغات',
+        'referral_traffic' => 'ترافیک هدیه ثبت نام',
+        'register_ip_limit' => 'محدودیت ثبت نام همان IP',
+        'reset_password_times' => 'حد بالای بازنشانی رمز روزانه',
+        'reset_traffic' => 'بازنشانی دوره‌ای ترافیک',
+        'server_chan_key' => 'SCKEY ServerChan',
+        'standard_currency' => 'ارز پایه',
+        'stripe_public_key' => 'کلید عمومی',
+        'stripe_secret_key' => 'کلید مخفی',
+        'stripe_signing_secret' => 'WebHook Signing Secret',
+        'subject_name' => 'نام محصول',
+        'subscribe_domain' => 'آدرس اشتراک',
+        'subscribe_max' => 'تعداد نودهای اشتراک',
+        'subscribe_rate_limit' => 'محدودیت درخواست اشتراک',
+        'tasks_chunk' => 'مقدار پردازش بخشی',
+        'tasks_clean' => 'وظایف پاک‌سازی',
+        'tasks_close' => 'وظایف بستن',
+        'telegram_token' => 'توکن تلگرام',
+        'tg_chat_token' => 'توکن TG Chat',
+        'theadpay_key' => 'کلید بازرگان',
+        'theadpay_mchid' => 'شناسه بازرگان',
+        'theadpay_url' => 'آدرس رابط',
+        'ticket_closed_notification' => 'اعلان بستن تیکت',
+        'ticket_created_notification' => 'اعلان تیکت جدید',
+        'ticket_replied_notification' => 'اعلان پاسخ تیکت',
+        'traffic_abuse_limit' => 'آستانه ناهنجاری ترافیک',
+        'traffic_warning_percent' => 'آستانه هشدار تمام شدن ترافیک',
+        'trojan_license' => 'مجوز Trojan',
+        'user_invite_days' => 'اعتبار کد دعوت کاربر',
+        'username_type' => 'نوع حساب',
+        'v2ray_license' => 'مجوز V2Ray',
+        'v2ray_tls_provider' => 'TLS V2Ray',
+        'web_api_url' => 'دامنه دسترسی مجوز/بک‌اند',
+        'webmaster_email' => 'ایمیل مدیر سایت',
+        'website_customer_service_code' => 'کد پشتیبانی',
+        'website_home_logo' => 'لوگوی صفحه اصلی',
+        'website_logo' => 'لوگوی داخل سایت',
+        'website_name' => 'نام وب‌سایت',
+        'website_security_code' => 'کد امنیتی',
+        'website_statistics_code' => 'کد آمار',
+        'website_url' => 'آدرس وب‌سایت',
+        'wechat_aid' => 'شناسه اپلیکیشن',
+        'wechat_cid' => 'شناسه شرکت',
+        'wechat_encodingAESKey' => 'EncodingAESKey',
+        'wechat_qrcode' => 'کد QR WeChat',
+        'wechat_secret' => 'کلید مخفی اپلیکیشن',
+        'wechat_token' => 'TOKEN',
     ],
 ];

+ 34 - 34
resources/lang/fa/notification.php

@@ -4,48 +4,48 @@ declare(strict_types=1);
 
 return [
     'account_expired' => 'یادآوری انقضای حساب',
-    'account_expired_blade' => 'حساب شما در :days روز آینده منقضی خواهد شد، لطفاً به موقع تمدید کنید',
-    'account_expired_content' => 'حساب شما در :days روز آینده منقضی خواهد شد، لطفاً برای جلوگیری از هرگونه مشکل در استفاده به موقع تمدید کنید',
-    'active_email' => 'لطفاً در عرض 30 دقیقه تأیید را تکمیل کنید',
+    'account_expired_blade' => 'حساب شما در :days روز منقضی می‌شود، لطفاً به موقع تمدید کنید',
+    'account_expired_content' => 'حساب شما در :days روز منقضی خواهد شد. لطفاً به موقع تمدید کنید تا خدمات شما قطع نشود',
+    'active_email' => 'لطفاً ظرف 30 دقیقه تأیید را تکمیل کنید',
     'attribute' => 'اعلان',
-    'block_report' => 'گزارش مسدود شدن:',
-    'close_ticket' => 'تیکت [شماره: :id، عنوان: :title] بسته شد',
-    'data_anomaly' => 'اعلان داده غیرعادی برای کاربر',
-    'data_anomaly_content' => 'کاربر [ID: :id] در یک ساعت گذشته از داده‌های زیر استفاده کرده است: [آپلود: :upload، دانلود: :download، مجموع: :total]',
+    'block_report' => 'گزارش تفصیلی مسدودی:',
+    'close_ticket' => 'تیکت [شناسه: :id، عنوان: :title] بسته شد',
+    'data_anomaly' => 'هشدار ناهنجاری ترافیک کاربر',
+    'data_anomaly_content' => 'کاربر [شناسه: :id]، وضعیت ترافیک در 1 ساعت گذشته (آپلود: :upload، دانلود: :download، مجموع: :total)',
     'details' => 'مشاهده جزئیات',
-    'details_btn' => 'لطفاً روی دکمه زیر کلیک کنید تا جزئیات را مشاهده کنید.',
-    'ding_bot_limit' => 'هر ربات می‌تواند حداکثر 20 پیام در دقیقه ارسال کند. در صورت تجاوز از این حد، 10 دقیقه محدودیت اعمال می‌شود.',
-    'empty' => 'شما در حال حاضر هیچ پیام جدیدی ندارید',
-    'error' => '[:channel] ارسال پیام با استثنا: :reason',
-    'get_access_token_failed' => 'دریافت توکن دسترسی با شکست مواجه شد!\nبا پارامترهای درخواست: :body',
-    'into_maintenance' => 'به‌طور خودکار وارد حالت نگهداری شوید',
-    'new' => 'شما :num پیام جدید دارید',
-    'new_ticket' => 'یک تیکت جدید با عنوان :title ایجاد شده است، لطفاً بررسی کنید.',
-    'next_check_time' => 'زمان بعدی بررسی انسداد گره: :time',
+    'details_btn' => 'لطفاً روی دکمه زیر کلیک کنید【مشاهده جزئیات】',
+    'ding_bot_limit' => 'هر ربات حداکثر 20 پیام در دقیقه می‌تواند ارسال کند، تجاوز از این حد باعث محدودیت 10 دقیقه‌ای می‌شود.',
+    'empty' => 'در حال حاضر پیام جدیدی ندارید',
+    'error' => '[:channel] استثنای ارسال پیام: :reason',
+    'get_access_token_failed' => 'دریافت access_token ناموفق!\nپارامترهای دسترسی همراه: :body',
+    'into_maintenance' => 'ورود خودکار به حالت تعمیر',
+    'new' => ':num پیام جدید دارید',
+    'new_ticket' => 'تیکت جدیدی دریافت کرده‌اید [عنوان: :title]، لطفاً برای مشاهده محتوای تفصیلی کلیک کنید.',
+    'next_check_time' => 'زمان تشخیص مسدودی نود بعدی: :time',
     'node' => [
         'download' => 'دانلود',
         'total' => 'مجموع',
         'upload' => 'آپلود',
     ],
-    'node_block' => 'هشدار مسدود شدن گره',
-    'node_offline' => 'هشدار آفلاین بودن گره',
-    'node_offline_content' => 'گره‌های زیر غیرعادی هستند و ممکن است آفلاین باشند:',
-    'node_renewal' => 'یادآوری تمدید گره',
-    'node_renewal_blade' => 'گره‌های زیر در حال نزدیک شدن به تاریخ انقضا هستند. لطفاً به‌موقع تمدید کنید: :nodes',
-    'node_renewal_content' => 'گره‌های زیر در حال نزدیک شدن به تاریخ انقضا هستند. لطفاً قبل از انقضا تمدید کنید تا از قطع خدمات جلوگیری شود.',
-    'payment_received' => 'پرداخت سفارش شما با مبلغ :amount با موفقیت انجام شد، لطفاً برای مشاهده جزئیات سفارش کلیک کنید',
+    'node_block' => 'اعلان هشدار مسدودی نود',
+    'node_offline' => 'هشدار آفلاین نود',
+    'node_offline_content' => 'نودهای زیر ناهنجار هستند، احتمالاً آفلاین شده‌اند:',
+    'node_renewal' => 'یادآوری تمدید نود',
+    'node_renewal_blade' => 'نود در حال انقضا است، لطفاً به موقع تمدید کنید: :nodes',
+    'node_renewal_content' => 'نودهای زیر در حال انقضا هستند، لطفاً قبل از انقضا تمدید کنید تا خدمات قطع نشود.',
+    'payment_received' => 'پرداخت سفارش شما موفق بود، مبلغ :amount، لطفاً برای مشاهده جزئیات سفارش کلیک کنید',
     'reply_ticket' => 'پاسخ تیکت: :title',
-    'reset_failed' => '[وظیفه روزانه] بازنشانی داده‌ها برای کاربر [ID: :uid، نام کاربری: :username] ناموفق بود',
-    'serverChan_exhausted' => 'حد مجاز امروز به پایان رسید!',
-    'serverChan_limit' => 'فرکانس در هر دقیقه بیش از حد بالا است. لطفاً تنظیمات اعلان را بهینه کنید!',
-    'sign_failed' => 'تأیید امضای امنیتی ناموفق بود',
+    'reset_failed' => '[وظیفه روزانه] کاربر [شناسه: :uid، نام کاربری: :username] بازنشانی ترافیک ناموفق',
+    'serverChan_exhausted' => 'سهمیه امروز تمام شده!',
+    'serverChan_limit' => 'فرکانس دقیقه‌ای بالا، لطفاً سناریوی اعلان را بهینه کنید!',
+    'sign_failed' => 'تأیید امضای امنیتی ناموفق',
     'ticket_content' => 'محتوای تیکت:',
-    'traffic_remain' => 'میزان مصرف داده شما :percent% است. لطفاً با احتیاط استفاده کنید.',
-    'traffic_tips' => 'لطفاً تاریخ بازنشانی داده را چک کنید و داده‌ها را به‌صورت منطقی مصرف کنید یا در صورت اتمام، مجدداً شارژ کنید.',
-    'traffic_warning' => 'هشدار استفاده از داده',
+    'traffic_remain' => 'شما :percent% از ترافیک خود را استفاده کرده‌اید، لطفاً ترافیک باقی‌مانده را به طور منطقی تنظیم کنید',
+    'traffic_tips' => 'لطفاً به تاریخ بازنشانی ترافیک توجه کنید، استفاده را به طور منطقی برنامه‌ریزی کنید یا پس از تمام شدن ترافیک شارژ کنید.',
+    'traffic_warning' => 'یادآوری استفاده از ترافیک',
     'verification' => 'کد تأیید شما:',
-    'verification_account' => 'تأیید حساب',
-    'verification_limit' => 'لطفاً در عرض :minutes دقیقه تأیید را تکمیل کنید',
-    'view_ticket' => 'مشاهده وضعیت این تیکت',
-    'view_web' => 'بازدید از وب‌سایت ما',
+    'verification_account' => 'اعلان تأیید حساب',
+    'verification_limit' => 'لطفاً ظرف :minutes دقیقه تأیید کنید',
+    'view_ticket' => 'مشاهده پیشرفت این تیکت',
+    'view_web' => 'مراجعه به وب‌سایت رسمی ما',
 ];

+ 182 - 172
resources/lang/fa/user.php

@@ -4,273 +4,283 @@ declare(strict_types=1);
 
 return [
     'account' => [
-        'connect_password' => 'رمز اتصال پروکسی',
+        'connect_password' => 'رمز عبور اتصال نود',
         'credit' => 'موجودی حساب',
-        'group' => 'گروه',
+        'group' => 'گروه کاربری',
         'last_login' => 'آخرین ورود',
         'level' => 'سطح حساب',
         'reason' => [
-            'expired' => 'طرح شما منقضی شده است',
-            'normal' => 'حساب عادی است',
-            'overused' => 'شما از حد <code>:data</code> گیگابایت برای این دوره فراتر رفته‌اید<br/> محدودیت در <code id="banedTime">:min</code> دقیقه برداشته می‌شود',
-            'traffic_exhausted' => 'داده تمام شده است',
-            'unknown' => 'دلیل ناشناخته، لطفاً مرورگر را تازه‌سازی کنید! اگر مشکل ادامه داشت، با پشتیبانی تماس بگیرید.',
+            'expired' => 'اشتراک منقضی شده',
+            'normal' => 'وضعیت حساب عادی',
+            'overused' => 'از حد <code>:data</code>GB تجاوز کرده<br>رفع مسدودی در <code id="banedTime">:min</code> دقیقه',
+            'traffic_exhausted' => 'تخصیص ترافیک تمام شده',
+            'unknown' => 'خطای سیستم. تازه‌سازی یا تماس با پشتیبانی',
         ],
-        'remain' => 'داده باقیمانده',
-        'reset' => '{0} داده در <code id="restTime">:days</code> بازنشانی می‌شود |{1} :days روز تا بازنشانی داده باقی مانده است |[2,*] :days روز تا بازنشانی داده باقی مانده است',
+        'remain' => 'ترافیک باقی‌مانده',
+        'reset' => '{0} بازنشانی در <code id="restTime">:days</code> |{1} :days روز تا بازنشانی|[2,*] :days روز تا بازنشانی',
         'speed_limit' => 'محدودیت سرعت',
         'status' => 'وضعیت حساب',
-        'time' => 'مدت طرح',
+        'time' => 'دوره اشتراک',
     ],
     'attribute' => [
-        'address' => 'مکان',
-        'data' => 'داده',
+        'address' => 'منطقه',
+        'data' => 'ترافیک',
         'ip' => 'آدرس IP',
         'isp' => 'ISP',
-        'node' => 'گره',
+        'node' => 'نود',
     ],
     'bought_at' => 'تاریخ خرید',
     'clients' => 'کلاینت‌ها',
-    'contact' => 'تماس',
+    'contact' => 'روش تماس',
     'coupon' => [
         'discount' => 'تخفیف',
         'error' => [
             'expired' => 'کوپن منقضی شده',
-            'inactive' => 'کوپن فعال نیست',
-            'minimum' => 'حداقل مبلغ :amount است',
-            'overused' => 'فقط می‌توان :times بار استفاده کرد',
-            'run_out' => 'کوپن تمام شده',
-            'services' => 'کالاها واجد شرایط تخفیف نیستند، شرایط تبلیغ را بررسی کنید',
+            'inactive' => 'کوپن هنوز فعال نشده',
+            'minimum' => 'حداقل خرید: :amount',
+            'overused' => 'محدودیت استفاده: :times بار',
+            'run_out' => 'همه کوپن‌ها دریافت شده‌اند',
+            'services' => 'محصول از تبلیغات مستثنی است',
             'unknown' => 'کوپن نامعتبر',
-            'unmet' => 'شرایط استفاده رعایت نشده',
+            'unmet' => 'شرایط برآورده نشده',
             'used' => 'کوپن قبلاً استفاده شده',
-            'users' => 'حساب واجد شرایط برای تبلیغ نیست',
-            'wait' => 'در :time فعال خواهد شد، لطفاً صبر کنید!',
+            'users' => 'حساب واجد شرایط تبلیغات نیست',
+            'wait' => 'این تبلیغ در :time شروع می‌شود. لطفاً صبر کنید!',
         ],
         'input' => 'کد کوپن را وارد کنید',
     ],
-    'current_role' => 'نقش فعلی به عنوان',
-    'error_response' => 'خطایی رخ داده است، لطفاً بعداً دوباره امتحان کنید.',
+    'current_role' => 'نقش فعلی',
+    'error_response' => 'سیستم مشغول. لطفاً مجدداً تلاش کنید',
     'home' => [
-        'announcement' => 'اعلانات',
+        'announcement' => 'اعلامیه‌ها',
         'attendance' => [
-            'attribute' => 'ثبت حضور',
-            'disable' => 'ثبت حضور غیرفعال است',
-            'done' => 'شما قبلاً ثبت حضور کرده‌اید. فردا برگردید!',
-            'failed' => 'خطای سیستم',
-            'success' => 'شما :data داده دریافت کردید',
+            'attribute' => 'چک‌این',
+            'disable' => 'چک‌این غیرفعال',
+            'done' => 'امروز قبلاً چک‌این کرده‌اید',
+            'failed' => 'استثنای سیستم',
+            'success' => 'چک‌این موفق +:data ترافیک',
         ],
-        'chat_group' => 'گروه چت',
-        'empty_announcement' => 'هیچ اعلانی وجود ندارد',
-        'traffic_logs' => 'سوابق داده',
-        'wechat_push' => 'اعلانات وی‌چت',
+        'chat_group' => 'انجمن',
+        'empty_announcement' => 'اعلامیه‌ای وجود ندارد',
+        'traffic_logs' => 'سوابق ترافیک',
+        'wechat_push' => 'اعلان‌های WeChat',
     ],
     'invite' => [
         'attribute' => 'کد دعوت',
-        'counts' => 'مجموع <code>:num</code> کد دعوت',
-        'generate_failed' => 'تولید ناموفق: سهمیه تمام شده',
-        'logs' => 'سوابق دعوت',
-        'promotion' => 'هم شما و هم دعوت‌شونده <mark>:traffic</mark> داده دریافت خواهید کرد وقتی که با کد شما ثبت نام کنند؛ شما <mark>:referral_percent%</mark> کمیسیون دریافت خواهید کرد وقتی که آنها خرید کنند.',
-        'tips' => '<strong>:num</strong> دعوت باقی مانده، کدها :days روز پس از ایجاد منقضی می‌شوند',
+        'counts' => 'موجود: <code>:num</code>',
+        'generate_failed' => 'سهمیه تولید تجاوز کرده',
+        'logs' => 'سوابق معرفی',
+        'promotion' => [
+            'base' => 'هنگام دعوت کسی:<br>• شما و دعوت‌شده هر دو <mark>:traffic</mark> ترافیک دریافت خواهید کرد؛',
+            'bonus' => [
+                0 => '',
+                1 => '<br>&nbsp;&nbsp;&nbsp;&nbsp;• هنگام <strong>اولین</strong> خرید دعوت‌شده، <mark>:referral_percent%</mark> تخفیف کسب خواهید کرد؛',
+                2 => '<br>&nbsp;&nbsp;&nbsp;&nbsp;• <strong>هر بار</strong> که دعوت‌شده خرید کند، <mark>:referral_percent%</mark> تخفیف کسب خواهید کرد؛',
+            ],
+        ],
+        'tips' => 'دعوت‌های باقی‌مانده: <strong>:num</strong>. اعتبار: :days روز',
     ],
-    'invitee' => 'دعوت‌شونده',
-    'inviter' => 'دعوت‌کننده',
+    'invitee' => 'کاربر معرفی شده',
+    'inviter' => 'معرف',
     'invoice' => [
-        'active_prepaid_question' => 'فعال سازی بسته پیش پرداخت زودتر؟',
-        'active_prepaid_tips' => 'پس از فعال‌سازی:<br>طرح فعلی شما بلافاصله منقضی می‌شود<br>تاریخ انقضای طرح جدید از امروز محاسبه می‌شود',
-        'amount' => 'مقدار',
+        'active_prepaid_question' => 'فعال‌سازی بسته پیش‌پرداخت؟',
+        'active_prepaid_tips' => '<p class="text-left">فعال‌سازی زودهنگام یعنی:</p><ol class="text-left"><li>پلن فعلی فوراً خاتمه می‌یابد. اعتبار باقی‌مانده دور ریخته می‌شود.</li><li>پلن جدید فوراً اثر می‌کند و از الان شروع می‌شود.</li></ol>',
+        'amount' => 'مبلغ',
         'attribute' => 'سفارش',
-        'detail' => 'جزئیات سفارش',
-    ],
-    'knowledge' => [
-        'basic' => 'پایه',
-        'title' => 'پایگاه دانش',
+        'detail' => 'تاریخچه تراکنش',
     ],
     'menu' => [
-        'admin_dashboard' => 'داشبورد',
-        'help' => 'کمک',
-        'home' => 'خانه',
-        'invites' => 'دعوت',
-        'invoices' => 'فاکتور',
-        'nodes' => 'گره‌ها',
+        'admin_dashboard' => 'ادمین',
+        'help' => 'راهنما',
+        'home' => 'داشبورد',
+        'invites' => 'دعوت‌ها',
+        'invoices' => 'سفارشات',
+        'nodes' => 'نودها',
         'profile' => 'پروفایل',
-        'promotion' => 'ارجاع',
+        'promotion' => 'تبلیغات',
         'shop' => 'فروشگاه',
         'tickets' => 'تیکت‌ها',
     ],
     'node' => [
-        'info' => 'اطلاعات پیکربندی',
-        'rate' => ':ratio برابر مصرف داده',
-        'setting' => 'تنظیمات پروکسی',
-        'unstable' => 'ناپایدار/در حال نگهداری',
+        'info' => 'جزئیات اتصال',
+        'rate' => 'ضریب ترافیک: :ratio',
+        'setting' => 'پیکربندی پروکسی',
+        'unstable' => 'ناپایدار یا در حال تعمیر',
     ],
     'oauth' => [
-        'bind' => 'اتصال',
-        'bind_title' => 'اتصال حساب اجتماعی',
-        'not_bind' => 'متصل نشده',
-        'rebind' => 'اتصال مجدد',
-        'unbind' => 'لغو اتصال',
+        'bind' => 'پیوند حساب',
+        'bind_title' => 'پیوند حساب اجتماعی',
+        'not_bind' => 'پیوند نشده',
+        'rebind' => 'پیوند مجدد',
+        'unbind' => 'قطع پیوند',
     ],
-    'pay' => 'پرداخت',
+    'pay' => 'ادامه پرداخت',
     'payment' => [
-        'close_tips' => 'پرداخت را در عرض <code>:minutes</code> دقیقه کامل کنید، در غیر این صورت سفارش به‌طور خودکار بسته می‌شود',
-        'creating' => 'در حال ایجاد پرداخت...',
-        'error' => 'مبلغ شارژ نامعتبر',
-        'insufficient_balance' => 'موجودی شما کافی نیست. لطفاً ابتدا اعتبار خود را شارژ کنید.',
+        'close_tips' => 'پرداخت را در <code>:minutes</code> دقیقه تکمیل کنید',
+        'creating' => 'ایجاد سفارش...',
+        'error' => 'مبلغ نامعتبر',
+        'insufficient_balance' => 'موجودی ناکافی',
         'manual' => [
-            'hint' => 'پس از اسکن کد QR برای پرداخت، لطفاً مراحل را دنبال کنید تا زمانی که بر روی "ارسال" کلیک کنید و پرداخت را تکمیل کنید.',
-            'next' => 'بعدی',
-            'payment_tips' => 'لطفاً مبلغ دقیق را پرداخت کنید (بازپرداخت برای پرداخت اضافی وجود ندارد، برای پرداخت کمتر باید شارژ کنید)',
+            'hint' => 'تأیید پرداخت را پس از تراکنش ارسال کنید',
+            'next' => 'ادامه',
+            'payment_tips' => 'لطفاً مبلغ دقیق را پرداخت کنید (پرداخت اضافی بازپرداخت نمی‌شود)',
             'pre' => 'قبلی',
-            'red_packet' => 'بسته قرمز Alipay',
+            'red_packet' => 'بسته قرمز علی‌پی',
             'steps' => [
                 'complete' => [
-                    'description' => 'در انتظار تأیید دستی پرداخت',
-                    'title' => 'تکمیل',
+                    'description' => 'در انتظار تأیید دستی',
+                    'title' => 'تکمیل پرداخت',
                 ],
                 'notice' => [
-                    'description' => 'چگونه به صورت دستی پرداخت کنیم',
-                    'title' => 'توجهات',
+                    'description' => 'دستورالعمل‌های پرداخت',
+                    'title' => 'نکات پرداخت',
                 ],
                 'payment' => [
-                    'description' => 'دریافت کد QR و پرداخت',
-                    'title' => 'پرداخت',
+                    'description' => 'QR کد را اسکن کنید تا پرداخت کنید',
+                    'title' => 'فرآیند پرداخت',
                 ],
                 'remark' => [
-                    'description' => 'نام کاربری خود را برای تأیید دستی وارد کنید',
-                    'title' => 'یادداشت حساب',
+                    'description' => 'جزئیات حساب برای تأیید ارائه دهید',
+                    'title' => 'اطلاعات یادداشت',
                 ],
             ],
         ],
         'method' => 'روش پرداخت',
-        'mobile_tips' => '<strong>کاربران موبایل:</strong> کد QR را فشار طولانی دهید -> تصویر را ذخیره کنید -> برنامه پرداخت را باز کنید -> تصویر را برای پرداخت اسکن کنید',
+        'mobile_tips' => '<strong>موبایل:</strong> فشار طولانی → ذخیره تصویر → باز کردن اپ پرداخت → اسکن از گالری',
         'order_creation' => [
-            'failed' => 'ایجاد سفارش ناموفق بود. لطفاً روش پرداخت دیگری را امتحان کنید!',
-            'info' => 'ما در مدت [24 ساعت] پرداخت/شارژ شما را فعال خواهیم کرد! لطفاً صبور باشید.',
-            'order_limit' => 'این کالا محدود به :limit_num خرید است. شما تاکنون :count بار خرید کرده‌اید.',
-            'order_timeout' => 'سفارش به دلیل عدم پرداخت به طور خودکار بسته شد.',
-            'payment_disabled' => 'ایجاد سفارش ناموفق بود: قابلیت پرداخت آنلاین فعال نیست.',
-            'pending_order' => 'ایجاد سفارش ناموفق بود: سفارشات پرداخت نشده وجود دارد. لطفاً ابتدا آن‌ها را تکمیل کنید.',
-            'plan_required' => 'لطفاً قبل از خرید بسته شارژ، یک پلن خریداری کنید.',
-            'price_issue' => 'ایجاد سفارش ناموفق بود: قیمت کل سفارش غیرعادی است',
-            'price_zero' => 'ایجاد سفارش ناموفق بود: قیمت کل سفارش 0 است؛ نیازی به پرداخت آنلاین نیست.',
-            'product_unavailable' => 'ایجاد سفارش ناموفق بود: کالا از فروش خارج شده است.',
-            'success' => 'سفارش با موفقیت ایجاد شد!',
-            'unknown_order' => 'سفارش نامشخص',
-            'unknown_payment' => 'روش پرداخت ناشناخته',
+            'failed' => 'ایجاد سفارش ناموفق. روش پرداخت جایگزین امتحان کنید',
+            'info' => 'پردازش سفارش در 24 ساعت!',
+            'order_limit' => 'محدودیت: :limit_num در هر کاربر. شما قبلاً :count خریده‌اید!',
+            'order_timeout' => 'سفارش به دلیل عدم پرداخت منقضی شد!',
+            'payment_disabled' => 'سفارش ناموفق: پرداخت آنلاین فعال نیست!',
+            'pending_order' => 'سفارش ناموفق: سفارشات پرداخت نشده تشخیص داده شد. لطفاً ابتدا پرداخت یا لغو کنید!',
+            'plan_required' => 'لطفاً قبل از خرید افزونه‌ها ابتدا اشتراک خریداری کنید!',
+            'price_issue' => 'سفارش ناموفق: قیمت کل غیرعادی!',
+            'price_zero' => 'سفارش پردازش نشد: پرداختی برای آیتم‌های رایگان لازم نیست.',
+            'product_unavailable' => 'سفارش ناموفق: محصول دیگر موجود نیست!',
+            'success' => 'سفارش با موفقیت ایجاد شد',
+            'unknown_order' => 'سفارش شناسایی نشد',
+            'unknown_payment' => 'روش پرداخت شناسایی نشد',
         ],
-        'qrcode_tips' => 'لطفاً با <strong class="red-600">:software</strong> اسکن کنید',
-        'redirect_stripe' => 'انتقال به صفحه پرداخت Stripe',
+        'qrcode_tips' => 'با <strong class="red-600">:software</strong> اسکن کنید',
+        'redirect_stripe' => 'هدایت به Stripe',
     ],
     'purchase' => [
-        'completed' => 'خرید با موفقیت انجام شد!',
-        'promotion' => 'همین حالا خدمات را خریداری کنید!',
-        'required' => 'این ویژگی برای کاربران غیرپرداختی غیرفعال است. لطفاً',
-        'to_unlock' => 'خرید برای باز کردن قفل',
+        'completed' => 'خرید تکمیل شد',
+        'promotion' => 'فوری سرویس‌ها را باز کنید',
+        'required' => 'این قابلیت مخصوص کاربران پرمیوم است. لطفاً',
+        'to_unlock' => 'سرویس خریداری کنید تا دسترسی پیدا کنید',
     ],
     'recharge' => 'شارژ',
-    'recharge_credit' => 'شارژ اعتبار',
-    'recharging' => 'در حال شارژ...',
+    'recharge_credit' => 'شارژ موجودی',
+    'recharging' => 'پردازش پرداخت...',
     'referral' => [
-        'link' => 'لینک ارجاع',
+        'link' => 'لینک معرفی',
         'logs' => 'سوابق کمیسیون',
         'msg' => [
-            'account' => 'حساب منقضی شده، لطفاً ابتدا یک طرح خریداری کنید',
-            'applied' => 'درخواست موجود است، لطفاً منتظر پردازش باشید',
-            'error' => 'خطا در ایجاد سفارش، بعداً دوباره امتحان کنید یا با پشتیبانی تماس بگیرید',
-            'unfulfilled' => 'نیاز به :amount برای برداشت، ادامه بده!',
-            'wait' => 'لطفاً منتظر تأیید مدیر باشید',
+            'account' => 'حساب منقضی شده',
+            'applied' => 'پردازش درخواست',
+            'error' => 'خطای درخواست',
+            'unfulfilled' => 'برای برداشت حداقل :amount لازم دارید. به کسب ادامه دهید!',
+            'wait' => 'در انتظار بررسی',
         ],
-        'total' => 'مجموع کمیسیون: :amount (:total بار)، می‌توانید پس از رسیدن به :money برداشت کنید',
+        'total' => 'تخفیف انباشته: :amount (:total بار)، هنگام رسیدن به :money قابل برداشت',
     ],
-    'registered_at' => 'تاریخ ثبتنام',
+    'registered_at' => 'تاریخ ثبت نام',
     'reset_data' => [
-        'action' => 'بازنشانی داده',
-        'cost' => 'هزینه: <code>:amount</code>',
-        'cost_tips' => 'بازنشانی داده :amount کسر خواهد کرد!',
+        'action' => 'بازنشانی ترافیک',
+        'cost' => 'نیاز به <code>:amount</code>',
+        'cost_tips' => 'این :amount از موجودی حساب شما کسر خواهد کرد.',
     ],
-    'scan_qrcode' => 'با استفاده از کلاینت کد QR را اسکن کنید',
+    'scan_qrcode' => 'اسکن با اپلیکیشن کلاینت',
     'service' => [
-        'country_count' => 'پوشش <code>:num</code> کشور یا منطقه',
-        'node_count' => '<code>:num</code> گره با کیفیت بالا',
-        'unlimited' => 'بدون محدودیت سرعت',
+        'country_count' => '<code>:num</code> کشور/منطقه',
+        'node_count' => '<code>:num</code> نود ممتاز',
+        'unlimited' => 'سرعت نامحدود',
     ],
     'shop' => [
         'buy' => 'خرید',
-        'call4help' => 'اگر سوالی دارید با پشتیبانی تماس بگیرید',
+        'support' => 'برای کمک با پشتیبانی تماس بگیرید',
         'change_amount' => 'مبلغ شارژ',
-        'change_amount_help' => 'مبلغ شارژ را وارد کنید',
-        'conflict' => 'تضاد',
-        'conflict_tips' => '<p>خرید فعلی به عنوان <code>طرح پیش پرداخت</code> تنظیم خواهد شد</p><ol><li>طرح پیش پرداخت پس از انقضای طرح فعلی به طور خودکار فعال می‌شود</li><li>شما می‌توانید پس از پرداخت آن را به صورت دستی فعال کنید</li></ol>',
-        'description' => 'توضیحات',
-        'hot' => 'داغ',
-        'limited' => 'محدود',
-        'pay_credit' => 'پرداخت با اعتبار',
+        'change_amount_help' => 'مبلغ سفارشی وارد کنید',
+        'conflict' => 'تضاد اشتراک',
+        'conflict_tips' => '<p>این خرید <code>پیش‌پرداخت</code> تعیین خواهد شد</p><ol class="text-left"><li>پس از انقضای اشتراک فعلی فعال می‌شود</li><li>پس از پرداخت قابل فعال‌سازی دستی</li></ol>',
+        'description' => 'توضیحات سرویس',
+        'hot' => 'محبوب',
+        'limited' => 'پیشنهاد محدود',
+        'pay_credit' => 'پرداخت با موجودی',
         'pay_online' => 'پرداخت آنلاین',
         'price' => 'قیمت',
         'quantity' => 'تعداد',
-        'service' => 'خدمات',
-        'subtotal' => 'جمع کل',
+        'service' => 'سرویس',
+        'subtotal' => 'جمع جزء',
         'total' => 'مجموع',
     ],
     'subscribe' => [
         'custom' => 'اشتراک سفارشی',
-        'error' => 'خطا در تغییر لینک اشتراک',
-        'exchange_warning' => 'تغییر لینک اشتراک باعث می‌شود:\n1. لینک فعلی بلافاصله لغو شود\n2. رمز اتصال تغییر کند',
+        'error' => 'به‌روزرسانی اشتراک ناموفق',
+        'exchange_warning' => '<p>تغییر لینک اشتراک شما باعث:</p><ol class="text-left"><li>فوری غیرفعال شدن لینک قدیمی؛</li><li>تولید مجدد رمز عبور اتصال شما؛</li></ol>',
         'info' => [
-            'download' => 'دانلود',
-            'title' => 'خلاصه حساب [غیر زمان واقعی]',
-            'total' => 'داده طرح',
-            'upload' => 'آپلود',
+            'download' => 'دانلود استفاده شده',
+            'title' => 'خلاصه حساب [تأخیری]',
+            'total' => 'ترافیک تخصیص یافته',
+            'upload' => 'آپلود استفاده شده',
         ],
         'link' => 'لینک اشتراک',
-        'ss_only' => 'فقط اشتراک SS',
-        'ssr_only' => 'فقط اشتراک SSR (شامل SS)',
-        'tips' => 'هشدار: این لینک فقط برای استفاده شخصی است. آن را به اشتراک نگذارید، در غیر این صورت حساب شما به دلیل استفاده غیرعادی مسدود می‌شود.',
-        'trojan_only' => 'فقط اشتراک Trojan',
-        'v2ray_only' => 'فقط اشتراک V2Ray',
+        'ss_only' => 'فقط SS',
+        'ssr_only' => 'SSR (شامل SS)',
+        'tips' => 'توجه: این لینک شخصی است و فقط برای استفاده خودتان می‌باشد. به اشتراک گذاری آن ممکن است باعث مسدودی حساب شود.',
+        'trojan_only' => 'فقط Trojan',
+        'v2ray_only' => 'فقط V2Ray',
+        'page' => [
+            'get_link' => 'دریافت لینک',
+            'connect' => 'اتصال و استفاده',
+            'error' => [
+                'no_app' => 'کلاینت موجود نیست',
+            ],
+        ],
     ],
     'telegram' => [
-        'bind_exists' => 'این حساب قبلاً به یک حساب تلگرام متصل است.',
-        'bind_missing' => 'اطلاعات کاربر شما پیدا نشد. لطفاً ابتدا حساب خود را پیوند دهید.',
+        'bind_exists' => 'حساب تلگرام قبلاً پیوند شده',
+        'bind_missing' => 'حساب پیوند شده یافت نشد',
         'command' => [
-            'bind' => 'حساب :web_name خود را پیوند دهید',
-            'intro' => 'شما می‌توانید از دستورات زیر استفاده کنید',
-            'traffic' => 'بررسی مصرف داده‌ها',
-            'unbind' => 'قطع ارتباط',
-            'web_url' => 'دریافت جدیدترین URL :web_name',
+            'bind' => 'پیوند به حساب :web_name',
+            'intro' => 'می‌توانید از دستورات زیر استفاده کنید:',
+            'traffic' => 'پرس‌وجوی ترافیک',
+            'unbind' => 'قطع پیوند حساب',
+            'web_url' => 'بازیابی آخرین لینک دسترسی :web_name',
         ],
-        'get_url' => 'جدیدترین URL برای :web_name عبارت است از',
-        'params_missing' => 'پارامترها نامعتبر هستند. لطفاً آدرس ایمیل خود را ضمیمه کرده و دوباره ارسال کنید.',
-        'ticket_missing' => 'تیکت وجود ندارد',
-        'ticket_reply' => 'پاسخ به تیکت #`:id` با موفقیت انجام شد',
-        'traffic_query' => 'بررسی مصرف داده‌ها',
-        'user_missing' => 'کاربر وجود ندارد',
+        'get_url' => 'آخرین URL :web_name',
+        'params_missing' => 'پارامترهای نامعتبر. آدرس ایمیل را شامل کنید',
+        'ticket_missing' => 'تیکت یافت نشد',
+        'ticket_reply' => 'تیکت #:id پاسخ جدیدی دارد',
+        'traffic_query' => 'نتیجه پرس‌وجوی ترافیک',
+        'user_missing' => 'کاربر یافت نشد',
     ],
     'ticket' => [
-        'attribute' => 'تیکت',
-        'close_msg' => 'تیکت ID :id توسط کاربر بسته شد',
-        'close_tips' => 'تأیید بستن تیکت؟',
-        'content_placeholder' => 'توضیحات دقیق مشکل خود را ارائه دهید تا ما بتوانیم بهتر به شما کمک کنیم',
-        'error' => 'خطای ناشناخته! لطفاً با پشتیبانی تماس بگیرید',
-        'new' => 'ایجاد تیکت جدید',
-        'online_hour' => 'ساعات آنلاین',
+        'attribute' => 'تیکت پشتیبانی',
+        'close_msg' => 'تیکت #:id بسته شد',
+        'close_tips' => 'آیا مطمئن هستید که می‌خواهید این تیکت را ببندید؟',
+        'content_placeholder' => 'لطفاً مسئله یا درخواست خود را تا حد امکان با جزئیات شرح دهید تا بتوانیم سریعاً به شما کمک کنیم',
+        'error' => 'خطای سیستم. با پشتیبانی تماس بگیرید',
+        'new' => 'ایجاد تیکت',
+        'online_hour' => 'ساعات پشتیبانی',
         'reply' => 'پاسخ',
-        'reply_confirm' => 'تأیید پاسخ به تیکت؟',
-        'reply_placeholder' => 'چیزی بنویسید...',
-        'service_hours' => 'ساعات خدمات مشتری',
-        'service_tips' => 'لطفاً فقط از <code>یک</code> روش تماس برای تماس با پشتیبانی استفاده کنید! درخواست‌های مکرر زمان پاسخگویی را به تأخیر می‌اندازد.',
-        'submit_tips' => 'تأیید ارسال تیکت؟',
-        'title_placeholder' => 'به طور خلاصه مشکل خود را توضیح دهید',
+        'reply_confirm' => 'تأیید پاسخ؟',
+        'reply_placeholder' => 'پاسخ را وارد کنید...',
+        'service_hours' => 'دسترسی پشتیبانی',
+        'service_tips' => 'لطفاً هنگام تماس با پشتیبانی از <code>فقط یک</code> روش تماس استفاده کنید. ارسال‌های متعدد ممکن است پاسخ ما را به تأخیر بیندازد.',
+        'submit_tips' => 'ارسال تیکت؟',
+        'title_placeholder' => 'مسئله‌ای که تجربه می‌کنید را به طور خلاصه شرح دهید',
     ],
     'traffic_logs' => [
-        'daily' => 'مصرف داده این ماه',
-        'hourly' => 'مصرف داده امروز',
-        'tips' => 'توجه: به‌روزرسانی آمار داده‌ها با تأخیر انجام می‌شود.',
+        'daily' => 'استفاده ماهانه',
+        'hourly' => 'استفاده روزانه',
+        'tips' => 'توجه: به‌روزرسانی داده‌ها ممکن است تأخیر داشته باشد',
     ],
-    'tutorials' => 'آموزش‌ها',
-    'withdraw' => 'برداشت',
+    'tutorials' => 'راهنماها',
+    'withdraw' => 'برداشت وجه',
     'withdraw_at' => 'تاریخ برداشت',
     'withdraw_commission' => 'برداشت کمیسیون',
-    'withdraw_logs' => 'سوابق برداشت',
+    'withdraw_logs' => 'تاریخچه برداشت',
 ];

+ 105 - 105
resources/lang/ja.json

@@ -1,141 +1,141 @@
 {
-  "(and :count more error)": "(その他、:countエラーあり)",
-  "(and :count more errors)": "(その他、:countエラーあり)",
-  "----「:job」Completed, Used :time seconds ----": "----「:job」完了、所要時間 :time 秒 ----",
-  "[Auto Task] Blocked service: Abnormal traffic within 1 hour": "[自動タスク] サービスがブロックされました:1時間以内に異常なトラフィックが発生",
-  "[Auto Task] Blocked service: Run out of traffic": "[自動タスク] サービスがブロックされました:トラフィックが使い切られました",
-  "[Auto Task] Blocked Subscription: Subscription with abnormal requests within 24 hours": "[自動タスク] サブスクリプションがブロックされました:24時間以内に異常なリクエストが発生",
-  "[Auto Task] Unblocked Service: Account ban expired": "[自動タスク] サービスが解除されました:アカウントの禁止期間が終了",
-  "[Auto Task] Unblocked Service: Account has available data traffic": "[自動タスク] サービスが解除されました:アカウントに利用可能なデータトラフィックがあります",
-  "[Daily Task] Account Expiration: Block Login & Clear Account": "[日次タスク] アカウントが期限切れ:ログインをブロックし、アカウントデータをクリア",
-  "[Daily Task] Account Expiration: Stop Service": "[日次タスク] アカウント期限切れ:サービス停止",
-  "[Daily Task] Reset Account Traffic, Next Reset Date: :date": "[日次タスク] アカウントトラフィックをリセット、次のリセット日::date",
-  "[Service Timer] Service Expiration": "[サービスタイマー] サービスが期限切れ",
-  "A Timeout Occurred": "タイムアウト発生",
-  "Accepted": "承認済",
+  "(and :count more error)": "(他に :count 件のエラー)",
+  "(and :count more errors)": "(他に :count 件のエラー)",
+  "----「:job」Completed, Used :time seconds ----": "----「:job」完了、実行時間 :time 秒 ----",
+  "[Auto Task] Blocked service: Abnormal traffic within 1 hour": "[自動タスク] サービス停止:1時間以内の異常トラフィック",
+  "[Auto Task] Blocked service: Run out of traffic": "[自動タスク] サービス停止:トラフィック使い切り",
+  "[Auto Task] Blocked Subscription: Subscription with abnormal requests within 24 hours": "[自動タスク] サブスクリプション停止:24時間以内の異常リクエスト",
+  "[Auto Task] Unblocked Service: Account ban expired": "[自動タスク] サービス復旧:アカウント停止期間終了",
+  "[Auto Task] Unblocked Service: Account has available data traffic": "[自動タスク] サービス復旧:アカウントに利用可能トラフィックあり",
+  "[Daily Task] Account Expiration: Block Login & Clear Account": "[日次タスク] アカウント期限切れ:ログイン禁止・アカウントデータクリア",
+  "[Daily Task] Account Expiration: Stop Service": "[日次タスク] アカウント期限切れ:サービス停止",
+  "[Daily Task] Reset Account Traffic, Next Reset Date: :date": "[日次タスク] アカウントトラフィックリセット、次回リセット日::date",
+  "[Service Timer] Service Expiration": "[定時タスク] サービス期限切れ",
+  "A Timeout Occurred": "タイムアウト発生しました",
+  "Accepted": "受理済み",
   "All rights reserved.": "全著作権所有。",
-  "Already Reported": "報告済",
-  "Bad Gateway": "不正なゲートウェイ",
+  "Already Reported": "既に報告済",
+  "Bad Gateway": "ゲートウェイエラー",
   "Bad Request": "不正なリクエスト",
-  "Bandwidth Limit Exceeded": "帯域幅の制限超過",
-  "Client Closed Request": "クライアントによるリクエストの終了",
+  "Bandwidth Limit Exceeded": "帯域制限超過",
+  "Client Closed Request": "クライアントがリクエストを終了",
   "Conflict": "競合",
-  "Connection Closed Without Response": "応答なしで接続が閉じられました",
-  "Connection Timed Out": "接続タイムアウト",
+  "Connection Closed Without Response": "レスポンスなしで接続終了",
+  "Connection Timed Out": "接続タイムアウト",
   "Continue": "継続",
-  "Created": "作成済",
-  "Daily Data Usage Report": "日次データ使用報告",
-  "Expectation Failed": "指定された要件を満たしていません",
-  "Failed Dependency": "失敗した依存関係",
-  "Forbidden": "禁止されています",
-  "Found": "発見",
-  "Gateway Timeout": "ゲートウェイタイムアウト",
-  "Go to page :page": ":Pageページへ",
-  "Gone": "消滅",
-  "Hello!": "こんにちは",
-  "HTTP Version Not Supported": "サポートしていないHTTPバージョン",
+  "Created": "作成済",
+  "Daily Data Usage Report": "日次トラフィック使用レポート",
+  "Expectation Failed": "期待値エラー",
+  "Failed Dependency": "依存関係エラー",
+  "Forbidden": "アクセス禁止",
+  "Found": "一時移動",
+  "Gateway Timeout": "ゲートウェイタイムアウト",
+  "Go to page :page": ":page ページに移動",
+  "Gone": "利用不可",
+  "Hello!": "こんにちは",
+  "HTTP Version Not Supported": "HTTPバージョン未対応",
   "I'm a teapot": "私はティーポットです",
-  "If you did not create an account, no further action is required.": "アカウント作成にお心当たりがない場合は、このメールを無視してください。",
-  "If you did not request a password reset, no further action is required.": "パスワード再設定のリクエストにお心当たりがない場合は、このメールを無視してください。",
-  "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "\":actionText\"ボタンがクリックできない場合は、以下のURLに直接アクセスしてください。",
-  "IM Used": "IM 使用済み",
+  "If you did not create an account, no further action is required.": "アカウントを作成していない場合は、このメールを無視してください。",
+  "If you did not request a password reset, no further action is required.": "パスワードリセットを申請していない場合は、このメールを無視してください。",
+  "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "「:actionText」ボタンをクリックできない場合は、以下のURLをコピーしてブラウザに貼り付けてください:",
+  "IM Used": "IM使用済み",
   "Insufficient Storage": "ストレージ不足",
   "Internal Server Error": "内部サーバーエラー",
-  "Invalid JSON was returned from the route.": "無効な JSON ルートから返されました。",
+  "Invalid JSON was returned from the route.": "ルートから無効なJSONが返されました。",
   "Invalid SSL Certificate": "無効なSSL証明書",
-  "Invoice Detail": "請求書の詳細",
+  "Invoice Detail": "注文詳細",
   "Length Required": "長さが必要",
-  "Locked": "ロック済",
+  "Locked": "ロック済",
   "Login": "ログイン",
   "Logout": "ログアウト",
   "Loop Detected": "ループを検出",
-  "Maintenance Mode": "メンテナンス",
-  "Method Not Allowed": "未許可のメソッド",
+  "Maintenance Mode": "メンテナンスモード",
+  "Method Not Allowed": "メソッド不許可",
   "Misdirected Request": "誤ったリクエスト",
-  "Moved Permanently": "恒久的に移動",
+  "Moved Permanently": "恒久移動",
   "Multi-Status": "マルチステータス",
-  "Multiple Choices": "複数選択",
+  "Multiple Choices": "複数選択",
   "Network Authentication Required": "ネットワーク認証が必要",
-  "Network Connect Timeout Error": "ネットワーク接続タイムアウトエラー",
-  "Network Read Timeout Error": "ネットワーク読み取りタイムアウトエラー",
+  "Network Connect Timeout Error": "ネットワーク接続タイムアウト",
+  "Network Read Timeout Error": "ネットワーク読み取りタイムアウト",
   "No Content": "コンテンツなし",
-  "Non-Authoritative Information": "信頼できない情報",
-  "Not Acceptable": "受不可",
+  "Non-Authoritative Information": "非権威情報",
+  "Not Acceptable": "受不可",
   "Not Extended": "拡張なし",
-  "Not Found": "見つかりません",
+  "Not Found": "ページが見つかりません",
   "Not Implemented": "未実装",
   "Not Modified": "未変更",
   "of": "の",
   "OK": "成功",
-  "Origin Is Unreachable": "オリジンに到達できません",
-  "Page Expired": "ページが無効です",
+  "Origin Is Unreachable": "オリジンに到達不可",
+  "Page Expired": "ページの有効期限切れ",
   "Pagination Navigation": "ページネーション",
-  "Partial Content": "部分的なコンテンツ",
+  "Partial Content": "部分コンテンツ",
   "Payload Too Large": "ペイロードが大きすぎます",
-  "Payment for #:sn has been received! Total amount: :amount.": "#:sn の支払いが受領されました!合計金額::amount。",
-  "Payment Received": "支払いが受領されました",
-  "Payment Required": "支払いが必要",
-  "Permanent Redirect": "恒久的なリダイレクト",
-  "Please click the button below to verify your email address.": "メールアドレスを確認するには、以下のボタンをクリックしてください。",
-  "Precondition Failed": "前提条件が失敗",
-  "Precondition Required": "前条件が必要",
+  "Payment for #:sn has been received! Total amount: :amount.": "注文#:sn の支払いを確認しました!合計金額::amount",
+  "Payment Received": "支払い完了",
+  "Payment Required": "支払いが必要",
+  "Permanent Redirect": "恒久リダイレクト",
+  "Please click the button below to verify your email address.": "以下のボタンをクリックしてメールアドレスを認証してください。",
+  "Precondition Failed": "前提条件エラー",
+  "Precondition Required": "前条件が必要",
   "Processing": "処理中",
   "Proxy Authentication Required": "プロキシ認証が必要",
-  "Railgun Error": "レールガンエラー",
-  "Range Not Satisfiable": "範囲外のレンジ",
-  "Regards": "よろしくお願いします",
-  "Register": "アカウント作成",
-  "Request Header Fields Too Large": "要求ヘッダーフィールドが大きすぎます",
+  "Railgun Error": "Railgunエラー",
+  "Range Not Satisfiable": "範囲指定エラー",
+  "Regards": "敬具",
+  "Register": "登録",
+  "Request Header Fields Too Large": "リクエストヘッダーが大きすぎます",
   "Request Timeout": "リクエストタイムアウト",
-  "Reset Content": "コンテンツリセット",
-  "Reset Password": "パスワード再設定",
-  "Reset Password Notification": "パスワード再設定のお知らせ",
-  "results": "結果",
+  "Reset Content": "コンテンツリセット",
+  "Reset Password": "パスワードリセット",
+  "Reset Password Notification": "パスワードリセット通知",
+  "results": "",
   "Retry With": "再試行",
-  "See Other": "他を参照",
+  "See Other": "他を参照",
   "Server Error": "サーバーエラー",
-  "Service Unavailable": "サービスは利用できません",
+  "Service Unavailable": "サービス利用不可",
   "Session Has Expired": "セッションの有効期限切れ",
   "Showing": "表示中",
-  "SSL Handshake Failed": "SSL接続エラー",
-  "Subscription link receive abnormal access and banned by the system": "サブスクリプションリンクに異常なアクセスがあり、システムによって禁止されました",
-  "Switching Protocols": "プロトコル切替",
-  "Temporary Redirect": "一時的なリダイレクト",
-  "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "ご登録ありがとうございます!始める前に、メールに送信されたリンクをクリックしてメールを確認してください。メールが届いていない場合は、再送信いたします。",
-  "The given data was invalid.": "指定されたデータは無効でした。",
-  "The response is not a streamed response.": "応答はストリーミング応答ではありません。",
-  "The response is not a view.": "応答はビューではありません。",
-  "This password reset link will expire in :count minutes.": "このパスワード再設定リンクの有効期限は:count分です。",
-  "to": "",
-  "Toggle navigation": "ナビゲーション切替",
-  "Too Early": "リクエスト間隔が早すぎる",
-  "Too Many Requests": "リクエストが多すぎます",
-  "Unauthorized": "認証が必要です",
-  "Unavailable For Legal Reasons": "法的理由により利用不可",
+  "SSL Handshake Failed": "SSLハンドシェイク失敗",
+  "Subscription link receive abnormal access and banned by the system": "サブスクリプションリンクに異常アクセスが発生し、システムにより停止されました",
+  "Switching Protocols": "プロトコル切",
+  "Temporary Redirect": "一時リダイレクト",
+  "Thank you for signing up! Before you start, you need to verify your email by clicking on the link we have just sent to your email! If you haven't received an email, we would be happy to send another one.": "ご登録ありがとうございます!ご利用開始前に、送信したメールのリンクをクリックしてメールアドレスを認証してください。メールが届いていない場合は、再送信いたします。",
+  "The given data was invalid.": "入力されたデータが無効です。",
+  "The response is not a streamed response.": "レスポンスはストリーミングレスポンスではありません。",
+  "The response is not a view.": "レスポンスはビューではありません。",
+  "This password reset link will expire in :count minutes.": "このパスワードリセットリンクは :count 分後に期限切れとなります。",
+  "to": "",
+  "Toggle navigation": "ナビゲーション切",
+  "Too Early": "早すぎます",
+  "Too Many Requests": "リクエスト過多",
+  "Unauthorized": "認証エラー",
+  "Unavailable For Legal Reasons": "法的理由により利用不可",
   "Unknown Error": "不明なエラー",
-  "Unprocessable Entity": "処理できないエンティティ",
-  "Unsupported Media Type": "サポートされていないメディアタイプ",
+  "Unprocessable Entity": "処理不可エンティティ",
+  "Unsupported Media Type": "未対応メディアタイプ",
   "Upgrade Required": "アップグレードが必要",
-  "URI Too Long": "URLが長すぎます",
-  "Use Proxy": "プロキシ使用",
-  "Variant Also Negotiates": "バリアントの再認証",
-  "Verify Email Address": "メールアドレスの確認",
-  "Verify Your Email Address": "メールアドレスを認してください",
-  "Web Server is Down": "Webサーバーが停止中",
+  "URI Too Long": "URIが長すぎます",
+  "Use Proxy": "プロキシ使用",
+  "Variant Also Negotiates": "バリアント交渉",
+  "Verify Email Address": "メールアドレス認",
+  "Verify Your Email Address": "メールアドレスを認してください",
+  "Web Server is Down": "Webサーバーがダウンしています",
   "Whoops!": "おっと!",
-  "You are receiving this email because we received a password reset request for your account.": "パスワード再設定のリクエストを受け付けました。",
-  "You have not responded this ticket in :num hours, System has closed your ticket.": ":num 時間以内にこのチケットに返信しなかったため、システムがチケットを閉じました。",
+  "You are receiving this email because we received a password reset request for your account.": "アカウントのパスワードリセット申請を受信したため、このメールをお送りしています。",
+  "You have not responded this ticket in :num hours, System has closed your ticket.": ":num 時間以内にチケットへの返信がなかったため、システムが自動的にチケットをクローズしました。",
   "You must have a valid subscription to view the content in this area!": "このエリアのコンテンツを表示するには、有効なサブスクリプションが必要です!",
-  "Your subscription has been disabled by the administrator, please contact the administrator to restore it": "お客様のサブスクリプションは管理者によって無効にされました。復元するには管理者に連絡してください。",
-  "Manually add in dashboard.": "ダッシュボードで手動で追加",
-  "Manually edit in dashboard.": "ダッシュボードで手動で編集",
-  "Batch generate user accounts in dashboard.": "バックグラウンドでユーザーアカウントを一括生成",
-  "Coupon used in order.": "注文でクーポンが使用されました",
-  "Order canceled, coupon reinstated.": "注文がキャンセルされ、クーポンが復元されました",
-  "Used for credit recharge.": "残高チャージに使用される",
-  "The user manually reset the data.": "ユーザーデータリセット記録",
-  "Recharge using a recharge voucher.": "チャージ券を使ってチャージする",
-  "The user topped up the balance.": "ユーザーが残高をチャージしました",
-  "Purchased an item.": "商品を購入しました",
-  "[:payment] plus the users purchased data plan.": "[:payment] にユーザーが購入したデータプランを追加"
+  "Your subscription has been disabled by the administrator, please contact the administrator to restore it": "サブスクリプションが管理者により無効化されています。復旧については管理者にお問い合わせください。",
+  "Manually add in dashboard.": "管理画面で手動追加",
+  "Manually edit in dashboard.": "管理画面で手動編集",
+  "Batch generate user accounts in dashboard.": "管理画面でユーザーアカウント一括生成",
+  "Coupon used in order.": "注文でクーポンを使用",
+  "Order canceled, coupon reinstated.": "注文キャンセル、クーポン復旧",
+  "Used for credit recharge.": "残高チャージに使用",
+  "The user manually reset the data.": "ユーザーが手動でトラフィックをリセット",
+  "Recharge using a recharge voucher.": "チャージ券を使用してチャージ",
+  "The user topped up the balance.": "ユーザーが残高をチャージ",
+  "Purchased an item.": "商品を購入",
+  "[:payment] plus the user's purchased data plan.": "[:payment] にユーザーが購入したデータプランを追加"
 }

File diff suppressed because it is too large
+ 363 - 444
resources/lang/ja/admin.php


+ 47 - 47
resources/lang/ja/auth.php

@@ -3,87 +3,87 @@
 declare(strict_types=1);
 
 return [
-    'accept_term' => '内容を読み、同意します',
+    'accept_term' => '私は以下を読み、同意します',
     'active' => [
-        'attribute' => 'アクティベート',
+        'attribute' => 'アカウント有効化',
         'error' => [
-            'activated' => 'アカウントがアクティブです。今すぐログインしてください!',
-            'disable' => 'このサイトはアカウントが停止されているため、直接ログインすることができます。',
-            'throttle' => '起動要求の上限に達しました。後でもう一度試してください。',
+            'activated' => 'アカウントは既に有効化されています。直接ログインしてください!',
+            'disable' => '当サイトではアカウント有効化機能を無効にしています。直接ログインできます!',
+            'throttle' => '有効化リクエストの制限に達しました。しばらく待ってから再度お試しください!',
         ],
-        'promotion' => 'アカウントがまだアクティベートされていません、まず「:action」を行ってください!',
-        'sent' => 'アクティベートリンクがメールに送信されましたので、受信トレイ(スパムフォルダも含む)を確認してください。',
+        'promotion' => 'アカウントがまだ有効化されていません。まず「:action」してください!',
+        'sent' => '有効化リンクをメールアドレスに送信しました。しばらくお待ちいただくか、迷惑メールフォルダもご確認ください。',
     ],
-    'aup' => '使用許諾ポリシー',
+    'aup' => '利用規約',
     'captcha' => [
-        'attribute' => 'キャプチャ',
+        'attribute' => '認証コード',
         'error' => [
-            'failed' => '確認コードが正しくありません。入力し直してください!',
-            'timeout' => '認証コードが期限切れです。再読み込みしてから再度お試しください。',
+            'failed' => '認証コードが正しくありません。再度入力してください!',
+            'timeout' => '認証コードの有効期限が切れました。ページを更新してから再度お試しください!',
         ],
-        'required' => 'キャプチャを完了してください!',
-        'sent' => 'キャプチャがメールに送信されましたので、受信トレイ(スパムフォルダも含む)を確認してください。',
+        'required' => '認証コードを正しく完了してください',
+        'sent' => '認証コードをメールアドレスに送信しました。しばらくお待ちいただくか、迷惑メールフォルダもご確認ください。',
     ],
     'email' => [
         'error' => [
-            'banned' => 'メールプロバイダーはサポートされていません。メールボックスを換えてください!',
-            'invalid' => 'あなたのメールアドレスはこのサイトでサポートされるメールアドレスではありません!',
+            'banned' => '当サイトではご利用のメールサービスプロバイダーをサポートしていません。別のメールアドレスをご利用ください!',
+            'invalid' => 'ご入力のメールアドレスは当サイトでサポートされていません!',
         ],
     ],
     'error' => [
-        'account_baned' => 'あなたのアカウントは禁止されています!',
-        'login_error' => 'ログインエラーが起こりました。後ほど再試行してください。',
-        'login_failed' => 'ログインに失敗しました、ユーザー名とパスワードを確認してください!',
-        'not_found_user' => '関連付けられているアカウントが見つかりませんでした。他の方法でサインインしてください。',
-        'repeat_request' => '再度リクエストする必要はありません。リフレッシュしてからもう一度お試しください!',
-        'url_timeout' => 'リンクは無効になっています、別の操作をやり直してください!',
+        'account_baned' => 'あなたのアカウントは停止されています!',
+        'login_error' => 'ログイン処理中にエラーが発生しました。しばらく待ってから再度お試しください!',
+        'login_failed' => 'ログインに失敗しました。ユーザー名またはパスワードが正しいかご確認ください!',
+        'not_found_user' => '関連するアカウントが見つかりません。他のログイン方法をお試しください!',
+        'repeat_request' => '重複したリクエストは避けてください。ページを更新してから再度お試しください!',
+        'url_timeout' => 'リンクの有効期限が切れました。再度操作してください!',
     ],
-    'failed' => '無効な資格情報です。',
+    'failed' => 'ユーザー名またはパスワードが正しくありません。',
     'invite' => [
         'get' => '招待コードを取得',
-        'not_required' => '招待コードは不要です直接登録できます!',
-        'unavailable' => '無効な招待コードです。再試行して下さい!',
+        'not_required' => '招待コードは不要です直接登録できます!',
+        'unavailable' => '招待コードが無効です。再度お試しください!',
     ],
     'login' => 'ログイン',
     'logout' => 'ログアウト',
-    'maintenance' => 'メンテナンス',
-    'maintenance_tip' => 'システムメンテナンス中です。少々お待ち下さい。',
+    'maintenance' => 'システムメンテナンス',
+    'maintenance_tip' => 'システムメンテナンス中です。しばらく待ってから再度アクセスしてください!',
     'oauth' => [
-        'login_failed' => 'サードパーティログインに失敗しました!',
+        'login_failed' => 'サードパーティログインに失敗しました!',
         'register' => 'クイック登録',
-        'registered' => '既に登録されています、直接ログインしてください。',
+        'registered' => '既に登録済みです。直接ログインしてください。',
     ],
-    'one-click_login' => 'ソーシャルログイン',
+    'one-click_login' => 'ワンクリックログイン',
     'optional' => 'オプション',
     'password' => [
-        'forget' => 'パスワードを忘れた方はこちら',
+        'forget' => 'パスワードをお忘れですか?',
         'new' => '新しいパスワードを入力',
         'original' => '現在のパスワード',
         'reset' => [
-            'attribute' => 'パスワードの再設定',
+            'attribute' => 'パスワードリセット',
             'error' => [
-                'demo' => 'このデモ版では管理者のパスワードの変更が無効になっています。',
-                'disabled' => 'はパスワードリセット機能を無効にしています!',
-                'same' => '新しいパスワードを古いパスワードと同じにはできません。もう一度設定してください。',
-                'throttle' => '24時間ごとにユーザ名のみ再設定できます。パスワードは time 回、頻繁に行わないでください。',
-                'wrong' => '古いパスワードが正しくありません。再度入力してください。',
+                'demo' => 'デモ環境では管理者パスワードの変更は禁止されています!',
+                'disabled' => '当サイトではパスワードリセット機能を無効にしています!',
+                'same' => '新しいパスワードは現在のパスワードと同じにできません。別のパスワードを設定してください!',
+                'throttle' => '24時間以内にパスワードをリセットできるのは:time回までです。しばらく待ってから再度お試しください!',
+                'wrong' => '現在のパスワードが正しくありません。再度入力してください!',
             ],
-            'sent' => 'リセットリンクがメールに送信されましたので、受信トレイ(スパムフォルダも含む)を確認してください。',
-            'success' => '新しいパスワードが設定されました。ログインページに移動してください。',
+            'sent' => 'リセットリンクをメールアドレスに送信しました。メールをご確認ください(迷惑メールフォルダも含む)。',
+            'success' => '新しいパスワードが正常に設定されました。ログインページでログインしてください。',
         ],
     ],
     'register' => [
-        'attribute' => 'サインアップ',
-        'code' => '登録コード',
+        'attribute' => '新規登録',
+        'code' => '登録認証コード',
         'error' => [
-            'disable' => '申し訳ありませんが、現在新規ユーザーの受付を停止しています。',
-            'throttle' => 'アンチボットシステムが作動しました!頻繁な提出は避けてください。',
+            'disable' => '申し訳ございませんが、当サイトでは現在新規登録を一時停止しています。',
+            'throttle' => 'スパム防止機能が作動しました。頻繁な登録はお控えください!',
         ],
-        'failed' => '登録に失敗しました、後でもう一度お試しください。',
-        'promotion' => 'アカウントをお持ちでない方は、まず',
+        'failed' => '登録に失敗しました。しばらく待ってから再度お試しください。',
+        'promotion' => 'まだアカウントをお持ちでないですか?まず',
     ],
-    'remember_me' => 'ログイン状態を保持する',
-    'request' => 'リクエスト',
-    'throttle' => '試行回数が多すぎます、:seconds秒後にもう一度お試しください。',
+    'remember_me' => 'ログイン状態を保持',
+    'request' => '取得',
+    'throttle' => 'ログイン試行回数が多すぎます。:seconds秒後に再度お試しください。',
     'tos' => '利用規約',
 ];

+ 57 - 57
resources/lang/ja/common.php

@@ -4,148 +4,148 @@ declare(strict_types=1);
 
 return [
     'account' => 'アカウント',
-    'action' => 'アクション',
+    'action' => '操作',
     'active_item' => ':attributeを有効化',
     'add' => '追加',
-    'advance' => '進む',
-    'all' => 'すべて',
+    'advance' => '高度な設定',
+    'all' => 'て',
     'applied' => ':attributeが適用されました',
     'apply' => '適用',
-    'available_date' => '有効期',
+    'available_date' => '有効期',
     'avatar' => 'アバター',
     'back' => '戻る',
     'back_to' => ':pageに戻る',
     'bark' => [
-        'custom' => 'カスタム情報',
-        'node_status' => 'ノードステータス',
+        'custom' => 'カスタム',
+        'node_status' => 'ノード状態',
     ],
     'cancel' => 'キャンセル',
     'change' => '変更',
     'close' => '閉じる',
     'close_item' => ':attributeを閉じる',
     'confirm' => '確認',
-    'continue' => '続ける',
+    'continue' => '続',
     'convert' => '変換',
     'copy' => [
         'attribute' => 'コピー',
-        'failed' => 'コピー失敗、手動でコピーしてください',
-        'success' => 'コピー成功',
+        'failed' => 'コピーに失敗しました。手動でコピーしてください',
+        'success' => 'コピーしました',
     ],
-    'create' => '作成する',
-    'created_at' => '作成日',
+    'create' => '作成',
+    'created_at' => '作成日',
     'customize' => 'カスタマイズ',
     'days' => [
-        'attribute' => '{1} |{2} 日目',
+        'attribute' => '{1}日|{2}日目',
         'next' => '翌日',
         'weekend' => '週末',
         'work' => '平日',
     ],
     'default' => 'デフォルト',
     'delete' => '削除',
-    'deleted' => '削除されました',
-    'deleted_item' => ':attributeが削除されました',
-    'developing' => '開発中です、お楽しみに!',
+    'deleted' => '削除済み',
+    'deleted_item' => ':attributeを削除しました',
+    'developing' => '機能開発中です。お楽しみに!',
     'download' => 'ダウンロード',
+    'download_item' => ':attribute をダウンロード',
     'edit' => '編集',
     'error' => 'エラー',
-    'error_action_item' => ':action:attribute エラー',
-    'error_item' => ':attribute エラー',
-    'exists_error' => ':attribute の下に関連アカウントがあります。先に関連付けを解除してください!',
+    'error_action_item' => ':attribute:actionでエラーが発生しました',
+    'error_item' => ':attributeエラー',
+    'exists_error' => ':attributeは他のアカウントと関連付けられています。先に関連付けを解除してください!',
     'expired_at' => '有効期限',
-    'export' => 'エクスポートする',
+    'export' => 'エクスポート',
     'failed' => '失敗',
-    'failed_action_item' => ':action:attribute 失敗',
-    'failed_item' => ':attribute失敗しました',
+    'failed_action_item' => ':attribute:actionに失敗しました',
+    'failed_item' => ':attribute失敗',
     'free' => '無料',
     'function' => [
-        'fullscreen' => '全画面表示',
+        'fullscreen' => 'フルスクリーン',
         'menubar' => 'メニューバー',
         'navigation' => 'ナビゲーション',
     ],
     'generate' => '生成',
     'generate_item' => ':attributeを生成',
     'goto' => '移動',
-    'hour' => '{1} 時間|{2} 時',
+    'hour' => '{1}時間|{2}時',
     'import' => 'インポート',
-    'latest_at' => '最近の活動',
-    'more' => 'もっと',
-    'new' => '新しい',
+    'latest_at' => '最終更新',
+    'more' => 'その他',
     'none' => 'なし',
     'open' => '開く',
     'or' => 'または',
     'order' => [
         'status' => [
-            'canceled' => 'キャンセルされました',
-            'completed' => '完了しました',
-            'ongoing' => '進行中',
+            'canceled' => 'キャンセル済み',
+            'completed' => '完了',
+            'ongoing' => '利用中',
             'prepaid' => '前払い',
-            'review' => '審査中',
+            'review' => '確認待ち',
         ],
     ],
     'payment' => [
-        'alipay' => 'アリペイ',
+        'alipay' => 'Alipay',
         'credit' => '残高',
-        'crypto' => '暗号通貨',
-        'manual' => '手動支払い',
+        'crypto' => '仮想通貨',
+        'manual' => '手動決済',
         'qq' => 'QQウォレット',
-        'wechat' => 'ウィーチャットペイ',
+        'wechat' => 'WeChat Pay',
     ],
     'print' => '印刷',
-    'qrcode' => ':attribute QRコード',
-    'random_generate' => 'ランダム生成するには空のままにしてください',
-    'recommend' => '推奨',
+    'qrcode' => ':attributeQRコード',
+    'random_generate' => '空白の場合はランダム生成',
+    'recommend' => 'おすすめ',
     'request' => 'リクエスト',
-    'request_failed' => 'リクエストに失敗しました、再試行してください',
+    'request_failed' => 'リクエストに失敗しました。再度お試しください',
     'request_url' => 'リクエストURL',
     'reset' => 'リセット',
     'search' => '検索',
     'send' => '送信',
-    'sorry' => '申し訳ありません',
+    'sorry' => '申し訳ございません',
     'status' => [
         'applying' => '申請中',
         'attribute' => 'ステータス',
-        'available' => '利用可能',
+        'available' => '有効',
         'banned' => '禁止',
-        'closed' => '閉じた',
+        'closed' => '終了',
         'disabled' => '無効',
         'enabled' => '有効',
         'expire' => '期限切れ',
-        'inactive' => '非アクティブ',
-        'limited' => '制限付き',
+        'inactive' => '未有効化',
+        'limited' => '制限',
         'normal' => '正常',
         'paid' => '支払い済み',
-        'pass' => '合格',
+        'pass' => '承認',
         'payment_pending' => '支払い待ち',
-        'pending' => '保留中',
+        'pending' => '処理待ち',
         'pending_dispatch' => '配送待ち',
-        'reject' => '拒否',
-        'rejected' => '却下されました',
+        'reject' => '却下',
+        'rejected' => '却下済み',
         'reply' => '返信済み',
         'review' => '審査中',
         'reviewed' => '審査済み',
-        'run_out' => 'データ使用済み',
-        'send_to_credit' => '残高への支払い',
+        'run_out' => '使い切り',
+        'send_to_credit' => '残高に追加',
         'unknown' => '不明',
         'unused' => '未使用',
         'used' => '使用済み',
-        'withdrawal_pending' => '未引き出し',
-        'withdrawn' => '引き出し済み',
+        'withdrawal_pending' => '出金待ち',
+        'withdrawn' => '出金済み',
     ],
-    'stay_unchanged' => '変更い場合は空のままにてください',
+    'stay_unchanged' => '空白の場合は変更なし',
     'storage_logo' => 'ロゴストレージ',
     'store' => 'ストレージ',
     'submit' => '送信',
     'success' => '成功',
-    'success_action_item' => ':action:attribute 成功',
-    'success_item' => ':attribute成功しました',
-    'to' => '',
+    'success_action_item' => ':attribute:actionが成功しました',
+    'success_item' => ':attribute成功',
+    'to' => '',
     'to_be_send' => '送信待ち',
-    'to_safari' => '右上の<i class="icon wb-more-horizontal" aria-hidden="true"></i>アイコンをクリックし、<img class="w-30 h-30 vertical-align-middle m-3" src="https://gw.alicdn.com/tfs/TB1xwiUNpXXXXaIXXXXXXXXXXXX-55-55.png" alt="Safari" /> Safari で開くを選択すると、当サイトに正常にアクセスできます!',
+    'to_safari' => '右上の <i class="icon wb-more-horizontal" aria-hidden="true"></i> をタップし、<img class="w-30 h-30 vertical-align-middle m-3" src="https://gw.alicdn.com/tfs/TB1xwiUNpXXXXaIXXXXXXXXXXXX-55-55.png" alt="Safari" /> Safari で開くを選択してください<br>正常にウェブサイトにアクセスできます!',
     'toggle' => '切り替え',
     'toggle_action' => ':actionを切り替え',
     'unlimited' => '無制限',
     'update' => '更新',
-    'updated_at' => '更新日',
+    'updated_at' => '更新日',
     'view' => '表示',
     'warning' => '警告',
 ];

+ 23 - 23
resources/lang/ja/errors.php

@@ -4,33 +4,33 @@ declare(strict_types=1);
 
 return [
     'forbidden' => [
-        'access' => '不明なIP またはプロキシが検出されました。アクセス禁止',
-        'bots' => 'ロボットがアクセスするとアクセスが拒否されました!',
-        'china' => '中国の IP またはプロキシが検出されました。アクセス禁止 IP の表示',
-        'oversea' => '海外のIPまたはプロキシが検出されました。アクセス禁止されました!',
-        'redirect' => '購読リンクを使用して接続する (:IP:url) を検出しました 強制リダイレクトです',
-        'unknown' => '不明なブロックモードです。 システム設定で設定を確認してください。',
+        'access' => '不明なIPまたはプロキシを検出しました。アクセスを拒否します!',
+        'bots' => 'ボットアクセスを検出しました。アクセスを拒否します!',
+        'china' => '中国のIPまたはプロキシを検出しました。アクセスを拒否します!',
+        'oversea' => '海外のIPまたはプロキシを検出しました。アクセスを拒否します!',
+        'redirect' => '(:ip :url) がサブスクリプションリンク経由でアクセスしているのを検出し、強制リダイレクトしました',
+        'unknown' => '不明な遮断モードです。システム設定で設定を確認してください!',
     ],
-    'get_ip' => 'IP データの取得に失敗しました',
+    'get_ip' => 'IP情報の取得に失敗しました',
     'log' => 'ログ',
-    'refresh' => 'リフレッシュ',
-    'refresh_page' => 'ページをリフレッシュしたら、再試行してください。',
-    'report' => 'クラッシュレポート:',
-    'safe_code' => '安全コードを入力してください',
-    'safe_enter' => '安全な入口',
+    'refresh' => '更新',
+    'refresh_page' => 'ページを更新してから再度お試しください',
+    'report' => 'エラーレポート内容:',
+    'safe_code' => 'セキュリティコードを入力してください',
+    'safe_enter' => 'セキュアエントリ経由でアクセス',
     'subscribe' => [
-        'banned_until' => 'アカウントのアクセス制限が禁止されました。更新を試みてください。',
-        'expired' => 'アカウントの有効期限が切れています。お手数ですが再度試してみてください',
-        'none' => '利用できるノードがありません。',
-        'out' => 'トラフィックは使い切れです。購入またはリセットしてください。',
-        'question' => 'エラーが発生しました。ネットクエリの詳細を確認してください!',
-        'sub_banned' => '購読リンクはブロックされています。理由を確認するにはホームページに移動してください!',
-        'unknown' => '無効なフィードリンクです。再取得してください!',
-        'user' => 'リンクが無効です。アカウントが存在しません。取得し直してください!',
-        'user_disabled' => 'アカウントが停止されています!',
+        'banned_until' => 'アカウントは :time まで停止されています。解除後に再度お試しください!',
+        'expired' => 'アカウントの有効期限が切れています。更新してからご利用ください!',
+        'none' => '利用可能なノードがありません',
+        'out' => 'トラフィックを使い切りました。追加購入またはリセットしてください!',
+        'question' => 'アカウントに異常があります。公式サイトで詳細をご確認ください!',
+        'sub_banned' => 'サブスクリプションリンクが停止されています。公式サイトで理由をご確認ください!',
+        'unknown' => 'サブスクリプションリンクが無効です。新しいリンクを取得してください!',
+        'user' => 'リンクが無効です。アカウントが存在しません。新しいリンクを取得してください!',
+        'user_disabled' => 'アカウントが無効化されています!',
     ],
     'title' => '⚠️ エラーが発生しました',
-    'unsafe_enter' => '安全でない入口',
-    'visit' => 'アクセスしてください',
+    'unsafe_enter' => '非セキュアエントリ経由でアクセス',
+    'visit' => 'こちらにアクセスしてください',
     'whoops' => 'おっと!',
 ];

+ 198 - 198
resources/lang/ja/model.php

@@ -4,24 +4,24 @@ declare(strict_types=1);
 
 return [
     'aff' => [
-        'amount' => '注文金額',
+        'amount' => '消費金額',
         'commission' => 'コミッション',
-        'created_at' => '注文日',
-        'invitee' => '購入者',
-        'updated_at' => '処理日',
+        'created_at' => '注文日',
+        'invitee' => '消費者',
+        'updated_at' => '処理日',
     ],
     'article' => [
         'attribute' => '記事',
-        'category' => 'カテゴリ',
-        'created_at' => '公開日',
+        'category' => 'カテゴリ',
+        'created_at' => '公開日',
         'language' => '言語',
-        'logo' => 'カバー',
+        'logo' => 'カバー画像',
     ],
     'common' => [
         'description' => '説明',
         'extend' => '拡張情報',
         'level' => 'レベル',
-        'sort' => 'ソート',
+        'sort' => 'ソート',
         'type' => 'タイプ',
     ],
     'country' => [
@@ -32,83 +32,83 @@ return [
         'attribute' => 'クーポン',
         'groups' => 'グループ制限',
         'levels' => 'レベル制限',
-        'logo' => 'ロゴ',
-        'minimum' => '最低注文数以下では販売しません',
-        'name' => '名',
-        'newbie' => '新規ユーザー専用',
-        'num' => '数',
+        'logo' => '画像',
+        'minimum' => '最低利用金額',
+        'name' => 'クーポン名',
+        'newbie' => '新規ユーザー限定',
+        'num' => '発行枚数',
         'priority' => '優先度',
-        'services_blacklist' => 'ブラックリスト商品',
-        'services_whitelist' => 'ホワイトリスト商品',
+        'services_blacklist' => '除外商品',
+        'services_whitelist' => '対象商品',
         'sn' => 'クーポンコード',
-        'usable_times' => '使用制限',
-        'used' => '1人限定',
-        'users_blacklist' => 'ブラックリストユーザー',
-        'users_whitelist' => 'ホワイトリストユーザー',
-        'value' => '価値',
+        'usable_times' => '利用回数制限',
+        'used' => '一人当たり利用制限',
+        'users_blacklist' => '除外ユーザー',
+        'users_whitelist' => '対象ユーザー',
+        'value' => '額面',
     ],
     'goods' => [
         'attribute' => '商品',
-        'available_date' => '有効期',
-        'category' => 'カテゴリ',
-        'color' => '',
-        'hot' => 'ベストセラー',
+        'available_date' => '有効期',
+        'category' => 'カテゴリ',
+        'color' => 'カラー',
+        'hot' => '人気商品',
         'info' => 'カスタム情報',
-        'invite_num' => '招待者の署名に力が付与されます',
-        'limit_num' => '1人限定購入',
+        'invite_num' => '招待枠付与数',
+        'limit_num' => '一人当たり購入制限',
         'logo' => '商品画像',
-        'name' => '名',
-        'period' => '期間をリセット',
-        'price' => '価格',
-        'renew' => '使用量のリセット',
-        'traffic' => 'データ許容量',
+        'name' => '商品名',
+        'period' => 'リセット周期',
+        'price' => '販売価格',
+        'renew' => 'トラフィックリセット価格',
+        'traffic' => 'トラフィック容量',
         'user_limit' => 'ユーザー速度制限',
     ],
     'ip' => [
-        'info' => '位置情報',
+        'info' => '所在地',
         'network_type' => 'ネットワークタイプ',
     ],
     'node' => [
         'attribute' => 'ノード',
-        'client_limit' => 'クライアント制限',
-        'country' => '国',
-        'data_consume' => 'トラフィックの消費',
-        'data_rate' => 'データ使用量:',
+        'client_limit' => 'デバイス制限',
+        'country' => '国・地域',
+        'data_consume' => '消費トラフィック',
+        'data_rate' => 'トラフィック係数',
         'ddns' => 'DDNS',
-        'detection' => 'ブロック検',
-        'display' => '表示サブスクリプション',
-        'domain' => 'ドメイン',
+        'detection' => 'ブロック検',
+        'display' => '表示サブスクリプション',
+        'domain' => 'ドメイン',
         'id' => 'ノードID',
-        'ipv4' => 'IPv4アドレス',
+        'ipv4' => 'IPv4アドレス',
         'ipv6' => 'IPv6アドレス',
         'label' => 'ラベル',
         'method' => '暗号化方式',
         'name' => 'ノード名',
         'next_renewal_date' => '次回更新日',
-        'obfs' => '混同契約',
+        'obfs' => '難読化プロトコル',
         'obfs_param' => '難読化パラメータ',
         'online_user' => 'オンラインユーザー',
-        'protocol' => '送プロトコル',
+        'protocol' => '送プロトコル',
         'protocol_param' => 'プロトコルパラメータ',
         'push_port' => 'プッシュポート',
-        'relay_port' => 'リレーポート',
-        'renewal_cost' => '請求額',
+        'relay_port' => '中継ポート',
+        'renewal_cost' => '更新料金',
         'service_port' => 'サービスポート',
         'single' => 'シングルポート',
         'single_passwd' => 'シングルポートパスワード',
-        'static' => '操作のステータス',
-        'subscription_term' => '契約期間',
-        'traffic_limit' => 'データ上限',
-        'transfer' => '継設定',
-        'udp' => 'UDP サポート',
+        'static' => '稼働状態',
+        'subscription_term' => 'サブスクリプション期間',
+        'traffic_limit' => 'トラフィック制限',
+        'transfer' => '継設定',
+        'udp' => 'UDP対応',
         'v2_alter_id' => '追加ID',
-        'v2_cover' => 'トラフィック',
-        'v2_host' => 'ホスト',
-        'v2_net' => '送プロトコル',
-        'v2_path' => 'パス/シークレット',
+        'v2_cover' => 'トラフィック偽装',
+        'v2_host' => '偽装ドメイン',
+        'v2_net' => '送プロトコル',
+        'v2_path' => 'パス/キー',
         'v2_sni' => 'SNI',
-        'v2_tls' => 'TLS暗号化',
-        'v2_tls_provider' => 'TLS 証明書の設定',
+        'v2_tls' => 'TLS暗号化',
+        'v2_tls_provider' => 'TLS証明書プロバイダー',
     ],
     'node_auth' => [
         'attribute' => 'ノード認証',
@@ -116,30 +116,30 @@ return [
         'secret' => 'リバースキー',
     ],
     'node_cert' => [
-        'attribute' => '証明書',
-        'domain' => 'ドメイン',
+        'attribute' => 'ドメイン証明書',
+        'domain' => 'ドメイン',
         'expired_date' => '有効期限',
-        'issuer' => '発行',
+        'issuer' => '発行機関',
         'key' => '秘密鍵',
-        'pem' => '証明書',
+        'pem' => 'PEM証明書',
         'signed_date' => '発行日',
     ],
     'notification' => [
-        'address' => '受信者',
-        'created_at' => '送信日',
+        'address' => '送信先',
+        'created_at' => '送信日',
         'status' => 'ステータス',
     ],
     'oauth' => [
-        'identifier' => '識別子',
-        'type' => 'チャンネル',
+        'identifier' => 'ユーザー識別子',
+        'type' => 'ログイン方式',
     ],
     'order' => [
         'attribute' => '注文',
-        'id' => '注文ID',
-        'original_price' => '元の価格',
+        'id' => '注文番号',
+        'original_price' => '定価',
         'pay_way' => '支払い方法',
-        'price' => '支払額',
-        'status' => 'ステータス',
+        'price' => '実際の支払額',
+        'status' => '注文ステータス',
     ],
     'permission' => [
         'attribute' => '権限',
@@ -147,219 +147,219 @@ return [
         'name' => 'ルート名',
     ],
     'referral' => [
-        'amount' => '金額',
-        'created_at' => '申請日',
-        'id' => '申請ID',
-        'user' => '申請',
+        'amount' => '申請金額',
+        'created_at' => '申請日',
+        'id' => '申請番号',
+        'user' => '申請アカウント',
     ],
     'role' => [
-        'attribute' => '役割',
-        'name' => '名',
+        'attribute' => 'ロール',
+        'name' => 'ロール名',
         'permissions' => '権限',
     ],
     'rule' => [
         'attribute' => 'ルール',
-        'name' => '説明',
-        'pattern' => '値',
-        'logs' => 'トリガー履歴',
+        'name' => 'ルール名',
+        'pattern' => 'マッチ値',
+        'logs' => 'トリガー記録',
     ],
     'rule_group' => [
         'attribute' => 'ルールグループ',
         'name' => 'グループ名',
         'rules' => 'ルール',
-        'type' => 'タイプ',
+        'type' => 'モード',
     ],
     'subscribe' => [
-        'attribute' => '購読する',
-        'ban_desc' => '止理由',
-        'ban_time' => '止時間',
+        'attribute' => 'サブスクリプション',
+        'ban_desc' => '止理由',
+        'ban_time' => '止時間',
         'code' => 'サブスクリプションコード',
         'req_header' => 'リクエストヘッダー',
         'req_ip' => 'リクエストIP',
         'req_times' => 'リクエスト回数',
-        'updated_at' => '最後のリクエスト',
+        'updated_at' => '最リクエスト',
     ],
     'user' => [
-        'account_status' => 'アカウントステータス',
+        'account_status' => 'アカウント状態',
         'attribute' => 'ユーザー',
-        'created_date' => '登録日',
+        'created_date' => '登録日',
         'credit' => '残高',
         'expired_date' => '有効期限',
         'id' => 'ユーザーID',
-        'invite_num' => '招待者の名前',
+        'invite_num' => '招待',
         'inviter' => '招待者',
         'nickname' => 'ニックネーム',
         'password' => 'パスワード',
         'port' => 'ポート',
-        'proxy_method' => '暗号化',
-        'proxy_obfs' => 'トラフィック',
+        'proxy_method' => '転送暗号化',
+        'proxy_obfs' => 'トラフィック偽装',
         'proxy_passwd' => 'プロキシパスワード',
-        'proxy_protocol' => 'プロトコル',
-        'proxy_status' => 'プロキシステータス',
+        'proxy_protocol' => 'プロキシプロトコル',
+        'proxy_status' => 'プロキシ状態',
         'qq' => 'QQ',
         'remark' => '備考',
-        'reset_date' => 'データリセット日',
-        'role' => '権限',
+        'reset_date' => 'トラフィックリセット日',
+        'role' => 'ユーザーロール',
         'service' => 'プロキシサービス',
-        'speed_limit' => 'レート制限は',
-        'traffic_used' => '使用済みデータ',
-        'usable_traffic' => '利用可能なデータ',
+        'speed_limit' => '速度制限',
+        'traffic_used' => '使用済みトラフィック',
+        'usable_traffic' => '利用可能トラフィック',
         'username' => 'ユーザー名',
         'uuid' => 'VMess UUID',
         'wechat' => 'WeChat',
     ],
     'user_credit' => [
         'after' => '変更後',
-        'amount' => 'お釣り',
+        'amount' => '変更額',
         'before' => '変更前',
-        'created_at' => '記録時',
+        'created_at' => '記録時',
     ],
     'user_data_modify' => [
         'after' => '変更後',
         'before' => '変更前',
-        'created_at' => '記録時',
+        'created_at' => '記録時',
     ],
     'user_group' => [
         'attribute' => 'ユーザーグループ',
         'name' => 'グループ名',
-        'nodes' => 'ノード',
+        'nodes' => '利用可能ノード',
     ],
     'user_traffic' => [
-        'download' => '通信量',
+        'download' => 'ダウンロード',
         'log_time' => '記録時間',
         'total' => '合計',
-        'upload' => '送信データ',
+        'upload' => 'アップロード',
     ],
     'config' => [
-        'AppStore_id' => 'AppleID',
-        'AppStore_password' => 'アップル パスワード',
-        'account_expire_notification' => 'アカウント有効期限通知',
-        'active_times' => '上限を有効にする',
-        'admin_invite_days' => '招待コードを管理する',
+        'AppStore_id' => 'Apple ID',
+        'AppStore_password' => 'Appleパスワード',
+        'account_expire_notification' => 'アカウント期限切れ通知',
+        'active_times' => 'アクティベーション回数制限',
+        'admin_invite_days' => '管理者招待コード有効期限',
         'affiliate_link_salt' => '招待リンク暗号化',
-        'alipay_qrcode' => 'PayPal決済コード',
+        'alipay_qrcode' => 'Alipay決済QRコード',
         'auto_release_port' => 'ポート回収',
-        'ban_duration' => 'ブロックする時間',
-        'bark_key' => 'Barkデバイスキー',
-        'captcha_key' => 'Captcha キー',
-        'captcha_secret' => 'Secret/ID',
-        'checkin_interval' => 'サインイン間隔',
-        'checkin_reward' => 'ボーナス記録',
+        'ban_duration' => '停止期間',
+        'bark_key' => 'Barkデバイス番号',
+        'captcha_key' => '認証コードキー',
+        'captcha_secret' => '認証コードシークレット/ID',
+        'checkin_interval' => 'チェックイン間隔',
+        'checkin_reward' => 'チェックイン報酬',
         'codepay_id' => 'CodePay ID',
         'codepay_key' => '通信キー',
         'codepay_url' => 'リクエストURL',
-        'cryptomus_api_key' => 'API キー',
-        'cryptomus_merchant_uuid' => 'Merchant ID',
-        'data_anomaly_notification' => 'データ異常通知',
-        'data_exhaust_notification' => 'データ使用量通知',
-        'ddns_key' => 'DNSキー',
+        'cryptomus_api_key' => 'Cryptomus APIキー',
+        'cryptomus_merchant_uuid' => 'Cryptomus マーチャントUUID',
+        'data_anomaly_notification' => 'トラフィック異常通知',
+        'data_exhaust_notification' => 'トラフィック枯渇通知',
+        'ddns_key' => 'DDNSキー',
         'ddns_mode' => 'DDNSモード',
-        'ddns_secret' => 'DNS Secret',
+        'ddns_secret' => 'DDNSシークレット',
         'default_days' => '初期有効期限',
-        'default_traffic' => '初期データ量',
-        'detection_check_times' => '通知をブロックする',
-        'dingTalk_access_token' => 'Todoubles をピン止めする',
-        'dingTalk_secret' => 'ピン留め',
-        'epay_key' => 'ePayキー',
-        'epay_mch_id' => 'ePay商人ID',
-        'epay_url' => 'THeadPay URL',
-        'expire_days' => '有効期限警告しきい値',
-        'f2fpay_app_id' => 'アプリID',
-        'f2fpay_private_key' => 'アプリプライベートキー',
-        'f2fpay_public_key' => 'Alipayパブリックキー',
-        'forbid_mode' => 'アクセスブロックモード',
+        'default_traffic' => '初期トラフィック',
+        'detection_check_times' => 'ブロック検出通知',
+        'dingTalk_access_token' => 'DingTalk アクセストークン',
+        'dingTalk_secret' => 'DingTalk シークレット',
+        'epay_key' => 'ePay マーチャントキー',
+        'epay_mch_id' => 'ePay マーチャントID',
+        'epay_url' => 'ePay ゲートウェイURL',
+        'expire_days' => '期限切れ警告閾値',
+        'f2fpay_app_id' => 'Alipay アプリID',
+        'f2fpay_private_key' => 'Alipay 秘密鍵',
+        'f2fpay_public_key' => 'Alipay 公開鍵',
+        'forbid_mode' => 'アクセス制限モード',
         'iYuu_token' => 'IYUUトークン',
-        'invite_num' => '初期招待者の名前',
-        'is_AliPay' => 'アリペイ',
+        'invite_num' => '初期招待',
+        'is_AliPay' => 'Alipay',
         'is_QQPay' => 'QQウォレット',
-        'is_WeChatPay' => 'WeChat支払い',
-        'is_activate_account' => 'アカウントの有効化',
-        'is_ban_status' => '有効期限自動禁止',
-        'is_captcha' => 'キャプチャ',
-        'is_clear_log' => 'ログの自動クリーンアップ',
+        'is_WeChatPay' => 'WeChat Pay',
+        'is_activate_account' => 'アカウントアクティベーション',
+        'is_ban_status' => '期限切れ自動停止',
+        'is_captcha' => '認証コード',
+        'is_clear_log' => 'ログ自動削除',
         'is_custom_subscribe' => 'カスタムサブスクリプション',
-        'is_email_filtering' => 'Eメールフィルター',
-        'is_forbid_robot' => 'ドロイドをブロック',
+        'is_email_filtering' => 'メールドメインフィルター',
+        'is_forbid_robot' => 'ボット遮断',
         'is_free_code' => '無料招待コード',
-        'is_invite_register' => '招待登録',
-        'is_otherPay' => 'カスタム支払い',
+        'is_invite_register' => '招待登録',
+        'is_otherPay' => 'カスタム決済チャネル',
         'is_rand_port' => 'ランダムポート',
-        'is_register' => 'すぐ実行',
-        'maintenance_content' => 'メンテナンスのお知らせ',
+        'is_register' => '新規登録',
+        'maintenance_content' => 'メンテナンス告知',
         'maintenance_mode' => 'メンテナンスモード',
         'maintenance_time' => 'メンテナンス終了時間',
         'min_port' => 'ポート範囲',
         'node_blocked_notification' => 'ノードブロック通知',
-        'node_daily_notification' => '毎日のノードレポート',
+        'node_daily_notification' => '日次ノードレポート',
         'node_offline_notification' => 'ノードオフライン通知',
-        'node_renewal_notification' => 'ノード更新リマインダー',
-        'oauth_path' => 'サードパーティログイン',
+        'node_renewal_notification' => 'ノード更新リマインダー',
+        'oauth_path' => 'サードパーティログイン',
         'offline_check_times' => 'オフライン通知回数',
         'password_reset_notification' => 'パスワードリセット',
-        'paybeaver_app_id' => 'App ID',
-        'paybeaver_app_secret' => 'Appシークレット',
-        'payjs_key' => '通信キー',
-        'payjs_mch_id' => '商人ID',
-        'payment_callback_url' => '支払コールバックアドレス',
-        'payment_confirm_notification' => '支払い通知',
-        'payment_received_notification' => '支払い成功通知',
-        'paypal_app_id' => 'App ID',
-        'paypal_client_id' => 'クライアントID',
-        'paypal_client_secret' => 'Client Secret キー',
-        'pushDeer_key' => 'PushDeerキー',
-        'pushplus_token' => 'PushPlusトークン',
+        'paybeaver_app_id' => 'PayBeaver アプリID',
+        'paybeaver_app_secret' => 'PayBeaver シークレットキー',
+        'payjs_key' => 'PayJS 通信キー',
+        'payjs_mch_id' => 'PayJS マーチャント番号',
+        'payment_callback_url' => '決済コールバックURL',
+        'payment_confirm_notification' => '手動決済通知',
+        'payment_received_notification' => '決済成功通知',
+        'paypal_app_id' => 'PayPal アプリID',
+        'paypal_client_id' => 'PayPal クライアントID',
+        'paypal_client_secret' => 'PayPal シークレットキー',
+        'pushDeer_key' => 'PushDeer キー',
+        'pushplus_token' => 'PushPlus トークン',
         'rand_subscribe' => 'ランダムサブスクリプション',
-        'recently_heartbeat' => '最近のノードポートレート閾値',
+        'recently_heartbeat' => 'ノード負荷最近閾値',
         'redirect_url' => 'リダイレクトURL',
-        'referral_money' => '最低金額',
-        'referral_percent' => 'リベート率',
-        'referral_reward_type' => 'リベートモード',
+        'referral_money' => '最低出金金額',
+        'referral_percent' => 'コミッション率',
+        'referral_reward_type' => 'コミッション計算方式',
         'referral_status' => 'アフィリエイト機能',
-        'referral_traffic' => '登録トラフィック合計',
+        'referral_traffic' => '登録ボーナストラフィック',
         'register_ip_limit' => '同一IP登録制限',
-        'reset_password_times' => '1日のリセット上限',
-        'reset_traffic' => '定期的にトラフィックリセット',
+        'reset_password_times' => '日次パスワードリセット上限',
+        'reset_traffic' => '周期的トラフィックリセット',
         'server_chan_key' => 'ServerChan SCKEY',
         'standard_currency' => '基準通貨',
-        'stripe_public_key' => 'パブリックキー',
-        'stripe_secret_key' => 'シークレットキー',
-        'stripe_signing_secret' => 'WebHook Signing Secret',
-        'subject_name' => 'このキャンペーンの送信に使用される電子メールアカウント',
-        'subscribe_domain' => 'サブスクリプション用URL',
-        'subscribe_max' => 'サブスクリプションノード数',
-        'subscribe_rate_limit' => 'サブスクリプション支払拒否の制限',
-        'tasks_chunk' => '分割処理数',
-        'tasks_clean' => 'タスクを終了',
-        'tasks_close' => 'タスクを閉じる',
-        'telegram_token' => 'Telegramトークン',
-        'tg_chat_token' => 'TGチャットトークン',
-        'theadpay_key' => 'ePayキー',
-        'theadpay_mchid' => 'ePay商人ID',
-        'theadpay_url' => 'THeadPay URL',
+        'stripe_public_key' => 'Stripe パブリックキー',
+        'stripe_secret_key' => 'Stripe シークレットキー',
+        'stripe_signing_secret' => 'Stripe Webhook署名シークレット',
+        'subject_name' => '商品名',
+        'subscribe_domain' => 'サブスクリプションドメイン',
+        'subscribe_max' => 'サブスクリプションノード数上限',
+        'subscribe_rate_limit' => 'サブスクリプションレート制限',
+        'tasks_chunk' => 'バッチ処理量',
+        'tasks_clean' => 'クリーンアップタスク',
+        'tasks_close' => 'クローズタスク',
+        'telegram_token' => 'Telegram ボットトークン',
+        'tg_chat_token' => 'TGトークン',
+        'theadpay_key' => 'THeadPay キー',
+        'theadpay_mchid' => 'THeadPay マーチャントID',
+        'theadpay_url' => 'THeadPay 決済URL',
         'ticket_closed_notification' => 'チケットクローズ通知',
-        'ticket_created_notification' => '新しいチケット通知',
+        'ticket_created_notification' => '新チケット通知',
         'ticket_replied_notification' => 'チケット返信通知',
-        'traffic_abuse_limit' => 'データ異常しきい値',
-        'traffic_warning_percent' => 'トラフィック使用警告閾値',
-        'trojan_license' => 'Trojanライセンス',
+        'traffic_abuse_limit' => 'トラフィック異常閾値',
+        'traffic_warning_percent' => 'トラフィック枯渇警告閾値',
+        'trojan_license' => 'Trojan ライセンス',
         'user_invite_days' => 'ユーザー招待コード有効期限',
-        'username_type' => 'アカウントの種類',
-        'v2ray_license' => 'V2Rayライセンス',
-        'v2ray_tls_provider' => 'V2Ray TLSS',
-        'web_api_url' => 'APIアクセスURL',
-        'webmaster_email' => 'サイト管理者の電子メール',
-        'website_customer_service_code' => 'サポート コード',
+        'username_type' => 'アカウントタイプ',
+        'v2ray_license' => 'V2Ray ライセンス',
+        'v2ray_tls_provider' => 'V2Ray TLS証明書プロバイダー',
+        'web_api_url' => 'バックエンドAPI URL',
+        'webmaster_email' => 'サイト管理者メール',
+        'website_customer_service_code' => 'カスタマーサービスコード',
         'website_home_logo' => 'ホームページロゴ',
-        'website_logo' => 'サイトロゴ',
-        'website_name' => 'サイト名',
+        'website_logo' => 'サイトロゴ',
+        'website_name' => 'ウェブサイト名',
         'website_security_code' => 'セキュリティコード',
         'website_statistics_code' => '統計コード',
-        'website_url' => 'サイトURL',
-        'wechat_aid' => 'アプリID',
-        'wechat_cid' => '企業ID',
-        'wechat_encodingAESKey' => 'EncodingAESキー',
-        'wechat_qrcode' => 'WeChat番号',
-        'wechat_secret' => 'アプリキー',
-        'wechat_token' => 'TOKEN',
+        'website_url' => 'ウェブサイトURL',
+        'wechat_aid' => 'WeChat アプリケーションID',
+        'wechat_cid' => 'WeChat 企業ID',
+        'wechat_encodingAESKey' => 'WeChat エンコーディングAESキー',
+        'wechat_qrcode' => 'WeChat Pay QRコード',
+        'wechat_secret' => 'WeChat アプリケーションシークレット',
+        'wechat_token' => 'WeChat トークン',
     ],
 ];

+ 36 - 36
resources/lang/ja/notification.php

@@ -3,49 +3,49 @@
 declare(strict_types=1);
 
 return [
-    'account_expired' => 'アカウントの有効期限通知',
-    'account_expired_blade' => 'アカウントは:days日後に有効期限切れます。早めに更新してください',
-    'account_expired_content' => 'あなたのアカウントは:days日後に有効期限が切れます。サービスの継続利用のため、早めに更新してください。',
+    'account_expired' => 'アカウント有効期限のお知らせ',
+    'account_expired_blade' => 'アカウントは :days 日後に期限切れとなります。早めに更新してください',
+    'account_expired_content' => 'アカウントは :days 日後に期限切れとなります。サービスの継続利用のため、お早めに更新手続きをお願いいたします。',
     'active_email' => '30分以内に認証を完了してください',
     'attribute' => '通知',
-    'block_report' => '詳細なブロックログ:',
+    'block_report' => 'ブロック詳細レポート:',
     'close_ticket' => 'チケット [ID: :id, タイトル: :title] がクローズされました',
-    'data_anomaly' => 'ユーザーのデータ異常通知',
-    'data_anomaly_content' => 'ユーザー [ID: :id] のデータ使用状況: [アップロード: :upload, ダウンロード: :download, 合計: :total](過去1時間)',
+    'data_anomaly' => 'ユーザートラフィック異常アラート',
+    'data_anomaly_content' => 'ユーザー [ID: :id] の過去1時間のトラフィック使用状況:[アップロード: :upload、ダウンロード: :download、合計: :total]',
     'details' => '詳細を表示',
-    'details_btn' => '下のボタンをクリックして、詳細をご覧ください。',
-    'ding_bot_limit' => '各ボットは1分あたり最大20件のメッセージを送信できます。制限を超えた場合、10分間のレート制限が適用されます。',
-    'empty' => '現在、新しいメッセージはありません',
-    'error' => '[:channel] メッセージプッシュの例外: :reason',
-    'get_access_token_failed' => 'アクセストークンの取得に失敗しました!\nリクエストパラメータ: :body',
-    'into_maintenance' => '自動的にメンテナンスモードに入る',
-    'new' => '新しいメッセージが:num件あります',
-    'new_ticket' => '新しいチケット [タイトル: :title] が作成されました。詳細を確認してください。',
-    'next_check_time' => '次回ノードブロック検出時間: :time',
+    'details_btn' => '下記ボタンをクリックして詳細をご確認ください。',
+    'ding_bot_limit' => '各ボットは1分間に最大20件のメッセージを送信できます。制限を超えると10分間の制限が適用されます。',
+    'empty' => '新しいメッセージはありません',
+    'error' => '[:channel] メッセージ送信エラー: :reason',
+    'get_access_token_failed' => 'アクセストークンの取得に失敗しました!\nリクエストパラメータ: :body',
+    'into_maintenance' => '自動メンテナンスモードに移行',
+    'new' => '新メッセージが:num件あります',
+    'new_ticket' => '新しいチケットを受信しました:[タイトル: :title]。詳細をご確認ください。',
+    'next_check_time' => '次回ノードブロック検出時刻::time',
     'node' => [
-        'download' => 'ダウンロードデータ',
-        'total' => '総データ',
-        'upload' => 'アップロードデータ',
+        'download' => 'ダウンロード',
+        'total' => '合計',
+        'upload' => 'アップロード',
     ],
-    'node_block' => 'ノードブロック警告通知',
+    'node_block' => 'ノードブロック警告',
     'node_offline' => 'ノードオフライン警告',
-    'node_offline_content' => '以下のノードが異常です。オフラインの可能性があります:',
-    'node_renewal' => 'ノード更新リマインダー',
-    'node_renewal_blade' => '以下のノードがまもなく期限切れになります。事前に更新してください: :nodes',
-    'node_renewal_content' => '以下のノードがまもなく期限切れになります。サービス中断を避けるため、期限前に更新してください。',
-    'payment_received' => '支払いを受け取りました。金額: :amount。注文の詳細を見る',
-    'reply_ticket' => 'チケット返信::title',
-    'reset_failed' => '[日次タスク] ユーザー [ID: :uid, ユーザー名: :username] のデータリセットに失敗しました。',
-    'serverChan_exhausted' => '今日の制限が使い切られました!',
-    'serverChan_limit' => '分単位の頻度が高すぎます。通知設定を最適化してください!',
-    'sign_failed' => 'セキュアサイン確認に失敗しました',
+    'node_offline_content' => '以下のノードに異常が発生し、オフラインの可能性があります:',
+    'node_renewal' => 'ノード更新リマインダー',
+    'node_renewal_blade' => '以下のノードが間もなく期限切れとなります。お早めに更新してください::nodes',
+    'node_renewal_content' => '以下のノードが間もなく期限切れとなります。サービス中断を避けるため、期限前に更新してください。',
+    'payment_received' => 'お支払いを確認いたしました。金額::amount。注文詳細を表示',
+    'reply_ticket' => 'チケット返信::title',
+    'reset_failed' => '[日次タスク] ユーザー [ID: :uid, ユーザー名: :username] のトラフィックリセットに失敗しました',
+    'serverChan_exhausted' => '本日の送信制限に達しました!',
+    'serverChan_limit' => '1分間の送信頻度が高すぎます。通知設定を最適化してください!',
+    'sign_failed' => 'セキュリティ署名の検証に失敗しました',
     'ticket_content' => 'チケット内容:',
-    'traffic_remain' => 'データ使用量が :percent% に達しました。残りのデータ使用量に注意してください。',
-    'traffic_tips' => 'データリセット日を確認し、適切にデータを使用してください。データがなくなった場合はリチャージしてください。',
-    'traffic_warning' => 'データ使用警告',
-    'verification' => 'あなたの認証コード:',
+    'traffic_remain' => 'トラフィックの :percent% をご利用になりました。残りトラフィックを計画的にご使用ください',
+    'traffic_tips' => 'トラフィックリセット日をご確認の上、計画的にご利用いただくか、不足時にはチャージをお願いします。',
+    'traffic_warning' => 'トラフィック使用量警告',
+    'verification' => '認証コード:',
     'verification_account' => 'アカウント認証通知',
-    'verification_limit' => ':minutes分以内に認証を完了してください',
-    'view_ticket' => 'このチケットの進捗を見る',
-    'view_web' => '公式サイトを見る',
+    'verification_limit' => ':minutes 分以内に認証を完了してください',
+    'view_ticket' => 'チケットを表示',
+    'view_web' => 'ウェブサイトを表示',
 ];

+ 159 - 149
resources/lang/ja/user.php

@@ -4,209 +4,212 @@ declare(strict_types=1);
 
 return [
     'account' => [
-        'connect_password' => 'プロキシ接続パスワード',
+        'connect_password' => 'ノード接続パスワード',
         'credit' => 'アカウント残高',
-        'group' => 'グループ',
+        'group' => 'ユーザーグループ',
         'last_login' => '最終ログイン',
         'level' => 'アカウントレベル',
         'reason' => [
-            'expired' => 'プランの有効期限が切れています',
-            'normal' => 'アカウントは正常です',
-            'overused' => 'この期間の <code>:data</code> GB 制限を超えました。<br/> 制限は <code id="banedTime">:min</code> 分後に解除されます',
-            'traffic_exhausted' => 'データが使い果たされました',
-            'unknown' => '不明な理由です。ブラウザをリフレッシュしてみてください。問題が続く場合はサポートに連絡してください。',
+            'expired' => 'プランが期限切れです',
+            'normal' => 'アカウント状態は正常です',
+            'overused' => '<code>:data</code>GB の制限を超過しました。<code id="banedTime">:min</code> 分後に利用再開',
+            'traffic_exhausted' => 'プラントラフィックを使い切りました',
+            'unknown' => '不明なエラーです。ページを更新するかサポートにお問い合わせください',
         ],
-        'remain' => '残りデータ',
-        'reset' => '{0} データは <code id="restTime">:days</code> 後にリセットされます |{1} データリセットまで :days 日 |[2,*] データリセットまで :days 日',
+        'remain' => '残りトラフィック',
+        'reset' => '{0} <code id="restTime">:days</code> でトラフィックリセット|{1} :days 日後にトラフィックリセット|[2,*] :days 日後にトラフィックリセット',
         'speed_limit' => '速度制限',
         'status' => 'アカウント状態',
-        'time' => 'プラン期間',
+        'time' => 'プラン有効期限',
     ],
     'attribute' => [
-        'address' => '所在地',
-        'data' => 'データ',
+        'address' => '地',
+        'data' => 'トラフィック',
         'ip' => 'IPアドレス',
         'isp' => 'ISP',
         'node' => 'ノード',
     ],
     'bought_at' => '購入日',
     'clients' => 'クライアント',
-    'contact' => 'お問い合わせ',
+    'contact' => '連絡方法',
     'coupon' => [
         'discount' => '割引',
         'error' => [
             'expired' => 'クーポンの有効期限が切れています',
-            'inactive' => 'クーポンはまだ有効ではありません',
-            'minimum' => '最低利用金額は :amount です',
-            'overused' => 'クーポンの利用可能回数は :times 回です',
-            'run_out' => 'クーポンの利用回数が制限に達しました',
-            'services' => '商品は割引対象外です。キャンペーン規約をご確認ください。',
-            'unknown' => '無効なクーポン',
+            'inactive' => 'クーポンはまだ有効になっていません',
+            'minimum' => '最低利用金額::amount',
+            'overused' => 'このクーポンは :times 回まで利用可能です',
+            'run_out' => 'クーポンは全て配布済みです',
+            'services' => '商品が利用条件に適合しません。プロモーション規約をご確認ください',
+            'unknown' => '無効なクーポンです',
             'unmet' => '利用条件を満たしていません',
-            'used' => 'クーポンは使用済みです',
-            'users' => 'アカウントはキャンペーン対象外です',
-            'wait' => ':timeに有効になりますので、お待ちください。',
+            'used' => 'クーポンは既に使用済みです',
+            'users' => 'アカウントがプロモーション条件に適合しません',
+            'wait' => 'このプロモーションは :time に開始予定です。しばらくお待ちください!',
         ],
         'input' => 'クーポンコードを入力',
     ],
-    'current_role' => '現在のロール',
-    'error_response' => 'エラーが発生しました。時間をおいて再度お試しください。',
+    'current_role' => '現在の権限',
+    'error_response' => 'システムが混雑しています。しばらく待ってから再度お試しください',
     'home' => [
         'announcement' => 'お知らせ',
         'attendance' => [
             'attribute' => 'チェックイン',
-            'disable' => 'チェックイン機能無効です',
-            'done' => '既にチェックイン済みです。明日また来てください!',
+            'disable' => 'チェックイン機能無効です',
+            'done' => '本日は既にチェックイン済みです',
             'failed' => 'システムエラー',
-            'success' => ':data データを受け取りました',
+            'success' => 'チェックイン成功 +:data トラフィック獲得',
         ],
-        'chat_group' => 'チャットグループ',
+        'chat_group' => 'コミュニティ',
         'empty_announcement' => 'お知らせはありません',
-        'traffic_logs' => 'データ記録',
+        'traffic_logs' => 'トラフィック記録',
         'wechat_push' => 'WeChat通知',
     ],
     'invite' => [
         'attribute' => '招待コード',
-        'counts' => '招待コード総数 <code>:num</code>',
-        'generate_failed' => '生成失敗: 招待コードの上限に達しました',
-        'logs' => '招待履歴',
-        'promotion' => '招待コードで登録すると、招待元と被招待者の両方に<mark>:traffic</mark>データが付与されます。被招待者が購入した場合、購入金額の<mark>:referral_percent%</mark>が報酬として付与されます。',
-        'tips' => '残招待コード <strong>:num</strong> 個。招待コードは発行後:days日で失効します。',
+        'counts' => '利用可能:<code>:num</code> 枚',
+        'generate_failed' => '生成枠が不足しています',
+        'logs' => '招待記録',
+        'promotion' => [
+            'base' => '招待成功報酬:<br>• 双方に <mark>:traffic</mark> トラフィックを付与',
+            'bonus' => [
+                0 => '',
+                1 => '<br>• 被招待者が<strong>初回</strong>購入時に <mark>:referral_percent%</mark> のリベートを獲得',
+                2 => '<br>• 被招待者の<strong>購入毎</strong>に <mark>:referral_percent%</mark> のリベートを獲得',
+            ],
+        ],
+        'tips' => '残り枠数:<strong>:num</strong>、有効期限 :days 日',
     ],
-    'invitee' => '被招待者',
+    'invitee' => '被招待ユーザー',
     'inviter' => '招待者',
     'invoice' => [
-        'active_prepaid_question' => '前払いパッケージをすぐにアクティブにしますか?',
-        'active_prepaid_tips' => 'アクティブ後:<br>- 現在のパッケージはすぐに失効!<br>- 有効期限は本日から再開!',
+        'active_prepaid_question' => '前払いプランを有効化しますか?',
+        'active_prepaid_tips' => '<p class="text-left">早期有効化の意味:</p><ol class="text-left"><li>現在のプランは即座に無効となり、残り有効期限は破棄されます</li><li>新しいプランが即座に有効となり、現在時刻から計算開始されます</li></ol>',
         'amount' => '金額',
         'attribute' => '注文',
-        'detail' => '購入履歴',
-    ],
-    'knowledge' => [
-        'basic' => '基本',
-        'title' => 'ナレッジベース',
+        'detail' => '取引履歴',
     ],
     'menu' => [
-        'admin_dashboard' => 'ダッシュボード',
-        'help' => 'ヘルプ',
+        'admin_dashboard' => '管理画面',
+        'help' => 'ヘルプセンター',
         'home' => 'ホーム',
-        'invites' => '招待',
-        'invoices' => '請求書',
-        'nodes' => 'ノード',
-        'profile' => 'プロフィール',
+        'invites' => '招待管理',
+        'invoices' => 'マイオーダー',
+        'nodes' => 'ノード一覧',
+        'profile' => 'アカウント設定',
         'promotion' => 'プロモーション',
-        'shop' => 'ショップ',
-        'tickets' => 'チケット',
+        'shop' => 'サービスショップ',
+        'tickets' => 'マイチケット',
     ],
     'node' => [
-        'info' => '構成情報',
-        'rate' => ':ratio 倍のデータ使用',
+        'info' => '接続情報',
+        'rate' => 'トラフィック倍率::ratio',
         'setting' => 'プロキシ設定',
-        'unstable' => 'ノードが不安定/メンテナンス中',
+        'unstable' => '不安定/メンテナンス中',
     ],
     'oauth' => [
-        'bind' => '連携する',
-        'bind_title' => 'ソーシャルアカウント連携',
+        'bind' => '連携',
+        'bind_title' => 'ソーシャルアカウント連携',
         'not_bind' => '未連携',
-        'rebind' => '再連携する',
+        'rebind' => '再連携',
         'unbind' => '連携解除',
     ],
-    'pay' => '支払',
+    'pay' => '支払',
     'payment' => [
-        'close_tips' => '<code>:minutes</code>以内に支払いを完了してください。そうしないと注文は自動的にキャンセルされます。',
-        'creating' => '支払いを作成中...',
-        'error' => 'チャージ金額が正しくありません',
-        'insufficient_balance' => '残高不足しています。先にチャージしてください。',
+        'close_tips' => '<code>:minutes</code>以内に支払いを完了してください',
+        'creating' => '注文作成中...',
+        'error' => '金額が無効です',
+        'insufficient_balance' => '残高不足',
         'manual' => [
-            'hint' => 'QRコードで支払いをスキャンした後、指示に従って【次へ】を順番にクリックし、最後に【送信】をクリックして支払いを完了してください。',
-            'next' => '次のステップ',
-            'payment_tips' => '正確な金額を支払ってください。オーバーペイの返金はなく、アンダーペイの場合は差額をチャージ',
-            'pre' => '前のステップ',
-            'red_packet' => 'Alipayレッドパケット',
+            'hint' => '支払い後、手順に従って証明書を提出してください',
+            'next' => '次',
+            'payment_tips' => '正確な金額をお支払いください(過払いは返金されません)',
+            'pre' => '前',
+            'red_packet' => 'Alipay紅包',
             'steps' => [
                 'complete' => [
-                    'description' => '支払いの手動確認を待っています',
+                    'description' => '手動審査待ち',
                     'title' => '完了',
                 ],
                 'notice' => [
-                    'description' => '手動支払いの方法',
-                    'title' => '注意',
+                    'description' => '手動支払いガイド',
+                    'title' => '注意事項',
                 ],
                 'payment' => [
-                    'description' => '支払いQRコードを取得',
-                    'title' => '支払',
+                    'description' => 'QRコードをスキャンして支払い',
+                    'title' => '支払',
                 ],
                 'remark' => [
-                    'description' => '手動確認のためにログインアカウントを入力してください',
-                    'title' => 'アカウント備考',
+                    'description' => 'アカウント情報を入力して照合に使用',
+                    'title' => '備考',
                 ],
             ],
         ],
-        'method' => '決済方法',
-        'mobile_tips' => '<strong>モバイルユーザー</strong>: QRコードを長押し -> 画像を保存 -> 支払アプリを開く -> アルバムから読み取る',
+        'method' => '支払い方法',
+        'mobile_tips' => '<strong>モバイルユーザー</strong>:長押しで保存 → 支払いアプリでアルバムからスキャン',
         'order_creation' => [
-            'failed' => '注文作成に失敗しました。他の支払い方法をお試しください!',
-            'info' => 'ご購入/ご入金の処理は【24時間】以内に行いますので、しばらくお待ちください。',
-            'order_limit' => 'この商品は :limit_num 回まで購入可能です。すでに :count 回購入しています。',
-            'order_timeout' => '注文がタイムアウトし、未払いのため自動的にキャンセルされました。',
-            'payment_disabled' => '注文の作成に失敗しました:システムでオンライン支払い機能が有効になっていません。',
-            'pending_order' => '注文の作成に失敗しました:未払いの注文があります。先にそれらの支払いを完了してください。',
-            'plan_required' => '追加パッケージを購入する前に、まずプランを購入してください。',
-            'price_issue' => '注文の作成に失敗しました:注文の総額が異常です',
-            'price_zero' => '注文の作成に失敗しました:注文の総額が0です。オンライン支払いは不要です。',
-            'product_unavailable' => '注文の作成に失敗しました:商品は販売終了しました。',
-            'success' => '注文が正常に作成されました!',
+            'failed' => '注文作成に失敗しました。他の支払い方法をお試しください!',
+            'info' => 'ご購入・チャージは【24時間】以内に反映されます。しばらくお待ちください',
+            'order_limit' => 'この商品は :limit_num 回まで購入可能です。既に :count 回購入済みです!',
+            'order_timeout' => '注文がタイムアウトしました。自動的にキャンセルされました!',
+            'payment_disabled' => '注文作成失敗:システムでオンライン決済機能が無効です!',
+            'pending_order' => '注文作成失敗:未払い注文があります。先に完了またはキャンセルしてください!',
+            'plan_required' => 'トラフィックパッケージ購入前に、まずプランを購入してください!',
+            'price_issue' => '注文作成失敗:注文総額に異常があります!',
+            'price_zero' => '注文作成失敗:注文総額が0のため、オンライン決済は不要です!',
+            'product_unavailable' => '注文作成失敗:商品は販売停止中です!',
+            'success' => '注文作成成功!',
             'unknown_order' => '不明な注文',
             'unknown_payment' => '不明な支払い方法',
         ],
-        'qrcode_tips' => '<strong class="red-600">:software</strong>でQRコードをスキャンしてください',
-        'redirect_stripe' => 'Stripeの支払ページに移動中',
+        'qrcode_tips' => '<strong class="red-600">:software</strong> このQRコードをスキャンしてください',
+        'redirect_stripe' => 'Stripe決済にリダイレクト中',
     ],
     'purchase' => [
-        'completed' => '購入完了しました!',
-        'promotion' => '今すぐサービスを購入!',
-        'required' => 'この機能は無料ユーザーには利用できません。まずは',
-        'to_unlock' => '購入して解',
+        'completed' => '購入完了!',
+        'promotion' => 'サービスを即座に解除',
+        'required' => 'この機能は有料ユーザー限定です。まず',
+        'to_unlock' => '購入して解',
     ],
     'recharge' => 'チャージ',
     'recharge_credit' => '残高チャージ',
     'recharging' => 'チャージ中...',
     'referral' => [
         'link' => '紹介リンク',
-        'logs' => '報酬履歴',
+        'logs' => 'コミッション記録',
         'msg' => [
-            'account' => 'アカウントの有効期限が切れています。まずはサービスを購入してください。',
-            'applied' => '申請はすでに存在しています。処理完了をお待ちください。',
-            'error' => '申請の作成に失敗しました。時間をおいて再度試すか、管理者にお問い合わせください。',
-            'unfulfilled' => '出金には :amount が必要です。頑張ってください!',
-            'wait' => '管理者による承認をお待ちください。',
+            'account' => 'アカウントが期限切れです。まずサービスを購入してください',
+            'applied' => '申請済みです。前回の申請処理完了をお待ちください',
+            'error' => '申請に失敗しました。しばらく待ってから再度お試しいただくか、チケットでお問い合わせください',
+            'unfulfilled' => ':amount 円以上で出金可能です。引き続き頑張ってください!',
+            'wait' => '審査待ち',
         ],
-        'total' => '総報酬 :amount(:total 回)。:money 以上になると出金できます。',
+        'total' => '累計リベート :amount(:total 回)、:money で出金可能',
     ],
     'registered_at' => '登録日',
     'reset_data' => [
-        'action' => 'データリセット',
-        'cost' => 'コスト <code>:amount</code>',
-        'cost_tips' => 'データリセットには :amount が差し引かれます!',
+        'action' => 'トラフィックリセット',
+        'cost' => '<code>:amount</code> が必要',
+        'cost_tips' => 'この操作により :amount の残高が差し引かれます',
     ],
-    'scan_qrcode' => 'クライアントでQRコードスキャン',
+    'scan_qrcode' => 'クライアントでQRコードスキャン',
     'service' => [
-        'country_count' => '<code>:num</code> カ国をカバー',
-        'node_count' => '<code>:num</code> の高品質ノード',
-        'unlimited' => '無制限',
+        'country_count' => '<code>:num</code> カ国・地域をカバー',
+        'node_count' => '<code>:num</code> の高品質ノード',
+        'unlimited' => '速度制限なし',
     ],
     'shop' => [
-        'buy' => '購入',
-        'call4help' => 'お問い合わせはチケットにてお願いします',
+        'buy' => '今すぐ購入',
+        'support' => 'ご質問?カスタマーサポートへ',
         'change_amount' => 'チャージ金額',
-        'change_amount_help' => 'チャージ金額を入力',
+        'change_amount_help' => '金額を入力',
         'conflict' => 'プラン競合',
-        'conflict_tips' => '<p>現在の購入は<code>前払いプラン</code>として設定されます</p><p><ol class="text-left"><li>前払いプランは、現在のプラン終了後に自動で開始されます!</li><li>支払い後に手動でプランを開始することができます!</li></ol>',
-        'description' => '説明',
+        'conflict_tips' => '<p>現在の購入プランは <code>前払いプラン</code> に設定されます</p><ol class="text-left"><li>前払いプランは現在のプラン失効後に自動有効化されます</li><li>支払い後、注文ページで手動有効化も可能です</li></ol>',
+        'description' => '商品説明',
         'hot' => '人気',
         'limited' => '限定',
         'pay_credit' => '残高支払い',
-        'pay_online' => 'オンライン支払い',
+        'pay_online' => 'オンライン決済',
         'price' => '価格',
         'quantity' => '数量',
         'service' => 'サービス',
@@ -215,62 +218,69 @@ return [
     ],
     'subscribe' => [
         'custom' => 'カスタムサブスクリプション',
-        'error' => 'サブスクリプション変更エラー',
-        'exchange_warning' => 'サブスクリプション変更により:\n1. 現在のサブはすぐに無効になる\n2. 接続パスワードが変更される',
+        'error' => 'サブスクリプションアドレスの更新に失敗しました',
+        'exchange_warning' => '<p>サブスクリプションアドレスの変更により</p><ol class="text-left"><li>旧アドレスは即座に無効になります</li><li>ノード接続パスワードが変更されます</li></ol>',
         'info' => [
-            'download' => 'ダウンロードデータ',
-            'title' => 'アカウント概要 [リアルタイムではない]',
-            'total' => 'プランデータ',
-            'upload' => 'アップロードデータ',
+            'download' => '使用済みダウンロード',
+            'title' => 'アカウント概要 [リアルタイムではありません]',
+            'total' => 'プラントラフィック',
+            'upload' => '使用済みアップロード',
         ],
         'link' => 'サブスクリプションリンク',
-        'ss_only' => 'SSのみ',
-        'ssr_only' => 'SSRのみ(SS含む)',
-        'tips' => '警告: このサブスクリプションリンクは個人利用目的でのみ共有可。アカウントの異常なデータ使用を検知し、自動的にBANする可能性があるため、リンクは共有しないでください。',
-        'trojan_only' => 'Trojanのみ',
-        'v2ray_only' => 'V2Rayのみ',
+        'ss_only' => 'SS専用サブスクリプション',
+        'ssr_only' => 'SSRサブスクリプション(SS含む)',
+        'tips' => '警告:このリンクは個人使用限定です。共有するとアカウント停止の原因となります',
+        'trojan_only' => 'Trojan専用サブスクリプション',
+        'v2ray_only' => 'V2Ray専用サブスクリプション',
+        'page' => [
+            'get_link' => 'リンク取得',
+            'connect' => '接続・使用',
+            'error' => [
+                'no_app' => '利用可能なアプリケーションがありません',
+            ],
+        ],
     ],
     'telegram' => [
-        'bind_exists' => 'このアカウントはすでにTelegramアカウントにリンクされています。',
-        'bind_missing' => 'ユーザー情報が見つかりません。先にアカウントをリンクしてください。',
+        'bind_exists' => 'Telegramアカウントは既に連携済みです',
+        'bind_missing' => '連携情報が見つかりません',
         'command' => [
-            'bind' => 'あなたの:web_nameアカウントをリンクしてください',
-            'intro' => '次のコマンドを使用できます',
-            'traffic' => 'データ使用量を確認する',
-            'unbind' => 'リンク解除',
-            'web_url' => '最新の:web_name URLを取得する',
+            'bind' => ':web_name アカウントと連携',
+            'intro' => '利用可能なコマンド',
+            'traffic' => 'トラフィック照会',
+            'unbind' => 'アカウント連携解除',
+            'web_url' => ':web_name 最新URLを取得',
         ],
-        'get_url' => ':web_nameの最新URLは',
-        'params_missing' => 'パラメーターに誤りがあります。メールアドレスを含めて送信してください',
+        'get_url' => ':web_name 最新URL',
+        'params_missing' => 'パラメータエラー。メールアドレスを含めて送信してください',
         'ticket_missing' => 'チケットが存在しません',
-        'ticket_reply' => 'チケット #`:id` への返信が成功しました',
-        'traffic_query' => 'データ使用量の確認',
+        'ticket_reply' => 'チケット #:id に返信がありました',
+        'traffic_query' => 'トラフィック照会',
         'user_missing' => 'ユーザーが存在しません',
     ],
     'ticket' => [
         'attribute' => 'チケット',
-        'close_msg' => 'チケット: ID :id がユーザーによって閉じられました',
-        'close_tips' => 'このチケットを閉じますか?',
-        'content_placeholder' => '問題を詳細に説明して助けを得ることができるようにしてください',
-        'error' => '不明なエラーです。スタッフに通知してください。',
+        'close_msg' => 'チケット #:id がクローズされました',
+        'close_tips' => 'このチケットをクローズしますか?',
+        'content_placeholder' => '問題や要望をできるだけ詳しく記述してください。迅速にサポートいたします',
+        'error' => 'システムエラー。カスタマーサポートにお問い合わせください',
         'new' => '新規チケット',
         'online_hour' => 'オンライン時間',
         'reply' => '返信',
-        'reply_confirm' => 'チケットへの返信を確定しますか?',
-        'reply_placeholder' => 'ここにコメントを入力...',
-        'service_hours' => '営業時間',
-        'service_tips' => 'ご連絡は<code>1つの</code>方法のみでお願いします。<br>重複したリクエストは対応を遅らせます。',
-        'submit_tips' => 'チケットを提出しますか?',
-        'title_placeholder' => '簡単に問題を説明してください',
+        'reply_confirm' => '返信を確認しますか?',
+        'reply_placeholder' => '何かお書きください',
+        'service_hours' => 'サポート時間',
+        'service_tips' => '<code>一つ</code> の連絡方法でカスタマーサポートにお問い合わせください。重複提出は処理の遅延を招く可能性があります。',
+        'submit_tips' => 'チケットを提出しますか',
+        'title_placeholder' => '遭遇した問題を簡潔に説明してください',
     ],
     'traffic_logs' => [
-        'daily' => '今月のデータ使用',
-        'hourly' => '今日のデータ使用',
-        'tips' => ':トラフィックのデータは遅延を提供します。',
+        'daily' => '今月のトラフィック',
+        'hourly' => '今日のトラフィック',
+        'tips' => 'ヒント:データ更新には遅延があります',
     ],
-    'tutorials' => 'チュートリアル',
+    'tutorials' => '使用ガイド',
     'withdraw' => '出金',
     'withdraw_at' => '出金日',
-    'withdraw_commission' => '報酬出金',
+    'withdraw_commission' => 'コミッション出金',
     'withdraw_logs' => '出金記録',
 ];

Some files were not shown because too many files changed in this diff