-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCursor Dashboard Language Switch.user.js
More file actions
1090 lines (1020 loc) · 48.7 KB
/
Copy pathCursor Dashboard Language Switch.user.js
File metadata and controls
1090 lines (1020 loc) · 48.7 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
// ==UserScript==
// @name Cursor Dashboard Language Switch
// @name:zh-CN Cursor 后台语言切换
// @namespace https://github.com/milli/myuserscripts
// @version 0.1.25
// @description Add a language setting shell for Cursor dashboard and prepare Simplified Chinese UI translation support.
// @description:zh-CN 为 Cursor 后台添加语言设置入口骨架,并为简体中文界面切换做准备。
// @author Codex
// @license MIT
// @match https://cursor.com/*
// @run-at document-idle
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
(function () {
'use strict';
const SCRIPT_VERSION = '0.1.25';
const SETTINGS_ROW_ID = 'cursor-language-switch-setting';
const SETTINGS_BUTTON_ID = 'cursor-language-switch-trigger';
const SETTINGS_LABEL_ID = 'cursor-language-switch-label';
const SETTINGS_DESCRIPTION_ID = 'cursor-language-switch-description';
const SETTINGS_TRIGGER_TEXT_ID = 'cursor-language-switch-trigger-text';
const SETTINGS_MENU_ID = 'cursor-language-switch-menu';
const LANGUAGE_STORAGE_KEY = 'cursor-dashboard-language-switch:language';
const DEFAULT_LANGUAGE_MODE = 'default';
const CHINESE_LANGUAGE_MODE = 'zh-CN';
const VERSION_MARKER_ID = 'cursor-language-switch-version-marker';
const VERSION_MARKER_TEXT = `Cursor Language Switch v${SCRIPT_VERSION}`;
const OWNED_ATTR = 'data-cursor-language-switch-owned';
const TRANSLATED_ATTR = 'data-cursor-language-switch-translated';
const EXCLUDED_TAGS = new Set(['INPUT', 'TEXTAREA', 'CODE', 'PRE', 'KBD', 'SCRIPT', 'STYLE']);
const ORIGINAL_TEXT_BY_NODE = new WeakMap();
const ORIGINAL_TEXT_BY_ELEMENT = new WeakMap();
const TRANSLATIONS = new Map([
['Settings', '设置'],
['New Agent', '新建 Agent'],
['Automations', '自动化'],
['Create an Automation', '创建自动化'],
['Save time by automating repetitive tasks with always-on agents that respond to triggers.', '使用可持续运行并响应触发器的 agents 自动化重复任务,节省时间。'],
['Popular', '热门'],
['Code Review', '代码审查'],
['Incidents & Triage', '事故与分诊'],
['Data & Research', '数据与研究'],
['Find bugs', '查找 bug'],
['Find critical bugs', '查找关键 bug'],
['Analyze recent commits for high-severity correctness bugs and submit safe fixes', '分析最近的提交,找出高严重性的正确性 bug,并提交安全修复。'],
['Find vulnerabilities', '查找漏洞'],
['Review pull requests for exploitable security issues and flag only validated findings before merge', '在合并前审查 pull requests 中可被利用的安全问题,并且只标记已验证的发现。'],
['Assign PR reviewers', '分配 PR 审查者'],
['Assign reviewers based on code changes and auto-approve low-risk PRs', '根据代码变更分配审查者,并自动批准低风险 PR。'],
['Monitor engineering invariants', '监控工程不变量'],
['Re-check critical repository invariants on a schedule and alert only when a rule regresses', '按计划重新检查仓库中的关键不变量,并且仅在规则退化时发出提醒。'],
['Remediate dependency vulnerabilities', '修复依赖漏洞'],
['Triage dependency-vulnerability tickets from Linear and open upgrade PRs when the fix is safe', '分诊来自 Linear 的依赖漏洞工单,并在修复安全时创建升级 PR。'],
['Scan codebase for vulnerabilities', '扫描代码库中的漏洞'],
['Review the full repository on a schedule and alert on validated high-impact security issues', '按计划审查整个仓库,并对已验证的高影响安全问题发出提醒。'],
['Generate docs', '生成文档'],
['Create and update developer documentation for recently changed or under-documented code', '为最近变更或文档不足的代码创建并更新开发者文档。'],
['Add test coverage', '补充测试覆盖'],
['Review recent changes and add tests for high-risk logic that lacks adequate coverage', '审查最近的变更,并为缺少充分覆盖的高风险逻辑补充测试。'],
['Fix bugs reported in Slack', '修复 Slack 中报告的 bug'],
['Monitor a Slack channel for bug reports, investigate the codebase, and fix with a PR', '监控 Slack 频道中的 bug 报告,调查代码库,并通过 PR 进行修复。'],
['Fix CI failures', '修复 CI 失败'],
['Detect CI failures on main and automatically open PRs', '检测 main 分支上的 CI 失败并自动创建 PR。'],
['Investigate PagerDuty incidents', '调查 PagerDuty 事故'],
['Investigate incidents using Datadog and code context', '结合 Datadog 和代码上下文调查事故。'],
['Investigate Sentry issues', '调查 Sentry 问题'],
['Investigate errors from Sentry, identify root causes, and propose fixes', '调查来自 Sentry 的错误,找出根因,并提出修复方案。'],
['Investigate top Datadog errors', '调查 Datadog 高发错误'],
['Investigate recurring production errors from Datadog, identify root causes, and propose fixes', '调查 Datadog 中反复出现的生产错误,找出根因,并提出修复方案。'],
['Triage Linear issues', '分诊 Linear 问题'],
['Triage new issues by investigating bugs, planning feature requests, and opening PRs for easy fixes', '通过调查 bug、规划功能请求,以及为简单修复创建 PR 来分诊新问题。'],
['Summarize changes daily', '每日汇总变更'],
['Post a daily Slack digest summarizing notable repository changes and risks from the previous day', '每天在 Slack 发布摘要,总结前一天仓库中的重要变更和风险。'],
['Customer Health Monitoring Agent', '客户健康监控 Agent'],
['Find at-risk customers using usage analytics, call notes, Slack escalations, and Linear blockers', '结合用量分析、通话记录、Slack 升级和 Linear 阻塞项来识别有流失风险的客户。'],
['Product Analytics Agent', '产品分析 Agent'],
['Weekly product usage, activation, retention, and feature adoption digest from Databricks', '来自 Databricks 的每周产品用量、激活、留存和功能采用摘要。'],
['Product FAQ Agent', '产品 FAQ Agent'],
['Answer product questions in a dedicated Slack channel using Slack, Notion, Linear, and GitHub context', '结合 Slack、Notion、Linear 和 GitHub 上下文,在专用 Slack 频道中回答产品问题。'],
['Product Finance Agent', '产品财务 Agent'],
['Analyze Stripe revenue, churn signals, and product pricing opportunities', '分析 Stripe 收入、流失信号和产品定价机会。'],
['Slack Digest Agent', 'Slack 摘要 Agent'],
["Summarize important DMs, mentions, and the user's top active Slack channels", '总结重要私信、提及内容以及该用户最活跃的 Slack 频道。'],
['Dashboard', '仪表盘'],
['No Agents Yet', '还没有 Agents'],
['Ask Cursor to build, fix bugs, explore', '让 Cursor 帮你构建、修复 bug、探索代码'],
['GPT-5.5 High', 'GPT-5.5 High'],
['MCPs', 'MCPs'],
['No MCP servers available', '没有可用的 MCP 服务器'],
['Use Multiple Models', '使用多个模型'],
['Long-running', '长时间运行'],
['Preview', '预览'],
['Apply maximum effort on any task', '对任何任务应用最高努力级别'],
['Run security audit', '运行安全审计'],
['Explore Marketplace', '浏览市场'],
['Try Commands', '试试 Commands'],
['Press /', '按下 /'],
['Wait for approval after planning', '规划完成后等待批准'],
['Running on Auto', '正在使用 Auto 运行'],
['Usage limits reached. This Agent is running on Auto for free.', '已达到用量上限。这个 Agent 正在免费使用 Auto 运行。'],
['Edit limits', '编辑限制'],
['Continue with Auto', '继续使用 Auto'],
['Overview', '概览'],
['Cloud Agents', '云代理'],
['Bugbot', 'Bugbot'],
['Automatically review pull requests (PRs) for bugs and issues. Bugbot runs are billed based on underlying agent usage.', '自动审查 pull requests(PRs)中的 bug 和问题。Bugbot 运行将按底层 agent 用量计费。'],
['Bugbot reviews are billed through your Cursor plan usage', 'Bugbot 审查会通过你的 Cursor 套餐用量计费'],
['Enable Bugbot on a repository to get started', '在仓库上启用 Bugbot 以开始使用'],
['To start using Bugbot, you need to enable it on at least one repository. Select an organization below to get started.', '要开始使用 Bugbot,你需要至少在一个仓库上启用它。请选择下方的一个组织以开始。'],
['Manage connected accounts and repositories', '管理已连接的账号和仓库'],
['Enable Bugbot', '启用 Bugbot'],
['Enable', '启用'],
['Source Control Providers', '源码控制提供方'],
['Plugins', '插件'],
['Integrations', '集成'],
['Shared Canvases', '共享画布'],
["All the Canvases you've shared from Cursor, in one place.", '你从 Cursor 分享的所有 Canvases 都会集中显示在这里。'],
['No Shared Canvases', '还没有共享画布'],
['Canvases you share from Cursor will appear here.', '你从 Cursor 分享的 Canvases 会显示在这里。'],
['Create Agents to edit and run code, asynchronously', '创建 Agents 以异步编辑和运行代码'],
['Environments', '环境'],
['New', '新建'],
['No environments configured', '尚未配置任何环境'],
['Start Setup', '开始设置'],
['Create environment', '创建环境'],
['Create a New Environment', '创建新环境'],
['Create a new environment by selecting one or more repositories.', '通过选择一个或多个仓库来创建新环境。'],
['Select one or more repositories.', '选择一个或多个仓库。'],
['Repositories', '仓库'],
['Select multiple', '多选'],
['Continue', '继续'],
['Self-Hosted', '自托管'],
['Monitor and manage your self-hosted cloud machines', '监控并管理你的自托管云机器'],
['Enable self-hosted pool', '启用自托管池'],
['Enable self-hosted pool to create a personal pool of workers.', '启用自托管池以创建个人 worker 池。'],
['My Machines', '我的机器'],
['View personal self-hosted workers and CLI commands to connect machines.', '查看个人自托管 workers 和连接机器所需的 CLI 命令。'],
['Defaults', '默认值'],
['Default Model', '默认模型'],
['Used when no model is specified', '在未指定模型时使用'],
['Select model', '选择模型'],
['Default Repository', '默认仓库'],
['Used when no repository is specified', '在未指定仓库时使用'],
['Select repository', '选择仓库'],
['Base Branch', '基础分支'],
["When empty, Cloud Agent will use a repository's default branch (recommended)", '留空时,Cloud Agent 将使用仓库的默认分支(推荐)'],
['Branch Prefix', '分支前缀'],
['Prefix for branch names created by Cloud Agent', 'Cloud Agent 创建分支名时使用的前缀'],
['Pull Requests', 'Pull Requests'],
['Security', '安全'],
['Create PRs', '创建 PR'],
['Automatically create a pull request when Cloud Agent completes.', 'Cloud Agent 完成后自动创建 pull request。'],
['For Single Model Runs', '适用于单模型运行'],
['Allow posting artifacts to GitHub', '允许将产物发布到 GitHub'],
['Allow cloud agents to embed images directly in PR descriptions using hard-to-guess public URLs.', '允许 cloud agents 使用难以猜测的公开 URL 将图片直接嵌入 PR 描述中。'],
['Allow cloud agents to embed images directly in PR descriptions using hard-to-guess public URLs. Learn more', '允许 cloud agents 使用难以猜测的公开 URL 将图片直接嵌入 PR 描述中。了解更多'],
['Link Only', '仅链接'],
['Notifications', '通知'],
['Slack Notifications', 'Slack 通知'],
['Get notified in Slack when a Cloud Agent completes a task', '当 Cloud Agent 完成任务时在 Slack 中接收通知'],
['Repository routing', '仓库路由'],
['Routing rules to help Cloud Agents pick the right repository.', '帮助 Cloud Agents 选择正确仓库的路由规则。'],
['Add Rule', '添加规则'],
['No routing rules yet', '还没有路由规则'],
['Network Access Settings', '网络访问设置'],
['Control which network destinations your cloud agents can access', '控制你的 cloud agents 可以访问哪些网络目标'],
['Allow all network access', '允许所有网络访问'],
['My Secrets', '我的 Secrets'],
['Securely set environment variables for your Cloud Agents.', '为你的 Cloud Agents 安全地设置环境变量。'],
['Add Secrets', '添加 Secrets'],
['No secrets yet', '还没有 Secrets'],
['Members', '成员'],
['Usage', '用量'],
['Spending', '支出'],
['Billing & Invoices', '计费与发票'],
['Language', '语言'],
['Default', '默认'],
['Chinese (Simplified)', '简体中文'],
['Privacy', '隐私'],
['Privacy Mode', '隐私模式'],
['Active', '已启用'],
['Edit', '编辑'],
['Learn More', '了解更多'],
['Learn more', '了解更多'],
['Student Verification', '学生认证'],
['Student Status', '学生状态'],
['Not eligible', '不符合条件'],
['Profile', '个人资料'],
['Email', '电子邮箱'],
['First Name', '名字'],
['Last Name', '姓氏'],
['Save', '保存'],
['Appearance', '外观'],
['Theme', '主题'],
['System', '跟随系统'],
['PR Preferences', 'PR 偏好'],
['Preferred PR destination', '首选 PR 打开位置'],
['Choose where PR links open across web, the desktop app and IDE.', '选择在网页、桌面应用和 IDE 中打开 PR 链接的位置。'],
['Source Control', '源码控制'],
['Connect', '连接'],
['Loading...', '加载中...'],
['Loading contribution data...', '正在加载贡献数据...'],
['Desktop App', '桌面应用'],
['About 1 hour ago', '约 1 小时前'],
['Showing 1-4 of 4', '显示第 1-4 项,共 4 项'],
['Connect GitHub for Cloud Agents, Bugbot and enhanced codebase context', '连接 GitHub 以用于 Cloud Agents、Bugbot 和增强代码库上下文'],
['Work with Cloud Agents from Slack', '在 Slack 中使用 Cloud Agents'],
["Connect external tools to extend your team's workflow.", '连接外部工具以扩展你团队的工作流。'],
['No API Keys Yet', '还没有 API Key'],
['New API Key', '新建 API Key'],
['Upgrade to Teams', '升级到 Teams'],
['Work with your team and unlock collaborative features', '与你的团队协作并解锁协作功能'],
['Create team', '创建团队'],
['Current Plan', '当前套餐'],
['CURRENT PLAN', '当前套餐'],
['On-Demand Spending', '按需支出'],
['Disabled', '已禁用'],
['Manage', '管理'],
['Switch to annual billing and save 20%', '切换到按年计费并节省 20%'],
['Manage in Stripe', '在 Stripe 中管理'],
['Paid', '已支付'],
['Cancel', '取消'],
['Extend Cursor with skills, rules, subagents, MCP tools, and hooks', '使用技能、规则、子代理、MCP 工具和 hooks 扩展 Cursor'],
['Included', '包含'],
['On-demand', '按需'],
['Model', '模型'],
['Tokens', 'Tokens'],
['Getting started', '开始使用'],
['Connect Slack', '连接 Slack'],
['Extend Cursor with plugins', '使用插件扩展 Cursor'],
['Set up your cloud environment for faster, parallelizable agents everywhere.', '设置你的云环境,以便在各处更快地并行运行代理。'],
['Work with Cloud Agents from Microsoft Teams', '在 Microsoft Teams 中使用 Cloud Agents'],
['Your code data will not be trained on or used to improve the product. Code may be stored for Cloud Agent, Team Rules, and other features.', '你的代码数据不会被用于训练或改进产品。代码可能会为 Cloud Agent、团队规则和其他功能而存储。'],
['Only .edu emails and specific educational domains are eligible for student verification.', '只有 .edu 邮箱和特定教育域名符合学生认证条件。'],
['Session revocation may take up to 10 minutes to complete.', '会话撤销最多可能需要 10 分钟完成。'],
['User API Keys provide secure, programmatic access to your Cursor account, including the headless version of the Cursor Agent CLI and Cloud Agent API.', '用户 API Key 可为你的 Cursor 账号提供安全的程序化访问,包括无头版 Cursor Agent CLI 和 Cloud Agent API。'],
['User API Keys provide secure, programmatic access to your Cursor account, including the headless version of the Cursor Agent CLI and', '用户 API Key 可为你的 Cursor 账号提供安全的程序化访问,包括无头版 Cursor Agent CLI 和'],
['User API Keys provide secure, programmatic access to your Cursor account, including the headless version of the Cursor Agent CLI', '用户 API Key 可为你的 Cursor 账号提供安全的程序化访问,包括无头版 Cursor Agent CLI'],
['and', '和'],
['. Treat them like passwords: keep them secure and never share them publicly.', '。请像对待密码一样妥善保管,切勿公开分享。'],
['Note: The', '注意:'],
['Treat them like passwords: keep them secure and never share them publicly.', '请像对待密码一样妥善保管,切勿公开分享。'],
['. Treat them like passwords: keep them secure and never share them publicly. Note: The', '。请像对待密码一样妥善保管,切勿公开分享。注意:'],
['Note: The Cloud Agent API is in beta.', '注意:Cloud Agent API 目前处于测试阶段。'],
['is in beta.', '目前处于测试阶段。'],
['Active Sessions', '活跃会话'],
['Device', '设备'],
['Created', '创建时间'],
['Web', '网页'],
['Revoke', '撤销'],
['Prev', '上一页'],
['Next', '下一页'],
['More', '更多'],
['Log Out', '退出登录'],
['Delete Account', '删除账号'],
['Delete', '删除'],
['Create a Team', '创建团队'],
['Back to Agents', '返回 Agents'],
]);
const PATTERN_TRANSLATIONS = [
{
pattern: /^(\d+\/\d+)\s+Completed$/u,
replace: (_, progress) => `已完成 ${progress}`,
},
{
pattern: /^(\d+)\/(\d+)\s+Repositories Enabled$/u,
replace: (_, enabled, total) => `已启用 ${enabled}/${total} 个仓库`,
},
{
pattern: /^(\d+)\s+Repositories Available$/u,
replace: (_, count) => `有 ${count} 个仓库可用`,
},
{
pattern: /^About\s+(\d+)\s+hour(?:s)?\s+ago$/u,
replace: (_, hours) => `约 ${hours} 小时前`,
},
{
pattern: /^Showing\s+(.+?)\s+of\s+(\d+)$/u,
replace: (_, range, total) => `显示第 ${range} 项,共 ${total} 项`,
},
{
pattern: /^May\s+(\d+)\s+-\s+May\s+(\d+)$/u,
replace: (_, startDay, endDay) => `5月${startDay}日 - 5月${endDay}日`,
},
{
pattern: /^Connected as\s+(.+?)\s+to repositories in organizations:\s+(.+)$/u,
replace: (_, account, organizations) => `已使用 ${account} 连接到这些组织中的仓库:${organizations}`,
},
{
pattern: /^Connect\s+(GitHub|GitLab)\s+for Cloud Agents, Bugbot and enhanced codebase context$/u,
replace: (_, provider) => `连接 ${provider} 以用于 Cloud Agents、Bugbot 和增强代码库上下文`,
},
{
pattern: /^Connect a\s+(Linear workspace|Jira site)\s+to delegate issues to Cloud Agents$/u,
replace: (_, target) => {
if (target === 'Linear workspace') {
return '连接 Linear 工作区以将问题委派给 Cloud Agents';
}
return '连接 Jira 站点以将问题委派给 Cloud Agents';
},
},
{
pattern: /^Need enterprise features\?$/u,
replace: () => '需要企业级功能?',
},
{
pattern: /^Contact sales$/u,
replace: () => '联系销售',
},
{
pattern: /^Team Management$/u,
replace: () => '团队管理',
},
{
pattern: /^Usage Analytics$/u,
replace: () => '用量分析',
},
{
pattern: /^Admin Controls$/u,
replace: () => '管理员控制',
},
{
pattern: /^Rules & Commands$/u,
replace: () => '规则与命令',
},
{
pattern: /^Invite members, manage roles, and control access$/u,
replace: () => '邀请成员、管理角色并控制访问权限',
},
{
pattern: /^Track team usage and optimize your subscription$/u,
replace: () => '跟踪团队用量并优化你的订阅',
},
{
pattern: /^Centralized billing and privacy mode controls$/u,
replace: () => '集中管理计费和隐私模式控制',
},
{
pattern: /^Share rules and commands across your team$/u,
replace: () => '在团队内共享规则和命令',
},
{
pattern: /^Get pooled usage, SCIM seat management, and granular admin controls$/u,
replace: () => '获取汇总用量、SCIM 席位管理和精细化管理员控制',
},
{
pattern: /^Last month$/u,
replace: () => '上个月',
},
{
pattern: /^Total spend$/u,
replace: () => '总支出',
},
{
pattern: /^Your Usage$/u,
replace: () => '你的用量',
},
{
pattern: /^Your usage per day across this billing period$/u,
replace: () => '你在当前计费周期内的每日用量',
},
{
pattern: /^Group By: Model$/u,
replace: () => '分组方式:模型',
},
{
pattern: /^Metric: Spend$/u,
replace: () => '指标:支出',
},
{
pattern: /^Cumulative Spend$/u,
replace: () => '累计支出',
},
{
pattern: /^Export CSV$/u,
replace: () => '导出 CSV',
},
{
pattern: /^Showing token usage and costs from (.+) to (.+)\. Use filters to narrow results by date range\.$/u,
replace: (_, start, end) => `显示从 ${start} 到 ${end} 的 token 用量和费用。可使用筛选器按日期范围缩小结果。`,
},
{
pattern: /^Rows:\s*(\d+)$/u,
replace: (_, rows) => `行数:${rows}`,
},
{
pattern: /^Resets on (.+)$/u,
replace: (_, dateText) => `将于 ${dateText} 重置`,
},
{
pattern: /^(\d+)% Auto and (\d+)% API used$/u,
replace: (_, autoPercent, apiPercent) => `已使用 ${autoPercent}% Auto 和 ${apiPercent}% API`,
},
{
pattern: /^Cycle Starting (.+)$/u,
replace: (_, dateText) => `周期开始于 ${dateText}`,
},
{
pattern: /^Adjust plan$/u,
replace: () => '调整套餐',
},
{
pattern: /^Included in Ultra$/u,
replace: () => 'Ultra 包含',
},
{
pattern: /^Total$/u,
replace: () => '总计',
},
{
pattern: /^On-Demand Usage$/u,
replace: () => '按需用量',
},
{
pattern: /^On-demand spending is currently disabled$/u,
replace: () => '当前已禁用按需支出',
},
{
pattern: /^Monthly Limit$/u,
replace: () => '月度上限',
},
{
pattern: /^Set a fixed amount or make it unlimited\.$/u,
replace: () => '设置固定金额或设为无限制。',
},
{
pattern: /^Upgrade Now$/u,
replace: () => '立即升级',
},
{
pattern: /^Get maximum value with 20x usage limits and early access to advanced features\.$/u,
replace: () => '通过 20 倍用量上限和高级功能抢先体验获得最大价值。',
},
{
pattern: /^Your subscription will auto renew on (.+)\.$/u,
replace: (_, dateText) => `你的订阅将于 ${dateText} 自动续费。`,
},
{
pattern: /^0% Auto and 0% API used$/u,
replace: () => '已使用 0% Auto 和 0% API',
},
{
pattern: /^Payment$/u,
replace: () => '付款',
},
{
pattern: /^Update your payment details$/u,
replace: () => '更新你的付款信息',
},
{
pattern: /^Included Usage$/u,
replace: () => '包含用量',
},
{
pattern: /^Item$/u,
replace: () => '项目',
},
{
pattern: /^Type$/u,
replace: () => '类型',
},
{
pattern: /^Cost$/u,
replace: () => '费用',
},
{
pattern: /^Qty$/u,
replace: () => '数量',
},
{
pattern: /^Subtotal:$/u,
replace: () => '小计:',
},
{
pattern: /^Invoices$/u,
replace: () => '发票',
},
{
pattern: /^Date$/u,
replace: () => '日期',
},
{
pattern: /^Description$/u,
replace: () => '说明',
},
{
pattern: /^Status$/u,
replace: () => '状态',
},
{
pattern: /^Amount$/u,
replace: () => '金额',
},
{
pattern: /^Invoice$/u,
replace: () => '发票',
},
{
pattern: /^View$/u,
replace: () => '查看',
},
{
pattern: /^We'll be sad to see you go\.$/u,
replace: () => '看到你离开我们会很难过。',
},
{
pattern: /^User API Keys$/u,
replace: () => '用户 API Key',
},
{
pattern: /^No API Keys have been created yet$/u,
replace: () => '还没有创建任何 API Key',
},
{
pattern: /^User API Keys provide secure, programmatic access to your Cursor account, including the headless version of the Cursor Agent CLI and Cloud Agent API\. Treat them like passwords: keep them secure and never share them publicly\. Note: The Cloud Agent API is in beta\.$/u,
replace: () => '用户 API Key 可为你的 Cursor 账号提供安全的程序化访问,包括无头版 Cursor Agent CLI 和 Cloud Agent API。请像对待密码一样妥善保管,切勿公开分享。注意:Cloud Agent API 目前处于测试阶段。',
},
{
pattern: /^All$/u,
replace: () => '全部',
},
{
pattern: /^Required$/u,
replace: () => '必需',
},
{
pattern: /^Optional$/u,
replace: () => '可选',
},
{
pattern: /^Add$/u,
replace: () => '添加',
},
{
pattern: /^Core skills library: TDD, debugging, collaboration patterns, and proven techniques$/u,
replace: () => '核心技能库:TDD、调试、协作模式和经过验证的实践',
},
];
let currentLanguageMode = DEFAULT_LANGUAGE_MODE;
let refreshTimer = null;
let routeHref = '';
let routeWatcher = null;
let mutationObserver = null;
function normalizeLanguageMode(value) {
if (typeof value !== 'string') return DEFAULT_LANGUAGE_MODE;
const normalized = value.trim();
if (!normalized) return DEFAULT_LANGUAGE_MODE;
if (normalized.toLowerCase() === DEFAULT_LANGUAGE_MODE) return DEFAULT_LANGUAGE_MODE;
return normalized === CHINESE_LANGUAGE_MODE ? CHINESE_LANGUAGE_MODE : DEFAULT_LANGUAGE_MODE;
}
function translateText(text, languageMode) {
if (normalizeLanguageMode(languageMode) === DEFAULT_LANGUAGE_MODE) {
return text;
}
const raw = String(text);
const match = raw.match(/^(\s*)(.*?)(\s*)$/su);
if (!match) {
return raw;
}
const [, leadingWhitespace, coreText, trailingWhitespace] = match;
const translated = TRANSLATIONS.get(coreText);
if (translated) {
return `${leadingWhitespace}${translated}${trailingWhitespace}`;
}
for (const entry of PATTERN_TRANSLATIONS) {
if (!entry.pattern.test(coreText)) continue;
const replaced = coreText.replace(entry.pattern, entry.replace);
return `${leadingWhitespace}${replaced}${trailingWhitespace}`;
}
return raw;
}
function isExcludedTextNode(node) {
if (!node || !node.parentElement) return true;
const parent = node.parentElement;
if (parent.closest(`[${OWNED_ATTR}]`)) return true;
if (EXCLUDED_TAGS.has(parent.tagName)) return true;
if (parent.isContentEditable) return true;
const value = node.nodeValue;
if (!value || !value.trim()) return true;
return false;
}
function shouldSkipTranslationForContext(node, sourceText) {
const text = String(sourceText || '').trim();
if (!text || !node || !node.parentElement) return false;
const ancestorText = node.parentElement.closest('.dashboard-subSection-list, .dashboard-section-list, .flex, .dashboard-cell')
?.innerText || '';
// GitLab's action button often enters a transient loading state. We
// wait for Cursor to settle to its stable action before translating.
if (text === 'Loading...' && ancestorText.includes('GitLab')) {
return true;
}
return false;
}
function restoreTextNode(node) {
if (!ORIGINAL_TEXT_BY_NODE.has(node)) return;
node.nodeValue = ORIGINAL_TEXT_BY_NODE.get(node);
ORIGINAL_TEXT_BY_NODE.delete(node);
if (node.parentElement) {
node.parentElement.removeAttribute(TRANSLATED_ATTR);
}
}
function translateTextNode(node, languageMode) {
if (isExcludedTextNode(node)) return;
const sourceText = ORIGINAL_TEXT_BY_NODE.get(node) || node.nodeValue;
if (shouldSkipTranslationForContext(node, sourceText)) {
restoreTextNode(node);
return;
}
if (normalizeLanguageMode(languageMode) === DEFAULT_LANGUAGE_MODE) {
restoreTextNode(node);
return;
}
const translatedText = translateText(sourceText, languageMode);
if (translatedText === sourceText) return;
if (!ORIGINAL_TEXT_BY_NODE.has(node)) {
ORIGINAL_TEXT_BY_NODE.set(node, sourceText);
}
node.nodeValue = translatedText;
if (node.parentElement) {
node.parentElement.setAttribute(TRANSLATED_ATTR, '1');
}
}
function isLeafTranslationElement(element) {
if (!element || !element.isConnected) return false;
if (element.closest(`[${OWNED_ATTR}]`)) return false;
if (EXCLUDED_TAGS.has(element.tagName)) return false;
if (element.isContentEditable) return false;
if (element.childElementCount > 0) return false;
const text = element.textContent;
return Boolean(text && text.trim());
}
function restoreElementText(element) {
if (!ORIGINAL_TEXT_BY_ELEMENT.has(element)) return;
element.textContent = ORIGINAL_TEXT_BY_ELEMENT.get(element);
ORIGINAL_TEXT_BY_ELEMENT.delete(element);
element.removeAttribute(TRANSLATED_ATTR);
}
function translateLeafElement(element, languageMode) {
if (!isLeafTranslationElement(element)) return;
const sourceText = ORIGINAL_TEXT_BY_ELEMENT.get(element) || element.textContent;
if (normalizeLanguageMode(languageMode) === DEFAULT_LANGUAGE_MODE) {
restoreElementText(element);
return;
}
const translatedText = translateText(sourceText, languageMode);
if (translatedText === sourceText) return;
if (!ORIGINAL_TEXT_BY_ELEMENT.has(element)) {
ORIGINAL_TEXT_BY_ELEMENT.set(element, sourceText);
}
element.textContent = translatedText;
element.setAttribute(TRANSLATED_ATTR, '1');
}
function applyElementLevelTranslations(root, languageMode) {
if (!root || normalizeLanguageMode(languageMode) === DEFAULT_LANGUAGE_MODE) return;
const elements = root.querySelectorAll('p, span, div, button, td, th, h1, h2, h3, h4, h5, h6');
elements.forEach((element) => {
translateLeafElement(element, languageMode);
});
}
function restoreElementLevelTranslations(root) {
if (!root) return;
const elements = root.querySelectorAll(`[${TRANSLATED_ATTR}]`);
elements.forEach((element) => {
restoreElementText(element);
});
}
function applyTranslations(root, languageMode) {
if (!root || normalizeLanguageMode(languageMode) === DEFAULT_LANGUAGE_MODE) return;
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
let currentNode = walker.nextNode();
while (currentNode) {
translateTextNode(currentNode, languageMode);
currentNode = walker.nextNode();
}
applyElementLevelTranslations(root, languageMode);
}
function restoreTranslations(root) {
if (!root) return;
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
let currentNode = walker.nextNode();
while (currentNode) {
restoreTextNode(currentNode);
currentNode = walker.nextNode();
}
restoreElementLevelTranslations(root);
}
function ensureVersionMarker() {
if (!document || !document.body) return null;
let marker = document.getElementById(VERSION_MARKER_ID);
if (marker) {
marker.textContent = VERSION_MARKER_TEXT;
marker.setAttribute('data-language-mode', currentLanguageMode);
return marker;
}
marker = document.createElement('div');
marker.id = VERSION_MARKER_ID;
marker.setAttribute(OWNED_ATTR, '1');
marker.textContent = VERSION_MARKER_TEXT;
marker.setAttribute('data-language-mode', DEFAULT_LANGUAGE_MODE);
marker.style.position = 'fixed';
marker.style.right = '16px';
marker.style.bottom = '16px';
marker.style.zIndex = '2147483647';
marker.style.padding = '6px 10px';
marker.style.borderRadius = '999px';
marker.style.fontSize = '12px';
marker.style.fontFamily = 'ui-sans-serif, system-ui, sans-serif';
marker.style.color = '#0f172a';
marker.style.background = 'rgba(255, 255, 255, 0.92)';
marker.style.boxShadow = '0 6px 20px rgba(15, 23, 42, 0.18)';
marker.style.pointerEvents = 'none';
document.body.appendChild(marker);
return marker;
}
function describeLanguageMode(languageMode) {
return normalizeLanguageMode(languageMode) === CHINESE_LANGUAGE_MODE
? { label: '语言', optionDefault: '默认', optionChinese: '简体中文', current: '简体中文' }
: { label: 'Language', optionDefault: 'Default', optionChinese: 'Chinese (Simplified)', current: 'Chinese (Simplified)' };
}
function describeLanguageSetting(languageMode) {
return {
...describeLanguageMode(languageMode),
description: normalizeLanguageMode(languageMode) === CHINESE_LANGUAGE_MODE
? '为 Cursor 后台界面选择显示语言。'
: 'Choose the display language for the Cursor dashboard interface.',
};
}
function getCurrentLanguageOptionText(texts) {
return currentLanguageMode === CHINESE_LANGUAGE_MODE
? texts.optionChinese
: texts.optionDefault;
}
async function refreshPageLanguage(languageMode) {
currentLanguageMode = normalizeLanguageMode(languageMode);
ensureVersionMarker();
if (currentLanguageMode === DEFAULT_LANGUAGE_MODE) {
restoreTranslations(document.body);
} else {
restoreTranslations(document.body);
applyTranslations(document.body, currentLanguageMode);
}
injectLanguageSetting();
}
function buildLanguageSettingRow() {
const texts = describeLanguageSetting(currentLanguageMode);
const row = document.createElement('div');
row.id = SETTINGS_ROW_ID;
row.className = 'dashboard-cell align-center';
row.setAttribute(OWNED_ATTR, '1');
const divider = document.createElement('div');
divider.className = 'dashboard-cell-divider';
const leading = document.createElement('div');
leading.className = 'dashboard-cell-leading-items align-center';
const content = document.createElement('div');
content.className = 'dashboard-cell-content';
const label = document.createElement('div');
label.id = SETTINGS_LABEL_ID;
label.className = 'dashboard-cell-label';
label.textContent = texts.label;
const description = document.createElement('div');
description.id = SETTINGS_DESCRIPTION_ID;
description.className = 'dashboard-cell-description';
description.textContent = texts.description;
content.append(label, description);
leading.appendChild(content);
const trailing = document.createElement('div');
trailing.className = 'dashboard-cell-trailing-items';
const triggerWrapper = document.createElement('div');
triggerWrapper.className = 'relative';
const trigger = document.createElement('button');
trigger.id = SETTINGS_BUTTON_ID;
trigger.type = 'button';
trigger.className = 'dashboard-outline-button !px-2 dashboard-outline-button-md cursor-language-switch-trigger';
trigger.setAttribute('aria-haspopup', 'menu');
trigger.setAttribute('aria-expanded', 'false');
trigger.setAttribute('data-state', 'closed');
const triggerText = document.createElement('span');
triggerText.id = SETTINGS_TRIGGER_TEXT_ID;
triggerText.className = 'min-w-0 flex-1 truncate text-left dashboard-outline-button-text';
triggerText.textContent = getCurrentLanguageOptionText(texts);
const triggerIcon = document.createElement('i');
triggerIcon.className = 'cursor-icon ui-icon text-[color:var(--icon-quaternary)] opacity-60';
triggerIcon.setAttribute('data-icon-name', 'chevron-down');
triggerIcon.setAttribute('aria-hidden', 'true');
triggerIcon.style.setProperty('--cursor-icon-content', '""');
triggerIcon.style.setProperty('--icon-size', '10px');
const menu = document.createElement('div');
menu.id = SETTINGS_MENU_ID;
menu.className = 'cursor-language-switch-menu';
menu.hidden = true;
[['default', texts.optionDefault], ['zh-CN', texts.optionChinese]].forEach(([value, labelText]) => {
const optionButton = document.createElement('button');
optionButton.type = 'button';
optionButton.className = 'cursor-language-switch-menu-item';
optionButton.setAttribute('data-language-value', value);
optionButton.setAttribute('data-selected', value === currentLanguageMode ? 'true' : 'false');
optionButton.textContent = labelText;
optionButton.addEventListener('click', async () => {
closeLanguageMenu(row);
const nextMode = normalizeLanguageMode(value);
if (nextMode === currentLanguageMode) {
syncExistingLanguageSettingRow(row);
return;
}
await GM_setValue(LANGUAGE_STORAGE_KEY, nextMode);
await refreshPageLanguage(nextMode);
});
menu.appendChild(optionButton);
});
trigger.addEventListener('click', () => {
toggleLanguageMenu(row);
});
trigger.append(triggerText, triggerIcon);
triggerWrapper.append(trigger, menu);
trailing.appendChild(triggerWrapper);
row.append(divider, leading, trailing);
return row;
}
function openLanguageMenu(row) {
const trigger = row.querySelector(`#${SETTINGS_BUTTON_ID}`);
const menu = row.querySelector(`#${SETTINGS_MENU_ID}`);
if (!trigger || !menu) return;
trigger.setAttribute('aria-expanded', 'true');
trigger.setAttribute('data-state', 'open');
menu.hidden = false;
}
function closeLanguageMenu(row) {
const trigger = row.querySelector(`#${SETTINGS_BUTTON_ID}`);
const menu = row.querySelector(`#${SETTINGS_MENU_ID}`);
if (!trigger || !menu) return;
trigger.setAttribute('aria-expanded', 'false');
trigger.setAttribute('data-state', 'closed');
menu.hidden = true;
}
function toggleLanguageMenu(row) {
const menu = row.querySelector(`#${SETTINGS_MENU_ID}`);
if (!menu) return;
if (menu.hidden) {
openLanguageMenu(row);
return;
}
closeLanguageMenu(row);
}
function syncLanguageMenu(row, texts) {
const triggerText = row.querySelector(`#${SETTINGS_TRIGGER_TEXT_ID}`);
const menu = row.querySelector(`#${SETTINGS_MENU_ID}`);
if (triggerText) {
triggerText.textContent = getCurrentLanguageOptionText(texts);
}
if (!menu) return;
const optionDefinitions = [
['default', texts.optionDefault],
['zh-CN', texts.optionChinese],
];
const items = Array.from(menu.querySelectorAll('[data-language-value]'));
items.forEach((item, index) => {
const [value, labelText] = optionDefinitions[index] || [];
if (!value) {
item.remove();
return;
}
item.textContent = labelText;
item.setAttribute('data-language-value', value);
item.setAttribute('data-selected', value === currentLanguageMode ? 'true' : 'false');
});
}
function syncExistingLanguageSettingRow(row) {
if (!row) return false;
const texts = describeLanguageSetting(currentLanguageMode);
const label = row.querySelector(`#${SETTINGS_LABEL_ID}`);
const description = row.querySelector(`#${SETTINGS_DESCRIPTION_ID}`);
const select = row.querySelector(`#${SETTINGS_BUTTON_ID}`);
if (label) {
label.textContent = texts.label;
}
if (description) {
description.textContent = texts.description;
}
syncLanguageMenu(row, texts);
return Boolean(label && description && select);
}
function findThemeRow() {
const labels = Array.from(document.querySelectorAll('.dashboard-cell-label'));
const themeLabel = labels.find((node) => {
const text = node.textContent ? node.textContent.trim() : '';
return text === 'Theme' || text === '主题';
});
return themeLabel ? themeLabel.closest('.dashboard-cell') : null;
}
function injectLanguageSetting() {
const themeRow = findThemeRow();
if (!themeRow || !themeRow.parentElement) return;
const existingRow = document.getElementById(SETTINGS_ROW_ID);
if (existingRow) {
if (syncExistingLanguageSettingRow(existingRow)) {
return;
}
existingRow.remove();
}
const newRow = buildLanguageSettingRow();
themeRow.insertAdjacentElement('afterend', newRow);
}
function ensureStyles() {
GM_addStyle(`
#${VERSION_MARKER_ID} {
backdrop-filter: blur(8px);
}
.cursor-language-switch-trigger {
min-width: 168px;
cursor: pointer;
}
.cursor-language-switch-menu {
position: absolute;
right: 0;
top: calc(100% + 8px);
z-index: 2147483646;
min-width: 168px;
padding: 6px;
border: 1px solid var(--border-tertiary);
border-radius: 10px;
background: var(--bg-elevated, #161817);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
}
.cursor-language-switch-menu-item {
width: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
border: 0;
border-radius: 8px;
background: transparent;
color: inherit;
padding: 8px 10px;
font: inherit;
cursor: pointer;
text-align: left;