davinci-resolve-mcp 2.63.2 → 2.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,780 @@
1
+ """Browser-side localization for the Resolve control panel."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+
7
+
8
+ ZH_CN_TRANSLATIONS = {
9
+ "Interface language": "界面语言",
10
+ "Open Overview": "打开概览",
11
+ "Breadcrumb": "面包屑导航",
12
+ "Control panel sections": "控制面板分区",
13
+ "Analysis pages": "分析页面",
14
+ "Diagnostic pages": "诊断页面",
15
+ "Documentation pages": "文档页面",
16
+ "Preference pages": "偏好设置页面",
17
+ "Project context": "项目上下文",
18
+ "MCP version and updates": "MCP 版本与更新",
19
+ "MCP version": "MCP 版本",
20
+ "GitHub Repository": "GitHub 仓库",
21
+ "Overview": "概览",
22
+ "Media": "媒体",
23
+ "AI Console": "AI 控制台",
24
+ "Setup": "设置",
25
+ "Projects": "项目",
26
+ "Docs": "文档",
27
+ "Preferences": "偏好设置",
28
+ "Inventory": "媒体清单",
29
+ "Review": "审阅",
30
+ "History": "历史记录",
31
+ "Edit Plans": "剪辑方案",
32
+ "Resolve": "Resolve",
33
+ "Storage": "存储",
34
+ "Tools": "工具",
35
+ "Advanced": "高级",
36
+ "Conform QC": "套底质检",
37
+ "Media Pool History": "媒体池历史",
38
+ "README": "自述文档",
39
+ "Release Notes": "发行说明",
40
+ "Release notes": "发行说明",
41
+ "Media Analysis Guide": "媒体分析指南",
42
+ "Agent Skill": "智能体技能",
43
+ "Advanced Server": "高级服务器",
44
+ "Analysis": "分析",
45
+ "Caps + Safety": "限额与安全",
46
+ "Metadata And Markers": "元数据与标记",
47
+ "Paths And Workflow": "路径与工作流",
48
+ "MCP Updates": "MCP 更新",
49
+ "Loading project contexts": "正在加载项目上下文",
50
+ "View All Projects": "查看全部项目",
51
+ "Waiting for first refresh": "等待首次刷新",
52
+ "waiting for first refresh": "等待首次刷新",
53
+ "Refresh All": "全部刷新",
54
+ "Open Analysis": "打开分析",
55
+ "Active Project": "当前项目",
56
+ "Checking": "正在检查",
57
+ "Resolve connection pending": "正在等待 Resolve 连接",
58
+ "Source Clips": "源片段",
59
+ "Source clips only": "仅显示源片段",
60
+ "Sequences": "时间线",
61
+ "Read-only context": "只读上下文",
62
+ "Media Status": "媒体状态",
63
+ "Inventory pending": "正在等待媒体清单",
64
+ "Waiting for Resolve media inventory.": "正在等待 Resolve 媒体清单。",
65
+ "Resolve Projects": "Resolve 项目",
66
+ "Refresh Projects": "刷新项目",
67
+ "Current Project": "当前项目",
68
+ "Current folder pending": "正在读取当前文件夹",
69
+ "Open Projects": "已打开项目",
70
+ "Shown in the project dropdown": "显示在项目下拉菜单中",
71
+ "Database Projects": "数据库项目",
72
+ "Database pending": "正在读取数据库",
73
+ "Analysis Contexts": "分析上下文",
74
+ "Existing local reports and indexes": "已有的本地报告与索引",
75
+ "All Database Projects": "全部数据库项目",
76
+ "Filter": "筛选",
77
+ "project or folder": "项目或文件夹",
78
+ "Open Projects to scan the current Resolve database.": "打开项目列表以扫描当前 Resolve 数据库。",
79
+ "Open the full database view to inspect projects outside the current Project Manager folder.": "打开完整数据库视图,查看当前项目管理器文件夹之外的项目。",
80
+ "This view walks Resolve Project Manager folders read-only, then uses confirmation before loading a different project in Resolve.": "此视图以只读方式遍历 Resolve 项目管理器文件夹;在 Resolve 中载入其他项目前会要求确认。",
81
+ "Resolve media is inventoried read-only and filtered to source clips so timelines, compounds, titles, and generated items stay out of analysis queues.": "以只读方式盘点 Resolve 媒体,并仅保留源片段,避免时间线、复合片段、标题和生成项目进入分析队列。",
82
+ "Refresh Clips": "刷新片段",
83
+ "Find": "查找",
84
+ "clip, bin, path, or type": "片段、媒体夹、路径或类型",
85
+ "Bin": "媒体夹",
86
+ "All bins": "全部媒体夹",
87
+ "Media type": "媒体类型",
88
+ "All types": "全部类型",
89
+ "Clip status": "片段状态",
90
+ "clips": "片段",
91
+ "analysis ready": "可分析",
92
+ "online": "在线",
93
+ "missing/offline": "缺失/离线",
94
+ "Analysis status": "分析状态",
95
+ "all": "全部",
96
+ "not analyzed": "未分析",
97
+ "analyzed": "已分析",
98
+ "queued/running": "排队中/运行中",
99
+ "failed": "失败",
100
+ "Poll": "轮询",
101
+ "off": "关闭",
102
+ "Auto refresh Resolve clips": "自动刷新 Resolve 片段",
103
+ "0 selected": "已选择 0 项",
104
+ "Select Ready Clips": "选择可分析片段",
105
+ "Analyze": "分析",
106
+ "Analyze actions": "分析操作",
107
+ "No analyzable clips chosen yet.": "尚未选择可分析片段。",
108
+ "Copy Prompt": "复制提示词",
109
+ "Search clips, summaries, tags, transcripts…": "搜索片段、摘要、标签和转录…",
110
+ "Filter by bin": "按媒体夹筛选",
111
+ "View mode": "视图模式",
112
+ "Grid": "网格",
113
+ "List": "列表",
114
+ "Loading analyzed clips…": "正在加载已分析片段…",
115
+ "Bin overview · click a clip to drill into shot detail.": "媒体夹概览 · 点击片段查看镜头详情。",
116
+ "← Back": "← 返回",
117
+ "Shots": "镜头",
118
+ "Images": "图像",
119
+ "Timeline edit history": "时间线剪辑历史",
120
+ "Archive current timeline": "归档当前时间线",
121
+ "Select a timeline.": "请选择时间线。",
122
+ "Dry-run plans saved by the edit engine — review here, execute from chat.": "剪辑引擎保存的试运行方案,可在此审阅并从对话中执行。",
123
+ "Edit plans": "剪辑方案",
124
+ "Loading combined review…": "正在加载组合审阅…",
125
+ "Loading transcript…": "正在加载转录…",
126
+ "Filter transcript segments…": "筛选转录片段…",
127
+ "Recent runs": "最近运行",
128
+ "Active run:": "当前运行:",
129
+ "Begin run": "开始运行",
130
+ "End run": "结束运行",
131
+ "run label (e.g. 'rough cut tighten')": "运行标签(例如“粗剪收紧”)",
132
+ "reason (optional)": "原因(可选)",
133
+ "RESOLVE 21 AI CONSOLE": "RESOLVE 21 AI 控制台",
134
+ "Resolve 21 AI Console": "Resolve 21 AI 控制台",
135
+ "Resolve AI Console": "Resolve AI 控制台",
136
+ "Available now": "当前可用",
137
+ "Requires Resolve 21+": "需要 Resolve 21+",
138
+ "Unavailable on this build": "当前版本不可用",
139
+ "Unavailable actions are disabled. Resolve 21 methods may also require the named Extra from Extras Download Manager.": "不可用操作已禁用。Resolve 21 方法还可能需要通过扩展下载管理器安装所标注的扩展。",
140
+ "Requires DaVinci Resolve 21 or newer": "需要 DaVinci Resolve 21 或更高版本",
141
+ "Speaker detection requires DaVinci Resolve 21 or newer": "说话人检测需要 DaVinci Resolve 21 或更高版本",
142
+ "Available on this Resolve build": "当前 Resolve 版本可用能力",
143
+ "Checking which AI methods this Resolve build exposes…": "正在检查当前 Resolve 版本开放的 AI 方法…",
144
+ "Classify audio": "音频分类",
145
+ "Clear classification": "清除分类",
146
+ "IntelliSearch": "智能搜索",
147
+ "Analyze for slate": "场记板分析",
148
+ "Remove motion blur": "移除运动模糊",
149
+ "Generate speech": "生成语音",
150
+ "Disable background tasks": "停用后台任务",
151
+ "Governance": "资源管控",
152
+ "AI governance tier": "AI 管控级别",
153
+ "Governance mode": "管控模式",
154
+ "Off": "关闭",
155
+ "No limits": "无限制",
156
+ "Lenient": "宽松",
157
+ "Big jobs": "大型任务",
158
+ "Standard": "标准",
159
+ "ACTIVE": "当前",
160
+ "Sensible default": "合理默认值",
161
+ "Strict": "严格",
162
+ "Tight leash": "严格限制",
163
+ "Advisory": "仅提醒",
164
+ "Enforce": "强制执行",
165
+ "DEBLUR RUNS": "去模糊次数",
166
+ "SPEECH RUNS": "语音生成次数",
167
+ "MEDIA CREATED": "已创建媒体",
168
+ "RENDER TIME": "渲染时间",
169
+ "Target": "目标",
170
+ "Current folder": "当前文件夹",
171
+ "Specific clip": "指定片段",
172
+ "Clip id": "片段 ID",
173
+ "(clip UniqueId)": "(片段 UniqueId)",
174
+ "Safe, reversible. IntelliSearch and Slate require their AI Extras (Extras Download Manager).": "安全且可撤销。智能搜索和场记板分析需要通过扩展下载管理器安装对应 AI 扩展。",
175
+ "Identify faces": "识别人脸",
176
+ "Better mode": "高质量模式",
177
+ "Marker": "标记颜色",
178
+ "Transcribe": "转录",
179
+ "Speaker detection": "说话人检测",
180
+ "Clear transcription": "清除转录",
181
+ "Motion deblur": "运动去模糊",
182
+ "Format": "格式",
183
+ "Codec": "编码器",
184
+ "Extreme mode": "极致模式",
185
+ "Use mark in/out": "使用入点/出点",
186
+ "Render at source res": "按源分辨率渲染",
187
+ "Speech generator": "语音生成器",
188
+ "Text": "文本",
189
+ "Text to synthesize": "要合成的文本",
190
+ "Voice model": "声音模型",
191
+ "Speed": "语速",
192
+ "Pitch": "音高",
193
+ "Variation": "变化度",
194
+ "Add to timeline": "添加到时间线",
195
+ "Timecode": "时间码",
196
+ "Audio track": "音频轨道",
197
+ "Session": "会话",
198
+ "Result": "结果",
199
+ "No op run yet.": "尚未运行任何操作。",
200
+ "Run Resolve's local AI operations on the current Media Pool folder or a specific clip. These run on Resolve's GPU/AI engine — the analysis and slate ops are safe and reversible;": "在当前媒体池文件夹或指定片段上运行 Resolve 本地 AI 操作。这些操作使用 Resolve 的 GPU/AI 引擎;分析和场记板操作安全且可撤销;",
201
+ "motion-deblur": "运动去模糊",
202
+ "speech generation": "语音生成",
203
+ "create new media files and ask for confirmation first. Source media is never modified. Every run is recorded in the": "会创建新的媒体文件,并会先要求确认。源媒体不会被修改。每次运行都会记录在",
204
+ "Resolve 21 AI ops": "Resolve 21 AI 操作",
205
+ "ledger (Preferences → Caps + Safety).": "台账中(偏好设置 → 限额与安全)。",
206
+ "A grey dot means the method is absent (older Resolve). \"needs …\" means the method is present but requires that Extra to actually run — install via Extras Download Manager.": "灰点表示当前版本没有该方法(通常是旧版 Resolve)。“需要……”表示方法存在,但必须通过扩展下载管理器安装对应扩展后才能运行。",
207
+ "Per-session limits for the two media-creating ops (deblur, speech). In": "对两项会创建媒体的操作(去模糊、语音)设置会话级限制。在",
208
+ "mode you're warned in the confirm dialog but never blocked; in": "模式下只在确认框中提醒,不会阻止执行;在",
209
+ "mode an over-tier run is refused until you raise the tier, relax the mode, or consciously override. Pick the tier that matches the job.": "模式下,超出级别的运行会被拒绝,直到提高级别、放宽模式或主动覆盖。请选择与任务匹配的级别。",
210
+ "Folder ops run on the current Media Pool folder in Resolve. Choose": "文件夹操作会作用于 Resolve 当前媒体池文件夹。选择",
211
+ "and paste a clip id (from": "并粘贴片段 ID(可从",
212
+ ") to target one clip.": "获取)即可指定单个片段。",
213
+ "Renders new deblurred media. Creates files; asks for confirmation. Leave fields blank for Resolve defaults.": "渲染新的去模糊媒体。该操作会创建文件并要求确认;留空则使用 Resolve 默认值。",
214
+ "AI text-to-speech. Requires the AI Speech Generator Extra. Creates a new audio item; asks for confirmation.": "AI 文字转语音。需要 AI Speech Generator 扩展;会创建新的音频项目并要求确认。",
215
+ "Quiet Resolve's background tasks for this session before heavy work. Resets on restart.": "在执行繁重任务前暂停本次会话中的 Resolve 后台任务;重启后恢复。",
216
+ "Status legend": "状态图例",
217
+ "OK": "正常",
218
+ "Healthy": "运行正常",
219
+ "Warn": "警告",
220
+ "Needs attention": "需要处理",
221
+ "Error": "错误",
222
+ "Action required": "需要操作",
223
+ "Idle": "空闲",
224
+ "Not yet probed": "尚未检测",
225
+ "Project readiness": "项目就绪状态",
226
+ "Current read-only Resolve connection, product/version, project inventory, and any warnings from the media pool probe.": "显示当前只读 Resolve 连接、产品/版本、项目清单以及媒体池检测警告。",
227
+ "Resolve diagnostics pending.": "正在等待 Resolve 诊断。",
228
+ "Clients": "客户端",
229
+ "MCP diagnostics pending.": "正在等待 MCP 诊断。",
230
+ "Scanning clients…": "正在扫描客户端…",
231
+ "Scanning installed clients…": "正在扫描已安装客户端…",
232
+ "Analysis Storage": "分析存储",
233
+ "Project analysis paths used for reports, the durable job database, and the local search index.": "项目分析路径用于保存报告、持久任务数据库和本地搜索索引。",
234
+ "Storage diagnostics pending.": "正在等待存储诊断。",
235
+ "Runtime helpers detected by the dashboard. Missing tools may reduce technical metadata or media probing depth.": "面板检测到的运行时辅助工具。缺少工具可能会降低技术元数据或媒体探测深度。",
236
+ "Tool diagnostics pending.": "正在等待工具诊断。",
237
+ "Advanced-server capabilities pending.": "正在等待高级服务器能力信息。",
238
+ "Lineage sidecar": "沿袭记录旁车文件",
239
+ "Enter the path to a lineage sidecar and Load.": "输入沿袭记录旁车文件路径,然后点击加载。",
240
+ "Load": "加载",
241
+ "Read-only browser for a conform": "套底记录的只读浏览器",
242
+ "Action": "操作",
243
+ "Created": "创建时间",
244
+ "Reference": "参考",
245
+ "Structural diff": "结构差异",
246
+ "Group by target": "按目标分组",
247
+ "Where files live": "文件位置",
248
+ "Server Defaults": "服务器默认值",
249
+ "Defaults used when agents run analysis or publish analysis-backed outputs without specifying every option.": "智能体运行分析或发布分析结果时,若未指定全部选项,将使用这些默认值。",
250
+ "Save Defaults": "保存默认值",
251
+ "Reset Defaults": "重置默认值",
252
+ "Loading setup defaults": "正在加载设置默认值",
253
+ "These preferences are server-wide defaults for this local MCP install. Dashboard convenience settings stay in this browser.": "这些偏好是当前 MCP 安装的服务器级默认值;面板便捷设置仅保存在此浏览器中。",
254
+ "Vision default": "视觉分析默认值",
255
+ "Transcription default": "转录默认值",
256
+ "Slate detection": "场记板检测",
257
+ "ask": "询问",
258
+ "yes": "是",
259
+ "no": "否",
260
+ "Frame sampling mode": "帧采样模式",
261
+ "Economy · flat frames, cheapest & most predictable": "经济 · 固定帧数,成本最低且最可控",
262
+ "Balanced · frames scale with duration (linear cost)": "均衡 · 帧数随时长线性增加",
263
+ "Thorough · content-aware, bounded cost (recommended)": "深入 · 内容感知且有成本上限(推荐)",
264
+ "Thorough (uncapped) · content-aware, up to 512 frames": "深入(无常规上限)· 内容感知,最多 512 帧",
265
+ "Frames / minute": "每分钟帧数",
266
+ "Frame floor": "最低帧数",
267
+ "Frame ceiling": "最高帧数",
268
+ "Persistence": "持久化",
269
+ "session only": "仅当前会话",
270
+ "keep reports": "保留报告",
271
+ "keep artifacts": "保留产物",
272
+ "Summary style": "摘要风格",
273
+ "technical · camera, exposure, QC focus": "技术 · 聚焦摄影机、曝光和质检",
274
+ "creative · vibes, intent, editorial reads": "创意 · 聚焦氛围、意图和剪辑解读",
275
+ "concise · short, structured highlights": "简洁 · 简短的结构化重点",
276
+ "full · everything the model can say": "完整 · 模型可提供的全部信息",
277
+ "Report format": "报告格式",
278
+ "compact": "紧凑",
279
+ "full": "完整",
280
+ "machine readable": "机器可读",
281
+ "Default analysis depth": "默认分析深度",
282
+ "Default sample frames": "默认采样帧数",
283
+ "Source trust": "来源可信度",
284
+ "auto · conservative-by-default": "自动 · 默认保守判断",
285
+ "low · frames only, aggressive hedging": "低 · 仅依据画面,严格保守表述",
286
+ "medium · frames + filename + cultural recognition": "中 · 画面、文件名与常识识别",
287
+ "high · trusted archival source, confident claims": "高 · 可信档案来源,可明确判断",
288
+ "Token + frame budgets for analysis, plus safety rails for destructive edits. Usage is tracked per project in": "分析的令牌与帧预算,以及破坏性剪辑操作的安全限制。用量按项目记录在",
289
+ "Budget preset": "预算预设",
290
+ "Analysis caps preset": "分析限额预设",
291
+ "Pick the preset that matches the job. Override individual fields below if needed.": "选择与任务匹配的预设;需要时可在下方覆盖单项设置。",
292
+ "Vision token usage": "视觉令牌用量",
293
+ "Live consumption against the active caps. Green = comfortable, amber = approaching, red = at or over budget.": "当前限额的实时用量。绿色表示充足,琥珀色表示接近,红色表示达到或超过预算。",
294
+ "Per‑clip": "每片段",
295
+ "Per‑job": "每任务",
296
+ "Today": "今天",
297
+ "Daily vision-token usage (last 30 days)": "每日视觉令牌用量(最近 30 天)",
298
+ "Local Resolve AI operations (audio classification, IntelliSearch, slate, motion-deblur, speech). These run on Resolve's GPU/AI engine and do": "Resolve 本地 AI 操作(音频分类、智能搜索、场记板、运动去模糊、语音)。这些操作使用 Resolve 的 GPU/AI 引擎,",
299
+ "consume the Claude analysis token budget above — tracked here for invocations, time, and files created.": "消耗上方的 Claude 分析令牌预算;此处记录调用次数、耗时和创建的文件。",
300
+ "Op": "操作",
301
+ "Runs": "运行次数",
302
+ "Time": "耗时",
303
+ "Files": "文件",
304
+ "Safety": "安全",
305
+ "Auto-archive timelines before destructive edits and refuse high-blast-radius ops when archive fails.": "在破坏性剪辑前自动归档时间线;若归档失败,则拒绝高影响范围操作。",
306
+ "Auto-save Resolve project after each archive": "每次归档后自动保存 Resolve 项目",
307
+ "Strict mode — always on for:": "严格模式始终应用于:",
308
+ "Advanced — per-field overrides": "高级 · 单项覆盖",
309
+ "Leave blank to use the preset value shown in the placeholder. Type a number or": "留空将使用占位提示中的预设值。输入数字或",
310
+ "to override.": "进行覆盖。",
311
+ "Response chars": "响应字符数",
312
+ "Vision tokens / clip": "每片段视觉令牌",
313
+ "Frames / clip": "每片段帧数",
314
+ "Vision tokens / job": "每任务视觉令牌",
315
+ "Vision tokens / day": "每日视觉令牌",
316
+ "Wall clock seconds / call": "每次调用最长秒数",
317
+ "Max frame dimension (px)": "最大帧尺寸(像素)",
318
+ "Inspector & debug": "检查与调试",
319
+ "Look up usage for a specific clip or batch, browse refusals, or reset today's day-scope rollup.": "查询指定片段或批次的用量、查看拒绝记录,或重置今天的日级汇总。",
320
+ "or Job id": "或任务 ID",
321
+ "Look up": "查询",
322
+ "Reset today's usage": "重置今日用量",
323
+ "Delete today's day-scope usage rows (admin)": "删除今天的日级用量记录(管理员)",
324
+ "enter a clip_id or job_id and press Look up": "输入 clip_id 或 job_id,然后点击查询",
325
+ "Recent caps refusals": "最近的限额拒绝记录",
326
+ "Metadata fields": "元数据字段",
327
+ "Marker types": "标记类型",
328
+ "Controls for Resolve metadata writes and source-time marker suggestions. Source media remains read-only.": "控制 Resolve 元数据写入和源时间标记建议;源媒体保持只读。",
329
+ "Timed markers": "定时标记",
330
+ "Overwrite policy": "覆盖策略",
331
+ "preserve human": "保留人工内容",
332
+ "fill empty": "仅填充空字段",
333
+ "overwrite owned blocks": "覆盖本工具管理的区块",
334
+ "overwrite all": "全部覆盖",
335
+ "Max markers per clip": "每片段最大标记数",
336
+ "limited": "有限",
337
+ "unlimited": "无限",
338
+ "Marker custom data": "标记自定义数据",
339
+ "namespaced": "带命名空间",
340
+ "minimal": "最小化",
341
+ "Marker colors": "标记颜色",
342
+ "Include confidence scores": "包含置信度",
343
+ "Include source time notes": "包含源时间备注",
344
+ "Ask before metadata publish": "发布元数据前询问",
345
+ "Dry run first": "先试运行",
346
+ "Optional locations and Resolve navigation behavior after operations complete.": "可选路径,以及操作完成后的 Resolve 页面跳转行为。",
347
+ "Preferred analysis root": "首选分析根目录",
348
+ "Default project analysis root": "默认项目分析根目录",
349
+ "Browse…": "浏览…",
350
+ "Recent project roots": "最近项目根目录",
351
+ "Recent project roots…": "最近项目根目录…",
352
+ "Generated media folder": "生成媒体文件夹",
353
+ "Default generated media folder": "默认生成媒体文件夹",
354
+ "Inventory limit": "清单数量上限",
355
+ "Default 500": "默认 500",
356
+ "Inventory exclude bins (comma separated)": "清单排除媒体夹(逗号分隔)",
357
+ "None — index every folder": "无 · 索引全部文件夹",
358
+ "Post-operation page": "操作完成后页面",
359
+ "stay put": "保持当前页面",
360
+ "media": "媒体",
361
+ "cut": "快编",
362
+ "edit": "剪辑",
363
+ "fusion": "Fusion",
364
+ "color": "调色",
365
+ "fairlight": "Fairlight",
366
+ "deliver": "交付",
367
+ "Locations the dashboard uses for server defaults and browser-local preferences. Read-only diagnostics.": "面板用于服务器默认值和浏览器本地偏好设置的路径。仅供只读诊断。",
368
+ "Loading preference storage.": "正在加载偏好设置存储信息。",
369
+ "MCP server restart needed": "需要重启 MCP 服务器",
370
+ "Got it": "知道了",
371
+ "Server-wide update-check behavior for the MCP package. Checks are best-effort and should not block startup.": "MCP 包的服务器级更新检查设置。检查为尽力而为,不应阻塞启动。",
372
+ "Update policy": "更新策略",
373
+ "prompt": "提示",
374
+ "notify": "通知",
375
+ "auto": "自动",
376
+ "never": "从不",
377
+ "Channel": "更新通道",
378
+ "stable": "稳定版",
379
+ "beta": "测试版",
380
+ "dev": "开发版",
381
+ "Check interval hours": "检查间隔(小时)",
382
+ "Snooze hours": "暂缓时长(小时)",
383
+ "Apply update": "应用更新",
384
+ "Loading status…": "正在加载状态…",
385
+ "Preview release notes": "预览发行说明",
386
+ "Stash local changes if dirty": "工作区有改动时暂存",
387
+ "Override active-job lock": "覆盖活动任务锁",
388
+ "Rollback last update": "回滚上次更新",
389
+ "Update history": "更新历史",
390
+ "Resolve Project Switch": "切换 Resolve 项目",
391
+ "Change Active Project?": "切换当前项目?",
392
+ "This will load the selected project in DaVinci Resolve and scope the control panel to that project.": "这将在 DaVinci Resolve 中加载所选项目,并让控制面板切换到该项目上下文。",
393
+ "Search, jobs, logs, diagnostics, and index status will refresh after the switch.": "切换后将刷新搜索、任务、日志、诊断和索引状态。",
394
+ "Cancel": "取消",
395
+ "Load Project": "加载项目",
396
+ "Confirm": "确认",
397
+ "Are you sure?": "确定要继续吗?",
398
+ "MCP Version": "MCP 版本",
399
+ "Loading version information…": "正在加载版本信息…",
400
+ "Update command": "更新命令",
401
+ "Close": "关闭",
402
+ "Copy Command": "复制命令",
403
+ "Update Now": "立即更新",
404
+ "Developed by": "开发者:",
405
+ "for": "服务于",
406
+ "Open source. Patches, features, and community contributions welcome.": "开源项目,欢迎补丁、新功能和社区贡献。",
407
+ "DaVinci Resolve is a trademark of Blackmagic Design Pty Ltd. Bradford Operations is not affiliated with, endorsed by, or sponsored by Blackmagic Design. All third-party trademarks are the property of their respective owners.": "DaVinci Resolve 是 Blackmagic Design Pty Ltd. 的商标。Bradford Operations 与 Blackmagic Design 无隶属、认可或赞助关系。所有第三方商标均归各自所有者所有。",
408
+ "and": "和",
409
+ "not": "不",
410
+ "with": "且",
411
+ "because the addressable entity is a media pool item, not a timeline.": "因为这里可寻址的实体是媒体池项目,而不是时间线。",
412
+ "Resolve is not connected. Open a project in DaVinci Resolve, then reload.": "Resolve 尚未连接。请在 DaVinci Resolve 中打开项目,然后重新加载。",
413
+ "Deblur runs": "去模糊次数",
414
+ "Speech runs": "语音生成次数",
415
+ "Media created": "已创建媒体",
416
+ "Render time": "渲染时间",
417
+ "lenient": "宽松",
418
+ "standard": "标准",
419
+ "strict": "严格",
420
+ "Blue": "蓝色",
421
+ "Cyan": "青色",
422
+ "Green": "绿色",
423
+ "Yellow": "黄色",
424
+ "Red": "红色",
425
+ "Pink": "粉色",
426
+ "Purple": "紫色",
427
+ "Fuchsia": "品红",
428
+ "Rose": "玫红",
429
+ "Lavender": "薰衣草紫",
430
+ "Sky": "天蓝",
431
+ "Mint": "薄荷绿",
432
+ "Lemon": "柠檬黄",
433
+ "Sand": "沙色",
434
+ "Cocoa": "可可色",
435
+ "Cream": "奶油色",
436
+ "Resolve project": "Resolve 项目",
437
+ "Read-only": "只读",
438
+ "No sequences detected": "未检测到时间线",
439
+ "No timelines detected": "未检测到时间线",
440
+ "Source clips": "源片段",
441
+ "Clip media status": "片段媒体状态",
442
+ "Analysis progress": "分析进度",
443
+ "Search index": "搜索索引",
444
+ "Search unavailable until built": "建立索引后才能使用搜索",
445
+ "Not built": "尚未建立",
446
+ "Source media is read-only; outputs stay in the active project analysis root.": "源媒体保持只读;输出保存在当前项目的分析根目录中。",
447
+ "missing": "缺失",
448
+ "Showing first 500 Resolve records before clip filtering. Use analysis jobs for full long-form progress.": "片段筛选前最多显示前 500 条 Resolve 记录。长项目的完整进度请使用分析任务查看。",
449
+ "Clip": "片段",
450
+ "Ready": "就绪",
451
+ "Path": "路径",
452
+ "analyzable": "可分析",
453
+ "not analyzable": "不可分析",
454
+ "offline": "离线",
455
+ "Online source media": "在线源媒体",
456
+ "Refresh": "刷新",
457
+ "Analyzed": "已分析",
458
+ "Superseded": "已被替代",
459
+ "Vision pending": "等待视觉分析",
460
+ "Warnings": "警告",
461
+ "Nothing analyzed yet.": "尚无分析结果。",
462
+ "Review fills in as clips are analyzed. Ask your assistant to analyze this project’s media:": "片段分析完成后,审阅区会自动填充。可以请助手分析当前项目媒体:",
463
+ "“Analyze the source clips in my current Resolve project and build the search index.”": "“分析当前 Resolve 项目中的源片段,并建立搜索索引。”",
464
+ "Copy prompt": "复制提示词",
465
+ "Connection": "连接",
466
+ "Connected": "已连接",
467
+ "Product": "产品",
468
+ "Version": "版本",
469
+ "Active page": "当前页面",
470
+ "Status": "状态",
471
+ "Read-only API live": "只读 API 已连接",
472
+ "Project": "项目",
473
+ "Loaded": "已加载",
474
+ "Name": "名称",
475
+ "Project ID": "项目 ID",
476
+ "No warnings": "无警告",
477
+ "Total records": "记录总数",
478
+ "Hidden": "已隐藏",
479
+ "MCP Server": "MCP 服务器",
480
+ "Python": "Python",
481
+ "Server script": "服务器脚本",
482
+ "Resolve scripting paths": "Resolve 脚本路径",
483
+ "Detected": "已检测",
484
+ "API path": "API 路径",
485
+ "Library": "库文件",
486
+ "Auto-detected from standard install paths": "从标准安装路径自动检测",
487
+ "Transport": "传输方式",
488
+ "Local (stdio)": "本地(stdio)",
489
+ "Mode": "模式",
490
+ "stdio (local)": "stdio(本地)",
491
+ "Default. Networked access is opt-in (launch with --transport sse|streamable-http).": "默认使用本地连接。网络访问需主动启用(启动参数:--transport sse|streamable-http)。",
492
+ "Start networked": "启动网络服务",
493
+ "Install": "安装",
494
+ "Remove": "移除",
495
+ "Not configured": "未配置",
496
+ "Config": "配置文件",
497
+ "Install writes a managed": "安装操作会将受管理的",
498
+ "entry into the client's MCP config file. Remove reverses it. Existing entries in the file are preserved.": "条目写入客户端 MCP 配置文件;移除操作会撤销该条目,并保留文件中的其他现有配置。",
499
+ "Google's agentic AI coding assistant (VS Code fork)": "Google 的智能体编程助手(VS Code 分支)",
500
+ "Anthropic's desktop app for Claude": "Anthropic 的 Claude 桌面应用",
501
+ "Project-scoped config (committed to repo)": "项目级配置(提交到仓库)",
502
+ "AI-native code editor (VS Code fork)": "AI 原生代码编辑器(VS Code 分支)",
503
+ "Workspace-scoped config for Copilot agent mode": "Copilot 智能体模式的工作区级配置",
504
+ "Codeium's AI code editor": "Codeium 的 AI 代码编辑器",
505
+ "AI coding assistant (VS Code extension)": "AI 编程助手(VS Code 扩展)",
506
+ "Autonomous AI coding assistant (VS Code extension)": "自主 AI 编程助手(VS Code 扩展)",
507
+ "High-performance code editor (macOS/Linux only)": "高性能代码编辑器(仅 macOS/Linux)",
508
+ "Open-source AI code assistant": "开源 AI 编程助手",
509
+ "AI coding agent (uses its own type/enabled/command-array format)": "AI 编程智能体(使用独立的 type/enabled/command 数组格式)",
510
+ "Analysis root": "分析根目录",
511
+ "Active": "当前",
512
+ "Base root": "基础根目录",
513
+ "Detail": "详情",
514
+ "Database": "数据库",
515
+ "Jobs database": "任务数据库",
516
+ "Missing": "缺失",
517
+ "Copy command": "复制命令",
518
+ "Ask": "询问",
519
+ "Recheck": "重新检查",
520
+ "Pure-Python reference implementation. Choose this OR whisper_cpp OR mlx_whisper.": "纯 Python 参考实现。请在它、whisper_cpp 和 mlx_whisper 中选择一个。",
521
+ "Fastest CPU option. Choose this OR whisper_cli OR mlx_whisper.": "最快的 CPU 方案。请在它、whisper_cli 和 mlx_whisper 中选择一个。",
522
+ "Apple Silicon only. Choose this OR whisper_cli OR whisper_cpp.": "仅支持 Apple Silicon。请在它、whisper_cli 和 whisper_cpp 中选择一个。",
523
+ "Optional. Standard frame-difference motion scoring works without it.": "可选。未安装时仍可使用标准帧差运动评分。",
524
+ "Local embedding model (~270 MB). sentence-transformers is an alternative text backend.": "本地嵌入模型(约 270 MB)。sentence-transformers 可作为替代文本后端。",
525
+ "Needs torch. Model weights (~350 MB) download on first use.": "需要 torch。首次使用时会下载约 350 MB 的模型权重。",
526
+ "Needs torch + ffmpeg. Uses laion/clap-htsat-unfused (~600 MB, downloads on first use); the laion_clap package works as an alternative backend.": "需要 torch 和 ffmpeg。默认使用 laion/clap-htsat-unfused(约 600 MB,首次使用时下载);也可使用 laion_clap 包作为替代后端。",
527
+ "The optional offline Node sibling (": "可选的离线 Node 配套服务(",
528
+ ") edits Resolve files (": ")可编辑 Resolve 文件(",
529
+ ") and patches the project DB — no running Resolve required. The core is pure-JS and always available; a few features need user-installed helpers, shown live below. Full 18-tool catalog: Docs → Advanced Server.": ")并修改项目数据库,无需运行 Resolve。核心功能由纯 JavaScript 实现并始终可用;少数功能需要用户安装辅助程序,其状态显示在下方。完整的 18 项工具目录见:文档 → 高级服务器。",
530
+ "checking…": "正在检查…",
531
+ "lineage sidecar": "沿袭记录旁车文件",
532
+ "— the content-hashed snapshot store the advanced server's": "是高级服务器的内容哈希快照存储;高级服务器的",
533
+ "tool writes (": "工具会写入(",
534
+ "snapshot": "快照",
535
+ "ingest →": "导入 →",
536
+ "conform": "套底",
537
+ "qc": "质检",
538
+ "per-cut frame verdicts). Point at a sidecar": "逐剪辑点评)。指定旁车",
539
+ "to inspect snapshots, diffs, and verdicts; ingest and QC runs stay with the MCP tools.": "即可检查快照、差异和点评;导入与质检运行仍由 MCP 工具执行。",
540
+ "Provenance log for destructive media-pool operations — deletes, replaces, relinks. Each row captures the action, the target clip or folder, the analysis run that triggered it, and the initiator. Kept separate from timeline": "破坏性媒体池操作(删除、替换、重新链接)的来源记录。每行记录操作、目标片段或文件夹、触发它的分析运行和发起者。该记录与时间线",
541
+ "Limit": "数量上限",
542
+ "no media-pool changes recorded": "尚无媒体池更改记录",
543
+ "Controls whether visual frame analysis is used by default when an operation supports it.": "控制操作支持视觉帧分析时是否默认启用。",
544
+ "Sets the default answer for transcript generation on audio-bearing clips.": "设置含音频片段生成转录时的默认选择。",
545
+ "Controls whether slate detection should run or ask before adding slate-informed context.": "控制场记板检测是直接运行,还是在添加场记板上下文前询问。",
546
+ "Default trust level for non-frame context (filename, cultural recognition). Auto keeps conservative-by-default hedging.": "非画面上下文(文件名、常识识别)的默认可信度。自动模式默认采用保守表述。",
547
+ "Default analysis depth applied to MCP analysis prompts.": "MCP 分析提示词使用的默认分析深度。",
548
+ "Default number of sample frames to inspect per clip for visual analysis.": "视觉分析时每个片段默认检查的采样帧数。",
549
+ "Chooses how many frames each clip gets for visual analysis: Economy (flat), Balanced (scales with duration), or Thorough (content-aware, bounded). Drives both coverage and token cost.": "选择每个片段用于视觉分析的帧数:经济(固定)、均衡(随时长变化)或深入(内容感知且有上限)。这会同时影响覆盖率和令牌成本。",
550
+ "You will be asked to pick a mode the first time you analyze. Recommended: Thorough.": "首次分析时会要求选择模式。推荐:深入。",
551
+ "Frames sampled per minute in Balanced mode (also seeds Thorough on short clips).": "均衡模式下每分钟采样帧数(短片段的深入模式也以此为基础)。",
552
+ "Minimum frames per clip for duration/content-scaled modes.": "按时长或内容缩放的模式中,每个片段的最低帧数。",
553
+ "Maximum frames per clip for Balanced and Thorough modes (the Thorough per-clip cap).": "均衡和深入模式下每个片段的最高帧数。",
554
+ "Chooses whether analysis artifacts stay session-only or keep reusable reports and frames.": "选择分析产物仅保留在当前会话,还是保存可复用的报告和帧。",
555
+ "Tunes the language of generated summaries for editorial, QC, producer, or full-detail review.": "调整生成摘要的表达方式,以适应剪辑、质检、制片或完整细节审阅。",
556
+ "Chooses compact readable reports, full reports, or machine-readable output for downstream agents.": "选择紧凑易读报告、完整报告或供下游智能体使用的机器可读输出。",
557
+ "Preview / triage": "预览 / 初筛",
558
+ "response chars": "响应字符数",
559
+ "tokens / clip": "每片段令牌",
560
+ "frames / clip": "每片段帧数",
561
+ "tokens / job": "每任务令牌",
562
+ "tokens / day": "每日令牌",
563
+ "wall clock (s)": "最长耗时(秒)",
564
+ "frame dim px": "帧尺寸(像素)",
565
+ "Realistic default": "实用默认值",
566
+ "generous": "宽裕",
567
+ "High-fidelity, few clips": "高保真、少量片段",
568
+ "Guards off — use with care": "关闭限制 · 请谨慎使用",
569
+ "no usage recorded yet": "尚无用量记录",
570
+ "No Resolve AI ops recorded yet for this project.": "当前项目尚无 Resolve AI 操作记录。",
571
+ "When on,": "启用后,",
572
+ "fires after every version-on-mutate archive. Protects against Resolve crashes losing history.": "会在每次变更前版本归档后执行,避免 Resolve 崩溃导致历史丢失。",
573
+ "Strict mode REFUSES the underlying call if the pre-mutation archive can't be created. Other destructive ops degrade silently — pass": "如果无法创建变更前归档,严格模式会拒绝底层调用。其他破坏性操作不会因此拒绝;可传入",
574
+ "in any action's params to opt in to refusal for that call.": "到任意操作参数中,让该次调用在归档失败时被拒绝。",
575
+ "no refusals recorded": "尚无限额拒绝记录",
576
+ "Sets the default answer for writing source-time analysis notes as Resolve clip markers.": "设置将源时间分析备注写为 Resolve 片段标记时的默认选择。",
577
+ "Controls how analysis text interacts with existing human-entered Resolve metadata.": "控制分析文本如何处理 Resolve 中现有的人工元数据。",
578
+ "Limited caps marker suggestions per clip. Unlimited stores 0 and does not truncate marker candidates.": "有限模式会限制每个片段的标记建议;无限模式存储为 0,且不会截断候选标记。",
579
+ "Namespaced custom data keeps provenance richer; minimal keeps Resolve marker payloads smaller.": "带命名空间的自定义数据会保留更多来源信息;最小化模式可减小 Resolve 标记载荷。",
580
+ "Description": "描述",
581
+ "Comments": "评论",
582
+ "Keywords": "关键词",
583
+ "People": "人物",
584
+ "Scene": "场景",
585
+ "Shot": "镜头",
586
+ "Take": "条次",
587
+ "Camera #": "摄影机编号",
588
+ "Roll Card #": "卷卡编号",
589
+ "Good Take": "好条",
590
+ "Location": "地点",
591
+ "Notes": "备注",
592
+ "Enabled fields receive analysis metadata when publishing is confirmed.": "确认发布后,启用的字段会接收分析元数据。",
593
+ "shots": "镜头",
594
+ "slate clap": "打板",
595
+ "sync events": "同步事件",
596
+ "best moments": "精彩时刻",
597
+ "qc warnings": "质检警告",
598
+ "Enabled marker types decide which source-time moments can become Resolve markers.": "启用的标记类型决定哪些源时间时刻可转为 Resolve 标记。",
599
+ "JSON map from marker type to Resolve marker color.": "标记类型到 Resolve 标记颜色的 JSON 映射。",
600
+ "Adds model or detector confidence values where analysis provides them.": "在分析结果提供置信度时,将其一并写入。",
601
+ "Includes source time references in generated metadata and marker notes.": "在生成的元数据和标记备注中包含源时间引用。",
602
+ "Keeps metadata writes behind an explicit confirmation step by default.": "默认要求明确确认后才写入元数据。",
603
+ "Prefers a preview pass before committing metadata or marker changes.": "提交元数据或标记更改前,优先进行预览。",
604
+ "Optional absolute path for analysis databases and reports. Empty uses the project analysis root.": "分析数据库和报告的可选绝对路径。留空则使用项目分析根目录。",
605
+ "Optional folder for generated sidecars or scratch outputs when a workflow needs them.": "工作流需要时,用于生成旁车文件或临时输出的可选文件夹。",
606
+ "Maximum number of clips to index during the Media Pool inventory walk (1–10000).": "遍历媒体池清单时最多索引的片段数(1–10000)。",
607
+ "Comma-separated folder names to skip entirely during the inventory walk. Empty indexes every folder.": "清单遍历时完全跳过的文件夹名称,以逗号分隔。留空则索引全部文件夹。",
608
+ "Resolve page to open after an operation completes, or stay put.": "操作完成后要打开的 Resolve 页面,也可保持当前页面。",
609
+ "Project root": "项目根目录",
610
+ "Server defaults": "服务器默认值",
611
+ "Persisted": "已持久化",
612
+ "Update state": "更新状态",
613
+ "Tracked": "已跟踪",
614
+ "Dashboard prefs": "面板偏好",
615
+ "Browser-local": "浏览器本地",
616
+ "Store": "存储位置",
617
+ "Browser localStorage": "浏览器 localStorage",
618
+ "Scope": "作用范围",
619
+ "This browser profile only": "仅当前浏览器配置文件",
620
+ "Controls update checks for the MCP package.": "控制 MCP 包的更新检查。",
621
+ "Minimum time between best-effort release checks.": "两次尽力而为的版本检查之间的最短时间。",
622
+ "How long snoozed update notices stay quiet.": "更新通知暂缓后保持静默的时长。",
623
+ "no update history yet": "尚无更新历史",
624
+ }
625
+
626
+
627
+ _LOCALIZATION_SCRIPT = r"""
628
+ const CONTROL_PANEL_LOCALE_KEY = 'davinci-resolve-mcp.locale';
629
+ const CONTROL_PANEL_TRANSLATIONS = __TRANSLATIONS__;
630
+ const CONTROL_PANEL_TEXT_ORIGINALS = new WeakMap();
631
+ const CONTROL_PANEL_ATTRIBUTE_ORIGINALS = new WeakMap();
632
+ const CONTROL_PANEL_LOCALIZED_ATTRIBUTES = ['aria-label', 'title', 'placeholder'];
633
+ let controlPanelLocale = (() => {
634
+ const stored = localStorage.getItem(CONTROL_PANEL_LOCALE_KEY);
635
+ if (stored === 'en' || stored === 'zh-CN') return stored;
636
+ return navigator.languages?.some(language => language.toLowerCase().startsWith('zh'))
637
+ ? 'zh-CN'
638
+ : 'en';
639
+ })();
640
+
641
+ function translateControlPanelValue(value) {
642
+ const exact = CONTROL_PANEL_TRANSLATIONS[value];
643
+ if (exact) return exact;
644
+ const patterns = [
645
+ [/^(\d+) selected$/, '已选择 $1 项'],
646
+ [/^(\d+) clips?$/, '$1 个片段'],
647
+ [/^(\d+) ready · (\d+) analyzed$/, '$1 个就绪 · $2 个已分析'],
648
+ [/^(\d+) ready$/, '$1 个就绪'],
649
+ [/^(\d+) missing$/, '$1 个缺失'],
650
+ [/^(\d+) of (\d+) analyzed \((\d+)%\)$/, '已分析 $1 / $2($3%)'],
651
+ [/^(\d+) warnings?$/, '$1 条警告'],
652
+ [/^(\d+) enabled$/, '已启用 $1 项'],
653
+ [/^(\d+) clips · (\d+) ready$/, '$1 个片段 · $2 个就绪'],
654
+ [/^Resolve: (.+) · read-only$/, 'Resolve:$1 · 只读'],
655
+ [/^(\d+) missing\/offline · (\d+) non-source records$/, '$1 个缺失/离线 · $2 条非源记录'],
656
+ [/^evidence base: (\d+)\/(\d+) clips analyzed \((\d+)%\)\.$/, '分析依据:已分析 $1 / $2 个片段($3%)。'],
657
+ [/^error — (.+)$/, '错误 · $1'],
658
+ [/^polling every (.+) · last (.+) · (\d+) clips$/, '每 $1 轮询 · 上次 $2 · $3 个片段'],
659
+ [/^Server defaults loaded · (.+)$/, '服务器默认值已加载 · $1'],
660
+ [/^(\d+) of (\d+) AI console actions available now$/, '当前可用 $1 / $2 项 AI 控制台操作'],
661
+ [/^(\d+) online · (\d+) missing\/offline$/, '$1 个在线 · $2 个缺失/离线'],
662
+ [/^(.+) · click for details$/, '$1 · 点击查看详情'],
663
+ [/^· needs (.+)$/, '· 需要 $1'],
664
+ [/^Loading (.+)…$/, '正在加载 $1…'],
665
+ [/^Updated (.+)$/, '更新于 $1'],
666
+ ];
667
+ for (const [pattern, replacement] of patterns) {
668
+ if (pattern.test(value)) return value.replace(pattern, replacement);
669
+ }
670
+ return value;
671
+ }
672
+
673
+ function splitControlPanelWhitespace(value) {
674
+ const match = value.match(/^(\s*)(.*?)(\s*)$/s);
675
+ return match ? { leading: match[1], core: match[2], trailing: match[3] } : null;
676
+ }
677
+
678
+ function localizeControlPanelTextNode(node) {
679
+ const current = node.data;
680
+ const previous = CONTROL_PANEL_TEXT_ORIGINALS.get(node);
681
+ if (controlPanelLocale === 'en') {
682
+ if (previous && current === previous.localized) node.data = previous.english;
683
+ return;
684
+ }
685
+ if (previous && current === previous.localized) return;
686
+ const parts = splitControlPanelWhitespace(current);
687
+ if (!parts || !parts.core) return;
688
+ const translated = translateControlPanelValue(parts.core);
689
+ if (translated === parts.core) return;
690
+ const localized = parts.leading + translated + parts.trailing;
691
+ CONTROL_PANEL_TEXT_ORIGINALS.set(node, { english: current, localized });
692
+ node.data = localized;
693
+ }
694
+
695
+ function localizeControlPanelAttribute(element, attribute) {
696
+ const current = element.getAttribute(attribute);
697
+ if (!current) return;
698
+ const originals = CONTROL_PANEL_ATTRIBUTE_ORIGINALS.get(element) || {};
699
+ const previous = originals[attribute];
700
+ if (controlPanelLocale === 'en') {
701
+ if (previous && current === previous.localized) element.setAttribute(attribute, previous.english);
702
+ return;
703
+ }
704
+ if (previous && current === previous.localized) return;
705
+ const translated = translateControlPanelValue(current);
706
+ if (translated === current) return;
707
+ originals[attribute] = { english: current, localized: translated };
708
+ CONTROL_PANEL_ATTRIBUTE_ORIGINALS.set(element, originals);
709
+ element.setAttribute(attribute, translated);
710
+ }
711
+
712
+ function localizeControlPanelTree(root) {
713
+ if (root.nodeType === Node.TEXT_NODE) {
714
+ localizeControlPanelTextNode(root);
715
+ return;
716
+ }
717
+ if (root.nodeType !== Node.ELEMENT_NODE && root.nodeType !== Node.DOCUMENT_NODE) return;
718
+ if (root.nodeType === Node.ELEMENT_NODE) {
719
+ for (const attribute of CONTROL_PANEL_LOCALIZED_ATTRIBUTES) {
720
+ localizeControlPanelAttribute(root, attribute);
721
+ }
722
+ }
723
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT);
724
+ let node = walker.nextNode();
725
+ while (node) {
726
+ if (node.nodeType === Node.TEXT_NODE) {
727
+ localizeControlPanelTextNode(node);
728
+ } else {
729
+ for (const attribute of CONTROL_PANEL_LOCALIZED_ATTRIBUTES) {
730
+ localizeControlPanelAttribute(node, attribute);
731
+ }
732
+ }
733
+ node = walker.nextNode();
734
+ }
735
+ }
736
+
737
+ function setControlPanelLocale(locale, persist = true) {
738
+ if (locale !== 'en' && locale !== 'zh-CN') throw new Error(`Unsupported locale: ${locale}`);
739
+ controlPanelLocale = locale;
740
+ document.documentElement.lang = locale;
741
+ if (persist) localStorage.setItem(CONTROL_PANEL_LOCALE_KEY, locale);
742
+ localizeControlPanelTree(document.body);
743
+ document.querySelectorAll('[data-locale]').forEach(button => {
744
+ const active = button.dataset.locale === locale;
745
+ button.classList.toggle('active', active);
746
+ button.setAttribute('aria-pressed', String(active));
747
+ });
748
+ }
749
+
750
+ document.querySelectorAll('[data-locale]').forEach(button => {
751
+ button.addEventListener('click', () => setControlPanelLocale(button.dataset.locale));
752
+ });
753
+ setControlPanelLocale(controlPanelLocale, false);
754
+ new MutationObserver(mutations => {
755
+ for (const mutation of mutations) {
756
+ if (mutation.type === 'attributes') {
757
+ localizeControlPanelAttribute(mutation.target, mutation.attributeName);
758
+ continue;
759
+ }
760
+ if (mutation.type === 'characterData') {
761
+ localizeControlPanelTextNode(mutation.target);
762
+ continue;
763
+ }
764
+ mutation.addedNodes.forEach(localizeControlPanelTree);
765
+ }
766
+ }).observe(document.body, {
767
+ subtree: true,
768
+ childList: true,
769
+ characterData: true,
770
+ attributes: true,
771
+ attributeFilter: CONTROL_PANEL_LOCALIZED_ATTRIBUTES,
772
+ });
773
+ """
774
+
775
+
776
+ def localization_script() -> str:
777
+ """Return the self-contained control-panel localization script."""
778
+
779
+ translations = json.dumps(ZH_CN_TRANSLATIONS, ensure_ascii=False, separators=(",", ":"))
780
+ return _LOCALIZATION_SCRIPT.replace("__TRANSLATIONS__", translations)