@ragnoryok1/dsh-client-locale-ru 0.2.15 → 0.2.17

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,59 @@
1
+ ## [0.2.17] — 2026-10-24
2
+
3
+ ### Изменено
4
+
5
+ - **Описание пакета написано простым языком.** В npm, в мета-блоке README и в
6
+ описании каталогов стояло «Russian (ru) interface for the DeepSeek Harness web GUI:
7
+ 584 namespaces, 67666 strings… **Hybrid dsh.bundle + dsh.client plugin**» — набор
8
+ внутренних терминов, из которого человек, выбирающий перевод, не узнавал ни что он
9
+ получит, ни зачем ему это. Теперь описание говорит о деле: весь интерфейс харнесса
10
+ и меню 520 сторонних плагинов, 584 раздела, 67 666 строк.
11
+ - **README сокращён с 607 строк до 210.** Убраны шесть разделов, написанных для
12
+ разработчика: «Область покрытия», «Разработка», «Релизы», «Содержимое», «Что
13
+ переведено» (118 строк истории версий — она и так в этом файле) и «Проверки» (184
14
+ строки про аудит, снимки словарей и совместимость). Команды сборки остаются в
15
+ git-истории, проверки — в `scripts/audit.mjs`; в README теперь только то, что
16
+ нужно читателю: что это, как поставить, как выглядит, типографика, имена плагинов.
17
+
18
+ ### Исправлено
19
+
20
+ - **Ошибка в описании области покрытия.** Русское и китайское вступления говорили
21
+ «521 стороннего плагина», а в пакете 520 — цифра отставала от словаря, который был
22
+ удалён как несуществующий.
23
+ - **Аудит читал группированные числа неправильно.** В «67,666 strings» он видел
24
+ «666 строк» и валил сборку на верной цифре; шаблоны «<число> строк» ловили в тексте
25
+ посторонние числа — пример форматирования и длину самого файла словаря. Сверка
26
+ стала якорной, с поддержкой разрядов через запятую, пробел и неразрывный пробел.
27
+
28
+ ## [0.2.16] — 2026-10-23
29
+
30
+ ### Добавлено
31
+
32
+ - **Словарь плагина `@norman-else/dsh-claude@0.1.66` — 470 строк.** Плагин запускает
33
+ локальный Claude Code CLI как основную беседу внутри харнесса; своей русской локали у него
34
+ нет, поэтому весь его экран настроек — режимы разрешений, работа с рабочим деревом,
35
+ `/usage` подписки, ограничение простоя — оставался английским. Перевод лежит в namespace
36
+ `settings.claude-code`: термины взяты из уже переведённых словарей пакета («рабочее
37
+ дерево», «пул-реквест», «ревью», «раунд», «поле ввода»), а имена продуктов и команд
38
+ (`Claude Code`, `Jira`, `Atlassian`, `Bedrock`, `Vertex`, `Doctor`, `/usage`)
39
+ остались как есть. Английский текст выгружен из публикации плагина вместе с версией и
40
+ лицензией.
41
+
42
+ ### Исправлено
43
+
44
+ - **Удалён словарь-призрак `acp_turn_sep_` — 528 строк, которых у плагина нет.**
45
+ Улучшенный разбор бандлов принял за имя словаря константу
46
+ `RESPONSES_TURN_SEPARATOR_ID_PREFIX` из плагина `billion-context`: `acp_turn_sep_` — это
47
+ префикс служебных идентификаторов, а не имя словаря. Эти строки в принципе не
48
+ могли появиться на экране, зато попадали в пакет, в счётчики и в политики. Настоящий словарь того
49
+ же плагина — `foldPositions` — остался на месте.
50
+ - **Режим `verbose` больше не называется «Подробный».** В списке «Детали работы»
51
+ два режима подписывались одним словом — «Подробный», и пункт «показывать всё, ничего не
52
+ сворачивая» выбирался наугад. Теперь у `verbose` своё
53
+ слово — «Развёрнутый»: рядом с «Компактным», «Стандартным» и «Подробным» он читается как
54
+ отдельный режим. Дефект нашёл `bondarevpm-tech` в PR #4; правка применена в `main`,
55
+ потому что пул-реквест разошёлся с веткой.
56
+
1
57
  ## [0.2.15] — 2026-10-22
2
58
 
3
59
  ### Изменено
package/NAMESPACES.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <!-- Сгенерировано `npm run namespaces`; проверяется `npm run check:namespaces`. -->
2
2
  # Что переведено
3
3
 
4
- Пакет переводит **584 namespace** — 67724 строк. Колонка «как это выглядит»
4
+ Пакет переводит **584 namespace** — 67666 строк. Колонка «как это выглядит»
5
5
  берёт заголовок самого namespace (то, что читает пользователь), а не отдельное описание:
