@ragnoryok1/dsh-client-locale-ru 0.2.16 → 0.2.18

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,43 @@
1
+ ## [0.2.18] — 2026-10-24
2
+
3
+ ### Исправлено
4
+
5
+ - **Страница пакета на npm открывалась по-китайски.** npm принудительно кладёт в
6
+ архив любой файл, чьё имя начинается с `README`, — список `files` такие файлы не
7
+ исключает. Из трех README реестр брал последний по алфавиту, а им был
8
+ `README.zh.md`, поэтому в поле `readme` уезжал китайский текст: человек,
9
+ открывавший пакет, видел иероглифы вместо русского вступления. Языковые файлы
10
+ переименованы в `README-en.md` и `README-zh.md`: дефис ставит их перед точкой, и
11
+ последним по алфавиту теперь идёт `README.md`. Ссылки между тремя README и пути
12
+ в проверках обновлены.
13
+
14
+ ## [0.2.17] — 2026-10-24
15
+
16
+ ### Изменено
17
+
18
+ - **Описание пакета написано простым языком.** В npm, в мета-блоке README и в
19
+ описании каталогов стояло «Russian (ru) interface for the DeepSeek Harness web GUI:
20
+ 584 namespaces, 67666 strings… **Hybrid dsh.bundle + dsh.client plugin**» — набор
21
+ внутренних терминов, из которого человек, выбирающий перевод, не узнавал ни что он
22
+ получит, ни зачем ему это. Теперь описание говорит о деле: весь интерфейс харнесса
23
+ и меню 520 сторонних плагинов, 584 раздела, 67 666 строк.
24
+ - **README сокращён с 607 строк до 210.** Убраны шесть разделов, написанных для
25
+ разработчика: «Область покрытия», «Разработка», «Релизы», «Содержимое», «Что
26
+ переведено» (118 строк истории версий — она и так в этом файле) и «Проверки» (184
27
+ строки про аудит, снимки словарей и совместимость). Команды сборки остаются в
28
+ git-истории, проверки — в `scripts/audit.mjs`; в README теперь только то, что
29
+ нужно читателю: что это, как поставить, как выглядит, типографика, имена плагинов.
30
+
31
+ ### Исправлено
32
+
33
+ - **Ошибка в описании области покрытия.** Русское и китайское вступления говорили
34
+ «521 стороннего плагина», а в пакете 520 — цифра отставала от словаря, который был
35
+ удалён как несуществующий.
36
+ - **Аудит читал группированные числа неправильно.** В «67,666 strings» он видел
37
+ «666 строк» и валил сборку на верной цифре; шаблоны «<число> строк» ловили в тексте
38
+ посторонние числа — пример форматирования и длину самого файла словаря. Сверка
39
+ стала якорной, с поддержкой разрядов через запятую, пробел и неразрывный пробел.
40
+
1
41
  ## [0.2.16] — 2026-10-23
2
42
 
3
43
  ### Добавлено
