-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
3005 lines (2779 loc) · 121 KB
/
main.py
File metadata and controls
3005 lines (2779 loc) · 121 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import datetime
import calendar
import base64
import math
import re
import json
import os
import threading
import time
import webbrowser
import tempfile
import zipfile
import shutil
import subprocess
import plistlib
import hashlib
from typing import Any, Dict, Optional, Tuple
import requests
import rumps
try:
from AppKit import NSAlert
except Exception:
NSAlert = None # 运行在无 GUI/无 pyobjc 环境时兜底
# ---------------------------
# 语言与本地化
# ---------------------------
# 支持的语言代码
LANG_ZH_CN = "zh_CN"
LANG_EN = "en"
LANG_ZH_TW = "zh_TW"
LANG_JA = "ja"
LANG_KO = "ko"
LANG_RU = "ru"
_current_language = LANG_ZH_CN
class LocalizedError(Exception):
def __init__(self, key: str, **kwargs):
self.key = key
self.kwargs = kwargs
super().__init__(key)
def message(self) -> str:
try:
return _t(self.key, **self.kwargs)
except Exception:
return self.key
def __str__(self) -> str:
return self.message()
def set_current_language(lang: str) -> None:
global _current_language
if lang in {LANG_ZH_CN, LANG_EN, LANG_ZH_TW, LANG_JA, LANG_KO, LANG_RU}:
_current_language = lang
else:
_current_language = LANG_ZH_CN
# 文本字典
I18N = {
# 占位符/片段
"version_prefix": {
LANG_ZH_CN: "版本:",
LANG_EN: "Version: ",
LANG_ZH_TW: "版本:",
LANG_JA: "バージョン:",
LANG_KO: "버전: ",
LANG_RU: "Версия: ",
},
"title_req_label": {
LANG_ZH_CN: "调用",
LANG_EN: "Req",
LANG_ZH_TW: "調用",
LANG_JA: "リクエスト",
LANG_KO: "요청",
LANG_RU: "Запрос",
},
# 顶部信息区(占位)
"status_uninitialized": {
LANG_ZH_CN: "状态:未初始化",
LANG_EN: "Status: Not initialized",
LANG_ZH_TW: "狀態:未初始化",
LANG_JA: "状態:未初期化",
LANG_KO: "상태: 초기화되지 않음",
LANG_RU: "Статус: не инициализировано",
},
"status_ok": {
LANG_ZH_CN: "状态:正常",
LANG_EN: "Status: OK",
LANG_ZH_TW: "狀態:正常",
LANG_JA: "状態:正常",
LANG_KO: "상태: 정상",
LANG_RU: "Статус: ОК",
},
"daily_placeholder": {
LANG_ZH_CN: "每日:-/- (剩余 -)",
LANG_EN: "Daily: -/- (left -)",
LANG_ZH_TW: "每日:-/- (剩餘 -)",
LANG_JA: "日次:-/- (残り -)",
LANG_KO: "일일: -/- (잔여 -)",
LANG_RU: "День: -/- (осталось -)",
},
"requests_placeholder": {
LANG_ZH_CN: "调用次数:-",
LANG_EN: "Requests: -",
LANG_ZH_TW: "調用次數:-",
LANG_JA: "リクエスト数:-",
LANG_KO: "요청 수: -",
LANG_RU: "Запросов: -",
},
"usage_span_placeholder": {
LANG_ZH_CN: "7日日均:-",
LANG_EN: "Last 7 days: -",
LANG_ZH_TW: "7日日均:-",
LANG_JA: "直近7日:-",
LANG_KO: "최근 7일: -",
LANG_RU: "За 7 дней: -",
},
"monthly_placeholder": {
LANG_ZH_CN: "本周期:-/- (剩余 -)",
LANG_EN: "Cycle: -/- (left -)",
LANG_ZH_TW: "本週期:-/- (剩餘 -)",
LANG_JA: "サイクル:-/- (残り -)",
LANG_KO: "주기: -/- (잔여 -)",
LANG_RU: "Цикл: -/- (осталось -)",
},
"cycle_placeholder": {
LANG_ZH_CN: "周期:-",
LANG_EN: "Cycle: -",
LANG_ZH_TW: "週期:-",
LANG_JA: "サイクル:-",
LANG_KO: "주기: -",
LANG_RU: "Цикл: -",
},
"renew_placeholder": {
LANG_ZH_CN: "续费提醒:-",
LANG_EN: "Renewal: -",
LANG_ZH_TW: "續費提醒:-",
LANG_JA: "更新通知:-",
LANG_KO: "갱신 알림: -",
LANG_RU: "Продление: -",
},
"balance_placeholder": {
LANG_ZH_CN: "余额:-",
LANG_EN: "Balance: -",
LANG_ZH_TW: "餘額:-",
LANG_JA: "残高:-",
LANG_KO: "잔액: -",
LANG_RU: "Баланс: -",
},
"last_update_placeholder": {
LANG_ZH_CN: "上次更新:-",
LANG_EN: "Last Update: -",
LANG_ZH_TW: "上次更新:-",
LANG_JA: "最終更新:-",
LANG_KO: "마지막 업데이트: -",
LANG_RU: "Последнее обновление: -",
},
"token_placeholder": {
LANG_ZH_CN: "Token:-",
LANG_EN: "Token: -",
LANG_ZH_TW: "Token:-",
LANG_JA: "トークン:-",
LANG_KO: "토큰: -",
LANG_RU: "Токен: -",
},
# 菜单分组/项
"menu_refresh": {
LANG_ZH_CN: "刷新",
LANG_EN: "Refresh",
LANG_ZH_TW: "刷新",
LANG_JA: "更新",
LANG_KO: "새로고침",
LANG_RU: "Обновить",
},
"menu_account": {
LANG_ZH_CN: "账号类型",
LANG_EN: "Account",
LANG_ZH_TW: "帳號類型",
LANG_JA: "アカウント種別",
LANG_KO: "계정 유형",
LANG_RU: "Аккаунт",
},
"menu_title_format": {
LANG_ZH_CN: "标题格式",
LANG_EN: "Title Format",
LANG_ZH_TW: "標題格式",
LANG_JA: "タイトル形式",
LANG_KO: "제목 형식",
LANG_RU: "Формат заголовка",
},
"menu_set_token": {
LANG_ZH_CN: "设置 Token...",
LANG_EN: "Set Token...",
LANG_ZH_TW: "設定 Token...",
LANG_JA: "トークンを設定...",
LANG_KO: "토큰 설정...",
LANG_RU: "Указать токен...",
},
"menu_toggle_hidden": {
LANG_ZH_CN: "隐藏/展示",
LANG_EN: "Hide/Show",
LANG_ZH_TW: "隱藏/顯示",
LANG_JA: "非表示/表示",
LANG_KO: "숨김/표시",
LANG_RU: "Скрыть/Показать",
},
"menu_open_dashboard": {
LANG_ZH_CN: "打开控制台",
LANG_EN: "Open Dashboard",
LANG_ZH_TW: "打開控制台",
LANG_JA: "ダッシュボードを開く",
LANG_KO: "대시보드 열기",
LANG_RU: "Открыть панель",
},
"menu_latency_monitor": {
LANG_ZH_CN: "延迟监控",
LANG_EN: "Latency Monitor",
LANG_ZH_TW: "延遲監控",
LANG_JA: "レイテンシ監視",
LANG_KO: "지연 모니터",
LANG_RU: "Мониторинг задержки",
},
"menu_check_update": {
LANG_ZH_CN: "检查更新",
LANG_EN: "Check Updates",
LANG_ZH_TW: "檢查更新",
LANG_JA: "更新を確認",
LANG_KO: "업데이트 확인",
LANG_RU: "Проверить обновления",
},
# 进度圆环菜单
"menu_ring": {
LANG_ZH_CN: "进度圆环",
LANG_EN: "Ring",
LANG_ZH_TW: "進度環",
LANG_JA: "進捗リング",
LANG_KO: "링",
LANG_RU: "Кольцо",
},
"ring_enable": {
LANG_ZH_CN: "在图标显示进度圆环",
LANG_EN: "Show ring in icon",
LANG_ZH_TW: "在圖示顯示進度環",
LANG_JA: "アイコンにリングを表示",
LANG_KO: "아이콘에 링 표시",
LANG_RU: "Показывать кольцо в иконке",
},
"ring_source": {
LANG_ZH_CN: "圆环来源",
LANG_EN: "Ring Source",
LANG_ZH_TW: "環來源",
LANG_JA: "リングのソース",
LANG_KO: "링 소스",
LANG_RU: "Источник кольца",
},
"ring_colored": {
LANG_ZH_CN: "使用彩色圆环",
LANG_EN: "Use colored ring",
LANG_ZH_TW: "使用彩色環",
LANG_JA: "カラー リングを使用",
LANG_KO: "컬러 링 사용",
LANG_RU: "Цветное кольцо",
},
"ring_reverse": {
LANG_ZH_CN: "反转模式(高亮未使用)",
LANG_EN: "Reverse mode (highlight unused)",
LANG_ZH_TW: "反轉模式(高亮未使用)",
LANG_JA: "反転表示(未使用を強調)",
LANG_KO: "반전 모드(미사용 강조)",
LANG_RU: "Режим инверсии (выделять остаток)",
},
"ring_text_enable": {
LANG_ZH_CN: "在圆环内显示百分比",
LANG_EN: "Show percentage in ring",
LANG_ZH_TW: "在圓環內顯示百分比",
LANG_JA: "リング内に割合を表示",
LANG_KO: "링 내부에 백분율 표시",
LANG_RU: "Показывать % в кольце",
},
"ring_text_mode": {
LANG_ZH_CN: "圆环文字",
LANG_EN: "Ring Text",
LANG_ZH_TW: "圓環文字",
LANG_JA: "リング文字",
LANG_KO: "링 텍스트",
LANG_RU: "Текст кольца",
},
"ring_text_mode_percent": {
LANG_ZH_CN: "百分比",
LANG_EN: "Percent",
LANG_ZH_TW: "百分比",
LANG_JA: "パーセント",
LANG_KO: "퍼센트",
LANG_RU: "Проценты",
},
"ring_text_mode_calls": {
LANG_ZH_CN: "调用次数",
LANG_EN: "Calls",
LANG_ZH_TW: "調用次數",
LANG_JA: "呼び出し回数",
LANG_KO: "호출 수",
LANG_RU: "Вызовы",
},
"ring_text_mode_spent": {
LANG_ZH_CN: "使用金额",
LANG_EN: "Spent",
LANG_ZH_TW: "使用金額",
LANG_JA: "使用金額",
LANG_KO: "사용 금액",
LANG_RU: "Потрачено",
},
"ring_text_show_percent": {
LANG_ZH_CN: "文本显示百分号",
LANG_EN: "Show % sign",
LANG_ZH_TW: "文字顯示百分號",
LANG_JA: "% 記号を表示",
LANG_KO: "% 기호 표시",
LANG_RU: "Показывать знак %",
},
"ring_text_show_label": {
LANG_ZH_CN: "文本显示来源标签 (D/M)",
LANG_EN: "Show source label (D/M)",
LANG_ZH_TW: "文字顯示來源標籤 (D/M)",
LANG_JA: "ソースラベルを表示 (D/M)",
LANG_KO: "원본 라벨 표시 (D/M)",
LANG_RU: "Показывать метку (D/M)",
},
"ring_color_mode": {
LANG_ZH_CN: "圆环颜色",
LANG_EN: "Ring Color",
LANG_ZH_TW: "圓環顏色",
LANG_JA: "リング色",
LANG_KO: "링 색상",
LANG_RU: "Цвет кольца",
},
"ring_color_colorful": {
LANG_ZH_CN: "彩色",
LANG_EN: "Colorful",
LANG_ZH_TW: "彩色",
LANG_JA: "カラフル",
LANG_KO: "컬러풀",
LANG_RU: "Разноцветный",
},
"ring_color_green": {
LANG_ZH_CN: "绿色",
LANG_EN: "Green",
LANG_ZH_TW: "綠色",
LANG_JA: "緑",
LANG_KO: "초록",
LANG_RU: "Зелёный",
},
"ring_color_blue": {
LANG_ZH_CN: "蓝色",
LANG_EN: "Blue",
LANG_ZH_TW: "藍色",
LANG_JA: "青",
LANG_KO: "파랑",
LANG_RU: "Синий",
},
"ring_color_gradient": {
LANG_ZH_CN: "渐变彩色",
LANG_EN: "Gradient",
LANG_ZH_TW: "漸變彩色",
LANG_JA: "グラデーション",
LANG_KO: "그라데이션",
LANG_RU: "Градиент",
},
"ring_source_daily": {
LANG_ZH_CN: "每日进度",
LANG_EN: "Daily",
LANG_ZH_TW: "每日進度",
LANG_JA: "日次",
LANG_KO: "일일",
LANG_RU: "День",
},
"ring_source_monthly": {
LANG_ZH_CN: "周期进度",
LANG_EN: "Cycle",
LANG_ZH_TW: "週期進度",
LANG_JA: "サイクル",
LANG_KO: "주기",
LANG_RU: "Цикл",
},
"menu_affiliates": {
LANG_ZH_CN: "推广",
LANG_EN: "Affiliates",
LANG_ZH_TW: "推廣",
LANG_JA: "アフィリエイト",
LANG_KO: "추천",
LANG_RU: "Партнёры",
},
"menu_quit": {
LANG_ZH_CN: "退出",
LANG_EN: "Quit",
LANG_ZH_TW: "退出",
LANG_JA: "終了",
LANG_KO: "종료",
LANG_RU: "Выход",
},
"menu_language": {
LANG_ZH_CN: "语言",
LANG_EN: "Language",
LANG_ZH_TW: "語言",
LANG_JA: "言語",
LANG_KO: "언어",
LANG_RU: "Язык",
},
"account_shared": {
LANG_ZH_CN: "共享(公交车)",
LANG_EN: "Shared (Bus)",
LANG_ZH_TW: "共享(公車)",
LANG_JA: "共有(バス)",
LANG_KO: "공유(버스)",
LANG_RU: "Общий (Bus)",
},
"account_private": {
LANG_ZH_CN: "滴滴车(私有)",
LANG_EN: "Private",
LANG_ZH_TW: "滴滴車(私有)",
LANG_JA: "DiDi(プライベート)",
LANG_KO: "DiDi(개인)",
LANG_RU: "Частный",
},
"account_codex": {
LANG_ZH_CN: "Codex 公交车",
LANG_EN: "Codex Shared",
LANG_ZH_TW: "Codex 公車",
LANG_JA: "Codex 共有",
LANG_KO: "Codex 공유",
LANG_RU: "Codex общий",
},
"titlefmt_percent": {
LANG_ZH_CN: "百分比",
LANG_EN: "Percent",
LANG_ZH_TW: "百分比",
LANG_JA: "パーセント",
LANG_KO: "퍼센트",
LANG_RU: "Проценты",
},
"titlefmt_custom": {
LANG_ZH_CN: "自定义...",
LANG_EN: "Custom...",
LANG_ZH_TW: "自訂...",
LANG_JA: "カスタム...",
LANG_KO: "사용자 지정...",
LANG_RU: "Пользовательский...",
},
"titlefmt_show_requests": {
LANG_ZH_CN: "显示调用次数",
LANG_EN: "Show Requests",
LANG_ZH_TW: "顯示調用次數",
LANG_JA: "リクエスト数を表示",
LANG_KO: "요청 수 표시",
LANG_RU: "Показывать запросы",
},
# 动态信息模板
"status_no_data": {
LANG_ZH_CN: "状态:无数据",
LANG_EN: "Status: No data",
LANG_ZH_TW: "狀態:無資料",
LANG_JA: "状態:データなし",
LANG_KO: "상태: 데이터 없음",
LANG_RU: "Статус: нет данных",
},
"title_no_data": {
LANG_ZH_CN: "无数据",
LANG_EN: "No data",
LANG_ZH_TW: "無資料",
LANG_JA: "データなし",
LANG_KO: "데이터 없음",
LANG_RU: "Нет данных",
},
"status_error_prefix": {
LANG_ZH_CN: "状态:错误 - {err}",
LANG_EN: "Status: Error - {err}",
LANG_ZH_TW: "狀態:錯誤 - {err}",
LANG_JA: "状態:エラー - {err}",
LANG_KO: "상태: 오류 - {err}",
LANG_RU: "Статус: ошибка - {err}",
},
"title_error": {
LANG_ZH_CN: "错误",
LANG_EN: "Error",
LANG_ZH_TW: "錯誤",
LANG_JA: "エラー",
LANG_KO: "오류",
LANG_RU: "Ошибка",
},
"last_update_prefix": {
LANG_ZH_CN: "上次更新:{time}",
LANG_EN: "Last Update: {time}",
LANG_ZH_TW: "上次更新:{time}",
LANG_JA: "最終更新:{time}",
LANG_KO: "마지막 업데이트: {time}",
LANG_RU: "Последнее обновление: {time}",
},
"requests_prefix": {
LANG_ZH_CN: "调用次数:{val}",
LANG_EN: "Requests: {val}",
LANG_ZH_TW: "調用次數:{val}",
LANG_JA: "リクエスト数:{val}",
LANG_KO: "요청 수: {val}",
LANG_RU: "Запросов: {val}",
},
"usage_span_prefix": {
LANG_ZH_CN: "7日日均:{val}",
LANG_EN: "Last 7 days: {val}",
LANG_ZH_TW: "7日日均:{val}",
LANG_JA: "直近7日:{val}",
LANG_KO: "최근 7일: {val}",
LANG_RU: "За 7 дней: {val}",
},
"usage_span_desc": {
LANG_ZH_CN: "总 {total},日均 {avg}",
LANG_EN: "Total {total}, Avg {avg}",
LANG_ZH_TW: "總 {total},日均 {avg}",
LANG_JA: "合計 {total}、日平均 {avg}",
LANG_KO: "총 {total}, 일평균 {avg}",
LANG_RU: "Всего {total}, в день {avg}",
},
"daily_full": {
LANG_ZH_CN: "每日:{spent}/{limit} (剩余 {remain})",
LANG_EN: "Daily: {spent}/{limit} (left {remain})",
LANG_ZH_TW: "每日:{spent}/{limit} (剩餘 {remain})",
LANG_JA: "日次:{spent}/{limit} (残り {remain})",
LANG_KO: "일일: {spent}/{limit} (잔여 {remain})",
LANG_RU: "День: {spent}/{limit} (ост. {remain})",
},
"daily_no_limit": {
LANG_ZH_CN: "每日:{spent}/- (剩余 -)",
LANG_EN: "Daily: {spent}/- (left -)",
LANG_ZH_TW: "每日:{spent}/- (剩餘 -)",
LANG_JA: "日次:{spent}/- (残り -)",
LANG_KO: "일일: {spent}/- (잔여 -)",
LANG_RU: "День: {spent}/- (ост. -)",
},
"monthly_full": {
LANG_ZH_CN: "本周期:{spent}/{limit} (剩余 {remain})",
LANG_EN: "Cycle: {spent}/{limit} (left {remain})",
LANG_ZH_TW: "本週期:{spent}/{limit} (剩餘 {remain})",
LANG_JA: "サイクル:{spent}/{limit} (残り {remain})",
LANG_KO: "주기: {spent}/{limit} (잔여 {remain})",
LANG_RU: "Цикл: {spent}/{limit} (ост. {remain})",
},
"monthly_no_limit": {
LANG_ZH_CN: "本周期:{spent}/- (剩余 -)",
LANG_EN: "Cycle: {spent}/- (left -)",
LANG_ZH_TW: "本週期:{spent}/- (剩餘 -)",
LANG_JA: "サイクル:{spent}/- (残り -)",
LANG_KO: "주기: {spent}/- (잔여 -)",
LANG_RU: "Цикл: {spent}/- (ост. -)",
},
"cycle_expired": {
LANG_ZH_CN: "周期:{start}-{end}(已到期)",
LANG_EN: "Cycle: {start}-{end} (expired)",
LANG_ZH_TW: "週期:{start}-{end}(已到期)",
LANG_JA: "サイクル:{start}-{end}(期限切れ)",
LANG_KO: "주기: {start}-{end} (만료)",
LANG_RU: "Цикл: {start}-{end} (истёк)",
},
"cycle_remaining": {
LANG_ZH_CN: "周期:{start}-{end}(剩余{days}天)",
LANG_EN: "Cycle: {start}-{end} (left {days} days)",
LANG_ZH_TW: "週期:{start}-{end}(剩餘{days}天)",
LANG_JA: "サイクル:{start}-{end}(残り{days}日)",
LANG_KO: "주기: {start}-{end} (잔여 {days}일)",
LANG_RU: "Цикл: {start}-{end} (ост. {days} дн.)",
},
"renew_expired": {
LANG_ZH_CN: "⚠️ 已到期,请尽快续费",
LANG_EN: "⚠️ Expired, please renew",
LANG_ZH_TW: "⚠️ 已到期,請儘快續費",
LANG_JA: "⚠️ 期限切れ、早めの更新を",
LANG_KO: "⚠️ 만료됨, 갱신 필요",
LANG_RU: "⚠️ Срок истёк, продлите",
},
"renew_soon": {
LANG_ZH_CN: "⚠️ 即将到期(剩余{days}天),建议提前续费",
LANG_EN: "⚠️ Expiring soon (left {days} days), renew early",
LANG_ZH_TW: "⚠️ 即將到期(剩餘{days}天),建議提前續費",
LANG_JA: "⚠️ まもなく期限(残り{days}日)、早めの更新を",
LANG_KO: "⚠️ 곧 만료(잔여 {days}일), 미리 갱신 권장",
LANG_RU: "⚠️ Скоро истекает (ост. {days} дн.), продлите заранее",
},
"renew_prefix": {
LANG_ZH_CN: "续费提醒:{text}",
LANG_EN: "Renewal: {text}",
LANG_ZH_TW: "續費提醒:{text}",
LANG_JA: "更新通知:{text}",
LANG_KO: "갱신 알림: {text}",
LANG_RU: "Продление: {text}",
},
"balance_prefix": {
LANG_ZH_CN: "余额:{val}",
LANG_EN: "Balance: {val}",
LANG_ZH_TW: "餘額:{val}",
LANG_JA: "残高:{val}",
LANG_KO: "잔액: {val}",
LANG_RU: "Баланс: {val}",
},
# Token 展示与提醒
"token_expired_label": {
LANG_ZH_CN: "Token:已过期({date})",
LANG_EN: "Token: expired ({date})",
LANG_ZH_TW: "Token:已過期({date})",
LANG_JA: "トークン:期限切れ({date})",
LANG_KO: "토큰: 만료됨 ({date})",
LANG_RU: "Токен: истёк ({date})",
},
"token_valid_until": {
LANG_ZH_CN: "Token:{date}({remain})",
LANG_EN: "Token: {date} ({remain})",
LANG_ZH_TW: "Token:{date}({remain})",
LANG_JA: "トークン:{date}({remain})",
LANG_KO: "토큰: {date} ({remain})",
LANG_RU: "Токен: {date} ({remain})",
},
"notify_token_expired_subtitle": {
LANG_ZH_CN: "Token 已过期",
LANG_EN: "Token expired",
LANG_ZH_TW: "Token 已過期",
LANG_JA: "トークンの有効期限切れ",
LANG_KO: "토큰 만료",
LANG_RU: "Токен истёк",
},
"notify_token_expired_message": {
LANG_ZH_CN: "请在“设置 Token...”中更换 JWT",
LANG_EN: "Please replace JWT in 'Set Token...'",
LANG_ZH_TW: "請在「設定 Token...」中更換 JWT",
LANG_JA: "『トークンを設定...』で JWT を更新してください",
LANG_KO: "'토큰 설정...'에서 JWT를 교체하세요",
LANG_RU: "Замените JWT в 'Указать токен...'",
},
# 自定义标题
"custom_title_window": {
LANG_ZH_CN: "自定义标题格式",
LANG_EN: "Custom Title Format",
LANG_ZH_TW: "自訂標題格式",
LANG_JA: "カスタムタイトル形式",
LANG_KO: "사용자 지정 제목 형식",
LANG_RU: "Пользовательский формат заголовка",
},
"custom_title_help": {
LANG_ZH_CN: "自定义标题模板,支持占位符:\n{d_pct} {m_pct} {d_spent} {d_limit} {m_spent} {m_limit} {bal} {d_req}\n例如: D {d_pct}% | M {m_pct}% 或 $ {bal}",
LANG_EN: "Custom title template with placeholders:\n{d_pct} {m_pct} {d_spent} {d_limit} {m_spent} {m_limit} {bal} {d_req}\nExample: D {d_pct}% | M {m_pct}% or $ {bal}",
LANG_ZH_TW: "自訂標題模板,支援占位符:\n{d_pct} {m_pct} {d_spent} {d_limit} {m_spent} {m_limit} {bal} {d_req}\n例如: D {d_pct}% | M {m_pct}% 或 $ {bal}",
LANG_JA: "タイトルテンプレート(プレースホルダー):\n{d_pct} {m_pct} {d_spent} {d_limit} {m_spent} {m_limit} {bal} {d_req}\n例: D {d_pct}% | M {m_pct}% または $ {bal}",
LANG_KO: "제목 템플릿, 자리표시자:\n{d_pct} {m_pct} {d_spent} {d_limit} {m_spent} {m_limit} {bal} {d_req}\n예: D {d_pct}% | M {m_pct}% 또는 $ {bal}",
LANG_RU: "Шаблон заголовка с плейсхолдерами:\n{d_pct} {m_pct} {d_spent} {d_limit} {m_spent} {m_limit} {bal} {d_req}\nПример: D {d_pct}% | M {m_pct}% или $ {bal}",
},
"btn_save": {
LANG_ZH_CN: "保存",
LANG_EN: "Save",
LANG_ZH_TW: "保存",
LANG_JA: "保存",
LANG_KO: "저장",
LANG_RU: "Сохранить",
},
"btn_cancel": {
LANG_ZH_CN: "取消",
LANG_EN: "Cancel",
LANG_ZH_TW: "取消",
LANG_JA: "キャンセル",
LANG_KO: "취소",
LANG_RU: "Отмена",
},
# Token 设置窗口
"set_token_title": {
LANG_ZH_CN: "设置 Token (JWT 或 API Key)",
LANG_EN: "Set Token (JWT or API Key)",
LANG_ZH_TW: "設定 Token (JWT 或 API Key)",
LANG_JA: "トークン設定 (JWT または API Key)",
LANG_KO: "토큰 설정 (JWT 또는 API Key)",
LANG_RU: "Указать токен (JWT или API Key)",
},
"set_token_message": {
LANG_ZH_CN: "粘贴从 PackyCode 获取的 JWT 或 API Key (将以 Bearer 形式发送)",
LANG_EN: "Paste JWT or API Key from PackyCode (sent as Bearer)",
LANG_ZH_TW: "貼上從 PackyCode 取得的 JWT 或 API Key(以 Bearer 方式發送)",
LANG_JA: "PackyCode から取得した JWT または API Key を貼り付け(Bearer で送信)",
LANG_KO: "PackyCode에서 받은 JWT 또는 API Key를 붙여넣으세요 (Bearer로 전송)",
LANG_RU: "Вставьте JWT или API Key из PackyCode (отправляется как Bearer)",
},
# 更新检查
"update_found_title": {
LANG_ZH_CN: "发现新版本",
LANG_EN: "New Version Found",
LANG_ZH_TW: "發現新版本",
LANG_JA: "新しいバージョンを検出",
LANG_KO: "새 버전 발견",
LANG_RU: "Найдена новая версия",
},
"update_found_message": {
LANG_ZH_CN: "发现新版本:{tag}\n当前版本:{cur}\n是否前往发布页下载?",
LANG_EN: "New version: {tag}\nCurrent: {cur}\nOpen releases page?",
LANG_ZH_TW: "發現新版本:{tag}\n目前版本:{cur}\n是否前往發布頁下載?",
LANG_JA: "新バージョン:{tag}\n現在:{cur}\nリリースページを開きますか?",
LANG_KO: "새 버전: {tag}\n현재: {cur}\n릴리스 페이지를 여시겠습니까?",
LANG_RU: "Новая версия: {tag}\nТекущая: {cur}\nОткрыть страницу релизов?",
},
"btn_go": {
LANG_ZH_CN: "前往",
LANG_EN: "Open",
LANG_ZH_TW: "前往",
LANG_JA: "開く",
LANG_KO: "열기",
LANG_RU: "Открыть",
},
"btn_ok": {
LANG_ZH_CN: "确定",
LANG_EN: "OK",
LANG_ZH_TW: "確定",
LANG_JA: "OK",
LANG_KO: "확인",
LANG_RU: "ОК",
},
# 不再提供在线更新按钮
"update_changelog_prefix": {
LANG_ZH_CN: "更新内容:\n{notes}",
LANG_EN: "Release Notes:\n{notes}",
LANG_ZH_TW: "更新內容:\n{notes}",
LANG_JA: "更新内容:\n{notes}",
LANG_KO: "업데이트 내용:\n{notes}",
LANG_RU: "Изменения:\n{notes}",
},
"update_check_title": {
LANG_ZH_CN: "检查更新",
LANG_EN: "Check Updates",
LANG_ZH_TW: "檢查更新",
LANG_JA: "更新を確認",
LANG_KO: "업데이트 확인",
LANG_RU: "Проверка обновлений",
},
"update_latest_message": {
LANG_ZH_CN: "当前已是最新版本。",
LANG_EN: "You are up to date.",
LANG_ZH_TW: "目前已是最新版本。",
LANG_JA: "最新バージョンです。",
LANG_KO: "이미 최신 버전입니다.",
LANG_RU: "У вас последняя версия.",
},
"update_check_failed": {
LANG_ZH_CN: "检查更新失败",
LANG_EN: "Update Check Failed",
LANG_ZH_TW: "檢查更新失敗",
LANG_JA: "更新確認に失敗",
LANG_KO: "업데이트 확인 실패",
LANG_RU: "Сбой проверки обновления",
},
# 在线更新
"online_update": {
LANG_ZH_CN: "在线更新",
LANG_EN: "Online Update",
LANG_ZH_TW: "線上更新",
LANG_JA: "オンライン更新",
LANG_KO: "온라인 업데이트",
LANG_RU: "Онлайн-обновление",
},
"online_update_not_found": {
LANG_ZH_CN: "未找到可下载的发行包,请前往发布页手动下载。",
LANG_EN: "No downloadable asset found. Please download from releases page.",
LANG_ZH_TW: "未找到可下載的發行包,請前往發布頁手動下載。",
LANG_JA: "ダウンロード可能なアセットがありません。リリースページから入手してください。",
LANG_KO: "다운로드 가능한 자산을 찾지 못했습니다. 릴리스 페이지에서 내려받으세요.",
LANG_RU: "Не найден загружаемый пакет. Скачайте на странице релизов.",
},
"online_update_latest_confirm": {
LANG_ZH_CN: "当前已是最新版本({cur})。是否仍然重新安装?",
LANG_EN: "Already latest ({cur}). Reinstall anyway?",
LANG_ZH_TW: "目前已是最新版本({cur})。是否仍要重新安裝?",
LANG_JA: "既に最新({cur})。再インストールしますか?",
LANG_KO: "이미 최신({cur}). 그래도 재설치할까요?",
LANG_RU: "Уже последняя ({cur}). Переустановить?",
},
"btn_continue": {
LANG_ZH_CN: "继续",
LANG_EN: "Continue",
LANG_ZH_TW: "繼續",
LANG_JA: "続行",
LANG_KO: "계속",
LANG_RU: "Продолжить",
},
"online_update_checksum_failed": {
LANG_ZH_CN: "校验失败或无法获取校验文件:{err}",
LANG_EN: "Checksum failed or missing checksum file: {err}",
LANG_ZH_TW: "校驗失敗或無法取得校驗檔:{err}",
LANG_JA: "検証失敗または検証ファイル取得不可:{err}",
LANG_KO: "검증 실패 또는 체크섬 파일 없음: {err}",
LANG_RU: "Сбой проверки или нет файла контрольной суммы: {err}",
},
"online_update_zip_missing": {
LANG_ZH_CN: ".zip 内未找到 .app 文件。",
LANG_EN: "No .app found inside the zip.",
LANG_ZH_TW: ".zip 內未找到 .app 檔案。",
LANG_JA: "ZIP 内に .app が見つかりません。",
LANG_KO: "ZIP 안에 .app 파일이 없습니다.",
LANG_RU: "В ZIP не найдено .app.",
},
"online_update_download_done": {
LANG_ZH_CN: "下载完成",
LANG_EN: "Download Completed",
LANG_ZH_TW: "下載完成",
LANG_JA: "ダウンロード完了",
LANG_KO: "다운로드 완료",
LANG_RU: "Загрузка завершена",
},
"online_update_manual_replace": {
LANG_ZH_CN: "已在 Finder 打开,请手动替换应用。",
LANG_EN: "Opened in Finder. Replace the app manually.",
LANG_ZH_TW: "已在 Finder 開啟,請手動替換應用程式。",
LANG_JA: "Finder で開きました。手動で置き換えてください。",
LANG_KO: "Finder에서 열렸습니다. 앱을 수동으로 교체하세요.",
LANG_RU: "Открыто в Finder. Замените приложение вручную.",
},
"online_update_bundle_mismatch": {
LANG_ZH_CN: "包标识不一致:当前 {cur},新包 {new}。已终止。",
LANG_EN: "Bundle ID mismatch: current {cur}, new {new}. Aborted.",
LANG_ZH_TW: "套件識別不一致:目前 {cur},新包 {new}。已終止。",
LANG_JA: "バンドルID不一致:現在 {cur}、新 {new}。中止。",
LANG_KO: "번들 ID 불일치: 현재 {cur}, 새 {new}. 중단.",
LANG_RU: "Несовпадение Bundle ID: текущий {cur}, новый {new}. Операция прервана.",
},
"online_update_codesign_failed": {
LANG_ZH_CN: "签名校验失败(TeamIdentifier/CodeSign 不匹配)。已终止。",
LANG_EN: "Code signature verification failed (TeamIdentifier/CodeSign). Aborted.",
LANG_ZH_TW: "簽名驗證失敗(TeamIdentifier/CodeSign 不匹配)。已終止。",
LANG_JA: "署名検証に失敗(TeamIdentifier/CodeSign 不一致)。中止。",
LANG_KO: "서명 검증 실패(TeamIdentifier/CodeSign). 중단.",
LANG_RU: "Сбой проверки подписи (TeamIdentifier/CodeSign). Операция прервана.",
},
"online_update_unverified_prompt": {
LANG_ZH_CN: "签名未通过或未公证,可能不安全。是否继续安装?",
LANG_EN: "Signature unverifed or not notarized. Continue anyway?",
LANG_ZH_TW: "簽名未通過或未公證,可能不安全。是否繼續安裝?",
LANG_JA: "署名未検証または未ノータライズ。続行しますか?",
LANG_KO: "서명이 검증되지 않았거나 공증되지 않았습니다. 계속하시겠습니까?",
LANG_RU: "Подпись не проверена или нет нотариата. Продолжить?",
},
"online_update_replace_now": {
LANG_ZH_CN: "更新包已下载,是否立即替换并重启?",
LANG_EN: "Package downloaded. Replace and restart now?",
LANG_ZH_TW: "更新包已下載,是否立即替換並重新啟動?",
LANG_JA: "パッケージをダウンロードしました。今すぐ置換して再起動しますか?",
LANG_KO: "패키지 다운로드 완료. 지금 교체 후 재시작할까요?",
LANG_RU: "Пакет скачан. Заменить и перезапустить сейчас?",
},
"btn_replace_and_restart": {
LANG_ZH_CN: "替换并重启",
LANG_EN: "Replace & Restart",
LANG_ZH_TW: "替換並重新啟動",
LANG_JA: "置換して再起動",
LANG_KO: "교체 및 재시작",
LANG_RU: "Заменить и перезапустить",
},
"btn_later": {
LANG_ZH_CN: "稍后",
LANG_EN: "Later",
LANG_ZH_TW: "稍後",
LANG_JA: "後で",
LANG_KO: "나중에",
LANG_RU: "Позже",
},
"online_update_failed": {
LANG_ZH_CN: "在线更新失败",
LANG_EN: "Online Update Failed",
LANG_ZH_TW: "線上更新失敗",
LANG_JA: "オンライン更新に失敗",
LANG_KO: "온라인 업데이트 실패",
LANG_RU: "Сбой онлайн-обновления",
},
# 错误
"error_no_token": {
LANG_ZH_CN: "未设置 Token,请通过“设置 Token...”配置",
LANG_EN: "Token not set. Use 'Set Token...'",
LANG_ZH_TW: "未設定 Token,請透過「設定 Token...」配置",
LANG_JA: "トークン未設定。『トークンを設定...』から設定",
LANG_KO: "토큰이 설정되지 않았습니다. '토큰 설정...' 사용",
LANG_RU: "Токен не задан. Используйте 'Указать токен...'",
},
"error_http": {
LANG_ZH_CN: "调用失败: HTTP {code}",
LANG_EN: "Request failed: HTTP {code}",
LANG_ZH_TW: "調用失敗: HTTP {code}",
LANG_JA: "リクエスト失敗: HTTP {code}",
LANG_KO: "요청 실패: HTTP {code}",
LANG_RU: "Ошибка запроса: HTTP {code}",
},
# _fmt_remaining
"rem_expired": {
LANG_ZH_CN: "已过期",
LANG_EN: "expired",
LANG_ZH_TW: "已過期",
LANG_JA: "期限切れ",
LANG_KO: "만료됨",
LANG_RU: "истёк",
},
"rem_days_hours": {
LANG_ZH_CN: "剩余{days}天{hours}小时",
LANG_EN: "{days}d {hours}h left",
LANG_ZH_TW: "剩餘{days}天{hours}小時",
LANG_JA: "残り{days}日{hours}時間",
LANG_KO: "{days}일 {hours}시간 남음",
LANG_RU: "ост. {days}д {hours}ч",
},
"rem_hours_minutes": {
LANG_ZH_CN: "剩余{hours}小时{minutes}分钟",
LANG_EN: "{hours}h {minutes}m left",
LANG_ZH_TW: "剩餘{hours}小時{minutes}分鐘",
LANG_JA: "残り{hours}時間{minutes}分",
LANG_KO: "{hours}시간 {minutes}분 남음",
LANG_RU: "ост. {hours}ч {minutes}м",
},
"rem_minutes": {
LANG_ZH_CN: "剩余{minutes}分钟",
LANG_EN: "{minutes}m left",
LANG_ZH_TW: "剩餘{minutes}分鐘",
LANG_JA: "残り{minutes}分",
LANG_KO: "{minutes}분 남음",
LANG_RU: "ост. {minutes}м",
},
}
def _t(key: str, **kwargs) -> str:
# 防御:若打包的旧版本缺失 I18N,避免 NameError
lang = _current_language
table = globals().get('I18N', {}).get(key, {})
text = table.get(lang) or table.get(LANG_ZH_CN)
if not text:
text = _fallback_text(key)
if not text:
text = key
try:
return text.format(**kwargs)
except Exception:
return text
def _fallback_text(key: str) -> Optional[str]:
"""在缺少 I18N 表时提供最小中文兜底,避免界面显示 key 名。"""
zh = {
# 顶部信息
"status_uninitialized": "状态:未初始化",
"daily_placeholder": "每日:-/- (剩余 -)",
"requests_placeholder": "调用次数:-",
"usage_span_placeholder": "7日日均:-",
"monthly_placeholder": "本周期:-/- (剩余 -)",
"cycle_placeholder": "周期:-",
"renew_placeholder": "续费提醒:-",
"balance_placeholder": "余额:-",
"last_update_placeholder": "上次更新:-",
"token_placeholder": "Token:-",
"version_prefix": "版本:",
"status_ok": "状态:正常",
"status_no_data": "状态:无数据",
"title_no_data": "无数据",
"title_error": "错误",
# 菜单
"menu_refresh": "刷新",
"menu_account": "账号类型",
"menu_title_format": "标题格式",
"menu_language": "语言",
"menu_set_token": "设置 Token...",
"menu_toggle_hidden": "隐藏/展示",
"menu_open_dashboard": "打开控制台",
"menu_latency_monitor": "延迟监控",
"menu_check_update": "检查更新",
"menu_ring": "进度圆环",
"menu_affiliates": "推广",
"menu_quit": "退出",
# 子菜单项
"account_shared": "共享(公交车)",
"account_private": "滴滴车(私有)",
"account_codex": "Codex 公交车",
"titlefmt_percent": "百分比",
"titlefmt_custom": "自定义...",
"titlefmt_show_requests": "显示调用次数",
"ring_enable": "在图标显示进度圆环",
"ring_source": "圆环来源",
"ring_source_daily": "每日进度",