dsh-manage-session 2.0.3 → 2.0.4

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.
Files changed (43) hide show
  1. package/CHANGELOG.ja.md +33 -33
  2. package/CHANGELOG.ko.md +33 -33
  3. package/CHANGELOG.md +33 -33
  4. package/INSTALL.ja.md +164 -164
  5. package/INSTALL.ko.md +164 -164
  6. package/INSTALL.md +164 -164
  7. package/INSTALL.zh.md +164 -164
  8. package/LICENSE +21 -21
  9. package/README.en.md +76 -62
  10. package/README.ja.md +53 -39
  11. package/README.ko.md +52 -38
  12. package/README.md +18 -4
  13. package/cordis.patch.yml +19 -19
  14. package/dsh.plugin.json +2 -2
  15. package/locale/de.json +6 -6
  16. package/locale/en.json +6 -6
  17. package/locale/es.json +6 -6
  18. package/locale/fr.json +6 -6
  19. package/locale/it.json +6 -6
  20. package/locale/ja.json +6 -6
  21. package/locale/ko.json +6 -6
  22. package/locale/ru.json +6 -6
  23. package/locale/zh.json +6 -6
  24. package/package.json +9 -9
  25. package/screenshots.json +3 -3
  26. package/src/client/bridge-scope.ts +202 -202
  27. package/src/client/card.tsx +154 -154
  28. package/src/client/host-api.ts +61 -61
  29. package/src/client/index.ts +594 -594
  30. package/src/client/locales.ts +893 -893
  31. package/src/client/manage-locales.ts +178 -178
  32. package/src/client/manage.tsx +430 -430
  33. package/src/client/panel.tsx +131 -131
  34. package/src/config.ts +43 -43
  35. package/src/host/health/attribution.ts +118 -118
  36. package/src/host/health/lossless.ts +128 -128
  37. package/src/host/health/source-kind.ts +350 -350
  38. package/src/host/history/archive-source.ts +250 -250
  39. package/src/host/history/archive.ts +249 -249
  40. package/src/host/history/favorites.ts +71 -71
  41. package/src/host/history/purge.ts +370 -370
  42. package/src/host/registry/archive-registry.ts +317 -317
  43. package/src/host/settings-bridge.ts +222 -222