6
6
  список нельзя разойтись с переводами.
7
7
 
@@ -15,7 +15,6 @@
15
15
  | `.ndjson.zst` | Английская страница (USD) | 2 | dsh-usage-badge@0.1.9 |
16
16
  | `.runs.json` | Название | 70 | dsh-tongflow@0.8.4 |
17
17
  | `about.huaqiu` | О плагине | 9 | @huaqiu/dsh-huaqiu-about@0.6.6 |
18
- | `acp_turn_sep_` | — | 528 | billion-context@0.1.204 |
19
18
  | `agency` | — | 94 | @michengai/dsh-agency-agents@1.2.10 |
20
19
  | `agent-community` | Bots团队 | 1 | @dsh-plugin-agent-community/ui-agent-community@0.3.9 |
21
20
  | `agent-schedule` | tool.call.toolview | 61 | dsh-agent-toolkit@0.4.13 |
@@ -423,6 +422,7 @@
423
422
  | `settings.catppuccin` | — | 34 | dsh-catppuccin@0.2.6 |
424
423
  | `settings.chatTranslate` | — | 45 | @lynn123411/dsh-chat-translate@3.5.9 |
425
424
  | `settings.checkpointRewind` | Хронология чекпоинтов | 49 | dsh-checkpoint-rewind@0.6.33 |
425
+ | `settings.claude-code` | Claude Code | 470 | @norman-else/dsh-claude@0.1.66 |
426
426
  | `settings.codebuddy` | CodeBuddy | 50 | @shatyuka/dsh-llm-codebuddy@0.9.3 |
427
427
  | `settings.codebuddy-cli` | Подключение DSH CodeBuddy CLI | 162 | dsh-codebuddy-cli@0.4.3 |
428
428
  | `settings.codeIndex` | — | 12 | dsh-code-index@0.11.0 |
@@ -597,4 +597,4 @@
597
597
  | `xrn1997-dsh-novel` | — | 160 | @xrn1997/dsh-novel@0.8.0 |
598
598
  | `zhihu-search` | — | 19 | dsh-zhihu-search@2.2.1 |
599
599
 
600
- Итого: **584** namespace, **67724** строк.
600
+ Итого: **584** namespace, **67666** строк.
package/README.en.md CHANGED
@@ -1,9 +1,9 @@
1
1
  <!-- deepseek-harness-meta
