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

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,25 @@
1
+ ## [0.2.15] — 2026-10-22
2
+
3
+ ### Изменено
4
+
5
+ - **Переписан текст пакета — без единой правки перевода.** Вступление README собиралось из
6
+ обрывков: «Что ещё осталось» повисало без продолжения, следующая фраза начиналась посреди
7
+ чужого предложения («shows what in your installation is still English»), а тринадцать
8
+ словарей семейства `@linxin666` перечислялись одной строкой на девятьсот символов с
9
+ девятью «и». Теперь это пять отдельных абзацев и таблица на тринадцать строк с итогом;
10
+ то же сделано в английском и китайском README, структура всех трёх совпадает.
11
+ - **Убран дубль в русском README.** Просьба поставить звезду повторялась дважды — сначала
12
+ по-английски, потом по-русски, вместе со вторым вступлением про `addLanguage`. На месте
13
+ дубля — три абзаца о том, чего в тексте не было: пакет делает сверх словаря три прохода
14
+ (экранная типографика, русские формы числа, названия плагинов) и каждый выключается в
15
+ строке профиля — `typography`, `plurals`, `pluginNames`.
16
+ - **Верхняя строка русского README** больше не «67724 перевод»: согласование с числом было
17
+ сломано, теперь это нормальное предложение о пакете с областью и установкой.
18
+ - **Дескриптор харнесса** очищен от лишнего пункта про менеджер задач, **описание для npm**
19
+ переписано той же логикой: сначала что это, потом объём, потом чем отличается.
20
+ - **Пересобран `lib/`.** В репозитории лежала сборка без `src/options.ts`, из-за чего
21
+ `check-built` справедливо расходился; сборка детерминирована и теперь совпадает.
22
+
1
23
  # Changelog
2
24
 
3
25
  Все значимые изменения пакета. Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/),
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.14",
4
+ "version": "0.2.15",
5
5
  "tags": ["localization", "russian", "i18n", "web-gui", "language-pack"],
6
- "description": "Full Russian interface (i18n) for the DeepSeek Harness web GUI: 584 namespaces, 67724 translated strings, Russian screen typography, and a locale audit in CI pinned to a harness tag. Includes the automation-task manager and the reminder catalog.",
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.",
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",
@@ -33,36 +33,30 @@ Russian (`ru`) language pack for the [DeepSeek Harness](https://github.com/deeps
33
33
 
34
34
  The complete inventory is in [NAMESPACES.md](NAMESPACES.md): 584 namespaces, 67,724 strings, with the bundled plugin dictionaries marked.
35
35
 
36
- The plugin names and descriptions on the Plugins page cannot come from this pack: the harness
37
- keeps them in `locale/<language>.json` inside each package, builds a translation map from them,
38
- and its locale service resolves that map without consulting dictionaries at all — «maps do not
39
- consult registered dictionaries». 221 harness packages ship `locale/en.json`, 219 of them ship
40
- `locale/zh.json`, and **none ships `locale/ru.json`**, so those 261 strings stay English with any
41
- language pack; only `locale/ru.json` files on the harness side can close them.
42
- `scripts/descriptor-coverage.mjs` counts that gap so it is not mixed into our dictionary coverage; the mechanism and the 32 prepared translations are in `docs/descriptor-upstream.md`.
43
- Plugins outside the harness tree are translated the same way, but their English comes from
44
- their own publication: `plugins/<namespace>.json` records the package, version, licence and the
45
- English strings extracted from its bundle (`npm run fetch:plugins`). `npm run report:plugins`
46
- It also warns about another thing: when the profile holds a second pack that registers
47
- Russian itself, the language belongs to whoever registered it first — the other one silently
48
- skips its namespaces, and the interface ends up showing someone else's translation.
49
- shows what in your installation is still English: it walks the profile's `node_modules`, reads
50
- each plugin's own dictionary and compares its keys with ours. A plugin without a dictionary is
51
- reported as such — there is nothing to translate until it asks for one — and a plugin that
52
- already carries its own Russian is marked separately. That is how dictionaries
53
- that would otherwise not exist at all get covered — today `update` (43 strings) for
54
- `@linxin666/dsh-update`, `git-graph` (63 strings) for `@linxin666/dsh-client-ui-git-graph`,
55
- `dsh-web-ui-usage` (63 strings) for `@linxin666/dsh-usage` and `dsh-web-ui-session-archive`
56
- (124 strings) for `@linxin666/dsh-session-archive`, `pet` (81 strings) for `@linxin666/dsh-pet`
57
- `remote` (115 strings) for `@linxin666/dsh-remote-web-ui`, `dsh-skill-explorer`
58
- (75 strings) for `@linxin666/dsh-client-ui-skill-explorer` and `dsh-web-ui-market`
59
- (106 strings) for `@linxin666/dsh-client-ui-market`, `liangshen` (42 strings) for
60
- `@linxin666/dsh-liangshen` and `dsh-web-ui-preset-center` (46 strings) for
61
- `@linxin666/dsh-client-ui-preset-center`, `dsh-ssh` (154 strings) for `@linxin666/dsh-ssh` and
62
- `task-board` (437 strings) for `@linxin666/dsh-client-ui-task-board` and `task-board-github`
63
- (113 strings) for `@linxin666/dsh-client-ui-task-board-github` — all thirteen extractable
64
- dictionaries of that family, each translated in full. Another pack's Russian is never copied: there would be nothing to verify it
65
- against, while the audit compares our value with the English template of that same plugin.
36
+ 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
+
38
+ One family deserves a word of its own, `@linxin666`: thirteen dictionaries, each translated in full — from a few dozen strings to four hundred.
39
+
40
+ | Dictionary | Package | Strings |
41
+ |---|---|---|
42
+ | `update` | `@linxin666/dsh-update` | 43 |
43
+ | `git-graph` | `@linxin666/dsh-client-ui-git-graph` | 63 |
44
+ | `dsh-web-ui-usage` | `@linxin666/dsh-usage` | 67 |
45
+ | `dsh-web-ui-session-archive` | `@linxin666/dsh-session-archive` | 124 |
46
+ | `pet` | `@linxin666/dsh-pet` | 81 |
47
+ | `remote` | `@linxin666/dsh-remote-web-ui` | 117 |
48
+ | `dsh-skill-explorer` | `@linxin666/dsh-client-ui-skill-explorer` | 114 |
49
+ | `dsh-web-ui-market` | `@linxin666/dsh-client-ui-market` | 106 |
50
+ | `liangshen` | `@linxin666/dsh-liangshen` | 42 |
51
+ | `dsh-web-ui-preset-center` | `@linxin666/dsh-client-ui-preset-center` | 46 |
52
+ | `dsh-ssh` | `@linxin666/dsh-ssh` | 158 |
53
+ | `task-board` | `@linxin666/dsh-client-ui-task-board` | 439 |
54
+ | `task-board-github` | `@linxin666/dsh-client-ui-task-board-github` | 113 |
55
+ | **Total** | 13 dictionaries | **1,513** |
56
+
57
+ 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).
58
+
59
+ 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.
66
60
 