@@ -1,893 +1,893 @@
1
- /**
2
- * 会话管理的多语言文案(zh 为键集基准,另含 en 与 ja/ko/fr/de/it/ru/es)。
3
- *
4
- * 硬约束:本文件**不得出现「搜索」二字** —— 搜索/索引属 dsh-manage-session-search 那一片。
5
- * 文案走 `translate(t, key, params)`,字典由官方 locale 服务注册(缺失时回退内置 zh)。
6
- */
7
-
8
- /** 全部文案 key。 */
9
- export type LocaleKey = keyof typeof zh
10
-
11
- /** 简体中文(内置回退字典)。 */
12
- export const zh = {
13
- 'card.title': '会话管理',
14
- 'card.desc': '历史文件 · 健康检查',
15
- 'card.enabled': '启用会话管理',
16
- 'card.enabled.desc': '关闭后不注册任何接口,侧边栏入口整体消失。',
17
- 'card.history': '会话历史文件',
18
- 'card.history.desc': '归档浏览与清理(养老院)。关闭后不注册历史接口、不渲染该页签。',
19
- 'card.health': '健康检查',
20
- 'card.health.desc': '体检 → 处方 → 出院。关闭后不注册体检接口、不渲染该页签。',
21
- 'card.directDelete': '直接删除入口',
22
- 'card.directDelete.desc': '开启后搜索面板结果行出现「删除」按钮(两步确认,与管理端同款链路)。默认关闭。',
23
- 'menu.deleteSession': '删除会话',
24
- 'menu.deleteSession.confirm': '确认删除',
25
- 'card.readonly': '当前会话无法修改设置(只读挂载)。',
26
- 'card.unavailable': '设置服务不可用,无法读取配置。',
27
-
28
- 'family.title': '起子插件设置',
29
-
30
- 'panel.title': '会话管理',
31
- 'panel.tab.history': '养老院',
32
- 'panel.tab.health': '体检',
33
- 'panel.tab.manage': '管理',
34
- 'panel.close': '关闭',
35
- 'panel.untitled': '(无标题)',
36
-
37
- 'history.loading': '正在读取归档列表…',
38
- 'history.empty': '归档集合为空。',
39
- 'history.edit': '编辑',
40
- 'history.done': '完成',
41
- 'history.selectAll': '全选',
42
- 'history.unselectAll': '取消全选',
43
- // 两个操作刻意用两个词,不可互换:
44
- // 「取消归档状态」= 只改归档数组(可逆);「清理归档文件」= 真删磁盘实体(不可逆)。
45
- 'history.unarchive': '取消归档状态 ({n})',
46
- 'history.unarchiving': '取消归档中…',
47
- 'history.purge': '清理归档文件 ({n}) · 释放 {size}',
48
- 'history.purging': '清理中…',
49
- 'history.editingHint': '编辑模式:勾选会话后二选一 ——「取消归档状态」只把它从官方归档数组移除(可逆,重启后回到侧边栏);「清理归档文件」真删磁盘上的转录与投影缓存(不可逆)。',
50
- 'history.restartHint': '已从归档数组移除 {removed} 个 id(剩余 {remaining})。请**立刻重启 DSH**:宿主退出前任何归档或工作区改动都会把整份内存快照写回文件,这次操作会被还原。',
51
- 'history.pendingRestart': '宿主内存里仍有 {n} 个归档未失效:列表已按存储文件显示(这些已不在),但它们对应的会话在重启 DSH 前仍被侧边栏隐藏。',
52
- 'history.count': '共 {n} 条 · 占 {size}',
53
- 'history.size': '转录 {t} · 缓存 {c}',
54
- 'history.sizeUnknown': '磁盘上无实体',
55
- 'history.source.registry': '来源:宿主归档注册表',
56
- 'history.source.storage-file': '来源:存储文件',
57
- 'history.source.none': '两处都读不到归档集合',
58
- 'history.confirmUnarchive': '确认取消这 {n} 个会话的归档状态?\n\n{summary}\n\n只改归档数组(自动备份),磁盘文件不动。重启 DSH 后这些会话回到原工作区。',
59
- 'history.confirmPurge': '确认清理这 {n} 个归档会话的磁盘文件?\n\n{summary}\n\n合计释放约 {size}。\n将删除:转录目录(含旧格式副本)、逐条投影缓存,并从归档数组与工作区成员表中移除。\n\n**此操作不可逆,会话将无法恢复。**',
60
- 'history.purgeResult': '已清理 {purged} 个归档会话,释放 {size}。请重启 DSH 使其彻底消失。',
61
- 'history.purgePartial': '已清理 {purged} 个,释放 {size};{failed} 个失败(见下方明细)。',
62
- 'history.purgeFailures': '失败明细',
63
-
64
- 'health.scan': '开始体检',
65
- 'health.scanning': '体检中…',
66
- // 分批扫描的进度读数:done/total + 已用秒数(total 由宿主报告的总数给出)
67
- 'health.progress': '{done}/{total} · 已用 {sec}s',
68
- 'health.empty': '未发现异常会话。',
69
- 'health.clean': '全部检查通过。',
70
- 'health.level.ok': '正常',
71
- 'health.level.warn': '注意',
72
- // 中性档:本门无从判定(如冷态会话无热态投影),不参与 warn 聚合
73
- 'health.level.skipped': '未检查',
74
- 'health.level.fail': '异常',
75
- // 处置优先级:当前代是从迁移暂存(TMP)发布出来的会话,排在最前
76
- 'health.priority.high': '优先',
77
- 'health.gate.generation': '代次产物',
78
- 'health.gate.log-integrity': '日志完整性',
79
- 'health.gate.projection-cache': '投影缓存',
80
- 'health.gate.lossless-json': '无损 JSON',
81
- 'health.gate.cold-read': '可接续性',
82
- 'health.gate.source-kind': '插件署名',
83
- 'health.attribution': '归属',
84
- 'health.field': '字段',
85
- 'health.unknownOwner': '归属未知',
86
- 'health.phase.checkup': '① 体检',
87
- 'health.phase.prescribe': '② 处方',
88
- 'health.phase.discharge': '③ 出院',
89
- 'health.repair': '执行可逆处置',
90
- 'health.repairing': '处置中…',
91
- 'health.sourceMigrate': '转换旧署名',
92
- 'health.sourceMigrating': '转换中…',
93
- 'health.sourceMigrated': '已转换 {n} 行旧署名(备份:{backup})',
94
- 'health.sourceMigrateNone': '没有需要转换的旧署名行',
95
- 'health.sourceMigrateFail': '转换失败:{error}',
96
- 'health.before': '处置前',
97
- 'health.after': '处置后',
98
- 'health.quarantined': '已隔离投影缓存记录(重启 DSH 后重折叠)',
99
- 'health.commands': '可执行命令',
100
- 'health.copy': '复制',
101
- 'health.copied': '已复制',
102
- 'health.detail': '查看详情',
103
- 'health.back': '返回列表',
104
-
105
- // 缓存:体检结果在宿主进程内留一份,关面板重开零延迟。
106
- // 生成时间必须显式露出——旧结果不能冒充刚扫的。
107
- 'health.cache.hint': '结果来自缓存 · {ago}',
108
- 'health.cache.refresh': '刷新',
109
- 'health.cache.corpusChanged': '语料已变化({was} → {now}),建议刷新',
110
- 'health.ago.justNow': '刚刚',
111
- 'health.ago.minutes': '{n} 分钟前',
112
- 'health.ago.hours': '{n} 小时前',
113
- 'health.ago.days': '{n} 天前',
114
- } as const
115
-
116
- /** English dictionary (same keys). */
117
- export const en: Record<LocaleKey, string> = {
118
- 'card.title': 'Session Manager',
119
- 'card.desc': 'History files · Health check',
120
- 'card.enabled': 'Enable Session Manager',
121
- 'card.enabled.desc': 'When off, no API is registered and the sidebar entry disappears.',
122
- 'card.history': 'Session history files',
123
- 'card.history.desc': 'Archive browsing and cleanup (Retirement Home). When off, history routes and the tab are gone.',
124
- 'card.health': 'Health check',
125
- 'card.health.desc': 'Checkup → Prescription → Discharge. When off, health routes and the tab are gone.',
126
- 'card.directDelete': 'Direct delete entry',
127
- 'card.directDelete.desc': 'When on, result rows in the search panel get a Delete button (two-step confirm, same pipeline as the manage view). Off by default.',
128
- 'menu.deleteSession': 'Delete session',
129
- 'menu.deleteSession.confirm': 'Confirm delete',
130
- 'card.readonly': 'This session cannot edit settings (read-only mount).',
131
- 'card.unavailable': 'Settings service unavailable; configuration cannot be read.',
132
- 'family.title': 'Screwdriver plugin settings',
133
-
134
- 'panel.title': 'Session Manager',
135
- 'panel.tab.history': 'Retirement Home',
136
- 'panel.tab.health': 'Checkup',
137
- 'panel.tab.manage': 'Manage',
138
- 'panel.close': 'Close',
139
- 'panel.untitled': '(untitled)',
140
-
141
- 'history.loading': 'Loading archived sessions…',
142
- 'history.empty': 'The archive set is empty.',
143
- 'history.edit': 'Edit',
144
- 'history.done': 'Done',
145
- 'history.selectAll': 'Select all',
146
- 'history.unselectAll': 'Clear selection',
147
- // Two deliberately distinct verbs — never interchangeable:
148
- // "unarchive state" edits only the archive array (reversible); "purge archive files" really deletes (irreversible).
149
- 'history.unarchive': 'Unarchive state ({n})',
150
- 'history.unarchiving': 'Unarchiving…',
151
- 'history.purge': 'Purge archive files ({n}) · free {size}',
152
- 'history.purging': 'Purging…',
153
- 'history.editingHint': 'Edit mode: tick sessions, then pick one — "Unarchive state" only removes them from the official archive array (reversible; they return to the sidebar after a restart); "Purge archive files" really deletes the transcript and projection cache from disk (irreversible).',
154
- 'history.restartHint': 'Removed {removed} id(s) from the archive array ({remaining} left). **Restart DSH now**: until the host exits, any archive or workspace change rewrites the whole in-memory snapshot back to the file and undoes this.',
155
- 'history.pendingRestart': '{n} archived id(s) are still live in host memory: the list follows the storage file (they are already gone), but their sessions stay hidden from the sidebar until DSH restarts.',
156
- 'history.count': '{n} total · {size} on disk',
157
- 'history.size': 'transcript {t} · cache {c}',
158
- 'history.sizeUnknown': 'no on-disk entity',
159
- 'history.source.registry': 'Source: host archive registry',
160
- 'history.source.storage-file': 'Source: storage file',
161
- 'history.source.none': 'The archive set is unreadable from both sources',
162
- 'history.confirmUnarchive': 'Unarchive these {n} session(s)?\n\n{summary}\n\nOnly the archive array is edited (auto backup); nothing on disk is touched. After a DSH restart these sessions return to their workspaces.',
163
- 'history.confirmPurge': 'Purge the on-disk files of these {n} archived session(s)?\n\n{summary}\n\nAbout {size} will be freed.\nThis deletes: the transcript directory (including older-format copies) and the per-session projection cache, and removes them from the archive array and the workspace membership tables.\n\n**This cannot be undone — the sessions will be unrecoverable.**',
164
- 'history.purgeResult': 'Purged {purged} archived session(s), freed {size}. Restart DSH to make them disappear completely.',
165
- 'history.purgePartial': 'Purged {purged}, freed {size}; {failed} failed (see details below).',
166
- 'history.purgeFailures': 'Failure details',
167
-
168
- 'health.scan': 'Run checkup',
169
- 'health.scanning': 'Running…',
170
- 'health.progress': '{done}/{total} · {sec}s elapsed',
171
- 'health.empty': 'No problem session found.',
172
- 'health.clean': 'All checks passed.',
173
- 'health.level.ok': 'ok',
174
- 'health.level.warn': 'warn',
175
- 'health.level.skipped': 'Not checked',
176
- 'health.level.fail': 'fail',
177
- 'health.priority.high': 'Priority',
178
- 'health.gate.generation': 'Generation artifact',
179
- 'health.gate.log-integrity': 'Log integrity',
180
- 'health.gate.projection-cache': 'Projection cache',
181
- 'health.gate.lossless-json': 'Lossless JSON',
182
- 'health.gate.cold-read': 'Continuability',
183
- 'health.gate.source-kind': 'Plugin signature',
184
- 'health.attribution': 'Owner',
185
- 'health.field': 'Field',
186
- 'health.unknownOwner': 'owner unknown',
187
- 'health.phase.checkup': '1) Checkup',
188
- 'health.phase.prescribe': '2) Prescription',
189
- 'health.phase.discharge': '3) Discharge',
190
- 'health.repair': 'Apply reversible repair',
191
- 'health.repairing': 'Repairing…',
192
- 'health.sourceMigrate': 'Migrate legacy signatures',
193
- 'health.sourceMigrating': 'Migrating…',
194
- 'health.sourceMigrated': 'Migrated {n} legacy signature rows (backup: {backup})',
195
- 'health.sourceMigrateNone': 'No legacy signature rows to migrate',
196
- 'health.sourceMigrateFail': 'Migration failed: {error}',
197
- 'health.before': 'Before',
198
- 'health.after': 'After',
199
- 'health.quarantined': 'Projection cache quarantined (refolds after a DSH restart)',
200
- 'health.commands': 'Commands',
201
- 'health.copy': 'Copy',
202
- 'health.copied': 'Copied',
203
- 'health.detail': 'Details',
204
- 'health.back': 'Back',
205
-
206
- 'health.cache.hint': 'Cached result · {ago}',
207
- 'health.cache.refresh': 'Refresh',
208
- 'health.cache.corpusChanged': 'Corpus changed ({was} → {now}); refresh recommended',
209
- 'health.ago.justNow': 'just now',
210
- 'health.ago.minutes': '{n} min ago',
211
- 'health.ago.hours': '{n} h ago',
212
- 'health.ago.days': '{n} d ago',
213
- }
214
-
215
- /** 日本語辞書(zh のキー集合に対し完全性を検査)。 */
216
- export const ja: Partial<Record<LocaleKey, string>> = {
217
- 'card.title': 'セッションマネージャー',
218
- 'card.desc': '履歴ファイル · ヘルスチェック',
219
- 'card.enabled': 'セッションマネージャーを有効化',
220
- 'card.enabled.desc': 'オフにすると API を一切登録せず、サイドバーの入口も消えます。',
221
- 'card.history': 'セッション履歴ファイル',
222
- 'card.history.desc': 'アーカイブの閲覧とクリーンアップ(老人ホーム)。オフにすると履歴 API を登録せず、このタブも描画しません。',
223
- 'card.health': 'ヘルスチェック',
224
- 'card.health.desc': '健診 → 処方 → 退院。オフにすると健診 API を登録せず、このタブも描画しません。',
225
- 'card.directDelete': '直接削除の入口',
226
- 'card.directDelete.desc': 'オンにすると検索パネルの結果行に「削除」ボタンが表示されます(二段階確認、管理画面と同じ経路)。デフォルトはオフ。',
227
- 'menu.deleteSession': 'セッションを削除',
228
- 'menu.deleteSession.confirm': '削除を確認',
229
- 'card.readonly': 'このセッションでは設定を変更できません(読み取り専用マウント)。',
230
- 'card.unavailable': '設定サービスが利用できず、構成を読み取れません。',
231
- 'family.title': 'スクリュードライバー プラグイン設定',
232
- 'panel.title': 'セッションマネージャー',
233
- 'panel.tab.history': '老人ホーム',
234
- 'panel.tab.health': '健診',
235
- 'panel.close': '閉じる',
236
- 'panel.untitled': '(無題)',
237
- 'history.loading': 'アーカイブ一覧を読み込んでいます…',
238
- 'history.empty': 'アーカイブ集合は空です。',
239
- 'history.edit': '編集',
240
- 'history.done': '完了',
241
- 'history.selectAll': 'すべて選択',
242
- 'history.unselectAll': '全選択を解除',
243
- 'history.unarchive': 'アーカイブ状態を解除 ({n})',
244
- 'history.unarchiving': 'アーカイブ解除中…',
245
- 'history.purge': 'アーカイブファイルを消去 ({n}) · {size} 解放',
246
- 'history.purging': '消去中…',
247
- 'history.editingHint': '編集モード:セッションにチェックを入れ、二者択一 ——「アーカイブ状態を解除」は公式アーカイブ配列から除去するだけ(可逆、再起動後サイドバーに戻る);「アーカイブファイルを消去」はディスク上のトランスクリプトと投影キャッシュを実削除します(不可逆)。',
248
- 'history.restartHint': 'アーカイブ配列から {removed} 件の id を除去しました(残り {remaining})。**今すぐ DSH を再起動**:ホスト終了前にアーカイブやワークスペースへの変更があると、メモリ全体のスナップショットがファイルへ書き戻され、この操作が巻き戻ります。',
249
- 'history.pendingRestart': 'ホストのメモリ内にはまだ {n} 件のアーカイブが無効化されていません:一覧はストレージファイルに従って表示されます(これらは既に存在しない)が、対応するセッションは DSH を再起動するまでサイドバーに隠れたままです。',
250
- 'history.count': '全 {n} 件 · 占有 {size}',
251
- 'history.size': 'トランスクリプト {t} · キャッシュ {c}',
252
- 'history.sizeUnknown': 'ディスク上に実体なし',
253
- 'history.source.registry': '取得元:ホストのアーカイブレジストリ',
254
- 'history.source.storage-file': '取得元:ストレージファイル',
255
- 'history.source.none': 'どちらのソースからもアーカイブ集合を読めません',
256
- 'history.confirmUnarchive': 'これら {n} 件のセッションのアーカイブ状態を解除しますか?\n\n{summary}\n\nアーカイブ配列のみを編集します(自動バックアップ)、ディスク上のファイルは動きません。DSH 再起動後、これらのセッションは元のワークスペースに戻ります。',
257
- 'history.confirmPurge': 'これら {n} 件のアーカイブ済みセッションのディスク上のファイルを消去しますか?\n\n{summary}\n\n合計約 {size} を解放します。\n削除対象:トランスクリプトディレクトリ(旧形式のコピーを含む)、個別の投影キャッシュ、およびアーカイブ配列とワークスペースメンバーテーブルからの除去。\n\n**この操作は不可逆で、セッションは復元できません。**',
258
- 'history.purgeResult': '{purged} 件のアーカイブ済みセッションを消去し、{size} を解放しました。完全に消失させるには DSH を再起動してください。',
259
- 'history.purgePartial': '{purged} 件を消去し、{size} を解放;{failed} 件が失敗(以下の明細参照)。',
260
- 'history.purgeFailures': '失敗の明細',
261
- 'health.scan': '健診を開始',
262
- 'health.scanning': '健診中…',
263
- 'health.progress': '{done}/{total} · 経過 {sec}s',
264
- 'health.empty': '異常なセッションは見つかりませんでした。',
265
- 'health.clean': 'すべてのチェックに合格しました。',
266
- 'health.level.ok': '正常',
267
- 'health.level.warn': '注意',
268
- 'health.level.skipped': '未チェック',
269
- 'health.level.fail': '異常',
270
- 'health.priority.high': '優先',
271
- 'health.gate.generation': '世代アーティファクト',
272
- 'health.gate.log-integrity': 'ログ完全性',
273
- 'health.gate.projection-cache': '投影キャッシュ',
274
- 'health.gate.lossless-json': '可逆 JSON',
275
- 'health.gate.cold-read': '継続可能性',
276
- 'health.gate.source-kind': 'プラグイン署名',
277
- 'health.attribution': '帰属',
278
- 'health.field': 'フィールド',
279
- 'health.unknownOwner': '帰属不明',
280
- 'health.phase.checkup': '① 健診',
281
- 'health.phase.prescribe': '② 処方',
282
- 'health.phase.discharge': '③ 退院',
283
- 'health.repair': '可逆的な処置を実行',
284
- 'health.repairing': '処置中…',
285
- 'health.sourceMigrate': '旧署名を変換',
286
- 'health.sourceMigrating': '変換中…',
287
- 'health.sourceMigrated': '{n} 行の旧署名を変換しました(バックアップ:{backup})',
288
- 'health.sourceMigrateNone': '変換すべき旧署名行はありません',
289
- 'health.sourceMigrateFail': '変換に失敗:{error}',
290
- 'health.before': '処置前',
291
- 'health.after': '処置後',
292
- 'health.quarantined': '投影キャッシュのレコードを隔離しました(DSH 再起動後に再折り畳み)',
293
- 'health.commands': '実行可能なコマンド',
294
- 'health.copy': 'コピー',
295
- 'health.copied': 'コピーしました',
296
- 'health.detail': '詳細を見る',
297
- 'health.back': '一覧に戻る',
298
- 'health.cache.hint': '結果はキャッシュ由来 · {ago}',
299
- 'health.cache.refresh': '更新',
300
- 'health.cache.corpusChanged': 'コーパスが変化({was} → {now})、更新を推奨',
301
- 'health.ago.justNow': 'たった今',
302
- 'health.ago.minutes': '{n} 分前',
303
- 'health.ago.hours': '{n} 時間前',
304
- 'health.ago.days': '{n} 日前',
305
- }
306
-
307
- /** 한국어 사전 (zh 키 집합에 대해 완전성 검사). */
308
- export const ko: Partial<Record<LocaleKey, string>> = {
309
- 'card.title': '세션 매니저',
310
- 'card.desc': '기록 파일 · 건강 점검',
311
- 'card.enabled': '세션 매니저 활성화',
312
- 'card.enabled.desc': '끄면 어떤 API도 등록하지 않고 사이드바 항목이 통째로 사라집니다.',
313
- 'card.history': '세션 기록 파일',
314
- 'card.history.desc': '아카이브 탐색 및 정리(요양소). 끄면 기록 API를 등록하지 않고 이 탭을 렌더링하지 않습니다.',
315
- 'card.health': '건강 점검',
316
- 'card.health.desc': '진료 → 처방 → 퇴원. 끄면 진료 API를 등록하지 않고 이 탭을 렌더링하지 않습니다.',
317
- 'card.directDelete': '직접 삭제 입구',
318
- 'card.directDelete.desc': '켜면 검색 패널 결과 행에 「삭제」 버튼이 표시됩니다(2단계 확인, 관리 화면과 동일한 경로). 기본은 끄기.',
319
- 'menu.deleteSession': '세션 삭제',
320
- 'menu.deleteSession.confirm': '삭제 확인',
321
- 'card.readonly': '이 세션에서는 설정을 수정할 수 없습니다(읽기 전용 마운트).',
322
- 'card.unavailable': '설정 서비스를 사용할 수 없어 구성을 읽을 수 없습니다.',
323
- 'family.title': '스크루드라이버 플러그인 설정',
324
- 'panel.title': '세션 매니저',
325
- 'panel.tab.history': '요양소',
326
- 'panel.tab.health': '진료',
327
- 'panel.close': '닫기',
328
- 'panel.untitled': '(제목 없음)',
329
- 'history.loading': '아카이브 목록을 읽는 중…',
330
- 'history.empty': '아카이브 집합이 비어 있습니다.',
331
- 'history.edit': '편집',
332
- 'history.done': '완료',
333
- 'history.selectAll': '전체 선택',
334
- 'history.unselectAll': '전체 선택 해제',
335
- 'history.unarchive': '아카이브 상태 해제 ({n})',
336
- 'history.unarchiving': '아카이브 해제 중…',
337
- 'history.purge': '아카이브 파일 정리 ({n}) · {size} 확보',
338
- 'history.purging': '정리 중…',
339
- 'history.editingHint': '편집 모드: 세션을 체크한 뒤 둘 중 하나 —— 「아카이브 상태 해제」는 공식 아카이브 배열에서만 제거(되돌릴 수 있음, 재시작 후 사이드바로 복귀); 「아카이브 파일 정리」는 디스크의 전사본과 투영 캐시를 실제 삭제(되돌릴 수 없음).',
340
- 'history.restartHint': '아카이브 배열에서 id {removed}개를 제거했습니다(남은 {remaining}). **지금 즉시 DSH를 재시작**: 호스트 종료 전 아카이브나 워크스페이스 변경이 있으면 메모리 전체 스냅샷이 파일로 다시 쓰여 이 작업이 되돌려집니다.',
341
- 'history.pendingRestart': '호스트 메모리에 아직 {n}개 아카이브가 유효합니다: 목록은 저장 파일을 따라 표시(이제 없음)하지만, 해당 세션은 DSH 재시작 전까지 사이드바에 숨겨진 채로 남아 있습니다.',
342
- 'history.count': '총 {n}건 · {size} 점유',
343
- 'history.size': '전사본 {t} · 캐시 {c}',
344
- 'history.sizeUnknown': '디스크에 실체 없음',
345
- 'history.source.registry': '출처: 호스트 아카이브 등록부',
346
- 'history.source.storage-file': '출처: 저장 파일',
347
- 'history.source.none': '두 소스 모두에서 아카이브 집합을 읽을 수 없음',
348
- 'history.confirmUnarchive': '이 {n}개 세션의 아카이브 상태를 해제하시겠습니까?\n\n{summary}\n\n아카이브 배열만 편집(자동 백업)하고 디스크 파일은 건드리지 않습니다. DSH 재시작 후 이 세션들은 원래 워크스페이스로 돌아갑니다.',
349
- 'history.confirmPurge': '이 {n}개 아카이브된 세션의 디스크 파일을 정리하시겠습니까?\n\n{summary}\n\n총 약 {size}를 확보합니다.\n삭제 대상: 전사본 디렉터리(구형식 사본 포함)와 건별 투영 캐시, 그리고 아카이브 배열 및 워크스페이스 구성원 표에서 제거.\n\n**이 작업은 되돌릴 수 없으며 세션은 복원되지 않습니다.**',
350
- 'history.purgeResult': '아카이브된 세션 {purged}개를 정리하고 {size}를 확보했습니다. 완전히 사라지게 하려면 DSH를 재시작하세요.',
351
- 'history.purgePartial': '{purged}개 정리, {size} 확보; {failed}개 실패(아래 명세 참조).',
352
- 'history.purgeFailures': '실패 명세',
353
- 'health.scan': '진료 시작',
354
- 'health.scanning': '진료 중…',
355
- 'health.progress': '{done}/{total} · {sec}s 경과',
356
- 'health.empty': '이상 세션을 찾지 못했습니다.',
357
- 'health.clean': '모든 점검을 통과했습니다.',
358
- 'health.level.ok': '정상',
359
- 'health.level.warn': '주의',
360
- 'health.level.skipped': '미점검',
361
- 'health.level.fail': '이상',
362
- 'health.priority.high': '우선',
363
- 'health.gate.generation': '세대 산출물',
364
- 'health.gate.log-integrity': '로그 무결성',
365
- 'health.gate.projection-cache': '투영 캐시',
366
- 'health.gate.lossless-json': '무손실 JSON',
367
- 'health.gate.cold-read': '연속 가능',
368
- 'health.gate.source-kind': '플러그인 서명',
369
- 'health.attribution': '귀속',
370
- 'health.field': '필드',
371
- 'health.unknownOwner': '귀속 불명',
372
- 'health.phase.checkup': '① 진료',
373
- 'health.phase.prescribe': '② 처방',
374
- 'health.phase.discharge': '③ 퇴원',
375
- 'health.repair': '가역적 처치 실행',
376
- 'health.repairing': '처치 중…',
377
- 'health.sourceMigrate': '구 서명 변환',
378
- 'health.sourceMigrating': '변환 중…',
379
- 'health.sourceMigrated': '구 서명 {n}행을 변환했습니다(백업: {backup})',
380
- 'health.sourceMigrateNone': '변환할 구 서명 행이 없습니다',
381
- 'health.sourceMigrateFail': '변환 실패: {error}',
382
- 'health.before': '처치 전',
383
- 'health.after': '처치 후',
384
- 'health.quarantined': '투영 캐시 기록을 격리했습니다(DSH 재시작 후 재접힘)',
385
- 'health.commands': '실행 가능 명령',
386
- 'health.copy': '복사',
387
- 'health.copied': '복사됨',
388
- 'health.detail': '상세 보기',
389
- 'health.back': '목록으로',
390
- 'health.cache.hint': '결과를 캐시에서 · {ago}',
391
- 'health.cache.refresh': '새로고침',
392
- 'health.cache.corpusChanged': '코퍼스 변경({was} → {now}), 새로고침 권장',
393
- 'health.ago.justNow': '방금',
394
- 'health.ago.minutes': '{n}분 전',
395
- 'health.ago.hours': '{n}시간 전',
396
- 'health.ago.days': '{n}일 전',
397
- }
398
-
399
- /** Dictionnaire français, vérifié complet contre le jeu de clés zh. */
400
- export const fr: Partial<Record<LocaleKey, string>> = {
401
- 'card.title': 'Gestionnaire de sessions',
402
- 'card.desc': 'Fichiers d’historique · Contrôle de santé',
403
- 'card.enabled': 'Activer l’intendant de session',
404
- 'card.enabled.desc': 'Désactivé, aucune API n’est enregistrée et l’entrée de la barre latérale disparaît.',
405
- 'card.history': 'Fichiers d’historique de session',
406
- 'card.history.desc': 'Consultation et nettoyage des archives (Maison de retraite). Désactivé, aucune API d’historique n’est enregistrée et cet onglet n’est pas rendu.',
407
- 'card.health': 'Contrôle de santé',
408
- 'card.health.desc': 'Examen → Ordonnance → Sortie. Désactivé, aucune API de contrôle n’est enregistrée et cet onglet n’est pas rendu.',
409
- 'card.directDelete': 'Entrée de suppression directe',
410
- 'card.directDelete.desc': 'Activé, les lignes de résultat du panneau de recherche affichent un bouton Supprimer (double confirmation, même pipeline que la gestion). Désactivé par défaut.',
411
- 'menu.deleteSession': 'Supprimer la session',
412
- 'menu.deleteSession.confirm': 'Confirmer la suppression',
413
- 'card.readonly': 'Cette session ne peut pas modifier les réglages (montage en lecture seule).',
414
- 'card.unavailable': 'Service de réglages indisponible ; configuration illisible.',
415
- 'family.title': 'Réglages du plugin Screwdriver',
416
- 'panel.title': 'Gestionnaire de sessions',
417
- 'panel.tab.history': 'Maison de retraite',
418
- 'panel.tab.health': 'Examen',
419
- 'panel.close': 'Fermer',
420
- 'panel.untitled': '(sans titre)',
421
- 'history.loading': 'Lecture de la liste des archives…',
422
- 'history.empty': 'L’ensemble des archives est vide.',
423
- 'history.edit': 'Modifier',
424
- 'history.done': 'Terminé',
425
- 'history.selectAll': 'Tout sélectionner',
426
- 'history.unselectAll': 'Tout désélectionner',
427
- 'history.unarchive': 'Retirer l’état archivé ({n})',
428
- 'history.unarchiving': 'Désarchivage…',
429
- 'history.purge': 'Purger les fichiers archivés ({n}) · libérer {size}',
430
- 'history.purging': 'Purge…',
431
- 'history.editingHint': 'Mode modification : cochez des sessions puis choisissez — « Retirer l’état archivé » ne fait que les retirer du tableau officiel des archives (réversible ; elles reviennent dans la barre latérale après un redémarrage) ; « Purger les fichiers archivés » supprime réellement le transcript et le cache de projection du disque (irréversible).',
432
- 'history.restartHint': '{removed} id retirés du tableau des archives ({remaining} restants). **Redémarrez DSH maintenant** : tant que l’hôte n’est pas fermé, toute modification des archives ou de l’espace de travail réécrit l’intégralité de l’instantané en mémoire vers le fichier et annule cette opération.',
433
- 'history.pendingRestart': '{n} archives sont encore vivantes en mémoire de l’hôte : la liste suit le fichier de stockage (elles n’y sont plus), mais leurs sessions restent masquées de la barre latérale tant que DSH n’a pas redémarré.',
434
- 'history.count': '{n} entrées · {size} occupés',
435
- 'history.size': 'transcript {t} · cache {c}',
436
- 'history.sizeUnknown': 'aucune entité sur le disque',
437
- 'history.source.registry': 'Source : registre d’archives de l’hôte',
438
- 'history.source.storage-file': 'Source : fichier de stockage',
439
- 'history.source.none': 'L’ensemble des archives est illisible des deux sources',
440
- 'history.confirmUnarchive': 'Retirer l’état archivé de ces {n} session(s) ?\n\n{summary}\n\nSeul le tableau des archives est modifié (sauvegarde automatique) ; rien sur le disque n’est touché. Après un redémarrage de DSH, ces sessions reviennent dans leurs espaces de travail.',
441
- 'history.confirmPurge': 'Purger les fichiers sur disque de ces {n} session(s) archivée(s) ?\n\n{summary}\n\nEnviron {size} seront libérés.\nSeront supprimés : le répertoire des transcripts (y compris les copies de format antérieur) et le cache de projection par session, puis retrait du tableau des archives et des tables de membres de l’espace de travail.\n\n**Cette opération est irréversible — les sessions seront irrécupérables.**',
442
- 'history.purgeResult': '{purged} session(s) archivée(s) purgée(s), {size} libérés. Redémarrez DSH pour qu’elles disparaissent complètement.',
443
- 'history.purgePartial': '{purged} purgée(s), {size} libérés ; {failed} en échec (voir le détail ci-dessous).',
444
- 'history.purgeFailures': 'Détail des échecs',
445
- 'health.scan': 'Lancer le contrôle',
446
- 'health.scanning': 'Contrôle en cours…',
447
- 'health.progress': '{done}/{total} · {sec}s écoulés',
448
- 'health.empty': 'Aucune session problématique trouvée.',
449
- 'health.clean': 'Tous les contrôles sont passés.',
450
- 'health.level.ok': 'ok',
451
- 'health.level.warn': 'attention',
452
- 'health.level.skipped': 'Non contrôlé',
453
- 'health.level.fail': 'anomalie',
454
- 'health.priority.high': 'Priorité',
455
- 'health.gate.generation': 'Artefact de génération',
456
- 'health.gate.log-integrity': 'Intégrité des journaux',
457
- 'health.gate.projection-cache': 'Cache de projection',
458
- 'health.gate.lossless-json': 'JSON sans perte',
459
- 'health.gate.cold-read': 'Continuité',
460
- 'health.gate.source-kind': 'Signature du plugin',
461
- 'health.attribution': 'Propriétaire',
462
- 'health.field': 'Champ',
463
- 'health.unknownOwner': 'propriétaire inconnu',
464
- 'health.phase.checkup': '1) Examen',
465
- 'health.phase.prescribe': '2) Ordonnance',
466
- 'health.phase.discharge': '3) Sortie',
467
- 'health.repair': 'Appliquer une réparation réversible',
468
- 'health.repairing': 'Réparation…',
469
- 'health.sourceMigrate': 'Convertir les signatures héritées',
470
- 'health.sourceMigrating': 'Conversion…',
471
- 'health.sourceMigrated': '{n} lignes de signature héritée converties (sauvegarde : {backup})',
472
- 'health.sourceMigrateNone': 'Aucune ligne de signature héritée à convertir',
473
- 'health.sourceMigrateFail': 'Échec de la conversion : {error}',
474
- 'health.before': 'Avant',
475
- 'health.after': 'Après',
476
- 'health.quarantined': 'Enregistrement du cache de projection mis en quarantaine (replié après un redémarrage de DSH)',
477
- 'health.commands': 'Commandes exécutables',
478
- 'health.copy': 'Copier',
479
- 'health.copied': 'Copié',
480
- 'health.detail': 'Voir le détail',
481
- 'health.back': 'Retour à la liste',
482
- 'health.cache.hint': 'Résultat issu du cache · {ago}',
483
- 'health.cache.refresh': 'Rafraîchir',
484
- 'health.cache.corpusChanged': 'Corpus modifié ({was} → {now}) ; rafraîchissement conseillé',
485
- 'health.ago.justNow': 'à l’instant',
486
- 'health.ago.minutes': 'il y a {n} min',
487
- 'health.ago.hours': 'il y a {n} h',
488
- 'health.ago.days': 'il y a {n} j',
489
- }
490
-
491
- /** Deutsches Wörterbuch, vollständig gegen den zh-Schlüsselsatz geprüft. */
492
- export const de: Partial<Record<LocaleKey, string>> = {
493
- 'card.title': 'Sitzungsmanager',
494
- 'card.desc': 'Verlaufsdateien · Health-Check',
495
- 'card.enabled': 'Sitzungsmanager aktivieren',
496
- 'card.enabled.desc': 'Wenn aus, wird keine API registriert und der Seitenleisten-Eintrag verschwindet komplett.',
497
- 'card.history': 'Sitzungsverlaufsdateien',
498
- 'card.history.desc': 'Archivansicht und Bereinigung (Altersheim). Wenn aus, werden keine Verlaufs-Routen registriert und dieser Tab nicht gerendert.',
499
- 'card.health': 'Health-Check',
500
- 'card.health.desc': 'Untersuchung → Rezept → Entlassung. Wenn aus, werden keine Check-Routen registriert und dieser Tab nicht gerendert.',
501
- 'card.directDelete': 'Direkt-Lösch-Eingang',
502
- 'card.directDelete.desc': 'Wenn aktiviert, zeigen Ergebniszeilen im Suchpanel eine Löschen-Schaltfläche (Bestätigung in zwei Schritten, gleiche Pipeline wie die Verwaltung). Standardmäßig aus.',
503
- 'menu.deleteSession': 'Sitzung löschen',
504
- 'menu.deleteSession.confirm': 'Löschen bestätigen',
505
- 'card.readonly': 'Diese Sitzung kann Einstellungen nicht ändern (schreibgeschützter Mount).',
506
- 'card.unavailable': 'Einstellungsdienst nicht verfügbar; Konfiguration kann nicht gelesen werden.',
507
- 'family.title': 'Screwdriver-Plugin-Einstellungen',
508
- 'panel.title': 'Sitzungsmanager',
509
- 'panel.tab.history': 'Altersheim',
510
- 'panel.tab.health': 'Untersuchung',
511
- 'panel.close': 'Schließen',
512
- 'panel.untitled': '(ohne Titel)',
513
- 'history.loading': 'Archivliste wird gelesen…',
514
- 'history.empty': 'Die Archivmenge ist leer.',
515
- 'history.edit': 'Bearbeiten',
516
- 'history.done': 'Fertig',
517
- 'history.selectAll': 'Alle auswählen',
518
- 'history.unselectAll': 'Auswahl aufheben',
519
- 'history.unarchive': 'Archivstatus aufheben ({n})',
520
- 'history.unarchiving': 'Aufheben des Archivstatus…',
521
- 'history.purge': 'Archivdateien löschen ({n}) · {size} freigeben',
522
- 'history.purging': 'Löschen…',
523
- 'history.editingHint': 'Bearbeitungsmodus: Sitzungen anhaken, dann eine von beiden — „Archivstatus aufheben“ entfernt sie nur aus dem offiziellen Archiv-Feld (umkehrbar; nach einem Neustart zurück in der Seitenleiste); „Archivdateien löschen“ löscht Transkript und Projektions-Cache wirklich von der Disk (unumkehrbar).',
524
- 'history.restartHint': '{removed} id(s) aus dem Archiv-Feld entfernt ({remaining} verbleiben). **DSH jetzt neu starten**: Bevor der Host beendet wird, schreibt jede Archiv- oder Workspace-Änderung den kompletten Speicher-Snapshot zurück in die Datei und macht diese Operation zunichte.',
525
- 'history.pendingRestart': 'Im Host-Speicher sind noch {n} Archive aktiv: Die Liste folgt der Speicherdatei (sie sind dort nicht mehr), aber ihre Sitzungen bleiben bis zu einem DSH-Neustart in der Seitenleiste verborgen.',
526
- 'history.count': '{n} Einträge · {size} belegt',
527
- 'history.size': 'Transkript {t} · Cache {c}',
528
- 'history.sizeUnknown': 'kein Objekt auf der Disk',
529
- 'history.source.registry': 'Quelle: Host-Archivregister',
530
- 'history.source.storage-file': 'Quelle: Speicherdatei',
531
- 'history.source.none': 'Die Archivmenge ist aus beiden Quellen nicht lesbar',
532
- 'history.confirmUnarchive': 'Den Archivstatus dieser {n} Sitzung(en) aufheben?\n\n{summary}\n\nNur das Archiv-Feld wird bearbeitet (automatische Sicherung); nichts auf der Disk wird angetastet. Nach einem DSH-Neustart kehren diese Sitzungen in ihre Workspaces zurück.',
533
- 'history.confirmPurge': 'Die Dateien auf der Disk dieser {n} archivierten Sitzung(en) löschen?\n\n{summary}\n\nInsgesamt werden etwa {size} freigegeben.\nGelöscht werden: das Transkript-Verzeichnis (inkl. Kopien im Altformat) und der sitzungsweite Projektions-Cache, zudem Entfernung aus dem Archiv-Feld und den Workspace-Mitgliedstabellen.\n\n**Dieser Vorgang ist unumkehrbar — die Sitzungen sind nicht wiederherstellbar.**',
534
- 'history.purgeResult': '{purged} archivierte Sitzung(en) gelöscht, {size} freigegeben. DSH neu starten, damit sie endgültig verschwinden.',
535
- 'history.purgePartial': '{purged} gelöscht, {size} freigegeben; {failed} fehlgeschlagen (Details unten).',
536
- 'history.purgeFailures': 'Fehlerdetails',
537
- 'health.scan': 'Untersuchung starten',
538
- 'health.scanning': 'Untersuchung läuft…',
539
- 'health.progress': '{done}/{total} · {sec}s vergangen',
540
- 'health.empty': 'Keine Problem-Sitzung gefunden.',
541
- 'health.clean': 'Alle Prüfungen bestanden.',
542
- 'health.level.ok': 'ok',
543
- 'health.level.warn': 'Achtung',
544
- 'health.level.skipped': 'Nicht geprüft',
545
- 'health.level.fail': 'Anomalie',
546
- 'health.priority.high': 'Priorität',
547
- 'health.gate.generation': 'Generations-Artefakt',
548
- 'health.gate.log-integrity': 'Protokoll-Integrität',
549
- 'health.gate.projection-cache': 'Projektions-Cache',
550
- 'health.gate.lossless-json': 'Verlustfreies JSON',
551
- 'health.gate.cold-read': 'Fortsetzbarkeit',
552
- 'health.gate.source-kind': 'Plugin-Signatur',
553
- 'health.attribution': 'Zugehörigkeit',
554
- 'health.field': 'Feld',
555
- 'health.unknownOwner': 'Zugehörigkeit unbekannt',
556
- 'health.phase.checkup': '1) Untersuchung',
557
- 'health.phase.prescribe': '2) Rezept',
558
- 'health.phase.discharge': '3) Entlassung',
559
- 'health.repair': 'Umkehrbaren Eingriff ausführen',
560
- 'health.repairing': 'Eingriff läuft…',
561
- 'health.sourceMigrate': 'Alt-Signaturen umwandeln',
562
- 'health.sourceMigrating': 'Umwandlung…',
563
- 'health.sourceMigrated': '{n} Zeilen Alt-Signaturen umgewandelt (Sicherung: {backup})',
564
- 'health.sourceMigrateNone': 'Keine umzuwandelnden Alt-Signatur-Zeilen',
565
- 'health.sourceMigrateFail': 'Umwandlung fehlgeschlagen: {error}',
566
- 'health.before': 'Vorher',
567
- 'health.after': 'Nachher',
568
- 'health.quarantined': 'Projektions-Cache-Datensatz in Quarantäne (faltet nach einem DSH-Neustart neu)',
569
- 'health.commands': 'Ausführbare Befehle',
570
- 'health.copy': 'Kopieren',
571
- 'health.copied': 'Kopiert',
572
- 'health.detail': 'Details anzeigen',
573
- 'health.back': 'Zurück zur Liste',
574
- 'health.cache.hint': 'Ergebnis aus dem Cache · {ago}',
575
- 'health.cache.refresh': 'Aktualisieren',
576
- 'health.cache.corpusChanged': 'Korpus geändert ({was} → {now}); Aktualisierung empfohlen',
577
- 'health.ago.justNow': 'gerade eben',
578
- 'health.ago.minutes': 'vor {n} Min',
579
- 'health.ago.hours': 'vor {n} Std',
580
- 'health.ago.days': 'vor {n} Tg',
581
- }
582
-
583
- /** Dizionario italiano, verificato completo contro l'insieme di chiavi zh. */
584
- export const it: Partial<Record<LocaleKey, string>> = {
585
- 'card.title': 'Gestore sessioni',
586
- 'card.desc': 'File della cronologia · Controllo di salute',
587
- 'card.enabled': 'Abilita l’amministratore di sessione',
588
- 'card.enabled.desc': 'Se disattivato, non registra alcuna API e la voce nella barra laterale scompare del tutto.',
589
- 'card.history': 'File della cronologia delle sessioni',
590
- 'card.history.desc': 'Consultazione e pulizia degli archivi (Casa di riposo). Se disattivato, non registra API di cronologia e non rende questa scheda.',
591
- 'card.health': 'Controllo di salute',
592
- 'card.health.desc': 'Visita → Prescrizione → Dimissione. Se disattivato, non registra API di controllo e non rende questa scheda.',
593
- 'card.directDelete': 'Ingresso eliminazione diretta',
594
- 'card.directDelete.desc': 'Se attivo, le righe dei risultati nel pannello di ricerca mostrano un pulsante Elimina (conferma in due passaggi, stessa pipeline della gestione). Predefinito disattivo.',
595
- 'menu.deleteSession': 'Elimina sessione',
596
- 'menu.deleteSession.confirm': 'Conferma eliminazione',
597
- 'card.readonly': 'Questa sessione non può modificare le impostazioni (montaggio in sola lettura).',
598
- 'card.unavailable': 'Servizio di impostazioni non disponibile; impossibile leggere la configurazione.',
599
- 'family.title': 'Impostazioni del plugin Screwdriver',
600
- 'panel.title': 'Gestore sessioni',
601
- 'panel.tab.history': 'Casa di riposo',
602
- 'panel.tab.health': 'Visita',
603
- 'panel.close': 'Chiudi',
604
- 'panel.untitled': '(senza titolo)',
605
- 'history.loading': 'Lettura dell’elenco degli archivi…',
606
- 'history.empty': 'L’insieme degli archivi è vuoto.',
607
- 'history.edit': 'Modifica',
608
- 'history.done': 'Fatto',
609
- 'history.selectAll': 'Seleziona tutto',
610
- 'history.unselectAll': 'Deseleziona tutto',
611
- 'history.unarchive': 'Rimuovi lo stato di archivio ({n})',
612
- 'history.unarchiving': 'Rimozione dell’archivio…',
613
- 'history.purge': 'Elimina i file archiviati ({n}) · libera {size}',
614
- 'history.purging': 'Eliminazione…',
615
- 'history.editingHint': 'Modalità modifica: seleziona le sessioni, poi scegli uno dei due — «Rimuovi lo stato di archivio» le toglie solo dall’array ufficiale degli archivi (reversibile; dopo un riavvio tornano nella barra laterale); «Elimina i file archiviati» cancella davvero la trascrizione e la cache di proiezione dal disco (irreversibile).',
616
- 'history.restartHint': 'Rimossi {removed} id dall’array degli archivi ({remaining} rimanenti). **Riavvia subito DSH**: finché l’host non è terminato, qualsiasi modifica ad archivi o workspace riscrive l’intera istantanea in memoria nel file e annulla l’operazione.',
617
- 'history.pendingRestart': 'Nella memoria dell’host ci sono ancora {n} archivi attivi: l’elenco segue il file di archiviazione (non ci sono più), ma le loro sessioni restano nascoste nella barra laterale finché DSH non viene riavviato.',
618
- 'history.count': '{n} voci · {size} occupati',
619
- 'history.size': 'trascrizione {t} · cache {c}',
620
- 'history.sizeUnknown': 'nessun elemento su disco',
621
- 'history.source.registry': 'Origine: registro degli archivi dell’host',
622
- 'history.source.storage-file': 'Origine: file di archiviazione',
623
- 'history.source.none': 'L’insieme degli archivi è illeggibile da entrambe le origini',
624
- 'history.confirmUnarchive': 'Rimuovere lo stato di archivio di queste {n} sessioni?\n\n{summary}\n\nViene modificato solo l’array degli archivi (backup automatico); nulla su disco viene toccato. Dopo un riavvio di DSH queste sessioni tornano nei rispettivi workspace.',
625
- 'history.confirmPurge': 'Eliminare i file su disco di queste {n} sessioni archiviate?\n\n{summary}\n\nVerranno liberati circa {size} in totale.\nVerranno eliminati: la directory delle trascrizioni (comprese le copie in formato vecchio) e la cache di proiezione per singolo elemento, e la rimozione dall’array degli archivi e dalle tabelle dei membri del workspace.\n\n**Questa operazione è irreversibile: le sessioni non saranno recuperabili.**',
626
- 'history.purgeResult': 'Eliminate {purged} sessioni archiviate, liberati {size}. Riavvia DSH perché scompaiano del tutto.',
627
- 'history.purgePartial': 'Eliminate {purged}, liberati {size}; {failed} fallite (vedi dettagli sotto).',
628
- 'history.purgeFailures': 'Dettagli dei fallimenti',
629
- 'health.scan': 'Avvia il controllo',
630
- 'health.scanning': 'Controllo in corso…',
631
- 'health.progress': '{done}/{total} · {sec}s impiegati',
632
- 'health.empty': 'Nessuna sessione problematica trovata.',
633
- 'health.clean': 'Tutti i controlli superati.',
634
- 'health.level.ok': 'ok',
635
- 'health.level.warn': 'attenzione',
636
- 'health.level.skipped': 'Non controllato',
637
- 'health.level.fail': 'anomalia',
638
- 'health.priority.high': 'Priorità',
639
- 'health.gate.generation': 'Artefatto di generazione',
640
- 'health.gate.log-integrity': 'Integrità dei log',
641
- 'health.gate.projection-cache': 'Cache di proiezione',
642
- 'health.gate.lossless-json': 'JSON senza perdita',
643
- 'health.gate.cold-read': 'Continuità',
644
- 'health.gate.source-kind': 'Firma del plugin',
645
- 'health.attribution': 'Appartenenza',
646
- 'health.field': 'Campo',
647
- 'health.unknownOwner': 'appartenenza sconosciuta',
648
- 'health.phase.checkup': '1) Visita',
649
- 'health.phase.prescribe': '2) Prescrizione',
650
- 'health.phase.discharge': '3) Dimissione',
651
- 'health.repair': 'Esegui intervento reversibile',
652
- 'health.repairing': 'Intervento…',
653
- 'health.sourceMigrate': 'Converti firme ereditate',
654
- 'health.sourceMigrating': 'Conversione…',
655
- 'health.sourceMigrated': 'Convertite {n} righe di firme ereditate (backup: {backup})',
656
- 'health.sourceMigrateNone': 'Nessuna riga di firma ereditata da convertire',
657
- 'health.sourceMigrateFail': 'Conversione fallita: {error}',
658
- 'health.before': 'Prima',
659
- 'health.after': 'Dopo',
660
- 'health.quarantined': 'Record della cache di proiezione messo in quarantena (si ripiega dopo un riavvio di DSH)',
661
- 'health.commands': 'Comandi eseguibili',
662
- 'health.copy': 'Copia',
663
- 'health.copied': 'Copiato',
664
- 'health.detail': 'Vedi dettagli',
665
- 'health.back': 'Torna all’elenco',
666
- 'health.cache.hint': 'Risultato dalla cache · {ago}',
667
- 'health.cache.refresh': 'Aggiorna',
668
- 'health.cache.corpusChanged': 'Corpo testuale modificato ({was} → {now}); aggiornamento consigliato',
669
- 'health.ago.justNow': 'proprio ora',
670
- 'health.ago.minutes': '{n} min fa',
671
- 'health.ago.hours': '{n} h fa',
672
- 'health.ago.days': '{n} g fa',
673
- }
674
-
675
- /** Русский словарь, проверен на полноту по набору ключей zh. */
676
- export const ru: Partial<Record<LocaleKey, string>> = {
677
- 'card.title': 'Менеджер сессий',
678
- 'card.desc': 'Файлы истории · Проверка здоровья',
679
- 'card.enabled': 'Включить распорядителя сессий',
680
- 'card.enabled.desc': 'При выключении не регистрируется ни один API, и пункт боковой панели полностью исчезает.',
681
- 'card.history': 'Файлы истории сессий',
682
- 'card.history.desc': 'Просмотр и очистка архивов (Пансионат). При выключении не регистрируются API истории и эта вкладка не отображается.',
683
- 'card.health': 'Проверка здоровья',
684
- 'card.health.desc': 'Осмотр → Рецепт → Выписка. При выключении не регистрируются API проверки и эта вкладка не отображается.',
685
- 'card.directDelete': 'Точка прямого удаления',
686
- 'card.directDelete.desc': 'Если включено, в строках результатов панели поиска появляется кнопка «Удалить» (двухшаговое подтверждение, тот же конвейер, что и в администраторе). По умолчанию выключено.',
687
- 'menu.deleteSession': 'Удалить сессию',
688
- 'menu.deleteSession.confirm': 'Подтвердить удаление',
689
- 'card.readonly': 'Эта сессия не может изменять настройки (монтирование только для чтения).',
690
- 'card.unavailable': 'Сервис настроек недоступен; конфигурацию невозможно прочитать.',
691
- 'family.title': 'Настройки плагинов Screwdriver',
692
- 'panel.title': 'Менеджер сессий',
693
- 'panel.tab.history': 'Пансионат',
694
- 'panel.tab.health': 'Осмотр',
695
- 'panel.close': 'Закрыть',
696
- 'panel.untitled': '(без названия)',
697
- 'history.loading': 'Чтение списка архивов…',
698
- 'history.empty': 'Множество архивов пусто.',
699
- 'history.edit': 'Изменить',
700
- 'history.done': 'Готово',
701
- 'history.selectAll': 'Выбрать все',
702
- 'history.unselectAll': 'Снять выбор',
703
- 'history.unarchive': 'Снять статус архива ({n})',
704
- 'history.unarchiving': 'Снятие с архива…',
705
- 'history.purge': 'Удалить файлы архива ({n}) · освободить {size}',
706
- 'history.purging': 'Удаление…',
707
- 'history.editingHint': 'Режим правки: отметьте сессии, затем выберите одно — «Снять статус архива» лишь убирает их из официального массива архивов (обратимо; после перезапуска вернутся в боковую панель); «Удалить файлы архива» реально стирает транскрипт и кэш проекций с диска (необратимо).',
708
- 'history.restartHint': 'Из массива архивов удалено {removed} id (осталось {remaining}). **Немедленно перезапустите DSH**: пока хост не завершился, любые изменения архивов или рабочего пространства перезаписывают весь снимок из памяти обратно в файл, и эта операция будет отменена.',
709
- 'history.pendingRestart': 'В памяти хоста ещё {n} архивов остаются действительными: список следует файлу хранения (их уже нет), но соответствующие сессии скрыты из боковой панели до перезапуска DSH.',
710
- 'history.count': 'Всего {n} · занято {size}',
711
- 'history.size': 'транскрипт {t} · кэш {c}',
712
- 'history.sizeUnknown': 'на диске сущностей нет',
713
- 'history.source.registry': 'Источник: реестр архивов хоста',
714
- 'history.source.storage-file': 'Источник: файл хранения',
715
- 'history.source.none': 'Множество архивов не читается из обоих источников',
716
- 'history.confirmUnarchive': 'Снять статус архива у этих {n} сессий?\n\n{summary}\n\nРедактируется только массив архивов (автоматическое резервное копирование); файлы на диске не трогаются. После перезапуска DSH эти сессии вернутся в свои рабочие пространства.',
717
- 'history.confirmPurge': 'Удалить файлы на диске у этих {n} архивированных сессий?\n\n{summary}\n\nВсего освободится около {size}.\nБудут удалены: каталог транскриптов (включая копии старого формата) и поэлементный кэш проекций, а также удаление из массива архивов и таблиц участников рабочего пространства.\n\n**Эта операция необратима — сессии невозможно будет восстановить.**',
718
- 'history.purgeResult': 'Удалено {purged} архивированных сессий, освобождено {size}. Перезапустите DSH, чтобы они исчезли окончательно.',
719
- 'history.purgePartial': 'Удалено {purged}, освобождено {size}; {failed} с ошибками (подробности ниже).',
720
- 'history.purgeFailures': 'Подробности ошибок',
721
- 'health.scan': 'Начать осмотр',
722
- 'health.scanning': 'Идёт осмотр…',
723
- 'health.progress': '{done}/{total} · прошло {sec}с',
724
- 'health.empty': 'Проблемных сессий не найдено.',
725
- 'health.clean': 'Все проверки пройдены.',
726
- 'health.level.ok': 'норма',
727
- 'health.level.warn': 'внимание',
728
- 'health.level.skipped': 'не проверено',
729
- 'health.level.fail': 'аномалия',
730
- 'health.priority.high': 'Приоритет',
731
- 'health.gate.generation': 'Артефакт поколения',
732
- 'health.gate.log-integrity': 'Целостность журнала',
733
- 'health.gate.projection-cache': 'Кэш проекций',
734
- 'health.gate.lossless-json': 'JSON без потерь',
735
- 'health.gate.cold-read': 'Возможность продолжения',
736
- 'health.gate.source-kind': 'Подпись плагина',
737
- 'health.attribution': 'Принадлежность',
738
- 'health.field': 'Поле',
739
- 'health.unknownOwner': 'принадлежность неизвестна',
740
- 'health.phase.checkup': '1) Осмотр',
741
- 'health.phase.prescribe': '2) Рецепт',
742
- 'health.phase.discharge': '3) Выписка',
743
- 'health.repair': 'Выполнить обратимое воздействие',
744
- 'health.repairing': 'Воздействие…',
745
- 'health.sourceMigrate': 'Преобразовать старые подписи',
746
- 'health.sourceMigrating': 'Преобразование…',
747
- 'health.sourceMigrated': 'Преобразовано {n} строк старых подписей (резервная копия: {backup})',
748
- 'health.sourceMigrateNone': 'Нет строк старых подписей для преобразования',
749
- 'health.sourceMigrateFail': 'Ошибка преобразования: {error}',
750
- 'health.before': 'До',
751
- 'health.after': 'После',
752
- 'health.quarantined': 'Запись кэша проекций помещена в карантин (пересоберётся после перезапуска DSH)',
753
- 'health.commands': 'Выполнимые команды',
754
- 'health.copy': 'Копировать',
755
- 'health.copied': 'Скопировано',
756
- 'health.detail': 'Показать подробности',
757
- 'health.back': 'К списку',
758
- 'health.cache.hint': 'Результат из кэша · {ago}',
759
- 'health.cache.refresh': 'Обновить',
760
- 'health.cache.corpusChanged': 'Корпус изменился ({was} → {now}); рекомендуется обновление',
761
- 'health.ago.justNow': 'только что',
762
- 'health.ago.minutes': '{n} мин назад',
763
- 'health.ago.hours': '{n} ч назад',
764
- 'health.ago.days': '{n} дн назад',
765
- }
766
-
767
- /** Diccionario español, verificado completo contra el conjunto de claves zh. */
768
- export const es: Partial<Record<LocaleKey, string>> = {
769
- 'card.title': 'Gestor de sesiones',
770
- 'card.desc': 'Archivos de historial · Comprobación de salud',
771
- 'card.enabled': 'Activar el gestor de sesiones',
772
- 'card.enabled.desc': 'Al desactivarlo no se registra ninguna API y la entrada de la barra lateral desaparece por completo.',
773
- 'card.history': 'Archivos de historial de sesión',
774
- 'card.history.desc': 'Visualización y limpieza del archivo (Residencia). Al desactivarlo no se registran rutas de historial ni se muestra esa pestaña.',
775
- 'card.health': 'Comprobación de salud',
776
- 'card.health.desc': 'Revisión → Receta → Alta. Al desactivarlo no se registran rutas de revisión ni se muestra esa pestaña.',
777
- 'card.directDelete': 'Entrada de borrado directo',
778
- 'card.directDelete.desc': 'Al activarlo, las filas de resultados del panel de búsqueda muestran un botón Eliminar (confirmación en dos pasos, misma ruta que la gestión). Desactivado de forma predeterminada.',
779
- 'menu.deleteSession': 'Eliminar sesión',
780
- 'menu.deleteSession.confirm': 'Confirmar eliminación',
781
- 'card.readonly': 'Esta sesión no puede modificar la configuración (montaje de solo lectura).',
782
- 'card.unavailable': 'Servicio de configuración no disponible; no se puede leer la configuración.',
783
- 'family.title': 'Ajustes del plugin Screwdriver',
784
- 'panel.title': 'Gestor de sesiones',
785
- 'panel.tab.history': 'Residencia',
786
- 'panel.tab.health': 'Revisión',
787
- 'panel.close': 'Cerrar',
788
- 'panel.untitled': '(sin título)',
789
- 'history.loading': 'Leyendo la lista de archivos…',
790
- 'history.empty': 'El conjunto de archivos está vacío.',
791
- 'history.edit': 'Editar',
792
- 'history.done': 'Listo',
793
- 'history.selectAll': 'Seleccionar todo',
794
- 'history.unselectAll': 'Deseleccionar todo',
795
- 'history.unarchive': 'Quitar el estado de archivo ({n})',
796
- 'history.unarchiving': 'Quitando del archivo…',
797
- 'history.purge': 'Purgar archivos del archivo ({n}) · liberar {size}',
798
- 'history.purging': 'Purgando…',
799
- 'history.editingHint': 'Modo edición: marca sesiones y elige una de dos — «Quitar el estado de archivo» solo las retira de la matriz oficial de archivos (reversible; vuelven a la barra lateral tras reiniciar); «Purgar archivos del archivo» borra de verdad la transcripción y la caché de proyección del disco (irreversible).',
800
- 'history.restartHint': 'Se retiraron {removed} id de la matriz de archivos (quedan {remaining}). **Reinicia DSH ahora**: hasta que el host se cierre, cualquier cambio en archivos o espacio de trabajo reescribe toda la instantánea en memoria al archivo y deshace esta operación.',
801
- 'history.pendingRestart': 'En la memoria del host aún hay {n} archivos vigentes: la lista sigue el archivo de almacenamiento (ya no están), pero sus sesiones quedan ocultas de la barra lateral hasta reiniciar DSH.',
802
- 'history.count': '{n} en total · {size} ocupados',
803
- 'history.size': 'transcripción {t} · caché {c}',
804
- 'history.sizeUnknown': 'sin entidad en el disco',
805
- 'history.source.registry': 'Origen: registro de archivos del host',
806
- 'history.source.storage-file': 'Origen: archivo de almacenamiento',
807
- 'history.source.none': 'El conjunto de archivos no se puede leer desde ninguno de los dos orígenes',
808
- 'history.confirmUnarchive': '¿Quitar el estado de archivo de estas {n} sesiones?\n\n{summary}\n\nSolo se edita la matriz de archivos (copia de seguridad automática); no se toca nada en el disco. Tras reiniciar DSH, estas sesiones vuelven a sus espacios de trabajo.',
809
- 'history.confirmPurge': '¿Purgar los archivos en disco de estas {n} sesiones archivadas?\n\n{summary}\n\nSe liberarán unos {size} en total.\nSe borrarán: el directorio de transcripciones (incluidas las copias de formato antiguo) y la caché de proyección por elemento, y se retirarán de la matriz de archivos y de las tablas de miembros del espacio de trabajo.\n\n**Esta operación es irreversible; las sesiones no se podrán recuperar.**',
810
- 'history.purgeResult': 'Purgadas {purged} sesiones archivadas, liberados {size}. Reinicia DSH para que desaparezcan por completo.',
811
- 'history.purgePartial': 'Purgadas {purged}, liberados {size}; {failed} fallidas (ver detalles abajo).',
812
- 'history.purgeFailures': 'Detalles de los fallos',
813
- 'health.scan': 'Iniciar revisión',
814
- 'health.scanning': 'Revisando…',
815
- 'health.progress': '{done}/{total} · {sec}s transcurridos',
816
- 'health.empty': 'No se encontró ninguna sesión problemática.',
817
- 'health.clean': 'Todas las comprobaciones pasaron.',
818
- 'health.level.ok': 'correcto',
819
- 'health.level.warn': 'atención',
820
- 'health.level.skipped': 'No comprobado',
821
- 'health.level.fail': 'anomalía',
822
- 'health.priority.high': 'Prioridad',
823
- 'health.gate.generation': 'Artefacto de generación',
824
- 'health.gate.log-integrity': 'Integridad del registro',
825
- 'health.gate.projection-cache': 'Caché de proyección',
826
- 'health.gate.lossless-json': 'JSON sin pérdida',
827
- 'health.gate.cold-read': 'Continuidad',
828
- 'health.gate.source-kind': 'Firma del plugin',
829
- 'health.attribution': 'Titular',
830
- 'health.field': 'Campo',
831
- 'health.unknownOwner': 'titular desconocido',
832
- 'health.phase.checkup': '1) Revisión',
833
- 'health.phase.prescribe': '2) Receta',
834
- 'health.phase.discharge': '3) Alta',
835
- 'health.repair': 'Aplicar reparación reversible',
836
- 'health.repairing': 'Reparando…',
837
- 'health.sourceMigrate': 'Convertir firmas heredadas',
838
- 'health.sourceMigrating': 'Convirtiendo…',
839
- 'health.sourceMigrated': 'Convertidas {n} filas de firmas heredadas (copia de seguridad: {backup})',
840
- 'health.sourceMigrateNone': 'No hay filas de firmas heredadas que convertir',
841
- 'health.sourceMigrateFail': 'Conversión fallida: {error}',
842
- 'health.before': 'Antes',
843
- 'health.after': 'Después',
844
- 'health.quarantined': 'Registro de la caché de proyección puesto en cuarentena (se repliega tras reiniciar DSH)',
845
- 'health.commands': 'Comandos ejecutables',
846
- 'health.copy': 'Copiar',
847
- 'health.copied': 'Copiado',
848
- 'health.detail': 'Ver detalles',
849
- 'health.back': 'Volver a la lista',
850
- 'health.cache.hint': 'Resultado desde la caché · {ago}',
851
- 'health.cache.refresh': 'Actualizar',
852
- 'health.cache.corpusChanged': 'El corpus cambió ({was} → {now}); se recomienda actualizar',
853
- 'health.ago.justNow': 'ahora mismo',
854
- 'health.ago.minutes': 'hace {n} min',
855
- 'health.ago.hours': 'hace {n} h',
856
- 'health.ago.days': 'hace {n} d',
857
- }
858
-
859
- /**
860
- * Todas las locales / 全部语言,按 locale 服务消费的形态组合。
861
- * 单次注册即可激活全部 9 种语言(zh/en 为宿主内置,其余走同一 map 重载)。
862
- */
863
- export const dictionaries: Record<string, Record<string, string>> = {
864
- zh,
865
- en,
866
- ja,
867
- ko,
868
- fr,
869
- de,
870
- it,
871
- ru,
872
- es,
873
- }
874
-
875
- /** 字典查找(缺 key 时回退到内置 zh,再回退 key 本身)。 */
876
- export function translate(
877
- t: ((key: LocaleKey, params?: Record<string, unknown>) => string) | undefined,
878
- key: LocaleKey,
879
- params?: Record<string, unknown>,
880
- ): string {
881
- if (t !== undefined) {
882
- try {
883
- return t(key, params)
884
- } catch { /* 宿主字典异常时回退 */ }
885
- }
886
- return format(zh[key] ?? key, params)
887
- }
888
-
889
- /** 极简 `{name}` 替换。 */
890
- function format(template: string, params?: Record<string, unknown>): string {
891
- if (params === undefined) return template
892
- return template.replace(/\{(\w+)\}/g, (_, name: string) => String(params[name] ?? ''))
893
- }
1
+ /**
2
+ * 会话管理的多语言文案(zh 为键集基准,另含 en 与 ja/ko/fr/de/it/ru/es)。
3
+ *
4
+ * 硬约束:本文件**不得出现「搜索」二字** —— 搜索/索引属 dsh-manage-session-search 那一片。
5
+ * 文案走 `translate(t, key, params)`,字典由官方 locale 服务注册(缺失时回退内置 zh)。
6
+ */
7
+
8
+ /** 全部文案 key。 */
9
+ export type LocaleKey = keyof typeof zh
10
+
11
+ /** 简体中文(内置回退字典)。 */
12
+ export const zh = {
13
+ 'card.title': '会话管理',
14
+ 'card.desc': '历史文件 · 健康检查',
15
+ 'card.enabled': '启用会话管理',
16
+ 'card.enabled.desc': '关闭后不注册任何接口,侧边栏入口整体消失。',
17
+ 'card.history': '会话历史文件',
18
+ 'card.history.desc': '归档浏览与清理(养老院)。关闭后不注册历史接口、不渲染该页签。',
19
+ 'card.health': '健康检查',
20
+ 'card.health.desc': '体检 → 处方 → 出院。关闭后不注册体检接口、不渲染该页签。',
21
+ 'card.directDelete': '直接删除入口',
22
+ 'card.directDelete.desc': '开启后搜索面板结果行出现「删除」按钮(两步确认,与管理端同款链路)。默认关闭。',
23
+ 'menu.deleteSession': '删除会话',
24
+ 'menu.deleteSession.confirm': '确认删除',
25
+ 'card.readonly': '当前会话无法修改设置(只读挂载)。',
26
+ 'card.unavailable': '设置服务不可用,无法读取配置。',
27
+
28
+ 'family.title': '起子插件设置',
29
+
30
+ 'panel.title': '会话管理',
31
+ 'panel.tab.history': '养老院',
32
+ 'panel.tab.health': '体检',
33
+ 'panel.tab.manage': '管理',
34
+ 'panel.close': '关闭',
35
+ 'panel.untitled': '(无标题)',
36
+
37
+ 'history.loading': '正在读取归档列表…',
38
+ 'history.empty': '归档集合为空。',
39
+ 'history.edit': '编辑',
40
+ 'history.done': '完成',
41
+ 'history.selectAll': '全选',
42
+ 'history.unselectAll': '取消全选',
43
+ // 两个操作刻意用两个词,不可互换:
44
+ // 「取消归档状态」= 只改归档数组(可逆);「清理归档文件」= 真删磁盘实体(不可逆)。
45
+ 'history.unarchive': '取消归档状态 ({n})',
46
+ 'history.unarchiving': '取消归档中…',
47
+ 'history.purge': '清理归档文件 ({n}) · 释放 {size}',
48
+ 'history.purging': '清理中…',
49
+ 'history.editingHint': '编辑模式:勾选会话后二选一 ——「取消归档状态」只把它从官方归档数组移除(可逆,重启后回到侧边栏);「清理归档文件」真删磁盘上的转录与投影缓存(不可逆)。',
50
+ 'history.restartHint': '已从归档数组移除 {removed} 个 id(剩余 {remaining})。请**立刻重启 DSH**:宿主退出前任何归档或工作区改动都会把整份内存快照写回文件,这次操作会被还原。',
51
+ 'history.pendingRestart': '宿主内存里仍有 {n} 个归档未失效:列表已按存储文件显示(这些已不在),但它们对应的会话在重启 DSH 前仍被侧边栏隐藏。',
52
+ 'history.count': '共 {n} 条 · 占 {size}',
53
+ 'history.size': '转录 {t} · 缓存 {c}',
54
+ 'history.sizeUnknown': '磁盘上无实体',
55
+ 'history.source.registry': '来源:宿主归档注册表',
56
+ 'history.source.storage-file': '来源:存储文件',
57
+ 'history.source.none': '两处都读不到归档集合',
58
+ 'history.confirmUnarchive': '确认取消这 {n} 个会话的归档状态?\n\n{summary}\n\n只改归档数组(自动备份),磁盘文件不动。重启 DSH 后这些会话回到原工作区。',
59
+ 'history.confirmPurge': '确认清理这 {n} 个归档会话的磁盘文件?\n\n{summary}\n\n合计释放约 {size}。\n将删除:转录目录(含旧格式副本)、逐条投影缓存,并从归档数组与工作区成员表中移除。\n\n**此操作不可逆,会话将无法恢复。**',
60
+ 'history.purgeResult': '已清理 {purged} 个归档会话,释放 {size}。请重启 DSH 使其彻底消失。',
61
+ 'history.purgePartial': '已清理 {purged} 个,释放 {size};{failed} 个失败(见下方明细)。',
62
+ 'history.purgeFailures': '失败明细',
63
+
64
+ 'health.scan': '开始体检',
65
+ 'health.scanning': '体检中…',
66
+ // 分批扫描的进度读数:done/total + 已用秒数(total 由宿主报告的总数给出)
67
+ 'health.progress': '{done}/{total} · 已用 {sec}s',
68
+ 'health.empty': '未发现异常会话。',
69
+ 'health.clean': '全部检查通过。',
70
+ 'health.level.ok': '正常',
71
+ 'health.level.warn': '注意',
72
+ // 中性档:本门无从判定(如冷态会话无热态投影),不参与 warn 聚合
73
+ 'health.level.skipped': '未检查',
74
+ 'health.level.fail': '异常',
75
+ // 处置优先级:当前代是从迁移暂存(TMP)发布出来的会话,排在最前
76
+ 'health.priority.high': '优先',
77
+ 'health.gate.generation': '代次产物',
78
+ 'health.gate.log-integrity': '日志完整性',
79
+ 'health.gate.projection-cache': '投影缓存',
80
+ 'health.gate.lossless-json': '无损 JSON',
81
+ 'health.gate.cold-read': '可接续性',
82
+ 'health.gate.source-kind': '插件署名',
83
+ 'health.attribution': '归属',
84
+ 'health.field': '字段',
85
+ 'health.unknownOwner': '归属未知',
86
+ 'health.phase.checkup': '① 体检',
87
+ 'health.phase.prescribe': '② 处方',
88
+ 'health.phase.discharge': '③ 出院',
89
+ 'health.repair': '执行可逆处置',
90
+ 'health.repairing': '处置中…',
91
+ 'health.sourceMigrate': '转换旧署名',
92
+ 'health.sourceMigrating': '转换中…',
93
+ 'health.sourceMigrated': '已转换 {n} 行旧署名(备份:{backup})',
94
+ 'health.sourceMigrateNone': '没有需要转换的旧署名行',
95
+ 'health.sourceMigrateFail': '转换失败:{error}',
96
+ 'health.before': '处置前',
97
+ 'health.after': '处置后',
98
+ 'health.quarantined': '已隔离投影缓存记录(重启 DSH 后重折叠)',
99
+ 'health.commands': '可执行命令',
100
+ 'health.copy': '复制',
101
+ 'health.copied': '已复制',
102
+ 'health.detail': '查看详情',
103
+ 'health.back': '返回列表',
104
+
105
+ // 缓存:体检结果在宿主进程内留一份,关面板重开零延迟。
106
+ // 生成时间必须显式露出——旧结果不能冒充刚扫的。
107
+ 'health.cache.hint': '结果来自缓存 · {ago}',
108
+ 'health.cache.refresh': '刷新',
109
+ 'health.cache.corpusChanged': '语料已变化({was} → {now}),建议刷新',
110
+ 'health.ago.justNow': '刚刚',
111
+ 'health.ago.minutes': '{n} 分钟前',
112
+ 'health.ago.hours': '{n} 小时前',
113
+ 'health.ago.days': '{n} 天前',
114
+ } as const
115
+
116
+ /** English dictionary (same keys). */
117
+ export const en: Record<LocaleKey, string> = {
118
+ 'card.title': 'Session Manager',
119
+ 'card.desc': 'History files · Health check',
120
+ 'card.enabled': 'Enable Session Manager',
121
+ 'card.enabled.desc': 'When off, no API is registered and the sidebar entry disappears.',
122
+ 'card.history': 'Session history files',
123
+ 'card.history.desc': 'Archive browsing and cleanup (Retirement Home). When off, history routes and the tab are gone.',
124
+ 'card.health': 'Health check',
125
+ 'card.health.desc': 'Checkup → Prescription → Discharge. When off, health routes and the tab are gone.',
126
+ 'card.directDelete': 'Direct delete entry',
127
+ 'card.directDelete.desc': 'When on, result rows in the search panel get a Delete button (two-step confirm, same pipeline as the manage view). Off by default.',
128
+ 'menu.deleteSession': 'Delete session',
129
+ 'menu.deleteSession.confirm': 'Confirm delete',
130
+ 'card.readonly': 'This session cannot edit settings (read-only mount).',
131
+ 'card.unavailable': 'Settings service unavailable; configuration cannot be read.',
132
+ 'family.title': 'Screwdriver plugin settings',
133
+
134
+ 'panel.title': 'Session Manager',
135
+ 'panel.tab.history': 'Retirement Home',
136
+ 'panel.tab.health': 'Checkup',
137
+ 'panel.tab.manage': 'Manage',
138
+ 'panel.close': 'Close',
139
+ 'panel.untitled': '(untitled)',
140
+
141
+ 'history.loading': 'Loading archived sessions…',
142
+ 'history.empty': 'The archive set is empty.',
143
+ 'history.edit': 'Edit',
144
+ 'history.done': 'Done',
145
+ 'history.selectAll': 'Select all',
146
+ 'history.unselectAll': 'Clear selection',
147
+ // Two deliberately distinct verbs — never interchangeable:
148
+ // "unarchive state" edits only the archive array (reversible); "purge archive files" really deletes (irreversible).
149
+ 'history.unarchive': 'Unarchive state ({n})',
150
+ 'history.unarchiving': 'Unarchiving…',
151
+ 'history.purge': 'Purge archive files ({n}) · free {size}',
152
+ 'history.purging': 'Purging…',
153
+ 'history.editingHint': 'Edit mode: tick sessions, then pick one — "Unarchive state" only removes them from the official archive array (reversible; they return to the sidebar after a restart); "Purge archive files" really deletes the transcript and projection cache from disk (irreversible).',
154
+ 'history.restartHint': 'Removed {removed} id(s) from the archive array ({remaining} left). **Restart DSH now**: until the host exits, any archive or workspace change rewrites the whole in-memory snapshot back to the file and undoes this.',
155
+ 'history.pendingRestart': '{n} archived id(s) are still live in host memory: the list follows the storage file (they are already gone), but their sessions stay hidden from the sidebar until DSH restarts.',
156
+ 'history.count': '{n} total · {size} on disk',
157
+ 'history.size': 'transcript {t} · cache {c}',
158
+ 'history.sizeUnknown': 'no on-disk entity',
159
+ 'history.source.registry': 'Source: host archive registry',
160
+ 'history.source.storage-file': 'Source: storage file',
161
+ 'history.source.none': 'The archive set is unreadable from both sources',
162
+ 'history.confirmUnarchive': 'Unarchive these {n} session(s)?\n\n{summary}\n\nOnly the archive array is edited (auto backup); nothing on disk is touched. After a DSH restart these sessions return to their workspaces.',
163
+ 'history.confirmPurge': 'Purge the on-disk files of these {n} archived session(s)?\n\n{summary}\n\nAbout {size} will be freed.\nThis deletes: the transcript directory (including older-format copies) and the per-session projection cache, and removes them from the archive array and the workspace membership tables.\n\n**This cannot be undone — the sessions will be unrecoverable.**',
164
+ 'history.purgeResult': 'Purged {purged} archived session(s), freed {size}. Restart DSH to make them disappear completely.',
165
+ 'history.purgePartial': 'Purged {purged}, freed {size}; {failed} failed (see details below).',
166
+ 'history.purgeFailures': 'Failure details',
167
+
168
+ 'health.scan': 'Run checkup',
169
+ 'health.scanning': 'Running…',
170
+ 'health.progress': '{done}/{total} · {sec}s elapsed',
171
+ 'health.empty': 'No problem session found.',
172
+ 'health.clean': 'All checks passed.',
173
+ 'health.level.ok': 'ok',
174
+ 'health.level.warn': 'warn',
175
+ 'health.level.skipped': 'Not checked',
176
+ 'health.level.fail': 'fail',
177
+ 'health.priority.high': 'Priority',
178
+ 'health.gate.generation': 'Generation artifact',
179
+ 'health.gate.log-integrity': 'Log integrity',
180
+ 'health.gate.projection-cache': 'Projection cache',
181
+ 'health.gate.lossless-json': 'Lossless JSON',
182
+ 'health.gate.cold-read': 'Continuability',
183
+ 'health.gate.source-kind': 'Plugin signature',
184
+ 'health.attribution': 'Owner',
185
+ 'health.field': 'Field',
186
+ 'health.unknownOwner': 'owner unknown',
187
+ 'health.phase.checkup': '1) Checkup',
188
+ 'health.phase.prescribe': '2) Prescription',
189
+ 'health.phase.discharge': '3) Discharge',
190
+ 'health.repair': 'Apply reversible repair',
191
+ 'health.repairing': 'Repairing…',
192
+ 'health.sourceMigrate': 'Migrate legacy signatures',
193
+ 'health.sourceMigrating': 'Migrating…',
194
+ 'health.sourceMigrated': 'Migrated {n} legacy signature rows (backup: {backup})',
195
+ 'health.sourceMigrateNone': 'No legacy signature rows to migrate',
196
+ 'health.sourceMigrateFail': 'Migration failed: {error}',
197
+ 'health.before': 'Before',
198
+ 'health.after': 'After',
199
+ 'health.quarantined': 'Projection cache quarantined (refolds after a DSH restart)',
200
+ 'health.commands': 'Commands',
201
+ 'health.copy': 'Copy',
202
+ 'health.copied': 'Copied',
203
+ 'health.detail': 'Details',
204
+ 'health.back': 'Back',
205
+
206
+ 'health.cache.hint': 'Cached result · {ago}',
207
+ 'health.cache.refresh': 'Refresh',
208
+ 'health.cache.corpusChanged': 'Corpus changed ({was} → {now}); refresh recommended',
209
+ 'health.ago.justNow': 'just now',
210
+ 'health.ago.minutes': '{n} min ago',
211
+ 'health.ago.hours': '{n} h ago',
212
+ 'health.ago.days': '{n} d ago',
213
+ }
214
+
215
+ /** 日本語辞書(zh のキー集合に対し完全性を検査)。 */
216
+ export const ja: Partial<Record<LocaleKey, string>> = {
217
+ 'card.title': 'セッションマネージャー',
218
+ 'card.desc': '履歴ファイル · ヘルスチェック',
219
+ 'card.enabled': 'セッションマネージャーを有効化',
220
+ 'card.enabled.desc': 'オフにすると API を一切登録せず、サイドバーの入口も消えます。',
221
+ 'card.history': 'セッション履歴ファイル',
222
+ 'card.history.desc': 'アーカイブの閲覧とクリーンアップ(老人ホーム)。オフにすると履歴 API を登録せず、このタブも描画しません。',
223
+ 'card.health': 'ヘルスチェック',
224
+ 'card.health.desc': '健診 → 処方 → 退院。オフにすると健診 API を登録せず、このタブも描画しません。',
225
+ 'card.directDelete': '直接削除の入口',
226
+ 'card.directDelete.desc': 'オンにすると検索パネルの結果行に「削除」ボタンが表示されます(二段階確認、管理画面と同じ経路)。デフォルトはオフ。',
227
+ 'menu.deleteSession': 'セッションを削除',
228
+ 'menu.deleteSession.confirm': '削除を確認',
229
+ 'card.readonly': 'このセッションでは設定を変更できません(読み取り専用マウント)。',
230
+ 'card.unavailable': '設定サービスが利用できず、構成を読み取れません。',
231
+ 'family.title': 'スクリュードライバー プラグイン設定',
232
+ 'panel.title': 'セッションマネージャー',
233
+ 'panel.tab.history': '老人ホーム',
234
+ 'panel.tab.health': '健診',
235
+ 'panel.close': '閉じる',
236
+ 'panel.untitled': '(無題)',
237
+ 'history.loading': 'アーカイブ一覧を読み込んでいます…',
238
+ 'history.empty': 'アーカイブ集合は空です。',
239
+ 'history.edit': '編集',
240
+ 'history.done': '完了',
241
+ 'history.selectAll': 'すべて選択',
242
+ 'history.unselectAll': '全選択を解除',
243
+ 'history.unarchive': 'アーカイブ状態を解除 ({n})',
244
+ 'history.unarchiving': 'アーカイブ解除中…',
245
+ 'history.purge': 'アーカイブファイルを消去 ({n}) · {size} 解放',
246
+ 'history.purging': '消去中…',
247
+ 'history.editingHint': '編集モード:セッションにチェックを入れ、二者択一 ——「アーカイブ状態を解除」は公式アーカイブ配列から除去するだけ(可逆、再起動後サイドバーに戻る);「アーカイブファイルを消去」はディスク上のトランスクリプトと投影キャッシュを実削除します(不可逆)。',
248
+ 'history.restartHint': 'アーカイブ配列から {removed} 件の id を除去しました(残り {remaining})。**今すぐ DSH を再起動**:ホスト終了前にアーカイブやワークスペースへの変更があると、メモリ全体のスナップショットがファイルへ書き戻され、この操作が巻き戻ります。',
249
+ 'history.pendingRestart': 'ホストのメモリ内にはまだ {n} 件のアーカイブが無効化されていません:一覧はストレージファイルに従って表示されます(これらは既に存在しない)が、対応するセッションは DSH を再起動するまでサイドバーに隠れたままです。',
250
+ 'history.count': '全 {n} 件 · 占有 {size}',
251
+ 'history.size': 'トランスクリプト {t} · キャッシュ {c}',
252
+ 'history.sizeUnknown': 'ディスク上に実体なし',
253
+ 'history.source.registry': '取得元:ホストのアーカイブレジストリ',
254
+ 'history.source.storage-file': '取得元:ストレージファイル',
255
+ 'history.source.none': 'どちらのソースからもアーカイブ集合を読めません',
256
+ 'history.confirmUnarchive': 'これら {n} 件のセッションのアーカイブ状態を解除しますか?\n\n{summary}\n\nアーカイブ配列のみを編集します(自動バックアップ)、ディスク上のファイルは動きません。DSH 再起動後、これらのセッションは元のワークスペースに戻ります。',
257
+ 'history.confirmPurge': 'これら {n} 件のアーカイブ済みセッションのディスク上のファイルを消去しますか?\n\n{summary}\n\n合計約 {size} を解放します。\n削除対象:トランスクリプトディレクトリ(旧形式のコピーを含む)、個別の投影キャッシュ、およびアーカイブ配列とワークスペースメンバーテーブルからの除去。\n\n**この操作は不可逆で、セッションは復元できません。**',
258
+ 'history.purgeResult': '{purged} 件のアーカイブ済みセッションを消去し、{size} を解放しました。完全に消失させるには DSH を再起動してください。',
259
+ 'history.purgePartial': '{purged} 件を消去し、{size} を解放;{failed} 件が失敗(以下の明細参照)。',
260
+ 'history.purgeFailures': '失敗の明細',
261
+ 'health.scan': '健診を開始',
262
+ 'health.scanning': '健診中…',
263
+ 'health.progress': '{done}/{total} · 経過 {sec}s',
264
+ 'health.empty': '異常なセッションは見つかりませんでした。',
265
+ 'health.clean': 'すべてのチェックに合格しました。',
266
+ 'health.level.ok': '正常',
267
+ 'health.level.warn': '注意',
268
+ 'health.level.skipped': '未チェック',
269
+ 'health.level.fail': '異常',
270
+ 'health.priority.high': '優先',
271
+ 'health.gate.generation': '世代アーティファクト',
272
+ 'health.gate.log-integrity': 'ログ完全性',
273
+ 'health.gate.projection-cache': '投影キャッシュ',
274
+ 'health.gate.lossless-json': '可逆 JSON',
275
+ 'health.gate.cold-read': '継続可能性',
276
+ 'health.gate.source-kind': 'プラグイン署名',
277
+ 'health.attribution': '帰属',
278
+ 'health.field': 'フィールド',
279
+ 'health.unknownOwner': '帰属不明',
280
+ 'health.phase.checkup': '① 健診',
281
+ 'health.phase.prescribe': '② 処方',
282
+ 'health.phase.discharge': '③ 退院',
283
+ 'health.repair': '可逆的な処置を実行',
284
+ 'health.repairing': '処置中…',
285
+ 'health.sourceMigrate': '旧署名を変換',
286
+ 'health.sourceMigrating': '変換中…',
287
+ 'health.sourceMigrated': '{n} 行の旧署名を変換しました(バックアップ:{backup})',
288
+ 'health.sourceMigrateNone': '変換すべき旧署名行はありません',
289
+ 'health.sourceMigrateFail': '変換に失敗:{error}',
290
+ 'health.before': '処置前',
291
+ 'health.after': '処置後',
292
+ 'health.quarantined': '投影キャッシュのレコードを隔離しました(DSH 再起動後に再折り畳み)',
293
+ 'health.commands': '実行可能なコマンド',
294
+ 'health.copy': 'コピー',
295
+ 'health.copied': 'コピーしました',
296
+ 'health.detail': '詳細を見る',
297
+ 'health.back': '一覧に戻る',
298
+ 'health.cache.hint': '結果はキャッシュ由来 · {ago}',
299
+ 'health.cache.refresh': '更新',
300
+ 'health.cache.corpusChanged': 'コーパスが変化({was} → {now})、更新を推奨',
301
+ 'health.ago.justNow': 'たった今',
302
+ 'health.ago.minutes': '{n} 分前',
303
+ 'health.ago.hours': '{n} 時間前',
304
+ 'health.ago.days': '{n} 日前',
305
+ }
306
+
307
+ /** 한국어 사전 (zh 키 집합에 대해 완전성 검사). */
308
+ export const ko: Partial<Record<LocaleKey, string>> = {
309
+ 'card.title': '세션 매니저',
310
+ 'card.desc': '기록 파일 · 건강 점검',
311
+ 'card.enabled': '세션 매니저 활성화',
312
+ 'card.enabled.desc': '끄면 어떤 API도 등록하지 않고 사이드바 항목이 통째로 사라집니다.',
313
+ 'card.history': '세션 기록 파일',
314
+ 'card.history.desc': '아카이브 탐색 및 정리(요양소). 끄면 기록 API를 등록하지 않고 이 탭을 렌더링하지 않습니다.',
315
+ 'card.health': '건강 점검',
316
+ 'card.health.desc': '진료 → 처방 → 퇴원. 끄면 진료 API를 등록하지 않고 이 탭을 렌더링하지 않습니다.',
317
+ 'card.directDelete': '직접 삭제 입구',
318
+ 'card.directDelete.desc': '켜면 검색 패널 결과 행에 「삭제」 버튼이 표시됩니다(2단계 확인, 관리 화면과 동일한 경로). 기본은 끄기.',
319
+ 'menu.deleteSession': '세션 삭제',
320
+ 'menu.deleteSession.confirm': '삭제 확인',
321
+ 'card.readonly': '이 세션에서는 설정을 수정할 수 없습니다(읽기 전용 마운트).',
322
+ 'card.unavailable': '설정 서비스를 사용할 수 없어 구성을 읽을 수 없습니다.',
323
+ 'family.title': '스크루드라이버 플러그인 설정',
324
+ 'panel.title': '세션 매니저',
325
+ 'panel.tab.history': '요양소',
326
+ 'panel.tab.health': '진료',
327
+ 'panel.close': '닫기',
328
+ 'panel.untitled': '(제목 없음)',
329
+ 'history.loading': '아카이브 목록을 읽는 중…',
330
+ 'history.empty': '아카이브 집합이 비어 있습니다.',
331
+ 'history.edit': '편집',
332
+ 'history.done': '완료',
333
+ 'history.selectAll': '전체 선택',
334
+ 'history.unselectAll': '전체 선택 해제',
335
+ 'history.unarchive': '아카이브 상태 해제 ({n})',
336
+ 'history.unarchiving': '아카이브 해제 중…',
337
+ 'history.purge': '아카이브 파일 정리 ({n}) · {size} 확보',
338
+ 'history.purging': '정리 중…',
339
+ 'history.editingHint': '편집 모드: 세션을 체크한 뒤 둘 중 하나 —— 「아카이브 상태 해제」는 공식 아카이브 배열에서만 제거(되돌릴 수 있음, 재시작 후 사이드바로 복귀); 「아카이브 파일 정리」는 디스크의 전사본과 투영 캐시를 실제 삭제(되돌릴 수 없음).',
340
+ 'history.restartHint': '아카이브 배열에서 id {removed}개를 제거했습니다(남은 {remaining}). **지금 즉시 DSH를 재시작**: 호스트 종료 전 아카이브나 워크스페이스 변경이 있으면 메모리 전체 스냅샷이 파일로 다시 쓰여 이 작업이 되돌려집니다.',
341
+ 'history.pendingRestart': '호스트 메모리에 아직 {n}개 아카이브가 유효합니다: 목록은 저장 파일을 따라 표시(이제 없음)하지만, 해당 세션은 DSH 재시작 전까지 사이드바에 숨겨진 채로 남아 있습니다.',
342
+ 'history.count': '총 {n}건 · {size} 점유',
343
+ 'history.size': '전사본 {t} · 캐시 {c}',
344
+ 'history.sizeUnknown': '디스크에 실체 없음',
345
+ 'history.source.registry': '출처: 호스트 아카이브 등록부',
346
+ 'history.source.storage-file': '출처: 저장 파일',
347
+ 'history.source.none': '두 소스 모두에서 아카이브 집합을 읽을 수 없음',
348
+ 'history.confirmUnarchive': '이 {n}개 세션의 아카이브 상태를 해제하시겠습니까?\n\n{summary}\n\n아카이브 배열만 편집(자동 백업)하고 디스크 파일은 건드리지 않습니다. DSH 재시작 후 이 세션들은 원래 워크스페이스로 돌아갑니다.',
349
+ 'history.confirmPurge': '이 {n}개 아카이브된 세션의 디스크 파일을 정리하시겠습니까?\n\n{summary}\n\n총 약 {size}를 확보합니다.\n삭제 대상: 전사본 디렉터리(구형식 사본 포함)와 건별 투영 캐시, 그리고 아카이브 배열 및 워크스페이스 구성원 표에서 제거.\n\n**이 작업은 되돌릴 수 없으며 세션은 복원되지 않습니다.**',
350
+ 'history.purgeResult': '아카이브된 세션 {purged}개를 정리하고 {size}를 확보했습니다. 완전히 사라지게 하려면 DSH를 재시작하세요.',
351
+ 'history.purgePartial': '{purged}개 정리, {size} 확보; {failed}개 실패(아래 명세 참조).',
352
+ 'history.purgeFailures': '실패 명세',
353
+ 'health.scan': '진료 시작',
354
+ 'health.scanning': '진료 중…',
355
+ 'health.progress': '{done}/{total} · {sec}s 경과',
356
+ 'health.empty': '이상 세션을 찾지 못했습니다.',
357
+ 'health.clean': '모든 점검을 통과했습니다.',
358
+ 'health.level.ok': '정상',
359
+ 'health.level.warn': '주의',
360
+ 'health.level.skipped': '미점검',
361
+ 'health.level.fail': '이상',
362
+ 'health.priority.high': '우선',
363
+ 'health.gate.generation': '세대 산출물',
364
+ 'health.gate.log-integrity': '로그 무결성',
365
+ 'health.gate.projection-cache': '투영 캐시',
366
+ 'health.gate.lossless-json': '무손실 JSON',
367
+ 'health.gate.cold-read': '연속 가능',
368
+ 'health.gate.source-kind': '플러그인 서명',
369
+ 'health.attribution': '귀속',
370
+ 'health.field': '필드',
371
+ 'health.unknownOwner': '귀속 불명',
372
+ 'health.phase.checkup': '① 진료',
373
+ 'health.phase.prescribe': '② 처방',
374
+ 'health.phase.discharge': '③ 퇴원',
375
+ 'health.repair': '가역적 처치 실행',
376
+ 'health.repairing': '처치 중…',
377
+ 'health.sourceMigrate': '구 서명 변환',
378
+ 'health.sourceMigrating': '변환 중…',
379
+ 'health.sourceMigrated': '구 서명 {n}행을 변환했습니다(백업: {backup})',
380
+ 'health.sourceMigrateNone': '변환할 구 서명 행이 없습니다',
381
+ 'health.sourceMigrateFail': '변환 실패: {error}',
382
+ 'health.before': '처치 전',
383
+ 'health.after': '처치 후',
384
+ 'health.quarantined': '투영 캐시 기록을 격리했습니다(DSH 재시작 후 재접힘)',
385
+ 'health.commands': '실행 가능 명령',
386
+ 'health.copy': '복사',
387
+ 'health.copied': '복사됨',
388
+ 'health.detail': '상세 보기',
389
+ 'health.back': '목록으로',
390
+ 'health.cache.hint': '결과를 캐시에서 · {ago}',
391
+ 'health.cache.refresh': '새로고침',
392
+ 'health.cache.corpusChanged': '코퍼스 변경({was} → {now}), 새로고침 권장',
393
+ 'health.ago.justNow': '방금',
394
+ 'health.ago.minutes': '{n}분 전',
395
+ 'health.ago.hours': '{n}시간 전',
396
+ 'health.ago.days': '{n}일 전',
397
+ }
398
+
399
+ /** Dictionnaire français, vérifié complet contre le jeu de clés zh. */
400
+ export const fr: Partial<Record<LocaleKey, string>> = {
401
+ 'card.title': 'Gestionnaire de sessions',
402
+ 'card.desc': 'Fichiers d’historique · Contrôle de santé',
403
+ 'card.enabled': 'Activer l’intendant de session',
404
+ 'card.enabled.desc': 'Désactivé, aucune API n’est enregistrée et l’entrée de la barre latérale disparaît.',
405
+ 'card.history': 'Fichiers d’historique de session',
406
+ 'card.history.desc': 'Consultation et nettoyage des archives (Maison de retraite). Désactivé, aucune API d’historique n’est enregistrée et cet onglet n’est pas rendu.',
407
+ 'card.health': 'Contrôle de santé',
408
+ 'card.health.desc': 'Examen → Ordonnance → Sortie. Désactivé, aucune API de contrôle n’est enregistrée et cet onglet n’est pas rendu.',
409
+ 'card.directDelete': 'Entrée de suppression directe',
410
+ 'card.directDelete.desc': 'Activé, les lignes de résultat du panneau de recherche affichent un bouton Supprimer (double confirmation, même pipeline que la gestion). Désactivé par défaut.',
411
+ 'menu.deleteSession': 'Supprimer la session',
412
+ 'menu.deleteSession.confirm': 'Confirmer la suppression',
413
+ 'card.readonly': 'Cette session ne peut pas modifier les réglages (montage en lecture seule).',
414
+ 'card.unavailable': 'Service de réglages indisponible ; configuration illisible.',
415
+ 'family.title': 'Réglages du plugin Screwdriver',
416
+ 'panel.title': 'Gestionnaire de sessions',
417
+ 'panel.tab.history': 'Maison de retraite',
418
+ 'panel.tab.health': 'Examen',
419
+ 'panel.close': 'Fermer',
420
+ 'panel.untitled': '(sans titre)',
421
+ 'history.loading': 'Lecture de la liste des archives…',
422
+ 'history.empty': 'L’ensemble des archives est vide.',
423
+ 'history.edit': 'Modifier',
424
+ 'history.done': 'Terminé',
425
+ 'history.selectAll': 'Tout sélectionner',
426
+ 'history.unselectAll': 'Tout désélectionner',
427
+ 'history.unarchive': 'Retirer l’état archivé ({n})',
428
+ 'history.unarchiving': 'Désarchivage…',
429
+ 'history.purge': 'Purger les fichiers archivés ({n}) · libérer {size}',
430
+ 'history.purging': 'Purge…',
431
+ 'history.editingHint': 'Mode modification : cochez des sessions puis choisissez — « Retirer l’état archivé » ne fait que les retirer du tableau officiel des archives (réversible ; elles reviennent dans la barre latérale après un redémarrage) ; « Purger les fichiers archivés » supprime réellement le transcript et le cache de projection du disque (irréversible).',
432
+ 'history.restartHint': '{removed} id retirés du tableau des archives ({remaining} restants). **Redémarrez DSH maintenant** : tant que l’hôte n’est pas fermé, toute modification des archives ou de l’espace de travail réécrit l’intégralité de l’instantané en mémoire vers le fichier et annule cette opération.',
433
+ 'history.pendingRestart': '{n} archives sont encore vivantes en mémoire de l’hôte : la liste suit le fichier de stockage (elles n’y sont plus), mais leurs sessions restent masquées de la barre latérale tant que DSH n’a pas redémarré.',
434
+ 'history.count': '{n} entrées · {size} occupés',
435
+ 'history.size': 'transcript {t} · cache {c}',
436
+ 'history.sizeUnknown': 'aucune entité sur le disque',
437
+ 'history.source.registry': 'Source : registre d’archives de l’hôte',
438
+ 'history.source.storage-file': 'Source : fichier de stockage',
439
+ 'history.source.none': 'L’ensemble des archives est illisible des deux sources',
440
+ 'history.confirmUnarchive': 'Retirer l’état archivé de ces {n} session(s) ?\n\n{summary}\n\nSeul le tableau des archives est modifié (sauvegarde automatique) ; rien sur le disque n’est touché. Après un redémarrage de DSH, ces sessions reviennent dans leurs espaces de travail.',
441
+ 'history.confirmPurge': 'Purger les fichiers sur disque de ces {n} session(s) archivée(s) ?\n\n{summary}\n\nEnviron {size} seront libérés.\nSeront supprimés : le répertoire des transcripts (y compris les copies de format antérieur) et le cache de projection par session, puis retrait du tableau des archives et des tables de membres de l’espace de travail.\n\n**Cette opération est irréversible — les sessions seront irrécupérables.**',
442
+ 'history.purgeResult': '{purged} session(s) archivée(s) purgée(s), {size} libérés. Redémarrez DSH pour qu’elles disparaissent complètement.',
443
+ 'history.purgePartial': '{purged} purgée(s), {size} libérés ; {failed} en échec (voir le détail ci-dessous).',
444
+ 'history.purgeFailures': 'Détail des échecs',
445
+ 'health.scan': 'Lancer le contrôle',
446
+ 'health.scanning': 'Contrôle en cours…',
447
+ 'health.progress': '{done}/{total} · {sec}s écoulés',
448
+ 'health.empty': 'Aucune session problématique trouvée.',
449
+ 'health.clean': 'Tous les contrôles sont passés.',
450
+ 'health.level.ok': 'ok',
451
+ 'health.level.warn': 'attention',
452
+ 'health.level.skipped': 'Non contrôlé',
453
+ 'health.level.fail': 'anomalie',
454
+ 'health.priority.high': 'Priorité',
455
+ 'health.gate.generation': 'Artefact de génération',
456
+ 'health.gate.log-integrity': 'Intégrité des journaux',
457
+ 'health.gate.projection-cache': 'Cache de projection',
458
+ 'health.gate.lossless-json': 'JSON sans perte',
459
+ 'health.gate.cold-read': 'Continuité',
460
+ 'health.gate.source-kind': 'Signature du plugin',
461
+ 'health.attribution': 'Propriétaire',
462
+ 'health.field': 'Champ',
463
+ 'health.unknownOwner': 'propriétaire inconnu',
464
+ 'health.phase.checkup': '1) Examen',
465
+ 'health.phase.prescribe': '2) Ordonnance',
466
+ 'health.phase.discharge': '3) Sortie',
467
+ 'health.repair': 'Appliquer une réparation réversible',
468
+ 'health.repairing': 'Réparation…',
469
+ 'health.sourceMigrate': 'Convertir les signatures héritées',
470
+ 'health.sourceMigrating': 'Conversion…',
471
+ 'health.sourceMigrated': '{n} lignes de signature héritée converties (sauvegarde : {backup})',
472
+ 'health.sourceMigrateNone': 'Aucune ligne de signature héritée à convertir',
473
+ 'health.sourceMigrateFail': 'Échec de la conversion : {error}',
474
+ 'health.before': 'Avant',
475
+ 'health.after': 'Après',
476
+ 'health.quarantined': 'Enregistrement du cache de projection mis en quarantaine (replié après un redémarrage de DSH)',
477
+ 'health.commands': 'Commandes exécutables',
478
+ 'health.copy': 'Copier',
479
+ 'health.copied': 'Copié',
480
+ 'health.detail': 'Voir le détail',
481
+ 'health.back': 'Retour à la liste',
482
+ 'health.cache.hint': 'Résultat issu du cache · {ago}',
483
+ 'health.cache.refresh': 'Rafraîchir',
484
+ 'health.cache.corpusChanged': 'Corpus modifié ({was} → {now}) ; rafraîchissement conseillé',
485
+ 'health.ago.justNow': 'à l’instant',
486
+ 'health.ago.minutes': 'il y a {n} min',
487
+ 'health.ago.hours': 'il y a {n} h',
488
+ 'health.ago.days': 'il y a {n} j',
489
+ }
490
+
491
+ /** Deutsches Wörterbuch, vollständig gegen den zh-Schlüsselsatz geprüft. */
492
+ export const de: Partial<Record<LocaleKey, string>> = {
493
+ 'card.title': 'Sitzungsmanager',
494
+ 'card.desc': 'Verlaufsdateien · Health-Check',
495
+ 'card.enabled': 'Sitzungsmanager aktivieren',
496
+ 'card.enabled.desc': 'Wenn aus, wird keine API registriert und der Seitenleisten-Eintrag verschwindet komplett.',
497
+ 'card.history': 'Sitzungsverlaufsdateien',
498
+ 'card.history.desc': 'Archivansicht und Bereinigung (Altersheim). Wenn aus, werden keine Verlaufs-Routen registriert und dieser Tab nicht gerendert.',
499
+ 'card.health': 'Health-Check',
500
+ 'card.health.desc': 'Untersuchung → Rezept → Entlassung. Wenn aus, werden keine Check-Routen registriert und dieser Tab nicht gerendert.',
501
+ 'card.directDelete': 'Direkt-Lösch-Eingang',
502
+ 'card.directDelete.desc': 'Wenn aktiviert, zeigen Ergebniszeilen im Suchpanel eine Löschen-Schaltfläche (Bestätigung in zwei Schritten, gleiche Pipeline wie die Verwaltung). Standardmäßig aus.',
503
+ 'menu.deleteSession': 'Sitzung löschen',
504
+ 'menu.deleteSession.confirm': 'Löschen bestätigen',
505
+ 'card.readonly': 'Diese Sitzung kann Einstellungen nicht ändern (schreibgeschützter Mount).',
506
+ 'card.unavailable': 'Einstellungsdienst nicht verfügbar; Konfiguration kann nicht gelesen werden.',
507
+ 'family.title': 'Screwdriver-Plugin-Einstellungen',
508
+ 'panel.title': 'Sitzungsmanager',
509
+ 'panel.tab.history': 'Altersheim',
510
+ 'panel.tab.health': 'Untersuchung',
511
+ 'panel.close': 'Schließen',
512
+ 'panel.untitled': '(ohne Titel)',
513
+ 'history.loading': 'Archivliste wird gelesen…',
514
+ 'history.empty': 'Die Archivmenge ist leer.',
515
+ 'history.edit': 'Bearbeiten',
516
+ 'history.done': 'Fertig',
517
+ 'history.selectAll': 'Alle auswählen',
518
+ 'history.unselectAll': 'Auswahl aufheben',
519
+ 'history.unarchive': 'Archivstatus aufheben ({n})',
520
+ 'history.unarchiving': 'Aufheben des Archivstatus…',
521
+ 'history.purge': 'Archivdateien löschen ({n}) · {size} freigeben',
522
+ 'history.purging': 'Löschen…',
523
+ 'history.editingHint': 'Bearbeitungsmodus: Sitzungen anhaken, dann eine von beiden — „Archivstatus aufheben“ entfernt sie nur aus dem offiziellen Archiv-Feld (umkehrbar; nach einem Neustart zurück in der Seitenleiste); „Archivdateien löschen“ löscht Transkript und Projektions-Cache wirklich von der Disk (unumkehrbar).',
524
+ 'history.restartHint': '{removed} id(s) aus dem Archiv-Feld entfernt ({remaining} verbleiben). **DSH jetzt neu starten**: Bevor der Host beendet wird, schreibt jede Archiv- oder Workspace-Änderung den kompletten Speicher-Snapshot zurück in die Datei und macht diese Operation zunichte.',
525
+ 'history.pendingRestart': 'Im Host-Speicher sind noch {n} Archive aktiv: Die Liste folgt der Speicherdatei (sie sind dort nicht mehr), aber ihre Sitzungen bleiben bis zu einem DSH-Neustart in der Seitenleiste verborgen.',
526
+ 'history.count': '{n} Einträge · {size} belegt',
527
+ 'history.size': 'Transkript {t} · Cache {c}',
528
+ 'history.sizeUnknown': 'kein Objekt auf der Disk',
529
+ 'history.source.registry': 'Quelle: Host-Archivregister',
530
+ 'history.source.storage-file': 'Quelle: Speicherdatei',
531
+ 'history.source.none': 'Die Archivmenge ist aus beiden Quellen nicht lesbar',
532
+ 'history.confirmUnarchive': 'Den Archivstatus dieser {n} Sitzung(en) aufheben?\n\n{summary}\n\nNur das Archiv-Feld wird bearbeitet (automatische Sicherung); nichts auf der Disk wird angetastet. Nach einem DSH-Neustart kehren diese Sitzungen in ihre Workspaces zurück.',
533
+ 'history.confirmPurge': 'Die Dateien auf der Disk dieser {n} archivierten Sitzung(en) löschen?\n\n{summary}\n\nInsgesamt werden etwa {size} freigegeben.\nGelöscht werden: das Transkript-Verzeichnis (inkl. Kopien im Altformat) und der sitzungsweite Projektions-Cache, zudem Entfernung aus dem Archiv-Feld und den Workspace-Mitgliedstabellen.\n\n**Dieser Vorgang ist unumkehrbar — die Sitzungen sind nicht wiederherstellbar.**',
534
+ 'history.purgeResult': '{purged} archivierte Sitzung(en) gelöscht, {size} freigegeben. DSH neu starten, damit sie endgültig verschwinden.',
535
+ 'history.purgePartial': '{purged} gelöscht, {size} freigegeben; {failed} fehlgeschlagen (Details unten).',
536
+ 'history.purgeFailures': 'Fehlerdetails',
537
+ 'health.scan': 'Untersuchung starten',
538
+ 'health.scanning': 'Untersuchung läuft…',
539
+ 'health.progress': '{done}/{total} · {sec}s vergangen',
540
+ 'health.empty': 'Keine Problem-Sitzung gefunden.',
541
+ 'health.clean': 'Alle Prüfungen bestanden.',
542
+ 'health.level.ok': 'ok',
543
+ 'health.level.warn': 'Achtung',
544
+ 'health.level.skipped': 'Nicht geprüft',
545
+ 'health.level.fail': 'Anomalie',
546
+ 'health.priority.high': 'Priorität',
547
+ 'health.gate.generation': 'Generations-Artefakt',
548
+ 'health.gate.log-integrity': 'Protokoll-Integrität',
549
+ 'health.gate.projection-cache': 'Projektions-Cache',
550
+ 'health.gate.lossless-json': 'Verlustfreies JSON',
551
+ 'health.gate.cold-read': 'Fortsetzbarkeit',
552
+ 'health.gate.source-kind': 'Plugin-Signatur',
553
+ 'health.attribution': 'Zugehörigkeit',
554
+ 'health.field': 'Feld',
555
+ 'health.unknownOwner': 'Zugehörigkeit unbekannt',
556
+ 'health.phase.checkup': '1) Untersuchung',
557
+ 'health.phase.prescribe': '2) Rezept',
558
+ 'health.phase.discharge': '3) Entlassung',
559
+ 'health.repair': 'Umkehrbaren Eingriff ausführen',
560
+ 'health.repairing': 'Eingriff läuft…',
561
+ 'health.sourceMigrate': 'Alt-Signaturen umwandeln',
562
+ 'health.sourceMigrating': 'Umwandlung…',
563
+ 'health.sourceMigrated': '{n} Zeilen Alt-Signaturen umgewandelt (Sicherung: {backup})',
564
+ 'health.sourceMigrateNone': 'Keine umzuwandelnden Alt-Signatur-Zeilen',
565
+ 'health.sourceMigrateFail': 'Umwandlung fehlgeschlagen: {error}',
566
+ 'health.before': 'Vorher',
567
+ 'health.after': 'Nachher',
568
+ 'health.quarantined': 'Projektions-Cache-Datensatz in Quarantäne (faltet nach einem DSH-Neustart neu)',
569
+ 'health.commands': 'Ausführbare Befehle',
570
+ 'health.copy': 'Kopieren',
571
+ 'health.copied': 'Kopiert',
572
+ 'health.detail': 'Details anzeigen',
573
+ 'health.back': 'Zurück zur Liste',
574
+ 'health.cache.hint': 'Ergebnis aus dem Cache · {ago}',
575
+ 'health.cache.refresh': 'Aktualisieren',
576
+ 'health.cache.corpusChanged': 'Korpus geändert ({was} → {now}); Aktualisierung empfohlen',
577
+ 'health.ago.justNow': 'gerade eben',
578
+ 'health.ago.minutes': 'vor {n} Min',
579
+ 'health.ago.hours': 'vor {n} Std',
580
+ 'health.ago.days': 'vor {n} Tg',
581
+ }
582
+
583
+ /** Dizionario italiano, verificato completo contro l'insieme di chiavi zh. */
584
+ export const it: Partial<Record<LocaleKey, string>> = {
585
+ 'card.title': 'Gestore sessioni',
586
+ 'card.desc': 'File della cronologia · Controllo di salute',
587
+ 'card.enabled': 'Abilita l’amministratore di sessione',
588
+ 'card.enabled.desc': 'Se disattivato, non registra alcuna API e la voce nella barra laterale scompare del tutto.',
589
+ 'card.history': 'File della cronologia delle sessioni',
590
+ 'card.history.desc': 'Consultazione e pulizia degli archivi (Casa di riposo). Se disattivato, non registra API di cronologia e non rende questa scheda.',
591
+ 'card.health': 'Controllo di salute',
592
+ 'card.health.desc': 'Visita → Prescrizione → Dimissione. Se disattivato, non registra API di controllo e non rende questa scheda.',
593
+ 'card.directDelete': 'Ingresso eliminazione diretta',
594
+ 'card.directDelete.desc': 'Se attivo, le righe dei risultati nel pannello di ricerca mostrano un pulsante Elimina (conferma in due passaggi, stessa pipeline della gestione). Predefinito disattivo.',
595
+ 'menu.deleteSession': 'Elimina sessione',
596
+ 'menu.deleteSession.confirm': 'Conferma eliminazione',
597
+ 'card.readonly': 'Questa sessione non può modificare le impostazioni (montaggio in sola lettura).',
598
+ 'card.unavailable': 'Servizio di impostazioni non disponibile; impossibile leggere la configurazione.',
599
+ 'family.title': 'Impostazioni del plugin Screwdriver',
600
+ 'panel.title': 'Gestore sessioni',
601
+ 'panel.tab.history': 'Casa di riposo',
602
+ 'panel.tab.health': 'Visita',
603
+ 'panel.close': 'Chiudi',
604
+ 'panel.untitled': '(senza titolo)',
605
+ 'history.loading': 'Lettura dell’elenco degli archivi…',
606
+ 'history.empty': 'L’insieme degli archivi è vuoto.',
607
+ 'history.edit': 'Modifica',
608
+ 'history.done': 'Fatto',
609
+ 'history.selectAll': 'Seleziona tutto',
610
+ 'history.unselectAll': 'Deseleziona tutto',
611
+ 'history.unarchive': 'Rimuovi lo stato di archivio ({n})',
612
+ 'history.unarchiving': 'Rimozione dell’archivio…',
613
+ 'history.purge': 'Elimina i file archiviati ({n}) · libera {size}',
614
+ 'history.purging': 'Eliminazione…',
615
+ 'history.editingHint': 'Modalità modifica: seleziona le sessioni, poi scegli uno dei due — «Rimuovi lo stato di archivio» le toglie solo dall’array ufficiale degli archivi (reversibile; dopo un riavvio tornano nella barra laterale); «Elimina i file archiviati» cancella davvero la trascrizione e la cache di proiezione dal disco (irreversibile).',
616
+ 'history.restartHint': 'Rimossi {removed} id dall’array degli archivi ({remaining} rimanenti). **Riavvia subito DSH**: finché l’host non è terminato, qualsiasi modifica ad archivi o workspace riscrive l’intera istantanea in memoria nel file e annulla l’operazione.',
617
+ 'history.pendingRestart': 'Nella memoria dell’host ci sono ancora {n} archivi attivi: l’elenco segue il file di archiviazione (non ci sono più), ma le loro sessioni restano nascoste nella barra laterale finché DSH non viene riavviato.',
618
+ 'history.count': '{n} voci · {size} occupati',
619
+ 'history.size': 'trascrizione {t} · cache {c}',
620
+ 'history.sizeUnknown': 'nessun elemento su disco',
621
+ 'history.source.registry': 'Origine: registro degli archivi dell’host',
622
+ 'history.source.storage-file': 'Origine: file di archiviazione',
623
+ 'history.source.none': 'L’insieme degli archivi è illeggibile da entrambe le origini',
624
+ 'history.confirmUnarchive': 'Rimuovere lo stato di archivio di queste {n} sessioni?\n\n{summary}\n\nViene modificato solo l’array degli archivi (backup automatico); nulla su disco viene toccato. Dopo un riavvio di DSH queste sessioni tornano nei rispettivi workspace.',
625
+ 'history.confirmPurge': 'Eliminare i file su disco di queste {n} sessioni archiviate?\n\n{summary}\n\nVerranno liberati circa {size} in totale.\nVerranno eliminati: la directory delle trascrizioni (comprese le copie in formato vecchio) e la cache di proiezione per singolo elemento, e la rimozione dall’array degli archivi e dalle tabelle dei membri del workspace.\n\n**Questa operazione è irreversibile: le sessioni non saranno recuperabili.**',
626
+ 'history.purgeResult': 'Eliminate {purged} sessioni archiviate, liberati {size}. Riavvia DSH perché scompaiano del tutto.',
627
+ 'history.purgePartial': 'Eliminate {purged}, liberati {size}; {failed} fallite (vedi dettagli sotto).',
628
+ 'history.purgeFailures': 'Dettagli dei fallimenti',
629
+ 'health.scan': 'Avvia il controllo',
630
+ 'health.scanning': 'Controllo in corso…',
631
+ 'health.progress': '{done}/{total} · {sec}s impiegati',
632
+ 'health.empty': 'Nessuna sessione problematica trovata.',
633
+ 'health.clean': 'Tutti i controlli superati.',
634
+ 'health.level.ok': 'ok',
635
+ 'health.level.warn': 'attenzione',
636
+ 'health.level.skipped': 'Non controllato',
637
+ 'health.level.fail': 'anomalia',
638
+ 'health.priority.high': 'Priorità',
639
+ 'health.gate.generation': 'Artefatto di generazione',
640
+ 'health.gate.log-integrity': 'Integrità dei log',
641
+ 'health.gate.projection-cache': 'Cache di proiezione',
642
+ 'health.gate.lossless-json': 'JSON senza perdita',
643
+ 'health.gate.cold-read': 'Continuità',
644
+ 'health.gate.source-kind': 'Firma del plugin',
645
+ 'health.attribution': 'Appartenenza',
646
+ 'health.field': 'Campo',
647
+ 'health.unknownOwner': 'appartenenza sconosciuta',
648
+ 'health.phase.checkup': '1) Visita',
649
+ 'health.phase.prescribe': '2) Prescrizione',
650
+ 'health.phase.discharge': '3) Dimissione',
651
+ 'health.repair': 'Esegui intervento reversibile',
652
+ 'health.repairing': 'Intervento…',
653
+ 'health.sourceMigrate': 'Converti firme ereditate',
654
+ 'health.sourceMigrating': 'Conversione…',
655
+ 'health.sourceMigrated': 'Convertite {n} righe di firme ereditate (backup: {backup})',
656
+ 'health.sourceMigrateNone': 'Nessuna riga di firma ereditata da convertire',
657
+ 'health.sourceMigrateFail': 'Conversione fallita: {error}',
658
+ 'health.before': 'Prima',
659
+ 'health.after': 'Dopo',
660
+ 'health.quarantined': 'Record della cache di proiezione messo in quarantena (si ripiega dopo un riavvio di DSH)',
661
+ 'health.commands': 'Comandi eseguibili',
662
+ 'health.copy': 'Copia',
663
+ 'health.copied': 'Copiato',
664
+ 'health.detail': 'Vedi dettagli',
665
+ 'health.back': 'Torna all’elenco',
666
+ 'health.cache.hint': 'Risultato dalla cache · {ago}',
667
+ 'health.cache.refresh': 'Aggiorna',
668
+ 'health.cache.corpusChanged': 'Corpo testuale modificato ({was} → {now}); aggiornamento consigliato',
669
+ 'health.ago.justNow': 'proprio ora',
670
+ 'health.ago.minutes': '{n} min fa',
671
+ 'health.ago.hours': '{n} h fa',
672
+ 'health.ago.days': '{n} g fa',
673
+ }
674
+
675
+ /** Русский словарь, проверен на полноту по набору ключей zh. */
676
+ export const ru: Partial<Record<LocaleKey, string>> = {
677
+ 'card.title': 'Менеджер сессий',
678
+ 'card.desc': 'Файлы истории · Проверка здоровья',
679
+ 'card.enabled': 'Включить распорядителя сессий',
680
+ 'card.enabled.desc': 'При выключении не регистрируется ни один API, и пункт боковой панели полностью исчезает.',
681
+ 'card.history': 'Файлы истории сессий',
682
+ 'card.history.desc': 'Просмотр и очистка архивов (Пансионат). При выключении не регистрируются API истории и эта вкладка не отображается.',
683
+ 'card.health': 'Проверка здоровья',
684
+ 'card.health.desc': 'Осмотр → Рецепт → Выписка. При выключении не регистрируются API проверки и эта вкладка не отображается.',
685
+ 'card.directDelete': 'Точка прямого удаления',
686
+ 'card.directDelete.desc': 'Если включено, в строках результатов панели поиска появляется кнопка «Удалить» (двухшаговое подтверждение, тот же конвейер, что и в администраторе). По умолчанию выключено.',
687
+ 'menu.deleteSession': 'Удалить сессию',
688
+ 'menu.deleteSession.confirm': 'Подтвердить удаление',
689
+ 'card.readonly': 'Эта сессия не может изменять настройки (монтирование только для чтения).',
690
+ 'card.unavailable': 'Сервис настроек недоступен; конфигурацию невозможно прочитать.',
691
+ 'family.title': 'Настройки плагинов Screwdriver',
692
+ 'panel.title': 'Менеджер сессий',
693
+ 'panel.tab.history': 'Пансионат',
694
+ 'panel.tab.health': 'Осмотр',
695
+ 'panel.close': 'Закрыть',
696
+ 'panel.untitled': '(без названия)',
697
+ 'history.loading': 'Чтение списка архивов…',
698
+ 'history.empty': 'Множество архивов пусто.',
699
+ 'history.edit': 'Изменить',
700
+ 'history.done': 'Готово',
701
+ 'history.selectAll': 'Выбрать все',
702
+ 'history.unselectAll': 'Снять выбор',
703
+ 'history.unarchive': 'Снять статус архива ({n})',
704
+ 'history.unarchiving': 'Снятие с архива…',
705
+ 'history.purge': 'Удалить файлы архива ({n}) · освободить {size}',
706
+ 'history.purging': 'Удаление…',
707
+ 'history.editingHint': 'Режим правки: отметьте сессии, затем выберите одно — «Снять статус архива» лишь убирает их из официального массива архивов (обратимо; после перезапуска вернутся в боковую панель); «Удалить файлы архива» реально стирает транскрипт и кэш проекций с диска (необратимо).',
708
+ 'history.restartHint': 'Из массива архивов удалено {removed} id (осталось {remaining}). **Немедленно перезапустите DSH**: пока хост не завершился, любые изменения архивов или рабочего пространства перезаписывают весь снимок из памяти обратно в файл, и эта операция будет отменена.',
709
+ 'history.pendingRestart': 'В памяти хоста ещё {n} архивов остаются действительными: список следует файлу хранения (их уже нет), но соответствующие сессии скрыты из боковой панели до перезапуска DSH.',
710
+ 'history.count': 'Всего {n} · занято {size}',
711
+ 'history.size': 'транскрипт {t} · кэш {c}',
712
+ 'history.sizeUnknown': 'на диске сущностей нет',
713
+ 'history.source.registry': 'Источник: реестр архивов хоста',
714
+ 'history.source.storage-file': 'Источник: файл хранения',
715
+ 'history.source.none': 'Множество архивов не читается из обоих источников',
716
+ 'history.confirmUnarchive': 'Снять статус архива у этих {n} сессий?\n\n{summary}\n\nРедактируется только массив архивов (автоматическое резервное копирование); файлы на диске не трогаются. После перезапуска DSH эти сессии вернутся в свои рабочие пространства.',
717
+ 'history.confirmPurge': 'Удалить файлы на диске у этих {n} архивированных сессий?\n\n{summary}\n\nВсего освободится около {size}.\nБудут удалены: каталог транскриптов (включая копии старого формата) и поэлементный кэш проекций, а также удаление из массива архивов и таблиц участников рабочего пространства.\n\n**Эта операция необратима — сессии невозможно будет восстановить.**',
718
+ 'history.purgeResult': 'Удалено {purged} архивированных сессий, освобождено {size}. Перезапустите DSH, чтобы они исчезли окончательно.',
719
+ 'history.purgePartial': 'Удалено {purged}, освобождено {size}; {failed} с ошибками (подробности ниже).',
720
+ 'history.purgeFailures': 'Подробности ошибок',
721
+ 'health.scan': 'Начать осмотр',
722
+ 'health.scanning': 'Идёт осмотр…',
723
+ 'health.progress': '{done}/{total} · прошло {sec}с',
724
+ 'health.empty': 'Проблемных сессий не найдено.',
725
+ 'health.clean': 'Все проверки пройдены.',
726
+ 'health.level.ok': 'норма',
727
+ 'health.level.warn': 'внимание',
728
+ 'health.level.skipped': 'не проверено',
729
+ 'health.level.fail': 'аномалия',
730
+ 'health.priority.high': 'Приоритет',
731
+ 'health.gate.generation': 'Артефакт поколения',
732
+ 'health.gate.log-integrity': 'Целостность журнала',
733
+ 'health.gate.projection-cache': 'Кэш проекций',
734
+ 'health.gate.lossless-json': 'JSON без потерь',
735
+ 'health.gate.cold-read': 'Возможность продолжения',
736
+ 'health.gate.source-kind': 'Подпись плагина',
737
+ 'health.attribution': 'Принадлежность',
738
+ 'health.field': 'Поле',
739
+ 'health.unknownOwner': 'принадлежность неизвестна',
740
+ 'health.phase.checkup': '1) Осмотр',
741
+ 'health.phase.prescribe': '2) Рецепт',
742
+ 'health.phase.discharge': '3) Выписка',
743
+ 'health.repair': 'Выполнить обратимое воздействие',
744
+ 'health.repairing': 'Воздействие…',
745
+ 'health.sourceMigrate': 'Преобразовать старые подписи',
746
+ 'health.sourceMigrating': 'Преобразование…',
747
+ 'health.sourceMigrated': 'Преобразовано {n} строк старых подписей (резервная копия: {backup})',
748
+ 'health.sourceMigrateNone': 'Нет строк старых подписей для преобразования',
749
+ 'health.sourceMigrateFail': 'Ошибка преобразования: {error}',
750
+ 'health.before': 'До',
751
+ 'health.after': 'После',
752
+ 'health.quarantined': 'Запись кэша проекций помещена в карантин (пересоберётся после перезапуска DSH)',
753
+ 'health.commands': 'Выполнимые команды',
754
+ 'health.copy': 'Копировать',
755
+ 'health.copied': 'Скопировано',
756
+ 'health.detail': 'Показать подробности',
757
+ 'health.back': 'К списку',
758
+ 'health.cache.hint': 'Результат из кэша · {ago}',
759
+ 'health.cache.refresh': 'Обновить',
760
+ 'health.cache.corpusChanged': 'Корпус изменился ({was} → {now}); рекомендуется обновление',
761
+ 'health.ago.justNow': 'только что',
762
+ 'health.ago.minutes': '{n} мин назад',
763
+ 'health.ago.hours': '{n} ч назад',
764
+ 'health.ago.days': '{n} дн назад',
765
+ }
766
+
767
+ /** Diccionario español, verificado completo contra el conjunto de claves zh. */
768
+ export const es: Partial<Record<LocaleKey, string>> = {
769
+ 'card.title': 'Gestor de sesiones',
770
+ 'card.desc': 'Archivos de historial · Comprobación de salud',
771
+ 'card.enabled': 'Activar el gestor de sesiones',
772
+ 'card.enabled.desc': 'Al desactivarlo no se registra ninguna API y la entrada de la barra lateral desaparece por completo.',
773
+ 'card.history': 'Archivos de historial de sesión',
774
+ 'card.history.desc': 'Visualización y limpieza del archivo (Residencia). Al desactivarlo no se registran rutas de historial ni se muestra esa pestaña.',
775
+ 'card.health': 'Comprobación de salud',
776
+ 'card.health.desc': 'Revisión → Receta → Alta. Al desactivarlo no se registran rutas de revisión ni se muestra esa pestaña.',
777
+ 'card.directDelete': 'Entrada de borrado directo',
778
+ 'card.directDelete.desc': 'Al activarlo, las filas de resultados del panel de búsqueda muestran un botón Eliminar (confirmación en dos pasos, misma ruta que la gestión). Desactivado de forma predeterminada.',
779
+ 'menu.deleteSession': 'Eliminar sesión',
780
+ 'menu.deleteSession.confirm': 'Confirmar eliminación',
781
+ 'card.readonly': 'Esta sesión no puede modificar la configuración (montaje de solo lectura).',
782
+ 'card.unavailable': 'Servicio de configuración no disponible; no se puede leer la configuración.',
783
+ 'family.title': 'Ajustes del plugin Screwdriver',
784
+ 'panel.title': 'Gestor de sesiones',
785
+ 'panel.tab.history': 'Residencia',
786
+ 'panel.tab.health': 'Revisión',
787
+ 'panel.close': 'Cerrar',
788
+ 'panel.untitled': '(sin título)',
789
+ 'history.loading': 'Leyendo la lista de archivos…',
790
+ 'history.empty': 'El conjunto de archivos está vacío.',
791
+ 'history.edit': 'Editar',
792
+ 'history.done': 'Listo',
793
+ 'history.selectAll': 'Seleccionar todo',
794
+ 'history.unselectAll': 'Deseleccionar todo',
795
+ 'history.unarchive': 'Quitar el estado de archivo ({n})',
796
+ 'history.unarchiving': 'Quitando del archivo…',
797
+ 'history.purge': 'Purgar archivos del archivo ({n}) · liberar {size}',
798
+ 'history.purging': 'Purgando…',
799
+ 'history.editingHint': 'Modo edición: marca sesiones y elige una de dos — «Quitar el estado de archivo» solo las retira de la matriz oficial de archivos (reversible; vuelven a la barra lateral tras reiniciar); «Purgar archivos del archivo» borra de verdad la transcripción y la caché de proyección del disco (irreversible).',
800
+ 'history.restartHint': 'Se retiraron {removed} id de la matriz de archivos (quedan {remaining}). **Reinicia DSH ahora**: hasta que el host se cierre, cualquier cambio en archivos o espacio de trabajo reescribe toda la instantánea en memoria al archivo y deshace esta operación.',
801
+ 'history.pendingRestart': 'En la memoria del host aún hay {n} archivos vigentes: la lista sigue el archivo de almacenamiento (ya no están), pero sus sesiones quedan ocultas de la barra lateral hasta reiniciar DSH.',
802
+ 'history.count': '{n} en total · {size} ocupados',
803
+ 'history.size': 'transcripción {t} · caché {c}',
804
+ 'history.sizeUnknown': 'sin entidad en el disco',
805
+ 'history.source.registry': 'Origen: registro de archivos del host',
806
+ 'history.source.storage-file': 'Origen: archivo de almacenamiento',
807
+ 'history.source.none': 'El conjunto de archivos no se puede leer desde ninguno de los dos orígenes',
808
+ 'history.confirmUnarchive': '¿Quitar el estado de archivo de estas {n} sesiones?\n\n{summary}\n\nSolo se edita la matriz de archivos (copia de seguridad automática); no se toca nada en el disco. Tras reiniciar DSH, estas sesiones vuelven a sus espacios de trabajo.',
809
+ 'history.confirmPurge': '¿Purgar los archivos en disco de estas {n} sesiones archivadas?\n\n{summary}\n\nSe liberarán unos {size} en total.\nSe borrarán: el directorio de transcripciones (incluidas las copias de formato antiguo) y la caché de proyección por elemento, y se retirarán de la matriz de archivos y de las tablas de miembros del espacio de trabajo.\n\n**Esta operación es irreversible; las sesiones no se podrán recuperar.**',
810
+ 'history.purgeResult': 'Purgadas {purged} sesiones archivadas, liberados {size}. Reinicia DSH para que desaparezcan por completo.',
811
+ 'history.purgePartial': 'Purgadas {purged}, liberados {size}; {failed} fallidas (ver detalles abajo).',
812
+ 'history.purgeFailures': 'Detalles de los fallos',
813
+ 'health.scan': 'Iniciar revisión',
814
+ 'health.scanning': 'Revisando…',
815
+ 'health.progress': '{done}/{total} · {sec}s transcurridos',
816
+ 'health.empty': 'No se encontró ninguna sesión problemática.',
817
+ 'health.clean': 'Todas las comprobaciones pasaron.',
818
+ 'health.level.ok': 'correcto',
819
+ 'health.level.warn': 'atención',
820
+ 'health.level.skipped': 'No comprobado',
821
+ 'health.level.fail': 'anomalía',
822
+ 'health.priority.high': 'Prioridad',
823
+ 'health.gate.generation': 'Artefacto de generación',
824
+ 'health.gate.log-integrity': 'Integridad del registro',
825
+ 'health.gate.projection-cache': 'Caché de proyección',
826
+ 'health.gate.lossless-json': 'JSON sin pérdida',
827
+ 'health.gate.cold-read': 'Continuidad',
828
+ 'health.gate.source-kind': 'Firma del plugin',
829
+ 'health.attribution': 'Titular',
830
+ 'health.field': 'Campo',
831
+ 'health.unknownOwner': 'titular desconocido',
832
+ 'health.phase.checkup': '1) Revisión',
833
+ 'health.phase.prescribe': '2) Receta',
834
+ 'health.phase.discharge': '3) Alta',
835
+ 'health.repair': 'Aplicar reparación reversible',
836
+ 'health.repairing': 'Reparando…',
837
+ 'health.sourceMigrate': 'Convertir firmas heredadas',
838
+ 'health.sourceMigrating': 'Convirtiendo…',
839
+ 'health.sourceMigrated': 'Convertidas {n} filas de firmas heredadas (copia de seguridad: {backup})',
840
+ 'health.sourceMigrateNone': 'No hay filas de firmas heredadas que convertir',
841
+ 'health.sourceMigrateFail': 'Conversión fallida: {error}',
842
+ 'health.before': 'Antes',
843
+ 'health.after': 'Después',
844
+ 'health.quarantined': 'Registro de la caché de proyección puesto en cuarentena (se repliega tras reiniciar DSH)',
845
+ 'health.commands': 'Comandos ejecutables',
846
+ 'health.copy': 'Copiar',
847
+ 'health.copied': 'Copiado',
848
+ 'health.detail': 'Ver detalles',
849
+ 'health.back': 'Volver a la lista',
850
+ 'health.cache.hint': 'Resultado desde la caché · {ago}',
851
+ 'health.cache.refresh': 'Actualizar',
852
+ 'health.cache.corpusChanged': 'El corpus cambió ({was} → {now}); se recomienda actualizar',
853
+ 'health.ago.justNow': 'ahora mismo',
854
+ 'health.ago.minutes': 'hace {n} min',
855
+ 'health.ago.hours': 'hace {n} h',
856
+ 'health.ago.days': 'hace {n} d',
857
+ }
858
+
859
+ /**
860
+ * Todas las locales / 全部语言,按 locale 服务消费的形态组合。
861
+ * 单次注册即可激活全部 9 种语言(zh/en 为宿主内置,其余走同一 map 重载)。
862
+ */
863
+ export const dictionaries: Record<string, Record<string, string>> = {
864
+ zh,
865
+ en,
866
+ ja,
867
+ ko,
868
+ fr,
869
+ de,
870
+ it,
871
+ ru,
872
+ es,
873
+ }
874
+
875
+ /** 字典查找(缺 key 时回退到内置 zh,再回退 key 本身)。 */
876
+ export function translate(
877
+ t: ((key: LocaleKey, params?: Record<string, unknown>) => string) | undefined,
878
+ key: LocaleKey,
879
+ params?: Record<string, unknown>,
880
+ ): string {
881
+ if (t !== undefined) {
882
+ try {
883
+ return t(key, params)
884
+ } catch { /* 宿主字典异常时回退 */ }
885
+ }
886
+ return format(zh[key] ?? key, params)
887
+ }
888
+
889
+ /** 极简 `{name}` 替换。 */
890
+ function format(template: string, params?: Record<string, unknown>): string {
891
+ if (params === undefined) return template
892
+ return template.replace(/\{(\w+)\}/g, (_, name: string) => String(params[name] ?? ''))
893
+ }