2
2
  {
3
3
  "name": "Russian (ru) language pack for DeepSeek Harness",
4
- "version": "0.2.15",
4
+ "version": "0.2.17",
5
5
  "tags": ["localization", "russian", "i18n", "web-gui", "language-pack"],
6
- "description": "Full Russian (ru) interface for the DeepSeek Harness web GUI: 584 namespaces, 67724 strings, 520 third-party plugin dictionaries, Russian screen typography and Russian plural forms, with a locale audit in CI pinned to a harness tag.",
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
7
  "icon": "https://raw.githubusercontent.com/Ragnoryok1/dsh-client-locale-ru/main/assets/icon.png",
8
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
9
  "screenshots": "images",
@@ -22,16 +22,24 @@
22
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
23
  [![dsh](https://img.shields.io/badge/dsh-0.2.1--rc.2%2B-6366f1)](https://github.com/deepseek-ai/deepseek-harness)
24
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-67724-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
26
 
27
- Russian (`ru`) language pack for the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) web GUI (`dsh`). It registers `ru` as a selectable client language through the locale service (`ctx.locale.addLanguage`) plus one dictionary per namespace — 584 namespaces, 67,724 strings, built against dsh `0.2.1-rc.2`. Install with `dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru`, then pick **Русский** in Settings → General. Missing keys fall back to English, so a newer harness keeps working. The scope is the harness's own client interface — 64 namespaces (2 996 strings) — plus the dictionaries of 520 third-party plugins, translated from the English each plugin publishes (64728 strings in total). MIT, community-maintained, not affiliated with DeepSeek.
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.
28
36
 
29
37
  > **If the pack was useful to you, please star the repository.** It is the only signal that a
30
38
  > translation is wanted, and it is what decides which parts get worked on next.
31
39
 
32
40
  ---
33
41
 
34
- The complete inventory is in [NAMESPACES.md](NAMESPACES.md): 584 namespaces, 67,724 strings, with the bundled plugin dictionaries marked.
42
+ The complete inventory is in [NAMESPACES.md](NAMESPACES.md): 584 namespaces, 67,666 strings, with the bundled plugin dictionaries marked.
35
43
 
36
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.
37
45
 
@@ -99,7 +107,7 @@ two (the message names the version) or drop the `minimumReleaseAge` limit in the
99
107
  settings.
100
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.
101
109
 
102
- 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 2022 kB: the banner ships as JPEG, and the icon and source map stay out of the npm payload.
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 1975 kB: the banner ships as JPEG, and the icon and source map stay out of the npm payload.
103
111
 
104
112
  ## Why hybrid
105
113
 
@@ -194,403 +202,6 @@ translates the title, the description, the accessible name and a bundle row —
194
202
  it must alone.
195
203
 
196
204
 
197
- ## Checks
198
-
199
- ```sh
200
- npm run build # the three outputs
201
- npm run check:typography # 18 typography rules, including "leave alone"
202
- npm run check:locale # dictionaries side by side: 6 checks
203
- npm run check:plural # plural forms: 1 вызов / 2 вызова / 5 вызовов
204
- npm run check:composition # composed phrases: «Пн, Ср в 09:00», «Раз в 2 дня»
205
- npm run check:namespaces # NAMESPACES.md matches the built bundle
206
- npm run check:tarball # what gets installed: the archive is unpacked and checked
207
- npm run check:workflows # both workflows run the whole verify chain
208
- npm run check:readmes:pairs # the three READMEs move together (recorded section hashes)
209
- npm run check:plugin-names # plugin names in Russian on a real document
210
- npm run check:descriptor-patch # the harness patch matches the translations
211
- npm run audit -- <harness tree> # the locale audit
212
- npm run verify -- <harness tree> # everything together
213
-
214
- npm run namespaces # regenerate NAMESPACES.md and pack-count.json
215
- npm run readmes:record # re-record section hashes after editing all three READMEs
216
- npm run diff:harness -- <old> <new> # what the harness reworded under us
217
- ```
218
-
219
- A separate check composes phrases the way the harness does and reads the result whole. The
220
- audit compares a value with its own template, so a phrase that is correct in isolation but
221
- broken by substitution stays invisible to it: `frequency.weekly` was filled as «По {weekdays}
222
- в {time} », while the days arrive as «Пн, Ср» — producing «По Пн, Ср в 09:00», which no
223
- line-by-line check can see. The same way «Каждые 2 дня» (now «Раз в 2 дня»), «Каждый день в
224
- 09:00 (Europe/Moscow)» and a composed interval with the plural forms are pinned. The third find
225
- of the same kind: `time.locale` is not text but a tag for `Intl`, and it was `en` — a rule
226
- restricted to a month read «Каждый день в January», and the zone label «Moscow Standard Time»
227
- inside a Russian interface. The tag is `ru` now (the harness's Chinese dictionary uses `zh-CN`
228
- the same way), and the month sits in parentheses: «Каждый день (январь, март)».
229
-
230
- The same part-by-part reading found the cron wording: abbreviations («Каждые {step} мин»,
231
- «Каждые {count} ч») and bookish turns («в минуту {minutes}», «минуты {minutes} в часы
232
- {hours}»). The counted parts gained plural forms — «Каждые 3 минуты» / «Каждые 5 минут»,
233
- «Каждые 2 часа» / «Каждые 6 часов» — and the rest were rewritten into one readable labelled
234
- style: «Каждый час, минуты: 5, 30», «минуты: 5, часы: 09–17». The whole schedule sentence is
235
- asserted end to end: «Пн–Пт каждые 5 минут». The date stamps are pinned the same way: «5 окт. 2026 г., 15:51» and the zone label «Москва, стандартное время» come from Intl with our tag, without a single Latin word.
236
-
237
- The audit runs in CI (`.github/workflows/verify.yml`) against a **pinned harness tag**,
238
- not against some random local checkout. The same run also fires **weekly on a schedule**: a
239
- translation rots without anyone editing it — the upstream moves on, everything is green
240
- locally, and the drift surfaces only when a user builds. The gates cover completeness (keys and
241
- namespaces), plural forms, placeholders, English and Chinese residue, terminology, Latin words in
242
- prose, text hygiene, edge spaces, style, typography of values, «ё», composed phrases, the length
243
- of settings sidebar labels and the advertised numbers — and every one of them has already caught
244
- real defects. Beside them the audit
245
- prints **advisory reports**,
246
- which are not gates because they need a human decision: how many namespaces were compared and
247
- which could not be linked, whether any «number + inflecting word» is left, and whether a short
248
- string grew past 2.8× its English (a layout risk — «Effort» → «Уровень рассуждения» is normal
249
- Russian length, not a defect).
250
-
251
- The audit reads harness templates in the shape the harness writes them: a literal, an **array of
252
- paragraphs** (that is how the preset guides are stored) and a **reference to a constant**
253
- (`'hint.plan': PLAN_NEXT_ACTION_EN`). While only the first shape was read, ten keys — eight of
254
- them whole guide documents — silently fell out of every comparison. And the other way round: an
255
- empty policy file (`keep-english.json`, a `terminology.json` section) now fails the audit instead
256
- of widening a gate unnoticed. The numbers the READMEs advertise are checked against the pack the
257
- same way, because a stale count is simply a wrong claim — and the same numbers are read from the
258
- `package.json` description, which is what npm shows in search and on the package page (that is
259
- where «55 namespaces» lived longest) — including the grouped spelling: the
260
- old leads said `2 489`, and the gate only read the ungrouped form, so the pattern now accepts
261
- thousands separators. The sentence naming the harness version the pack was built against is
262
- checked next to it, against the tag the workflow pins, because that workflow is what verifies
263
- the pack (both leads claimed `0.2.1-alpha.2` while CI had already moved to
264
- `dsh-v0.2.1-rc.2`). The published size is measured there too: `npm run check:size` packs the
265
- archive and compares the advertised kilobytes with the real ones, allowing one kilobyte of slack
266
- because gzip compresses slightly differently across Node builds while the sentence rounds to
267
- whole kilobytes.
268
-
269
- - **dead namespaces** — every namespace the package registers has to
270
- exist in the harness. In 0.1.6 the schedule catalog translations were registered
271
- under the name `schedule.manager`, whereas `schedule.catalog` is what reads them: the per-key
272
- coverage check nonetheless reported «всё переведено»;
273
- - **plural-form pairs** — the `.one`/`.other` sets have to match, because the client
274
- chooses the form as `count === 1 ? '.one' : '.other'`. That is not enough for Russian:
275
- every pair also needs `.few` and `.many`, or the wrapper quietly falls back to the neutral
276
- `.other` and shows «2 д» where «2 дня» was possible. The check runs **per namespace** —
277
- the wrapper looks the form up in the namespace that asked, so a pair missing on one screen
278
- while present on another would otherwise stay invisible. It found the four time units
279
- (`unit.day/hour/minute/second` in `schedule.catalog`) and `trigger` in the task manager;
280
- the units stopped being abbreviations («д», «ч», «мин») and gained full forms in the right
281
- case — «через 2 дня», «через 5 часов», and «Каждые 2 дня» became «Раз в 2 дня». The same
282
- requirement covers the keys the pack added itself (`terminal.expand` and its kin: the
283
- harness passes them a count but never pairs them) — their forms rest on us alone, and
284
- losing one would be invisible to everything else;
285
- - **placeholders** — a Russian value has to carry exactly the `{name}` placeholders its
286
- English template carries: an extra one reaches the screen literally
287
- (`Заняло {duration}7мин 26с`, fixed in #2), a dropped one silently removes the
288
- interpolated value. Packages that ship only the Chinese dictionary are compared
289
- against `zh`. Our **own plural forms** — the `.few`/`.many` we add, and the `.one` of a key
290
- the harness never paired — do not exist upstream, so each is compared with the template of
291
- a sibling form of the same word (`.one`/`.other`) or of the key itself. Without that, 52
292
- values were never checked at all: that is exactly how a `{count}` can disappear from a form
293
- and leave the sentence without its number.
294
- - **residue** — a value has to be Russian: an exact copy of the English template, a leftover
295
- Chinese character, or an empty string where the template has text is a defect. English
296
- copies are allowed only by a decision recorded in `scripts/keep-english.json` (addresses,
297
- format-only strings, protocol and tool names, abbreviations), so a new untranslated line
298
- cannot slip through unnoticed. The decisions themselves are checked too: an entry that no
299
- longer excuses anything fails the build — either its key left the pack, or the value was
300
- translated and the decision went stale. This is how the `deepSeekBaseUrl` entry was found:
301
- the English template gained a note about `DEEPSEEK_BASE_URL`, while the input placeholder
302
- has to be a bare example URL, so that note would have been noise inside the field. The entry
303
- is gone, the value stays. A decision may also name a closed list: `chat · app.*` allows
304
- thirty application names in one line, because a dictionary of application names is a list of
305
- names. And a value with no word outside its placeholders (`{from}–{to}`, `{percent}%`, `, `)
306
- needs no decision at all: there is nothing in it to translate, so the policy stays about
307
- words rather than about punctuation. The residue rule reads **every** template of a key, not
308
- only the `en` block: 60 values in dictionaries with an unrecognised block used to go
309
- unexamined, while now 2911 values are compared, 68 of them covered by decisions (38 entries,
310
- one of them a pattern), and the zone is gone as a class. The rule did not become wider:
311
- an empty value is a defect only where no template is empty either, since Russian justly
312
- drops the prefix that English drops too (`message.stepProcess.sharedPrefix`).
313
- - **dead keys** — a key no harness dictionary defines and no harness source names is a
314
- leftover from an older version: it shows nothing and only misleads. Such keys fail the
315
- build. A key a component still asks for stays in the advisory report, because the pack's
316
- value is what reaches the screen.
317
- - **terminology** — one concept, one rendering. Forbidden variants («кеш» for «кэш», «сеанс»
318
- for «сессия», «этап» for «шаг») fail the build, and `scripts/terminology.json` keeps both
319
- the decision and counts of the chosen forms, so drift shows up before it becomes a defect.
320
- The check immediately found `confirm.description` translated two ways in two namespaces —
321
- «шаги» and «этапы». One word may not be spelled both with and without «ё»: «ещё» next to
322
- «еще» reads as machine translation, so the check folds «ё» to «е» and fails on two spellings
323
- of one folded form. Real distinct pairs such as «все» and «всё» are declared in
324
- `scripts/yo-pairs.json`, and a declaration whose spellings left the pack fails as well. The
325
- same report covers Latin words inside Russian phrases: a product,
326
- command or code token is allowed, an ordinary English word in a sentence is not. A product name
327
- has to be spelled the way the harness spells it: «Github Desktop» for «GitHub Desktop» reads as a
328
- typo in the product's own name, and the pack is the only place it can appear. The reference is
329
- the English templates; a word the harness writes only in lowercase («cursor», «desktop») does not
330
- count, because there a capital is exactly the product name. Next to it
331
- comes mechanical hygiene: double spaces, a space before punctuation, unpaired «ёлочки» and
332
- brackets, a non-breaking space inside a value, and edge spaces — a value that starts or ends
333
- with one is glued to a neighbour by the component, so it has to match the English template
334
- of its own namespace. The same template checks style: a terminator the English carries must
335
- not disappear (this is how `registryCustomHint` was found without its period), and the case
336
- of the first letter has to match — except for acronyms like `JSON` and templates that open
337
- with a placeholder. The address to the reader is checked on its own: inside a sentence the
338
- Russian interface writes «вы» lowercase, and «Вы» with a capital letter only at the start of
339
- a sentence, where it is nothing but spelling. Finally the pack is compared with itself: one
340
- English sentence has to come back as one Russian sentence. When two namespaces render it
341
- differently — templates of four words or more are compared, because a short label legitimately
342
- follows its context — the build fails. This is how `confirm.description` was found: one
343
- rendering promised to «пропускать шаги подтверждения», the other to «уменьшать число шагов»,
344
- and the English «subsequent tasks» had become «текущей задаче» in the second. A deliberate
345
- divergence is declared in `scripts/one-rendering.json` with its reason, and an exception that
346
- no longer matches anything fails as well — otherwise the list of decisions would quietly widen
347
- the check. The opposite direction is printed as an advisory: the same Russian answer to
348
- **different** English sentences of four words or more hints at a copy-paste — a hint carried
349
- over from a neighbouring key, or two distinct facts collapsed into one phrase. It cannot be a
350
- gate, because Russian legitimately merges words English keeps apart, so both sides are printed
351
- together and a human decides.
352
-
353
- A separate workflow watches the tag itself: `watch-upstream` compares the pinned tag with
354
- the harness tags once a week, opens an issue with the steps when a newer one exists, and turns
355
- the run red — so lagging behind is found on a schedule rather than in a user's screenshot. The
356
- version comparison is checked without the network (`npm run check:upstream`: prerelease order,
357
- equal versions, a move between lines) and is
358
- part of `verify`: a release has to outrank its own prereleases, or the watch would fire forever.
359
-
360
- The coverage number is not a gate: some sections are deliberately left in English. Next to it
361
- the audit prints a **per-namespace** report: it rebuilds the namespace-to-dictionary link from
362
- whoever registers it (`NS`, or a literal in `locale.register`; `bind` and `PropsLocale` only read
363
- a namespace) and lists keys missing from both their own namespace and the shared `common`
364
- dictionary the runtime falls back to. A global name comparison cannot see those holes: a
365
- translation filed under another namespace counts as coverage while the screen shows English.
366
- The report is advisory — the same key name can mean different things in different namespaces
367
- (`list.aria` is "Active reminders" in the catalog and "Background jobs" in jobs), so a
368
- translation is copied across namespaces only where one dictionary serves both. On the pinned
369
- tag the report is empty: every string the harness renders in a namespace we register lives in
370
- that namespace. All 584 of the pack's namespaces are compared — the link comes both from a
371
- dictionary imported beside the registration and from the names that registration passes
372
- (`locale.register(NS, { zh, en })`); without the second rule `common` and `settings.locale`,
373
- whose dictionaries sit in a neighbouring directory, would stay uncovered. The other direction is
374
- a failure rather than a report: a namespace the harness registers and fills with keys, which the
375
- pack never registers, leaves that screen in English while key coverage still looks complete.
376
- This is how `settings.sessionLog` was found — the “Upload Session Log” switch on the general
377
- settings page.
378
-
379
- ## Coverage scope: the harness and plugin dictionaries
380
- The package translates **the harness client's own namespaces** — 64 namespaces reconciled
381
- against the pinned tag — and **the dictionaries of third-party plugins**: 520 plugins whose
382
- English text is vendored from their own publication, with the version and the licence.
383
-
384
- Searching the npm registry — six queries instead of one «dsh» — found 40 more plugins with
385
- a dictionary of their own. 39 are translated; three names (`dsh-automation`, `remote`,
386
- `settings.ip-pool`) are already taken by other plugins' dictionaries and stay with their
387
- owners, so that nobody's product gets somebody else's translation. As of 22 October 2026:
388
- **584 namespaces, 67 724 values, 520 dictionaries, 47 700 distinct Russian strings** in the
389
- built package. The competitor measurement, by the same method —
390
- [docs/competitors.md](https://github.com/Ragnoryok1/dsh-client-locale-ru/blob/main/docs/competitors.md).
391
-
392
- Plugin dictionaries were out of scope before, and there was a reason: `dshmarket` is already
393
- at version 1.66.44, its keys change independently of the harness, and a silent promise of
394
- «everything is covered» is not a promise but a debt nobody can collect. The answer turned
395
- out to be simple: the foreign English text is **vendored** (`npm run fetch:plugins`) and
396
- stored next to the translation, with the package name, the version and the licence, and the
397
- audit holds our translation to that snapshot rather than to somebody else's Russian. We
398
- checked the 17 most-downloaded plugins in the ecosystem (npm, `keywords:dsh-plugin`, monthly
399
- downloads) and looked at which of them ship strings of their own at all:
400
-
401
- | What we found | Plugins |
402
- |---|---|
403
- | **No strings of their own** — the interface takes its text from harness namespaces | **11 of 17** |
404
- | A large dictionary of their own (confirmed by hand) | 2 — `dshmarket` (733 keys), `@nanmicoder/dsh-agent-teams` (420) |
405
- | `.d.ts` types only, no strings | 1 — `dsh-plugin-model-proxy` |
406
- | Needs a separate check | 3 |
407
-
408
- For 11 of the 17 most popular plugins the Russian interface already works without a
409
- dictionary of their own: their text lives in the shared harness namespaces, which the package
410
- translates in full. Dictionaries exist where a plugin carries strings of its own — there
411
- are 32 of them, and each records the version its English text was vendored from.
412
-
413
- Such a dictionary is a commitment to track someone else's releases, but it is now a
414
- checkable one: a new version of the plugin with different keys shows up as a difference from
415
- the snapshot instead of as silence. Need a specific plugin? Open an issue: a dictionary for
416
- it is possible as a separate layer pinned to a version, rather than as a silent promise that
417
- «everything is covered».
418
-
419
- ## Development
420
-
421
- ```sh
422
- pnpm install
423
- pnpm run build # tsdown -> lib/index.js (node half) + lib/client.js (client bundle)
424
- npm pack # -> ragnoryok1-dsh-client-locale-ru-<version>.tgz
425
- ```
426
-
427
- `lib/client.js` is built in the format the `dsh` client loader understands (`__ModuleLoader__.load`) and exports `inject`/`apply`.
428
-
429
- ## Releases
430
-
431
- Publishing to npm is done by the `publish` workflow on a tag — the credentials live in a repository
432
- secret (`NPM_TOKEN`), so the token never has to be typed into a terminal or passed around.
433
- Publishing by hand from a machine is possible, but unnecessary.
434
-
435
- ```
436
- npm version 0.3.0 --no-git-tag-version # поднять версию в package.json
437
- # …и синхронно обновить версию в блоке deepseek-harness-meta в этом README
438
- git commit -am "release: 0.3.0" && git tag v0.3.0 && git push origin v0.3.0
439
- ```
440
-
441
- Before publishing, the workflow checks:
442
-
443
- 1. the version in the tag matches the version in `package.json`;
444
- 2. the version in the `deepseek-harness-meta` block matches `package.json` — the marketplace takes it from there,
445
- and reads it **only on first indexing**, so a mismatch does not fix itself;
446
- 3. the typography (18 rules);
447
- 4. the localization audit against the pinned harness tag — dead namespaces, duplicates and plural-form
448
- pairs will fail and stop the publication;
449
- 5. **compatibility with every version in `compatible_versions`**: each one's locale runtime is
450
- downloaded from npm and three suites — language coexistence, plural forms and composed phrases
451
- — run against it (`npm run check:compat`, part of CI), so the promise in the meta block is
452
- checked rather than assumed.
453
-
454
- The declaration itself is checked separately (`npm run check:peers`, part of `verify`): the harness
455
- reads our `peerDependencies` for `@deepseek-ai/dsh*` not as package versions but as a range of
456
- **runtime versions**, with `includePrerelease`, and a range that fails to satisfy the running dsh
457
- makes the user see an incompatibility warning and an exemption prompt. The script runs that rule
458
- over every claimed version — all eight pass today.
459
-
460
- To check all the same things locally, without publishing:
461
-
462
- ```
463
- npm run verify -- путь/к/дереву/харнесса
464
- ```
465
-
466
- The workflow also has a manual run with a «только проверки, без публикации» toggle
467
- (`workflow_dispatch` → `dry-run`).
468
-
469
- ## Contents
470
-
471
- - `src/client/dicts.ts` — the Russian dictionaries (584 namespaces, 67,724 translations, ~46 000 lines). This is where the main value lies.
472
- - `src/client/index.ts` — the client plugin entry point (registration of `ru` + the dictionaries).
473
- - `src/index.ts` — the empty host half (`apply()`).
474
- - `cordis.patch.yml` — the profile patch (`- insert:` of the client entry `locale-ru`).
475
-
476
- ## What is translated
477
-
478
- The current set follows the harness tags: the keys were taken from the matching commit, and keys
479
- added later fall back to `en` on lookup, so the package does not break the interface on newer versions.
480
-
481
- ### 0.1.7 — dead namespace fixed, task manager translated, plural forms rewritten
482
-
483
- The headline item in the release is **a fix for a mistake from 0.1.6**. At the time it was recorded
484
- that the namespace `schedule.catalog` had been retired and that `schedule.manager` had taken its place.
485
- In reality **both** namespaces live in the harness, separately: `schedule.catalog` is the reminder
486
- catalog in the session header with frequencies and units, `schedule.manager` is the new automation-task
487
- manager. Because of that wrong conclusion, ~30 translation strings (reminders, frequency, time
488
- units) were registered under a name nobody reads and were **never shown** in the interface. The
489
- catalog is now registered as `schedule.catalog`.
490
-
491
- - **fixed:** the schedule catalog translations are visible again — `trigger.*`, `list.*`,
492
- `delete.*`, `frequency.*`, `cron.*`, `unit.*`, `relative.*`, `mark.aria`, `hover.more`;
493
- - **`schedule.manager` added** (188 keys) — the automation-task manager entirely in
494
- Russian: the task panel and catalog, search, status filter, task details and its tabs,
495
- the **run log** with retention rules, the schedule editor (date, time, hour, minute,
496
- second, time zone, repeat interval), repeat rules (once, every N minutes/hours/
497
- seconds, daily, Mon–Fri, weekly, custom cron), the cron form with date and minute
498
- pickers, deletion with confirmation, notifications and cards;
499
- - **plural forms rewritten** (17 key pairs): forms that broke on the numbers 2–4
500
- were replaced with ones that are correct for any `n ≥ 2` — for example «2 субагентов» → «Субагентов: 2»,
501
- «каждые 2 часов» → «каждые 2 ч». Another 8 strings with `{count}` before an inflected word
502
- were switched to abbreviations: «ещё 3 строк» → «ещё 3 стр.»;
503
- - **7 missing keys added**: `blocked.composer`,
504
- `defaultWorkspace.title`, `schedule.active`, `settings.transcript.expanded`,
505
- `status.scheduled`, `status.overdue` and the namespace `shortcuts.layout` (the label for
506
- the command that shows the left panel);
507
- - **a new check in the project:** a namespace-name audit. The previous coverage check
508
- compared keys inside a namespace but never compared the names themselves, so an entire
509
- namespace could be registered under a non-existent name and quietly do nothing. Names
510
- are now extracted from every way they can be declared (`const NS`/`SETTINGS_NS`/`namespace`,
511
- `locale.register`, `locale.bind`, `PropsLocale<…>`) and compared as sets.
512
-
513
- 0.1.2 added sections that 0.1.1 did not have: `open-in-app` («Открыть в
514
- приложении»), `sidebarFiles`, `sidebarRight` (the right sidebar with files) and
515
- `sidebarCodePreview` (code preview). It also updated the keys that changed in
516
- harness 0.1.5 and removed 21 keys that no longer exist there.
517
-
518
- In 0.1.3 the package caught up with harness 0.1.6-alpha.1: 139 keys and 7 new
519
- sections were added — `sidebarTerminal` (the terminal in the right sidebar), `settings.archivedSessions`
520
- (archived sessions) and the document-preview family (`sidebarDocumentPreview`,
521
- `documentHtml`, `sidebarImage`, `documentMarkdown`, `sidebarPdf`). Besides that, the experimental
522
- «Автопроверка» was translated (`permission.access`: `auto.*`), along with the command-palette
523
- labels and tokens, the endpoint fields in the model settings, and the
524
- changes in `conversation`, `trajectory`, `chat`, `session-log-download`.
525
- 28 keys that had disappeared from the harness were removed.
526
-
527
- The coverage check runs against the harness sources: for each namespace the
528
- key sets are compared, so the next release starts from an exact
529
- “add / check / remove” list.
530
-
531
- In 0.1.6 the package caught up with harness **0.1.7-rc.2**: **252 keys** added, 18 updated,
532
- 26 removed, and two new namespaces added. The changes:
533
-
534
- - **`shortcuts`** (new namespace, 56 keys) — the hotkey editor: recording
535
- a combination, conflicts, reserved combinations, hints for macOS and
536
- Windows, restoring the default values;
537
- - **`schedule.manager`** (70 keys at the time) — the automation-task manager: list,
538
- deletion, repeat, run history, Cron expressions. **It was wrongly recorded that
539
- the namespace `schedule.catalog` had been retired by this; 0.1.7 fixed that — both
540
- namespaces exist side by side**;
541
- - **`settings.account`** (50 keys) — the app's first launch: the welcome,
542
- the choice of purpose and of how much of the work is shown, topping up the balance, signing out of
543
- the account while tasks are running;
544
- - added the labels for adding and removing tools on the fly
545
- (`message.tools*`, `trajectory.layout.*`), the right sidebar commands
546
- (`sidebarRight`), the «сначала выберите сессию» states in the panels, and updated
547
- the name of the settings section: «Инструменты для кода» instead of «Инструментов
548
- разработчика»;
549
- - 18 keys updated to match the changed English text, 26 removed. A single term,
550
- «рабочая папка», instead of «рабочей области» in one place.
551
-
552
- In 0.1.5 the package caught up with harness **0.1.7-rc.1**: **49 keys** added, 13 updated,
553
- 16 removed, and the namespace `directory-browser` dropped (it is gone in rc.1). There appeared
554
- the labels for the steps tools go through (`message.stepProcess.prepare.*` and `done.*` —
555
- reading images, writing files, preparing for code search, web search,
556
- subagent coordination and others), full-screen image viewing
557
- (`image.*`), the «Стандартный» and «Подробный» modes for showing the work, installing plugins
558
- from GitHub with a timeout and a Chinese mirror, the warning about an incompatible
559
- plugin version, the choice of model download source for voice input, and `Cordis` command
560
- messages. The wording was tightened where upstream replaced “teammate/agent”
561
- with “subagent” and “Beta” with “Experimental”.
562
-
563
- 0.1.5 also fixed the counting unit in the plugin inventory: it was «187 плагины»,
564
- now it is «187 плагинов». The built-in interface screenshots were translated too, npm
565
- keywords were added, and the peer range of `@deepseek-ai/dsh-client-locale`
566
- (`>=0.1.0-rc.2 <0.2.0`) was fixed — previously `^0.1.0` did not resolve, because the package
567
- is published only under prerelease versions.
568
-
569
- In 0.1.4 the package caught up with harness **0.1.7-alpha.2** — the biggest jump:
570
- **905 keys** added, 29 changed, 126 removed. New sections:
571
-
572
- - **`pluginManager`** — the built-in plugin panel: installation, removal,
573
- enabling and disabling components, versions, registries and install hints;
574
- - **`settings.account`** — the DeepSeek account: sign-in, balance, spend,
575
- API key;
576
- - the separate settings sections the former `settings.plugins` split into:
577
- **`settings.agentLoop`**, **`settings.shell`**, **`settings.subagent`**,
578
- **`settings.webSearch`**;
579
- - **`sidebarBrowser`** — the built-in browser in the right panel (address, sandbox,
580
- restoring the page);
581
- - **`sidebarExcel`** and **`sidebarOffice`** — preview of Office spreadsheets and
582
- documents; zooming (`zoom.*`) was added to PDFs and images;
583
- - **`voice-input`** — experimental voice input.
584
-
585
- Also translated were the new texts in `conversation` (+194), `chat` (+53),
586
- `workspace` (+43: session archive, pinning), `deliverables` (+31: the review of
587
- work changes), `job` (+24: live output of background tasks), `settings` (+22:
588
- desktop app update), `plan`, `open-in-app`, `trajectory`,
589
- `model`, `reference`, `session-log-download`, and in the agent-mode
590
- guides (`settings.agentPreset`, 12 long texts). The section
591
- `settings.archivedSessions` was removed: in 0.1.7 upstream dropped that package, and the session
592
- archive moved to `workspace`.
593
-
594
205
  ## License
595
206
 
596
207
  MIT