67
61
  ## What it looks like
68
62
 
@@ -105,7 +99,7 @@ two (the message names the version) or drop the `minimumReleaseAge` limit in the
105
99
  settings.
106
100
  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.
107
101
 
108
- 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 2020 kB: the banner ships as JPEG, and the icon and source map stay out of the npm payload.
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.
109
103
 
110
104
  ## Why hybrid
111
105
 
@@ -391,7 +385,7 @@ Searching the npm registry — six queries instead of one «dsh» — found 40 m
391
385
  a dictionary of their own. 39 are translated; three names (`dsh-automation`, `remote`,
392
386
  `settings.ip-pool`) are already taken by other plugins' dictionaries and stay with their
393
387
  owners, so that nobody's product gets somebody else's translation. As of 22 October 2026:
394
- **584 namespaces, 67 724 values, 520 dictionaries, 48 086 distinct Russian strings** in the
388
+ **584 namespaces, 67 724 values, 520 dictionaries, 47 700 distinct Russian strings** in the
395
389
  built package. The competitor measurement, by the same method —
396
390
  [docs/competitors.md](https://github.com/Ragnoryok1/dsh-client-locale-ru/blob/main/docs/competitors.md).
397
391
 
package/README.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.14",
4
+ "version": "0.2.15",
5
5
  "tags": ["localization", "russian", "i18n", "web-gui", "language-pack"],
6
- "description": "Full Russian interface (i18n) for the DeepSeek Harness web GUI: 584 namespaces, 67724 translated strings, Russian screen typography, and a locale audit in CI pinned to a harness tag. Includes the automation-task manager and the reminder catalog.",
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.",
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",
@@ -24,49 +24,39 @@
24
24
  [![namespaces](https://img.shields.io/badge/namespaces-584-6466f1)](https://github.com/Ragnoryok1/dsh-client-locale-ru)
25
25
  [![strings](https://img.shields.io/badge/strings-67724-6466f1)](https://github.com/Ragnoryok1/dsh-client-locale-ru)
26
26
 
27
- Русский язык для веб-интерфейса DeepSeek Harness: 584 namespaces, 67724 перевод, экранная типографика и аудит локализации в CI.
27
+ Русский (`ru`) язык для веб-интерфейса [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`). Пакет регистрирует `ru` как выбираемый язык клиента через сервис локали (`ctx.locale.addLanguage`) и везёт по словарю на каждый namespace — 584 namespace, 67 724 строки, сборка под dsh `0.2.1-rc.2`. Установка: `dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru`, затем **Русский** в Settings → General. Недостающие ключи падают в английский, поэтому более новый харнесс продолжает работать. Область — собственный интерфейс харнесса (64 namespace и 2 996 строк) и словари 520 сторонних плагинов, переведённые с английского, который публикует сам плагин (64 728 строк всего). MIT, поддерживается сообществом, не связан с DeepSeek.
28
28
 
29
29
  > **If the pack was useful to you, please star the repository.** It is the only signal that a
30
30
  > translation is wanted, and it is what decides which parts get worked on next.
31
31
 
32
32
  ---
33
33
 
34
- Полный список того, что переведено, — в [NAMESPACES.md](NAMESPACES.md): 584 namespaces, 67 724 строк, с пометкой у словарей плагинов.
35
-
36
- Плагины за пределами дерева харнесса переводятся так же, но их английский текст берётся
37
- из их собственной публикации: `plugins/<namespace>.json` хранит пакет, версию, лицензию и
38
- английские строки, выгруженные из бандла (`npm run fetch:plugins`). Что ещё осталось
39
- Отдельно он предупреждает и о другом: если в профиле есть ещё один пакет, который сам
40
- регистрирует русский, язык достаётся тому, кто зарегистрировал его первым, — второй молча
41
- пропустит свои namespace, и в интерфейсе окажется перевод чужого пакета.
42
- английским в вашей установке, показывает `npm run report:plugins`: он обходит
43
- `node_modules` профиля, находит в каждом плагине его собственный словарь и сравнивает
44
- ключи с нашим. Плагин без словаря он так и называет — переводить нечего, пока плагин не
45
- спросит перевод, — а плагин, который уже несёт свой русский, отмечает отдельно. Так
46
- переводятся
47
- словари, которых у нас не было бы вовсе, — сейчас это `update` (43 строки) для
48
- `@linxin666/dsh-update`, `git-graph` (63 строки) для `@linxin666/dsh-client-ui-git-graph`,
49
- `dsh-web-ui-usage` (63 строки) для `@linxin666/dsh-usage`, `dsh-web-ui-session-archive`
50
- (124 строки) для `@linxin666/dsh-session-archive`, `pet` (81 строка) для
51
- `@linxin666/dsh-pet`, `remote` (115 строк) для `@linxin666/dsh-remote-web-ui` и
52
- `dsh-skill-explorer` (75 строк) для `@linxin666/dsh-client-ui-skill-explorer` и
53
- `dsh-web-ui-market` (106 строк) для `@linxin666/dsh-client-ui-market`, `liangshen`
54
- (42 строки) для `@linxin666/dsh-liangshen` и `dsh-web-ui-preset-center` (46 строк) для
55
- `@linxin666/dsh-client-ui-preset-center`, `dsh-ssh` (154 строки) для `@linxin666/dsh-ssh` и
56
- `task-board` (437 строк) для `@linxin666/dsh-client-ui-task-board` и `task-board-github`
57
- (113 строк) для `@linxin666/dsh-client-ui-task-board-github` — это все тринадцать
58
- выгружаемых словарей семейства, и каждый переведён целиком.
59
- Названия и описания самих плагинов на странице «Плагины» пакет перевести не может: харнесс
60
- хранит их в `locale/<язык>.json` внутри каждого пакета и собирает из них карту перевода,
61
- а `resolveText` в сервисе локали словари не спрашивает вовсе — «maps do not consult
62
- registered dictionaries». У 221 пакета харнесса есть `locale/en.json`, у 219 из них —
63
- `locale/zh.json`, и **ни у одного нет `locale/ru.json`**; поэтому эти 261 строка
64
- остаются английскими при любом языковом пакете, и закрыть их можно только файлами
65
- `locale/ru.json` на стороне харнесса. Скрипт `scripts/descriptor-coverage.mjs` считает
66
- этот разрыв, чтобы он не смешивался с нашим покрытием словарей; разбор механизма и 32 готовых
67
- перевода — в `docs/descriptor-upstream.md`.
68
- Чужой перевод мы не копируем: сверять его было бы не с чем, а аудит сравнивает наше
69
- значение с английским шаблоном того же плагина.
34
+ Полный список переведённого — в [NAMESPACES.md](NAMESPACES.md): 584 namespace, 67 724 строки, словари плагинов помечены отдельно.
35
+
36
+ Плагины за пределами дерева харнесса переводятся так же, но их английский текст берётся из собственной публикации: `plugins/<namespace>.json` хранит пакет, версию, лицензию и строки, выгруженные из бандла. Что осталось английским в вашей установке, покажет `npm run report:plugins`: он обходит `node_modules` профиля, находит у каждого плагина его собственный словарь и сверяет ключи с нашими, а плагин без словаря так и помечает — переводить нечего, пока он не спросит перевод.
37
+
38
+ Отдельно стоит сказать про семейство `@linxin666`: тринадцать словарей, и каждый переведён целиком — от нескольких десятков строк до четырёх сотен.
39
+
40
+ | Словарь | Пакет | Строк |
41
+ |---|---|---|
42
+ | `update` | `@linxin666/dsh-update` | 43 |
43
+ | `git-graph` | `@linxin666/dsh-client-ui-git-graph` | 63 |
44
+ | `dsh-web-ui-usage` | `@linxin666/dsh-usage` | 67 |
45
+ | `dsh-web-ui-session-archive` | `@linxin666/dsh-session-archive` | 124 |
46
+ | `pet` | `@linxin666/dsh-pet` | 81 |
47
+ | `remote` | `@linxin666/dsh-remote-web-ui` | 117 |
48
+ | `dsh-skill-explorer` | `@linxin666/dsh-client-ui-skill-explorer` | 114 |
49
+ | `dsh-web-ui-market` | `@linxin666/dsh-client-ui-market` | 106 |
50
+ | `liangshen` | `@linxin666/dsh-liangshen` | 42 |
51
+ | `dsh-web-ui-preset-center` | `@linxin666/dsh-client-ui-preset-center` | 46 |
52
+ | `dsh-ssh` | `@linxin666/dsh-ssh` | 158 |
53
+ | `task-board` | `@linxin666/dsh-client-ui-task-board` | 439 |
54
+ | `task-board-github` | `@linxin666/dsh-client-ui-task-board-github` | 113 |
55
+ | **Итого** | 13 словарей | **1 513** |
56
+
57
+ Названия и описания самих плагинов на странице «Плагины» пакет перевести не может: харнесс хранит их в `locale/<язык>.json` внутри каждого пакета и собирает из них карту перевода, а `resolveText` в сервисе локали словари не спрашивает вовсе. У 221 пакета есть `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).
58
+
59
+ Чужой перевод мы не копируем: сверять его было бы не с чем, а аудит сравнивает наше значение с английским шаблоном того же плагина. И одно предупреждение на будущее: если в профиле окажется второй пакет, который сам регистрирует русский, язык достанется тому, кто зарегистрировал его первым, — второй молча пропустит свои namespace, и в интерфейсе будет чужой перевод.
70
60
 
71
61
  ## Как это выглядит
72
62
 
@@ -81,17 +71,11 @@ registered dictionaries». У 221 пакета харнесса есть `locale
81
71
  Оба снимка обрезаны по левой границе панели: в кадр не попадают названия рабочих
82
72
  пространств и сессий.
83
73
 
84
- Русская локаль (`ru`) для веб-интерфейса [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`).
74
+ Пакет — не только словарь. Сверх перевода он делает три вещи: приводит текст к русской экранной типографике («ёлочки», тире с пробелами, одно многоточие вместо трёх точек), выбирает формы числа по `Intl.PluralRules('ru')` вместо нейтральных подписей и подставляет русские названия тем плагинам, чей пакет не везёт своей локали.
85
75
 
86
- Плагин добавляет **русский** как выбираемый язык через контракт языковых пакетов локали: `ctx.locale.addLanguage({ id: 'ru', label: 'Русский', fallback: 'en' })` и по `ru`-словарю на каждый namespace. После выбора **Русский** в Settings → General интерфейс переключается сразу.
76
+ Каждый проход можно выключить, не трогая перевод: в строке профиля `cordis.yml` достаточно `config: { typography: false }`, так же отключаются `plurals` и `pluginNames`. Пропущенный переключатель считается включённым, поэтому существующий профиль править не нужно.
87
77
 
88
- > **Если пакет вам пригодился — поставьте звезду.** Это единственный способ показать, что
89
- > перевод нужен, и он реально влияет на то, какие задачи берут в работу. Ни одна кнопка
90
- > «нравится» в интерфейсе не делает этого.
91
- >
92
- > Репозиторий при этом остаётся местом, где видно состояние перевода: [CHANGELOG](CHANGELOG.md)
93
- > с разбором каждого релиза (включая ошибки, которые находили в нём сами), проверки в CI и
94
- > перечень того, что сознательно не переводится.
78
+ Типографика — единственный проход, который переписывает введённый вами текст. Если она мешает в поле ввода, выключайте именно её: остальное останется русским.
95
79
 
96
80
  ## Установка
97
81
 
@@ -114,7 +98,7 @@ dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru
114
98
  половину; declared `dsh.client` без экспорта `./client` заставляет `client-modules` бросить
115
99
  ошибку; `dsh.bundle.patch` обязан указывать на существующий файл.
116
100
 
117
- Установка из GitHub тоже работает: `dsh plugin --profile web add Ragnoryok1/dsh-client-locale-ru` (в таком виде команду генерирует каталог плагинов, без `@`-скоупа) ставит архив репозитория, а собранный `lib/` лежит в git — сборка на вашей машине не запускается и не требуется. Поэтому `npm run check:built` (входит в `verify`) пересобирает `lib/` и сверяет его с коммитом: устаревший бандл в git иначе получил бы именно пользователь, а аудит этого не увидел бы — он читает исходники. Публикуемый пакет занимает 2020 КБ: баннер отдаётся JPEG, а иконка и карта исходников в npm не входят.
101
+ Установка из GitHub тоже работает: `dsh plugin --profile web add Ragnoryok1/dsh-client-locale-ru` (в таком виде команду генерирует каталог плагинов, без `@`-скоупа) ставит архив репозитория, а собранный `lib/` лежит в git — сборка на вашей машине не запускается и не требуется. Поэтому `npm run check:built` (входит в `verify`) пересобирает `lib/` и сверяет его с коммитом: устаревший бандл в git иначе получил бы именно пользователь, а аудит этого не увидел бы — он читает исходники. Публикуемый пакет занимает 2022 КБ: баннер отдаётся JPEG, а иконка и карта исходников в npm не входят.
118
102
 
119
103
  ## Почему гибрид
120
104
 
@@ -402,7 +386,7 @@ PLAN_NEXT_ACTION_EN`). Пока читался только первый вид,
402
386
  словарём. 39 из них переведены; три имени (`dsh-automation`, `remote`, `settings.ip-pool`)
403
387
  уже заняты словарями других плагинов и оставлены прежним владельцам, чтобы не подменить
404
388
  перевод чужого продукта. Итог на 22 октября 2026: **584 namespace, 67 724 значения,
405
- 520 словарей, 48 086 различных русских строк** в собранном пакете. Замер конкурентов тем же
389
+ 520 словарей, 47 700 различных русских строк** в собранном пакете. Замер конкурентов тем же
406
390
  методом — [docs/competitors.md](https://github.com/Ragnoryok1/dsh-client-locale-ru/blob/main/docs/competitors.md).
407
391
 
408
392
  Раньше словари плагинов в область не входили, и на то была причина: у `dshmarket` уже
package/README.zh.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.14",
4
+ "version": "0.2.15",
5
5
  "tags": ["localization", "russian", "i18n", "web-gui", "language-pack"],
6
- "description": "Full Russian interface (i18n) for the DeepSeek Harness web GUI: 584 namespaces, 67724 translated strings, Russian screen typography, and a locale audit in CI pinned to a harness tag. Includes the automation-task manager and the reminder catalog.",
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.",
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",
@@ -32,33 +32,30 @@
32
32
 
33
33
  完整清单见 [NAMESPACES.md](NAMESPACES.md):584 个 namespace、67,724 条字符串,随包附带的插件词典单独标注。
34
34
 
35
- 如果 pnpm 报 `ERR_PNPM_NO_MATURE_MATCHING_VERSION`,这不是包坏了,而是成熟度策略:配置文件
36
- 不安装发布不足 48 小时的版本。等一两天(报错信息里写着版本号),或在配置文件设置里去掉
37
- `minimumReleaseAge` 限制。
38
- 「插件」页面上的插件名称与说明无法由本包提供:harness 把她们放在每个包的 `locale/<语言>.json`
39
- 里并据此拼出翻译映射,而 locale 服务的 `resolveText` 根本不查词典(原文注释:maps do not
40
- consult registered dictionaries)。221 个 harness 包带有 `locale/en.json`,其中 219 个带有
41
- `locale/zh.json`,**没有一个带 `locale/ru.json`**,所以这 261 行在任何语言包下都保持英文,只能由
42
- harness 侧的 `locale/ru.json` 文件补上。`scripts/descriptor-coverage.mjs` 单独统计这个缺口,
43
- 不与我们的词典覆盖率混在一起;机制说明与 32 份已备好的译文见 `docs/descriptor-upstream.md`。
44
- harness 树之外的插件用同样的方式翻译,但英文取自它们自己的发布物:`plugins/<namespace>.json`
45
- 记录包名、版本、许可证,以及从其 bundle 中提取的英文字符串(`npm run fetch:plugins`)。
46
- 它还会提醒另一件事:如果配置文件里还有另一个自己注册俄语的包,语言就归先注册的那个——
47
- 第二个会静默跳过自己的 namespace,界面上出现的就是别人的译文。
48
- 安装里还剩哪些英文,用 `npm run report:plugins` 查看:它遍历 profile 的 `node_modules`,
49
- 读出每个插件自己的词典并与我们的对照。没有词典的插件会照实标注——在它请求翻译之前无从翻译;
50
- 自带俄语的插件单独标出。
51
- 这样也能覆盖本来完全不存在的词典——目前是 `@linxin666/dsh-update` 的 `update`(43 条)、`@linxin666/dsh-client-ui-git-graph`
52
- 的 `git-graph`(63 条)、`@linxin666/dsh-usage` 的 `dsh-web-ui-usage`(63 条)与
53
- `@linxin666/dsh-session-archive` 的 `dsh-web-ui-session-archive`(124 条)、`@linxin666/dsh-pet` 的 `pet`(81 条)与
54
- `@linxin666/dsh-remote-web-ui` 的 `remote`(115 条)与 `@linxin666/dsh-client-ui-skill-explorer`
55
- 的 `dsh-skill-explorer`(75 条)与 `@linxin666/dsh-client-ui-market` 的 `dsh-web-ui-market`
56
- (106 条)、`@linxin666/dsh-liangshen` 的 `liangshen`(42 条)与
57
- `@linxin666/dsh-client-ui-preset-center` 的 `dsh-web-ui-preset-center`(46 条)、
58
- `@linxin666/dsh-ssh` 的 `dsh-ssh`(154 条)与 `@linxin666/dsh-client-ui-task-board` 的
59
- `task-board`(437 条)与 `@linxin666/dsh-client-ui-task-board-github` 的 `task-board-github`
60
- (113 条)——该家族可提取的全部十三个词典,每一个都已完整翻译。我们不复制别的包的俄语译文:那样无从核对,
61
- 而审计是把我们的取值与同一个插件的英文模板作比较。
35
+ harness 树之外的插件用同样的方式翻译,但英文取自它们自己的发布物:`plugins/<namespace>.json` 记录包名、版本、许可证,以及从其 bundle 中提取的字符串。安装里还剩哪些英文,用 `npm run report:plugins` 查看:它遍历 profile 的 `node_modules`,找出每个插件自己的词典并与我们的对照;没有词典的插件会照实标注——在它请求翻译之前无从翻译。
36
+
37
+ `@linxin666` 这一族值得单独说明:十三个词典,每一个都已完整翻译——从几十条到四百条不等。
38
+
39
+ | 词典 | 包 | 条数 |
40
+ |---|---|---|
41
+ | `update` | `@linxin666/dsh-update` | 43 |
42
+ | `git-graph` | `@linxin666/dsh-client-ui-git-graph` | 63 |
43
+ | `dsh-web-ui-usage` | `@linxin666/dsh-usage` | 67 |
44
+ | `dsh-web-ui-session-archive` | `@linxin666/dsh-session-archive` | 124 |
45
+ | `pet` | `@linxin666/dsh-pet` | 81 |
46
+ | `remote` | `@linxin666/dsh-remote-web-ui` | 117 |
47
+ | `dsh-skill-explorer` | `@linxin666/dsh-client-ui-skill-explorer` | 114 |
48
+ | `dsh-web-ui-market` | `@linxin666/dsh-client-ui-market` | 106 |
49
+ | `liangshen` | `@linxin666/dsh-liangshen` | 42 |
50
+ | `dsh-web-ui-preset-center` | `@linxin666/dsh-client-ui-preset-center` | 46 |
51
+ | `dsh-ssh` | `@linxin666/dsh-ssh` | 158 |
52
+ | `task-board` | `@linxin666/dsh-client-ui-task-board` | 439 |
53
+ | `task-board-github` | `@linxin666/dsh-client-ui-task-board-github` | 113 |
54
+ | **合计** | 13 个词典 | **1,513** |
55
+
56
+ 「插件」页面上的插件名称与说明无法由本包提供: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)。
57
+
58
+ 我们不复制别的包的俄语译文:那样无从核对,而审计是把我们的取值与同一个插件的英文模板作比较。另外提醒一点:如果配置文件里还有另一个自己注册俄语的包,语言就归先注册的那个——第二个会静默跳过自己的 namespace,界面上出现的就是别人的译文。
62
59
 
63
60
  ## 效果展示
64
61
 
@@ -90,7 +87,7 @@ dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru
90
87
 
91
88
  清单按 harness 自身的规则检查,而不是靠肉眼(`npm run check:manifest`,包含在 `verify` 中)。每一条规则都取自 harness 源码,每一条都对应一种否则会先落到用户头上的失败:`parseDshClient` 接受任意字符串作为 `platform`,但消费方只保留 `platform === 'web'` 的行,因此写成 `'Web'` 这样的笔误会**悄悄关掉**客户端那一半;声明了 `dsh.client` 却没有 `./client` 导出会让 `client-modules` 抛错;`dsh.bundle.patch` 必须指向确实存在的文件。
92
89
 
93
- 从 GitHub 安装同样可用:`dsh plugin --profile web add Ragnoryok1/dsh-client-locale-ru`(插件目录生成的就是这种不带 npm scope 的写法)安装的是仓库归档,而构建好的 `lib/` 已提交进 git——你的机器上不会运行构建,也不需要运行。正因如此,`npm run check:built`(包含在 `verify` 中)会重新构建 `lib/` 并与提交内容比对:git 里过期的构建产物会直接落到用户手里,而审计看不到它——审计读的是源码。发布的包为 2020 KB:横幅以 JPEG 提供,图标与源码映射不进入 npm 包。
90
+ 从 GitHub 安装同样可用:`dsh plugin --profile web add Ragnoryok1/dsh-client-locale-ru`(插件目录生成的就是这种不带 npm scope 的写法)安装的是仓库归档,而构建好的 `lib/` 已提交进 git——你的机器上不会运行构建,也不需要运行。正因如此,`npm run check:built`(包含在 `verify` 中)会重新构建 `lib/` 并与提交内容比对:git 里过期的构建产物会直接落到用户手里,而审计看不到它——审计读的是源码。发布的包为 2022 KB:横幅以 JPEG 提供,图标与源码映射不进入 npm 包。
94
91
 
95
92
  ## 为什么是混合插件
96
93
 
@@ -158,7 +155,7 @@ npm run diff:harness -- <old> <new> # harness 改写了哪些英文
158
155
 
159
156
  审计在 CI(`.github/workflows/verify.yml`)中针对**锁定的 harness 标签**运行,而不是针对随便一份本地 checkout。同一次运行还会**每周按计划**触发一次:即使没人改动,译文也会腐坏——上游继续前进,本地一切正常,而偏差只在用户构建时才浮现。门禁覆盖完整性(key 与 namespace)、复数形式、占位符、英文与中文残留、术语、正文中的拉丁词、文本卫生、首尾空格、风格、取值的排版、«ё»、组合短语、设置侧栏标签的长度以及对外宣称的数字——每一项都已经抓到过真实缺陷。除此之外,审计还会打印**参考报告**——它们不是门禁,因为需要人来判断:比对了多少个 namespace、哪些无法建立对应关系、是否还留有「数字 + 需变格的名词」,以及短字符串是否长到英文的 2.8 倍以上(排版风险——«Effort» → «Уровень рассуждения» 是俄语的正常长度,不是缺陷)。
160
157
 
161
- 审计按 harness 实际书写的形式读取模板:字符串、**段落数组**(各模式的指南就是这样存储的)以及**常量引用**(`'hint.plan': PLAN_NEXT_ACTION_EN`)。在只读取第一种形式时,有十个 key——其中八个是整篇指南——悄悄退出了所有比对。反过来也一样:空的策略文件(`keep-english.json`、`terminology.json` 的某个分区)现在会让审计失败,而不是在无人察觉的情况下放宽门禁。README 中宣称的数字也按同样方式与包体核对,因为过期的数字就是错误的说法——同样的数字还会从 `package.json` 的 description 中读取,那是 npm 在搜索与包页面上显示的内容(「55 namespaces」正是在那里活得最久)——包括带千位分隔符的写法:旧版开头写的是 `2 489`,而检查此前只读取不带分隔符的形式,所以数字模式现在也接受千位分隔符。紧随其后还核对「针对哪个 harness 版本构建」这句话:它必须与 workflow 中锁定的标签一致,因为真正验证这个包的是那个 workflow(两个 README 的开头都还写着 `0.2.1-alpha.2`,而 CI 早已换成 `dsh-v0.2.1-rc.2`)。发布包的体积也在这里实测:`npm run check:size` 会打包归档,并把声明的千字节数与实际值对比(允许 2020 KB 误差——不同 Node 构建下的 gzip 压缩结果略有差别,而这句话本身也只精确到整千字节)。
158
+ 审计按 harness 实际书写的形式读取模板:字符串、**段落数组**(各模式的指南就是这样存储的)以及**常量引用**(`'hint.plan': PLAN_NEXT_ACTION_EN`)。在只读取第一种形式时,有十个 key——其中八个是整篇指南——悄悄退出了所有比对。反过来也一样:空的策略文件(`keep-english.json`、`terminology.json` 的某个分区)现在会让审计失败,而不是在无人察觉的情况下放宽门禁。README 中宣称的数字也按同样方式与包体核对,因为过期的数字就是错误的说法——同样的数字还会从 `package.json` 的 description 中读取,那是 npm 在搜索与包页面上显示的内容(「55 namespaces」正是在那里活得最久)——包括带千位分隔符的写法:旧版开头写的是 `2 489`,而检查此前只读取不带分隔符的形式,所以数字模式现在也接受千位分隔符。紧随其后还核对「针对哪个 harness 版本构建」这句话:它必须与 workflow 中锁定的标签一致,因为真正验证这个包的是那个 workflow(两个 README 的开头都还写着 `0.2.1-alpha.2`,而 CI 早已换成 `dsh-v0.2.1-rc.2`)。发布包的体积也在这里实测:`npm run check:size` 会打包归档,并把声明的千字节数与实际值对比(允许 2022 KB 误差——不同 Node 构建下的 gzip 压缩结果略有差别,而这句话本身也只精确到整千字节)。
162
159
 
163
160
  - **无效 namespace**——包注册的每一个 namespace 都必须在 harness 中存在。在 0.1.6 中,日程目录的翻译被注册在 `schedule.manager` 名下,而读取它们的是 `schedule.catalog`:此时按 key 做的覆盖率检查还报告「全部已翻译」;
164
161
  - **复数形式配对**——`.one`/`.other` 的集合必须一致,因为客户端按 `count === 1 ? '.one' : '.other'` 选择形式。这对俄语还不够:每一对还需要 `.few` 与 `.many`,否则包装层会悄悄退回中性的 `.other`,本该是 «2 дня» 的地方显示成 «2 д»。该检查**按 namespace** 进行——包装层在提问的那个 namespace 里查找形式,因此「某个界面上有、另一个界面上缺」这种情况否则不会被发现。它找到了四个时间单位(`schedule.catalog` 中的 `unit.day/hour/minute/second`)和任务管理器里的 `trigger`;同时这些单位不再是缩写(«д»、«ч»、«мин»),而是带正确格位的完整形式——«через 2 дня»、«через 5 часов»,而 «Каждые 2 дня» 现在写作 «Раз в 2 дня»。同样的要求也覆盖本包自己添加的 key(`terminal.expand` 及其同类:harness 会把计数传给它,却从不配对)——这些形式只由我们维护,少一个不会有任何其他检查发现;
@@ -178,7 +175,7 @@ npm run diff:harness -- <old> <new> # harness 改写了哪些英文
178
175
  我们按六个关键词(而不是只用「dsh」一个)检索 npm,又找到 40 个自带词典的插件:其中 39 个
179
176
  已翻译,三个名字(`dsh-automation`、`remote`、`settings.ip-pool`)已被别的插件的词典占用,
180
177
  仍归原来的所有者,以免把别人的翻译安到另一个产品上。截至 2026 年 10 月 22 日:**584 个
181
- namespace、67 724 条、520 个词典、构建产物中 48 086 条不同的俄文**。用同样方法做
178
+ namespace、67 724 条、520 个词典、构建产物中 47 700 条不同的俄文**。用同样方法做
182
179
  的竞争对手测量见 [docs/competitors.md](https://github.com/Ragnoryok1/dsh-client-locale-ru/blob/main/docs/competitors.md)。
183
180
 
184
181
  此前插件词典不在范围内,理由是:`dshmarket` 已经到 1.66.44 版本,它的 key 变化与 harness
package/lib/client.js CHANGED
@@ -69692,6 +69692,69 @@ window.__ModuleLoader__.load({
69692
69692
  return () => observer.disconnect();
69693
69693
  }
69694
69694
  //#endregion
69695
+ //#region src/options.ts
69696
+ /**
69697
+ * The switches that turn the pack's extra passes off.
69698
+ *
69699
+ * The pack is more than a dictionary: it substitutes Russian plural forms, rewrites
69700
+ * plugin titles the harness cannot translate, and applies Russian screen typography.
69701
+ * Each extra can disagree with a reader who wants only the translation — typography
69702
+ * in particular rewrites text the user may have typed into a form or a prompt. A
69703
+ * switch disables one pass without touching the dictionaries, so the interface stays
69704
+ * Russian and only the extra behaviour disappears.
69705
+ *
69706
+ * The switches arrive from the profile's `cordis.yml` row:
69707
+ *
69708
+ * ```yaml
69709
+ * - id: locale-ru
69710
+ * config:
69711
+ * typography: false
69712
+ * ```
69713
+ *
69714
+ * The browser half cannot read that row — the client boot graph carries no config —
69715
+ * so the host half hands the resolved switches to the page through the webserver's
69716
+ * index-injection table (see `src/index.ts`), and the browser half reads the global
69717
+ * this module names. Omitting a switch means «on», so an existing profile needs no
69718
+ * edit, and a surface without the webserver simply keeps the defaults.
69719
+ */
69720
+ /** The switches, by their name in the config. */
69721
+ const SWITCHES = [
69722
+ "typography",
69723
+ "plurals",
69724
+ "pluginNames"
69725
+ ];
69726
+ /** The global the host half writes and the browser half reads. */
69727
+ const BOOT_OPTIONS_KEY = "__DSH_RU_OPTIONS__";
69728
+ /**
69729
+ * Normalize any candidate into the switches the passes read.
69730
+ *
69731
+ * Only booleans count: a switch that arrived as a string (`"false"`, a common YAML
69732
+ * mistake) is ignored rather than treated as a truthy value, because «off» and «on»
69733
+ * must not depend on how generous a cast was.
69734
+ *
69735
+ * @param value - the candidate, typically the row config or the injected global.
69736
+ * @returns the switches that were explicitly set.
69737
+ */
69738
+ function normalizeOptions(value) {
69739
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
69740
+ const source = value;
69741
+ const normalized = {};
69742
+ for (const key of SWITCHES) {
69743
+ const raw = source[key];
69744
+ if (typeof raw === "boolean") normalized[key] = raw;
69745
+ }
69746
+ return normalized;
69747
+ }
69748
+ /**
69749
+ * Read the switches the host half injected into the page.
69750
+ *
69751
+ * @returns the switches, or an empty object when the host half did not run (a surface
69752
+ * without the webserver, or a build of the pack older than the switches).
69753
+ */
69754
+ function readBootOptions() {
69755
+ return normalizeOptions(globalThis[BOOT_OPTIONS_KEY]);
69756
+ }
69757
+ //#endregion
69695
69758
  //#region src/client/index.ts
69696
69759
  /**
69697
69760
  * Skip only occupied slots; validation and unexpected service errors still fail.
@@ -69750,8 +69813,13 @@ window.__ModuleLoader__.load({
69750
69813
  * replacing or disposing those resources. The typography pass runs only while
69751
69814
  * Russian is the active language, and follows a switch through `locale/change`.
69752
69815
  * @param ctx - client root context.
69816
+ * @param config - the switches that turn the extra passes off; omitted means on.
69753
69817
  */
69754
- function apply(ctx) {
69818
+ function apply(ctx, config = {}) {
69819
+ const options = {
69820
+ ...readBootOptions(),
69821
+ ...config
69822
+ };
69755
69823
  let languageTaken = false;
69756
69824
  const lostNamespaces = [];
69757
69825
  const report = (what) => {
@@ -69771,9 +69839,9 @@ window.__ModuleLoader__.load({
69771
69839
  const parts = [languageTaken ? "язык ru уже занят другим пакетом" : null, lostNamespaces.length > 0 ? `наши словари не зарегистрированы в ${lostNamespaces.length} namespace` : null].filter((part) => part !== null);
69772
69840
  console.warn(`[dsh-client-locale-ru] ${parts.join("; ")}. В интерфейсе останется перевод другого пакета — оставьте в профиле один пакет с русским языком.`);
69773
69841
  }
69774
- ctx.effect(() => startRussianPlurals(ctx.locale), "locale-ru: plurals");
69775
- ctx.effect(() => followLanguage(ctx, (isRussian) => startRussianTypography(document, isRussian)), "locale-ru: typography");
69776
- ctx.effect(() => followLanguage(ctx, (isRussian) => startRussianPluginNames(document, isRussian)), "locale-ru: plugin names");
69842
+ if (options.plurals !== false) ctx.effect(() => startRussianPlurals(ctx.locale), "locale-ru: plurals");
69843
+ if (options.typography !== false) ctx.effect(() => followLanguage(ctx, (isRussian) => startRussianTypography(document, isRussian)), "locale-ru: typography");
69844
+ if (options.pluginNames !== false) ctx.effect(() => followLanguage(ctx, (isRussian) => startRussianPluginNames(document, isRussian)), "locale-ru: plugin names");
69777
69845
  }
69778
69846
  //#endregion
69779
69847
  exports.apply = apply;
package/lib/index.d.ts CHANGED
@@ -1,11 +1,50 @@
1
+ import { Context } from "@deepseek-ai/cordis";
2
+ //#region src/options.d.ts
3
+ /** Switches for the passes the pack runs on top of its dictionaries. */
4
+ interface Options {
5
+ /** Russian screen typography: «ёлочки», em dashes, ellipses, non-breaking spaces. */
6
+ typography?: boolean;
7
+ /** Russian plural forms picked by `Intl.PluralRules('ru')` instead of the neutral label. */
8
+ plurals?: boolean;
9
+ /** Russian titles and descriptions for plugins whose package ships no Russian locale. */
10
+ pluginNames?: boolean;
11
+ }
12
+ /** What a Standard Schema validation answers: the normalized options, or the reasons it is not one. */
13
+ type ValidationResult = {
14
+ value: Options;
15
+ } | {
16
+ issues: {
17
+ message: string;
18
+ }[];
19
+ };
20
+ /**
21
+ * Config schema without a runtime dependency.
22
+ *
23
+ * The loader accepts any Standard Schema (`~standard.validate`), and this pack ships no
24
+ * dependencies on purpose — a language pack that pulls a validator into every profile
25
+ * to read three booleans is a bad trade. An unknown switch is an error rather than a
26
+ * silent no-op: a typo in `typografy: false` would otherwise leave the pass running and
27
+ * look like the option does not work.
28
+ */
29
+ declare const Config: {
30
+ '~standard': {
31
+ version: 1;
32
+ vendor: string;
33
+ validate(value: unknown): ValidationResult;
34
+ };
35
+ };
36
+ //#endregion
1
37
  //#region src/index.d.ts
2
38
  /**
3
- * Russian (ru) language pack, node half. Pure localization contribution: the
4
- * empty apply exists so the plugin appears in the host cordis.yml / Loader;
5
- * the browser half ships via exports["./client"], discovered through the
6
- * package.json dsh.client declaration.
39
+ * Host plugin body.
40
+ *
41
+ * `webServer` is optional on purpose: the pack also loads on surfaces that serve no
42
+ * HTML (a desktop shell, a headless boot), and requiring the service there would keep
43
+ * the whole plugin from activating — dictionaries and all.
44
+ *
45
+ * @param ctx - host root context.
46
+ * @param config - the switches from the profile row; omitted means all on.
7
47
  */
8
- /** Host plugin body — no host-side behavior for a language-pack plugin. */
9
- declare function apply(): void;
48
+ declare function apply(ctx: Context, config?: Options): void;
10
49
  //#endregion
11
- export { apply };
50
+ export { Config, type Options, apply };
package/lib/index.js CHANGED
@@ -1,11 +1,105 @@
1
+ //#region src/options.ts
2
+ /**
3
+ * The switches that turn the pack's extra passes off.
4
+ *
5
+ * The pack is more than a dictionary: it substitutes Russian plural forms, rewrites
6
+ * plugin titles the harness cannot translate, and applies Russian screen typography.
7
+ * Each extra can disagree with a reader who wants only the translation — typography
8
+ * in particular rewrites text the user may have typed into a form or a prompt. A
9
+ * switch disables one pass without touching the dictionaries, so the interface stays
10
+ * Russian and only the extra behaviour disappears.
11
+ *
12
+ * The switches arrive from the profile's `cordis.yml` row:
13
+ *
14
+ * ```yaml
15
+ * - id: locale-ru
16
+ * config:
17
+ * typography: false
18
+ * ```
19
+ *
20
+ * The browser half cannot read that row — the client boot graph carries no config —
21
+ * so the host half hands the resolved switches to the page through the webserver's
22
+ * index-injection table (see `src/index.ts`), and the browser half reads the global
23
+ * this module names. Omitting a switch means «on», so an existing profile needs no
24
+ * edit, and a surface without the webserver simply keeps the defaults.
25
+ */
26
+ /** The switches, by their name in the config. */
27
+ const SWITCHES = [
28
+ "typography",
29
+ "plurals",
30
+ "pluginNames"
31
+ ];
32
+ /** The global the host half writes and the browser half reads. */
33
+ const BOOT_OPTIONS_KEY = "__DSH_RU_OPTIONS__";
34
+ /**
35
+ * Config schema without a runtime dependency.
36
+ *
37
+ * The loader accepts any Standard Schema (`~standard.validate`), and this pack ships no
38
+ * dependencies on purpose — a language pack that pulls a validator into every profile
39
+ * to read three booleans is a bad trade. An unknown switch is an error rather than a
40
+ * silent no-op: a typo in `typografy: false` would otherwise leave the pass running and
41
+ * look like the option does not work.
42
+ */
43
+ const Config = { "~standard": {
44
+ version: 1,
45
+ vendor: "dsh-client-locale-ru",
46
+ validate(value) {
47
+ if (value === void 0 || value === null) return { value: {} };
48
+ if (typeof value !== "object" || Array.isArray(value)) return { issues: [{ message: `опции пакета — объект: ${SWITCHES.join(", ")}` }] };
49
+ const source = value;
50
+ const unknown = Object.keys(source).filter((key) => !SWITCHES.includes(key));
51
+ if (unknown.length > 0) return { issues: [{ message: `неизвестные опции: ${unknown.join(", ")}; известны: ${SWITCHES.join(", ")}` }] };
52
+ const normalized = {};
53
+ for (const key of SWITCHES) {
54
+ const raw = source[key];
55
+ if (raw === void 0) continue;
56
+ if (typeof raw !== "boolean") return { issues: [{ message: `${key} — true или false, получено ${typeof raw}` }] };
57
+ normalized[key] = raw;
58
+ }
59
+ return { value: normalized };
60
+ }
61
+ } };
62
+ /**
63
+ * Normalize any candidate into the switches the passes read.
64
+ *
65
+ * Only booleans count: a switch that arrived as a string (`"false"`, a common YAML
66
+ * mistake) is ignored rather than treated as a truthy value, because «off» and «on»
67
+ * must not depend on how generous a cast was.
68
+ *
69
+ * @param value - the candidate, typically the row config or the injected global.
70
+ * @returns the switches that were explicitly set.
71
+ */
72
+ function normalizeOptions(value) {
73
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
74
+ const source = value;
75
+ const normalized = {};
76
+ for (const key of SWITCHES) {
77
+ const raw = source[key];
78
+ if (typeof raw === "boolean") normalized[key] = raw;
79
+ }
80
+ return normalized;
81
+ }
82
+ //#endregion
1
83
  //#region src/index.ts
2
84
  /**
3
- * Russian (ru) language pack, node half. Pure localization contribution: the
4
- * empty apply exists so the plugin appears in the host cordis.yml / Loader;
5
- * the browser half ships via exports["./client"], discovered through the
6
- * package.json dsh.client declaration.
85
+ * Host plugin body.
86
+ *
87
+ * `webServer` is optional on purpose: the pack also loads on surfaces that serve no
88
+ * HTML (a desktop shell, a headless boot), and requiring the service there would keep
89
+ * the whole plugin from activating — dictionaries and all.
90
+ *
91
+ * @param ctx - host root context.
92
+ * @param config - the switches from the profile row; omitted means all on.
7
93
  */
8
- /** Host plugin body — no host-side behavior for a language-pack plugin. */
9
- function apply() {}
94
+ function apply(ctx, config = {}) {
95
+ const options = normalizeOptions(config);
96
+ ctx.on("webserver/index-inject", (table) => {
97
+ table.push({
98
+ kind: "global",
99
+ name: BOOT_OPTIONS_KEY,
100
+ value: options
101
+ });
102
+ });
103
+ }
10
104
  //#endregion
11
- export { apply };
105
+ export { Config, apply };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ragnoryok1/dsh-client-locale-ru",
3
- "description": "dsh language pack — i18n for the DeepSeek Harness Web GUI: Russian (ru) locale, 584 namespaces, 67724 strings, 520 third-party plugin dictionaries, ru plural forms",
4
- "version": "0.2.14",
3
+ "description": "Russian (ru) interface for the DeepSeek Harness web GUI: 584 namespaces, 67724 strings, 520 third-party plugin dictionaries, ru plural forms and screen typography. Hybrid dsh.bundle + dsh.client plugin.",
4
+ "version": "0.2.15",
5
5
  "keywords": [
6
6
  "dsh",
7
7
  "deepseek-harness",