package/README-en.md ADDED
@@ -0,0 +1,211 @@
1
+ <!-- deepseek-harness-meta
2
+ {
3
+ "name": "Russian (ru) language pack for DeepSeek Harness",
4
+ "version": "0.2.18",
5
+ "tags": ["localization", "russian", "i18n", "web-gui", "language-pack"],
6
+ "description": "Russian for the DeepSeek Harness web GUI: the whole interface plus the menus of 520 third-party plugins — 584 sections, 67 666 strings.",
7
+ "icon": "https://raw.githubusercontent.com/Ragnoryok1/dsh-client-locale-ru/main/assets/icon.png",
8
+ "compatible_versions": ["v0.2.1-rc.2", "v0.2.1-rc.1", "v0.2.1-alpha.5", "v0.2.1-alpha.4", "v0.2.1-alpha.3", "v0.2.1-alpha.2", "v0.2.1-alpha.1", "v0.2.0-rc.2", "v0.2.0-rc.1", "v0.1.7-rc.2", "v0.1.7-rc.1", "v0.1.7-alpha.2"],
9
+ "screenshots": "images",
10
+ "install_method": "dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru"
11
+ }
12
+ -->
13
+
14
+ # @ragnoryok1/dsh-client-locale-ru
15
+
16
+ [Русский](README.md) | **English** | [中文](README-zh.md)
17
+
18
+ ![Russian language pack for DeepSeek Harness](assets/banner.jpg)
19
+
20
+ [![npm](https://img.shields.io/npm/v/%40ragnoryok1%2Fdsh-client-locale-ru)](https://www.npmjs.com/package/@ragnoryok1/dsh-client-locale-ru)
21
+ [![license](https://img.shields.io/npm/l/%40ragnoryok1%2Fdsh-client-locale-ru)](LICENSE)
22
+ [![verify](https://github.com/Ragnoryok1/dsh-client-locale-ru/actions/workflows/verify.yml/badge.svg)](https://github.com/Ragnoryok1/dsh-client-locale-ru/actions/workflows/verify.yml)
23
+ [![dsh](https://img.shields.io/badge/dsh-0.2.1--rc.2%2B-6366f1)](https://github.com/deepseek-ai/deepseek-harness)
24
+ [![namespaces](https://img.shields.io/badge/namespaces-584-6466f1)](https://github.com/Ragnoryok1/dsh-client-locale-ru)
25
+ [![strings](https://img.shields.io/badge/strings-67666-6466f1)](https://github.com/Ragnoryok1/dsh-client-locale-ru)
26
+
27
+ The plugin makes the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) interface Russian. Everything you see is translated: settings, side panels, dialogs, hints, error messages — and the menus of third-party plugins that ship no Russian of their own.
28
+
29
+ The pack currently holds 584 sections and 67,666 strings, 520 of them dictionaries of third-party plugins — more than any other Russian pack for the harness I could find.
30
+
31
+ Install is one command: `dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru`; Russian is then picked in the settings, under General.
32
+
33
+ Beyond translation the pack does three things the harness cannot do by itself: it chooses the Russian plural form by the number instead of a neutral label, applies Russian typography («ёлочки», em dashes, ellipses), and translates plugin names whose package ships no locale of its own. Each extra can be turned off with one line in the profile — the translation stays.
34
+
35
+ The pack does not modify harness files and does not touch your data. If a newer harness adds a string the pack does not know yet, it stays English: the interface keeps working. MIT, maintained by the community, not affiliated with DeepSeek.
36
+
37
+ > **If the pack was useful to you, please star the repository.** It is the only signal that a
38
+ > translation is wanted, and it is what decides which parts get worked on next.
39
+
40
+ ---
41
+
42
+ The complete inventory is in [NAMESPACES.md](NAMESPACES.md): 584 namespaces, 67,666 strings, with the bundled plugin dictionaries marked.
43
+
44
+ Plugins outside the harness tree are translated the same way, but their English text comes from their own publication: `plugins/<namespace>.json` records the package, the version, the licence and the strings extracted from its bundle. What is still English in your installation is shown by `npm run report:plugins`: it walks the profile's `node_modules`, finds each plugin's own dictionary and compares its keys with ours, and a plugin without a dictionary is reported as such — there is nothing to translate until it asks for one.
45
+
46
+ One family deserves a word of its own, `@linxin666`: thirteen dictionaries, each translated in full — from a few dozen strings to four hundred.
47
+
48
+ | Dictionary | Package | Strings |
49
+ |---|---|---|
50
+ | `update` | `@linxin666/dsh-update` | 43 |
51
+ | `git-graph` | `@linxin666/dsh-client-ui-git-graph` | 63 |
52
+ | `dsh-web-ui-usage` | `@linxin666/dsh-usage` | 67 |
53
+ | `dsh-web-ui-session-archive` | `@linxin666/dsh-session-archive` | 124 |
54
+ | `pet` | `@linxin666/dsh-pet` | 81 |
55
+ | `remote` | `@linxin666/dsh-remote-web-ui` | 117 |
56
+ | `dsh-skill-explorer` | `@linxin666/dsh-client-ui-skill-explorer` | 114 |
57
+ | `dsh-web-ui-market` | `@linxin666/dsh-client-ui-market` | 106 |
58
+ | `liangshen` | `@linxin666/dsh-liangshen` | 42 |
59
+ | `dsh-web-ui-preset-center` | `@linxin666/dsh-client-ui-preset-center` | 46 |
60
+ | `dsh-ssh` | `@linxin666/dsh-ssh` | 158 |
61
+ | `task-board` | `@linxin666/dsh-client-ui-task-board` | 439 |
62
+ | `task-board-github` | `@linxin666/dsh-client-ui-task-board-github` | 113 |
63
+ | **Total** | 13 dictionaries | **1,513** |
64
+
65
+ The names and descriptions of the plugins themselves on the Plugins page cannot come from this pack: the harness keeps them in `locale/<language>.json` inside each package and builds a translation map from them, and `resolveText` in the locale service does not consult dictionaries at all. 221 packages ship `locale/en.json`, 219 ship `locale/zh.json`, and **none ships `locale/ru.json`**: those 261 strings stay English with any language pack. `scripts/descriptor-coverage.mjs` counts that gap; the mechanism and the 32 prepared translations are in [docs/descriptor-upstream.md](https://github.com/Ragnoryok1/dsh-client-locale-ru/blob/main/docs/descriptor-upstream.md).
66
+
67
+ Another pack's Russian is never copied: there would be nothing to verify it against, while the audit compares our value with the English template of that same plugin. And one warning for the future: if the profile holds a second pack that registers Russian itself, the language belongs to whoever registered it first — the other one silently skips its namespaces, and the interface ends up showing someone else's translation.
68
+
69
+ ## What it looks like
70
+
71
+ Russian in the language list and the translated settings sections (the screenshots were taken in build `0.1.8`:
72
+ the same screen, but the wording and some sections have changed since — the font-size caption, for one, now reads
73
+ «Влияет только на содержимое диалога»):):
74
+
75
+ ![Settings in Russian: language, appearance, themes](images/settings-ru.png)
76
+
77
+ ![The plugin list in Russian: section headings and the «Экспериментально» badges](images/plugins-ru.png)
78
+
79
+ Both screenshots are cropped at the left edge of the panel: workspace and session names are
80
+ not in frame.
81
+
82
+ Russian locale (`ru`) for the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) web GUI (`dsh`).
83
+
84
+ The plugin adds **Russian** as a selectable language through the locale's language-pack contract: `ctx.locale.addLanguage({ id: 'ru', label: 'Русский', fallback: 'en' })` plus one `ru` dictionary per namespace. Once **Русский** is picked in Settings → General, the interface switches over immediately.
85
+
86
+ > **If the package was useful to you, give it a star.** It is the only way to show that a
87
+ > translation is wanted, and it genuinely affects which tasks get taken on. Not a single
88
+ > “like” button in the interface does that.
89
+ >
90
+ > The repository, meanwhile, remains the place where the state of the translation is visible:
91
+ > a [CHANGELOG](CHANGELOG.md) breaking down every release (including the mistakes that were found
92
+ > in it by its own maintainers), the CI checks, and the list of what is deliberately not translated.
93
+
94
+ ## Installation
95
+
96
+ This is a **hybrid plugin** (`dsh.bundle` + `dsh.client`), so it installs as an ordinary profile bundle through `dsh plugin`:
97
+
98
+ ```sh
99
+ dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru
100
+ ```
101
+
102
+ After that, the next time `dsh web` starts, **Русский** will appear in the settings. A separate `pnpm add` into a built `dsh web` is not needed — the plugin pulls in the built-in locale through the `locale` service.
103
+
104
+ If pnpm answers `ERR_PNPM_NO_MATURE_MATCHING_VERSION`, that is not a broken package but a
105
+ maturity policy: a profile refuses versions published less than 48 hours ago. Wait a day or
106
+ two (the message names the version) or drop the `minimumReleaseAge` limit in the profile
107
+ settings.
108
+ The manifest is checked against the harness's own rules rather than by eye (`npm run check:manifest`, part of `verify`). Each rule was read out of the harness sources and each is a failure the user would otherwise meet first: `parseDshClient` accepts `platform` as any string while the consumers keep only `platform === 'web'`, so a typo like `'Web'` would **silently drop** the client half; a declared `dsh.client` without the `./client` export makes `client-modules` throw; and `dsh.bundle.patch` has to point at a file that exists.
109
+
110
+ Installing from GitHub works too: `dsh plugin --profile web add Ragnoryok1/dsh-client-locale-ru` (the form the plugin catalogue generates, without the npm scope) installs the repository archive, and the built `lib/` is committed — no build runs on your machine, and none is needed. That is exactly why `npm run check:built` (part of `verify`) rebuilds `lib/` and compares it with the commit: a stale bundle in git would reach the user, while the audit would never see it, because the audit reads the sources. The published package is 1976 kB: the banner ships as JPEG, and the icon and source map stay out of the npm payload.
111
+
112
+ ## Why hybrid
113
+
114
+ `dsh web` takes client (UI) plugins only from **workspace packages**, and an external npm package carrying just a `dsh.client` is not picked up. For an external plugin with a UI half to be installable, it has to declare **`dsh.bundle`** (the profile layer), whose `cordis.patch.yml` adds the client entry `locale-ru` to the browser roster through `- insert:`. Other third-party plugins are built the same way (for example `@zseven-w/dsh-android`).
115
+
116
+ ## How it works
117
+
118
+ - **Host half** (`lib/index.js`) — an empty `apply()`: a language plugin needs no server logic, it only provides a valid cordis entry for the Loader.
119
+ - **Browser half** (`lib/client.js`) — registers `ru` and the dictionaries through the `locale` service (injected from `ctx`). There are no framework runtime imports — only the service.
120
+ - **No runtime dependencies** — `lib/client.js` imports no other packages, so it does not drag in heavy dependencies.
121
+ - **Effects** — the language and the dictionaries are registered through `ctx.effect(...)`, so they are unloaded together with the plugin (HMR-safe).
122
+ - **Plural forms** — the locale contract carries no pluralization rules: components choose the
123
+ form as `count === 1 ? 'key.one' : 'key.other'`, while Russian needs three (1 вызов /
124
+ 2 вызова / 5 вызовов). The package takes that over: alongside `.one` and `.other` the
125
+ dictionaries carry `.few` and `.many`, and a wrapper around the runtime's `translate`
126
+ recomputes the form with `Intl.PluralRules('ru')` for any call carrying a numeric `count`
127
+ or `n`. The number decides, not the key the component picked, so «2 вызова инструментов»
128
+ and «5 вызовов инструментов» come out of the same call site. The forms are not limited to
129
+ paired keys: where the harness passes a number to a key it never paired (`terminal.expand`
130
+ with `{n}`), the pack adds its own `.one` / `.few` / `.many` — «Показать ещё 1 строку /
131
+ 3 строки / 7 строк» — and the wrapper looks a form up for those keys too. Where a form does
132
+ not fit (a sentence carrying several numbers), the value is rewritten to be count-neutral:
133
+ «Символов: {total}» instead of «{total} символов», «файлов: {files}» instead of
134
+ «{files} файлов». Checked by `npm run check:plural` at counts 0, 1, 2, 3, 5 and 22, on an
135
+ unpaired key, on a fractional count, and on a foreign language pack whose forms our rules
136
+ must not rewrite.
137
+ - **Fallback** — a missing Russian key falls back to `en` (the configured `fallback`).
138
+
139
+ ## Typography
140
+
141
+ The package brings already-rendered Russian text into line with typographic rules: guillemets
142
+ («ёлочки») instead of straight quotes, an em dash instead of a hyphen between words, an ellipsis
143
+ instead of three dots, a non-breaking space between a number and the following word or unit of
144
+ measurement (so that «187 плагинов» does not break across two lines), and the same space after a
145
+ short word — a preposition, a conjunction or a particle — so that «в режим» wraps as a whole
146
+ instead of leaving «в» alone at the end of a line. The rules are deliberately conservative: they
147
+ fire only on unambiguous patterns, and code, input fields, editable text and elements with
148
+ `data-typography="off"` are not touched at all. A second pass changes nothing.
149
+
150
+ A rule for numeric ranges (an en dash in «5–10») is **deliberately absent**: the only string in
151
+ the whole pack with a hyphen between digits is the cron example `0 9 * * 1-5`, and a dash would
152
+ corrupt it. Measured, not assumed.
153
+
154
+ The rules are checked by a script rather than by eye: `npm run check:typography`.
155
+
156
+ The pass itself sees text nodes only, while a dictionary value also travels into attributes —
157
+ `title`, `aria-label`, `placeholder` — that it never reaches. The values are therefore checked
158
+ separately: the pack may not contain a straight quote, a hyphen standing for a dash, a dash
159
+ without spaces, or three dots. Otherwise the same string would look different depending on
160
+ where it landed, and in a hint or a screen-reader label the rules would not work at all.
161
+
162
+ Text that changes **after** the first pass — token counters, timers, progress — is rewritten
163
+ again, so a live «4 с» keeps its non-breaking space. This is checked on a real document rather
164
+ than on strings: `npm run check:typography:dom` (jsdom, six cases — the first pass, a later text
165
+ change, an added subtree, disposal, a foreign language, and a language switch).
166
+
167
+ The pass runs **only while Russian is active**: the rules are Russian, and applying them to
168
+ another language would edit text this pack does not own — `"quotes"` would become «quotes» in an
169
+ English interface. The pack listens to `locale/change` and starts or stops the pass on a switch
170
+ rather than on installation.
171
+
172
+ ## Plugin names
173
+
174
+ A plugin's name comes from its own `locale/<language>.json` file, and the locale service
175
+ resolves that map without consulting registered dictionaries — its own documentation says
176
+ «maps do not consult registered dictionaries». A dictionary therefore cannot translate a
177
+ plugin's name: the pack has no access to another package's map, and the English name stays
178
+ English until the plugin itself ships `locale/ru.json`.
179
+
180
+ The pack carries those names itself: the names of the 32 harness plugins and the names and
181
+ descriptions of 33 third-party plugins whose dictionaries it translates. Each comes with the
182
+ English text the translation was made from and the Russian text. The rendered interface passes
183
+ through a pass that puts the Russian text where the English was — in the plugin cards, in the
184
+ rows of a bundle, on a plugin's page and in the settings inventory. The accessible name
185
+ (`aria-label`) is replaced too, so a card is read in Russian as a whole.
186
+
187
+ The pass matches exactly: a string is replaced only when the whole string is one of the titles
188
+ or descriptions the pack knows. A sentence that merely mentions a name keeps its wording;
189
+ code, input fields and the conversation are not touched. Like the typography pass, it runs
190
+ only while Russian is the active language.
191
+
192
+ The same 32 names go to the harness as a patch (`docs/harness-descriptors-ru.patch`): in the
193
+ harness interface the plugins are named in Russian, and the patch gets there first. The patch
194
+ is a build artefact — it is built from the translations and the harness tree, and the gate
195
+ `npm run check:descriptor-patch` fails when the built patch and the committed one differ.
196
+ The third-party names live in `src/client/plugin-names-ru.json`: the file is built from the
197
+ vendored plugin dictionaries (`npm run build:plugin-names`), and the gate
198
+ `npm run check:plugin-names:map` fails when the map has drifted from the dictionaries. The same
199
+ English name cannot be translated two ways — differently in the harness and in a plugin.
200
+ `npm run check:plugin-names` proves on a real document (jsdom, 12 cases) that the pass
201
+ translates the title, the description, the accessible name and a bundle row — and leaves what
202
+ it must alone.
203
+
204
+
205
+ ## License
206
+
207
+ MIT
208
+
209
+ ## Changelog
210
+
211
+ [CHANGELOG.md](CHANGELOG.md) — what was in each release.
package/README-zh.md ADDED
@@ -0,0 +1,144 @@
1
+ <!-- deepseek-harness-meta
2
+ {
3
+ "name": "Russian (ru) language pack for DeepSeek Harness",
4
+ "version": "0.2.18",
5
+ "tags": ["localization", "russian", "i18n", "web-gui", "language-pack"],
6
+ "description": "Russian for the DeepSeek Harness web GUI: the whole interface plus the menus of 520 third-party plugins — 584 sections, 67 666 strings.",
7
+ "icon": "https://raw.githubusercontent.com/Ragnoryok1/dsh-client-locale-ru/main/assets/icon.png",
8
+ "compatible_versions": ["v0.2.1-rc.2", "v0.2.1-rc.1", "v0.2.1-alpha.5", "v0.2.1-alpha.4", "v0.2.1-alpha.3", "v0.2.1-alpha.2", "v0.2.1-alpha.1", "v0.2.0-rc.2", "v0.2.0-rc.1", "v0.1.7-rc.2", "v0.1.7-rc.1", "v0.1.7-alpha.2"],
9
+ "screenshots": "images",
10
+ "install_method": "dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru"
11
+ }
12
+ -->
13
+
14
+ # @ragnoryok1/dsh-client-locale-ru
15
+
16
+ [Русский](README.md) | [English](README-en.md) | **中文**
17
+
18
+ ![DeepSeek Harness 俄语语言包](assets/banner.jpg)
19
+
20
+ [![npm](https://img.shields.io/npm/v/%40ragnoryok1%2Fdsh-client-locale-ru)](https://www.npmjs.com/package/@ragnoryok1/dsh-client-locale-ru)
21
+ [![license](https://img.shields.io/npm/l/%40ragnoryok1%2Fdsh-client-locale-ru)](LICENSE)
22
+ [![verify](https://github.com/Ragnoryok1/dsh-client-locale-ru/actions/workflows/verify.yml/badge.svg)](https://github.com/Ragnoryok1/dsh-client-locale-ru/actions/workflows/verify.yml)
23
+ [![dsh](https://img.shields.io/badge/dsh-0.2.1--rc.2%2B-6366f1)](https://github.com/deepseek-ai/deepseek-harness)
24
+ [![namespaces](https://img.shields.io/badge/namespaces-584-6466f1)](https://github.com/Ragnoryok1/dsh-client-locale-ru)
25
+ [![strings](https://img.shields.io/badge/strings-67666-6466f1)](https://github.com/Ragnoryok1/dsh-client-locale-ru)
26
+
27
+ 插件把 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的界面变成俄语。你看到的一切都会被翻译:设置、侧边栏、对话框、提示、错误信息——以及没有自带俄语的三方插件菜单。
28
+
29
+ 目前包里有 584 个分区、67 666 条文案,其中 520 份是第三方插件的词典——比我能找到的任何其他俄语包都多。
30
+
31
+ 安装只需一条命令:`dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru`,然后在设置 General 里选择俄语。
32
+
33
+ 除了翻译,插件还做三件 harness 自己做不到的事:按数字选择俄语复数形式(而不是中性标签)、套用俄语排版(«ёлочки»、长破折号、省略号),以及翻译没有自带 locale 的插件名称。每一项都能在配置里用一行关闭——翻译本身保留。
34
+
35
+ 插件不会修改 harness 的文件,也不会碰你的数据。如果新版 harness 出现包里还没有的文案,它会保持英文:界面照常工作。MIT 许可,由社区维护,与 DeepSeek 无关联。
36
+
37
+ > **如果这个包对你有用,请给仓库点一颗 star。** 这是表明「有人需要这份翻译」的唯一信号,它也决定了接下来优先做哪些部分。
38
+
39
+ ---
40
+
41
+ 完整清单见 [NAMESPACES.md](NAMESPACES.md):584 个 namespace、67,666 条字符串,随包附带的插件词典单独标注。
42
+
43
+ harness 树之外的插件用同样的方式翻译,但英文取自它们自己的发布物:`plugins/<namespace>.json` 记录包名、版本、许可证,以及从其 bundle 中提取的字符串。安装里还剩哪些英文,用 `npm run report:plugins` 查看:它遍历 profile 的 `node_modules`,找出每个插件自己的词典并与我们的对照;没有词典的插件会照实标注——在它请求翻译之前无从翻译。
44
+
45
+ `@linxin666` 这一族值得单独说明:十三个词典,每一个都已完整翻译——从几十条到四百条不等。
46
+
47
+ | 词典 | 包 | 条数 |
48
+ |---|---|---|
49
+ | `update` | `@linxin666/dsh-update` | 43 |
50
+ | `git-graph` | `@linxin666/dsh-client-ui-git-graph` | 63 |
51
+ | `dsh-web-ui-usage` | `@linxin666/dsh-usage` | 67 |
52
+ | `dsh-web-ui-session-archive` | `@linxin666/dsh-session-archive` | 124 |
53
+ | `pet` | `@linxin666/dsh-pet` | 81 |
54
+ | `remote` | `@linxin666/dsh-remote-web-ui` | 117 |
55
+ | `dsh-skill-explorer` | `@linxin666/dsh-client-ui-skill-explorer` | 114 |
56
+ | `dsh-web-ui-market` | `@linxin666/dsh-client-ui-market` | 106 |
57
+ | `liangshen` | `@linxin666/dsh-liangshen` | 42 |
58
+ | `dsh-web-ui-preset-center` | `@linxin666/dsh-client-ui-preset-center` | 46 |
59
+ | `dsh-ssh` | `@linxin666/dsh-ssh` | 158 |
60
+ | `task-board` | `@linxin666/dsh-client-ui-task-board` | 439 |
61
+ | `task-board-github` | `@linxin666/dsh-client-ui-task-board-github` | 113 |
62
+ | **合计** | 13 个词典 | **1,513** |
63
+
64
+ 「插件」页面上的插件名称与说明无法由本包提供:harness 把她们放在每个包的 `locale/<语言>.json` 里并据此拼出翻译映射,而 locale 服务的 `resolveText` 根本不查词典。221 个 harness 包带有 `locale/en.json`,其中 219 个带有 `locale/zh.json`,**没有一个带 `locale/ru.json`**,所以这 261 行在任何语言包下都保持英文。这个缺口由 `scripts/descriptor-coverage.mjs` 单独统计;机制说明与 32 份已备好的译文见 [docs/descriptor-upstream.md](https://github.com/Ragnoryok1/dsh-client-locale-ru/blob/main/docs/descriptor-upstream.md)。
65
+
66
+ 我们不复制别的包的俄语译文:那样无从核对,而审计是把我们的取值与同一个插件的英文模板作比较。另外提醒一点:如果配置文件里还有另一个自己注册俄语的包,语言就归先注册的那个——第二个会静默跳过自己的 namespace,界面上出现的就是别人的译文。
67
+
68
+ ## 效果展示
69
+
70
+ 语言列表中的俄语,以及已翻译的设置分区(截图截取自 `0.1.8` 构建:同一屏幕,但此后措辞和部分分区已有变化——例如字号说明现在写作 «Влияет только на содержимое диалога»):
71
+
72
+ ![俄语界面下的设置:语言、外观、主题](images/settings-ru.png)
73
+
74
+ ![俄语界面下的插件列表:分区标题与「Экспериментально」标记](images/plugins-ru.png)
75
+
76
+ 两张截图都沿面板左边界做了裁剪:工作区和会话的名称没有进入画面。
77
+
78
+ [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)Web 界面的俄语语言环境(`ru`)。
79
+
80
+ 插件通过 locale 语言包契约把**俄语**注册为可选语言:`ctx.locale.addLanguage({ id: 'ru', label: 'Русский', fallback: 'en' })`,并为每个 namespace 提供一份 `ru` 词典。在 Settings → General 中选择 **Русский** 后,界面会立即切换。
81
+
82
+ > **如果这个包帮到了你——请点一颗 star。** 这是说明这份翻译有人需要的唯一方式,而且它确实会影响接下来接手哪些任务。界面里的任何一个「喜欢」按钮都做不到这一点。
83
+ >
84
+ > 与此同时,仓库仍然是能看到翻译状态的地方:[CHANGELOG](CHANGELOG.md) 逐条拆解每个版本(包括在其中自己发现的错误)、CI 中的检查,以及一份有意不翻译的内容清单。
85
+
86
+ ## 安装
87
+
88
+ 这是一个**混合插件**(`dsh.bundle` + `dsh.client`),因此它像普通的 profile bundle 一样通过 `dsh plugin` 安装:
89
+
90
+ ```sh
91
+ dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru
92
+ ```
93
+
94
+ 之后在下次启动 `dsh web` 时,设置里就会出现 **Русский**。无需再单独把 `pnpm add` 装进已构建的 `dsh web`——插件通过 `locale` 服务接入内置语言环境。
95
+
96
+ 清单按 harness 自身的规则检查,而不是靠肉眼(`npm run check:manifest`,包含在 `verify` 中)。每一条规则都取自 harness 源码,每一条都对应一种否则会先落到用户头上的失败:`parseDshClient` 接受任意字符串作为 `platform`,但消费方只保留 `platform === 'web'` 的行,因此写成 `'Web'` 这样的笔误会**悄悄关掉**客户端那一半;声明了 `dsh.client` 却没有 `./client` 导出会让 `client-modules` 抛错;`dsh.bundle.patch` 必须指向确实存在的文件。
97
+
98
+ 从 GitHub 安装同样可用:`dsh plugin --profile web add Ragnoryok1/dsh-client-locale-ru`(插件目录生成的就是这种不带 npm scope 的写法)安装的是仓库归档,而构建好的 `lib/` 已提交进 git——你的机器上不会运行构建,也不需要运行。正因如此,`npm run check:built`(包含在 `verify` 中)会重新构建 `lib/` 并与提交内容比对:git 里过期的构建产物会直接落到用户手里,而审计看不到它——审计读的是源码。发布的包为 1976 kB:横幅以 JPEG 提供,图标与源码映射不进入 npm 包。
99
+
100
+ ## 为什么是混合插件
101
+
102
+ `dsh web` 只从 **workspace 包**中加载客户端(UI)插件,仅带一个 `dsh.client` 的外部 npm 包不会被识别。要让带 UI 部分的外部插件可安装,它必须声明 **`dsh.bundle`**(profile 层),再由其 `cordis.patch.yml` 通过 `- insert:` 把客户端条目 `locale-ru` 加入浏览器 roster。其他第三方插件(例如 `@zseven-w/dsh-android`)也是同样的结构。
103
+
104
+ ## 工作原理
105
+
106
+ - **宿主部分**(`lib/index.js`)——空的 `apply()`:语言插件不需要服务端逻辑,它只是为 Loader 提供一条正确的 cordis 条目。
107
+ - **浏览器部分**(`lib/client.js`)——通过 `locale` 服务(从 `ctx` 注入)注册 `ru` 和词典。没有框架的运行时导入——只有这个服务。
108
+ - **没有运行时依赖**——`lib/client.js` 不导入其他包,因此不会拖入沉重的依赖。
109
+ - **副作用**——语言和词典通过 `ctx.effect(...)` 注册,因此会随插件一起卸载(对 HMR 安全)。
110
+ - **复数形式**——locale 契约不包含复数规则:组件按 `count === 1 ? 'ключ.one' : 'ключ.other'` 选择形式,而俄语需要三种(1 вызов / 2 вызова / 5 вызовов)。本包接管了这件事:词典在 `.one` 与 `.other` 之外还提供 `.few` 和 `.many`,并通过对运行时 `translate` 的包装,用 `Intl.PluralRules('ru')` 为任何带有数字 `count` 或 `n` 的调用重新计算形式。决定形式的是数字,而不是组件选中的 key,因此 «2 вызова инструментов» 和 «5 вызовов инструментов» 出自同一个调用点。形式并不限于成对的 key:当 harness 把数字传给一个它从未配对的 key(如带 `{n}` 的 `terminal.expand`)时,本包会为它补上自己的 `.one` / `.few` / `.many`——«Показать ещё 1 строку / 3 строки / 7 строк»——包装层也会为这类 key 查找形式。在形式不适用处(一句话里出现多个数字),取值被改写为对数字中性:用 «Символов: {total}» 代替 «{total} символов»,用 «файлов: {files}» 代替 «{files} файлов»。由 `npm run check:plural` 检查——五个用例,包含未配对的 key 与小数。
111
+ - **回退**——缺失的俄语 key 会回退到 `en`(配置的 `fallback`)。
112
+
113
+ ## 排版
114
+
115
+ 本包会把已经渲染出来的俄语文本按排版规则处理:用「ёлочки」(« »)代替直引号,用长破折号代替词与词之间的连字符,用省略号代替三个点,在数字与其后的词语或计量单位之间加不换行空格(以免 «187 плагинов» 被折成两行),并在短词——前置词、连词或语气词——之后加同样的空格:«в режим» 会整体换行,而不会把 «в» 单独留在行尾。这些规则有意保持保守:只在无歧义的模板上生效,而代码、输入框、可编辑文本以及带 `data-typography="off"` 的元素完全不会被触碰。重复处理不会带来任何变化。
116
+
117
+ 数字区间规则(«5–10» 用短横线)**有意没有加入**:整个包中唯一在两个数字之间出现连字符的地方是 cron 表达式示例 `0 9 * * 1-5`,换成短横线会把它改坏。这是实测结论,而非假设。
118
+
119
+ 这些规则由脚本检查,而不是靠肉眼:`npm run check:typography`。
120
+
121
+ 排版处理只看到文本节点,而词典取值还会进入属性——`title`、`aria-label`、`placeholder`——它根本到不了那里。因此取值单独检查:包内不能出现直引号、用连字符代替破折号、没有空格的长破折号,或三个点。否则同一个字符串会因为落点不同而呈现两种样子,而提示与朗读标签里则完全没有规则生效。
122
+
123
+ **第一次处理之后**发生变化的文本——令牌计数器、计时器、进度——会被再次处理,因此实时的 «4 с» 仍保留不换行空格。这一点在真实文档上验证,而不只是对字符串:`npm run check:typography:dom`(jsdom:首次处理、文本变更、新增子树、卸载停止、其他语言,以及语言切换)。
124
+
125
+ 该处理**仅在俄语处于活动状态时**运行:规则是俄语的,把它们用于其他语言会改动本包并不拥有的文本——英文界面里的 `"quotes"` 会变成 «quotes»。本包监听 `locale/change`,在切换语言时启动或停止处理,而不是在安装时。
126
+
127
+ ## 插件名称
128
+
129
+ 插件的名称来自它自己的 `locale/<语言>.json` 文件,而本地化服务解析这张映射时不会去查已注册的词典——它自己的文档写着 «maps do not consult registered dictionaries»。因此词典无法翻译插件名称:本包拿不到别的包的映射,英文名称会一直是英文,直到插件自己带上 `locale/ru.json`。
130
+
131
+ 本包自带这些名称:32 个 harness 插件的名称,以及 33 个第三方插件的名称与说明(这些插件的词典本包在翻译)。每一项都记着翻译所依据的英文文本和俄文文本。已经渲染出来的界面会经过一个处理流程,把俄文放到英文的位置上:插件卡片、bundle 的组成行、插件详情页以及设置中的插件清单。朗读用的名称(`aria-label`)也会一并替换,因此整张卡片读起来都是俄文。
132
+
133
+ 这个流程只做精确匹配:整段文本与本包知道的标题或说明完全相同时才会替换。只是提到了某个名称的句子保持原样;代码、输入框和对话不会被触碰。与排版流程一样,它只在俄文是当前语言时工作。
134
+
135
+ 同样这 32 个名称会作为补丁(`docs/harness-descriptors-ru.patch`)送到 harness:在 harness 自己的界面里插件也有俄文名称,而这个补丁先到。补丁是构建产物——它由翻译和 harness 目录树生成,门禁 `npm run check:descriptor-patch` 在生成的补丁与仓库里那份不一致时失败。第三方插件的名称放在 `src/client/plugin-names-ru.json`:这个文件由插件词典生成(`npm run build:plugin-names`),门禁 `npm run check:plugin-names:map` 在它与词典不一致时失败。同一个英文名称不能有两种译法——harness 与插件各说各话。`npm run check:plugin-names` 在真实文档上证明(jsdom,12 个用例)这个流程会翻译标题、说明、朗读名称和 bundle 的一行,而该不动的它不动。
136
+
137
+
138
+ ## 许可证
139
+
140
+ MIT
141
+
142
+ ## 变更历史
143
+
144
+ [CHANGELOG.md](CHANGELOG.md)——每个版本都有什么。