@ragnoryok1/dsh-client-locale-ru 0.1.7 → 0.1.8
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 +22 -1
- package/README.md +66 -3
- package/lib/client.js +94 -4
- package/lib/client.js.map +1 -1
- package/lib/typography.js +89 -0
- package/package.json +9 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,27 @@
|
|
|
6
6
|
Пакет следует за харнессом: целевая версия — `dsh 0.1.7-rc.2` и новее
|
|
7
7
|
(`>=0.1.0-rc.2 <0.2.0` для `@deepseek-ai/dsh-client-locale`).
|
|
8
8
|
|
|
9
|
+
## [0.1.8] — 2026-09-27
|
|
10
|
+
|
|
11
|
+
### Добавлено
|
|
12
|
+
|
|
13
|
+
- **Русская экранная типографика** (`src/client/typography.ts`): «ёлочки» вместо прямых
|
|
14
|
+
кавычек, длинное тире между словами, многоточие, неразрывный пробел между числом и
|
|
15
|
+
следующим словом или единицей измерения. Правила осторожные: код, поля ввода,
|
|
16
|
+
редактируемый текст и `[data-typography="off"]` не трогаются, повторный проход ничего не
|
|
17
|
+
меняет. Работает как `ctx.effect`, поэтому выгружается вместе с плагином.
|
|
18
|
+
- **Проверки в CI** (`.github/workflows/verify.yml`): сборка, 12 правил типографики
|
|
19
|
+
(`scripts/check-typography.mjs`) и аудит локали (`scripts/audit.mjs`) против
|
|
20
|
+
**закреплённого тега** харнеса. Новые npm-скрипты: `check:typography`, `audit`, `verify`.
|
|
21
|
+
- Раздел README **«Область покрытия»** с измерением: из 17 самых скачиваемых плагинов
|
|
22
|
+
экосистемы 11 не поставляют собственных строк, поэтому их русский интерфейс обеспечивают
|
|
23
|
+
словари харнеса; словари сторонних плагинов в область пакета не входят.
|
|
24
|
+
|
|
25
|
+
### Исправлено
|
|
26
|
+
|
|
27
|
+
- **Число переводов в документации**: 2598 → **2304**. Прежняя цифра считала повторяющиеся
|
|
28
|
+
имена ключей в разных namespace дважды.
|
|
29
|
+
|
|
9
30
|
## [0.1.7] — 2026-09-27
|
|
10
31
|
|
|
11
32
|
### Исправлено
|
|
@@ -48,7 +69,7 @@
|
|
|
48
69
|
|
|
49
70
|
### Состояние
|
|
50
71
|
|
|
51
|
-
52 namespace,
|
|
72
|
+
52 namespace, 2304 перевода, ~2700 строк. Проверки: 0 отсутствующих ключей против
|
|
52
73
|
`dsh 0.1.7-rc.2`, 0 мёртвых namespace, 45/45 пар ключей с формами числа.
|
|
53
74
|
|
|
54
75
|
## [0.1.6] — 2026-09-25
|
package/README.md
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@ragnoryok1/dsh-client-locale-ru)
|
|
4
4
|
[](LICENSE)
|
|
5
|
+
[](https://github.com/Ragnoryok1/dsh-client-locale-ru/actions/workflows/verify.yml)
|
|
5
6
|
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
6
7
|
[](https://github.com/Ragnoryok1/dsh-client-locale-ru)
|
|
7
|
-
[](https://github.com/Ragnoryok1/dsh-client-locale-ru)
|
|
8
9
|
|
|
9
|
-
**In English.** 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 — 52 namespaces, 2,
|
|
10
|
+
**In English.** 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 — 52 namespaces, 2,304 strings, built against dsh `0.1.7-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 only — third-party plugin dictionaries are deliberately out of scope, and 11 of the 17 most-downloaded ecosystem plugins ship no strings of their own, so they are already Russian through these namespaces. MIT, community-maintained, not affiliated with DeepSeek. Details below are in Russian.
|
|
10
11
|
|
|
11
12
|
---
|
|
12
13
|
|
|
@@ -54,6 +55,68 @@ dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru
|
|
|
54
55
|
«ещё 3 стр.»). Ни одна видимая строка не показывает неверную форму числа.
|
|
55
56
|
- **Фолбэк** — отсутствующий русский ключ падает на `en` (настроенный `fallback`).
|
|
56
57
|
|
|
58
|
+
## Типографика
|
|
59
|
+
|
|
60
|
+
Пакет приводит уже отрисованный русский текст к типографским правилам: «ёлочки» вместо
|
|
61
|
+
прямых кавычек, длинное тире вместо дефиса между словами, многоточие вместо трёх точек и
|
|
62
|
+
неразрывный пробел между числом и следующим словом или единицей измерения (чтобы «187
|
|
63
|
+
плагинов» не разрывалось на две строки). Правила намеренно осторожны: срабатывают только
|
|
64
|
+
на однозначных шаблонах, а код, поля ввода, редактируемый текст и элементы с
|
|
65
|
+
`data-typography="off"` не трогаются вообще. Повторный проход ничего не меняет.
|
|
66
|
+
|
|
67
|
+
Правила проверяются скриптом, а не на глаз: `npm run check:typography`.
|
|
68
|
+
|
|
69
|
+
## Проверки
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
npm run build # сборка трёх выходов
|
|
73
|
+
npm run check:typography # 12 правил типографики, включая «не трогать»
|
|
74
|
+
npm run audit -- <дерево харнеса> # аудит локали
|
|
75
|
+
npm run verify -- <дерево харнеса> # всё вместе
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Аудит выполняется в CI (`.github/workflows/verify.yml`) против **закреплённого тега**
|
|
79
|
+
харнеса, а не против случайного локального чекаута. Гейтом проверяются две вещи, и обе
|
|
80
|
+
уже ловили настоящие дефекты:
|
|
81
|
+
|
|
82
|
+
- **мёртвые namespace** — каждый namespace, который регистрирует пакет, обязан
|
|
83
|
+
существовать в харнессе. В 0.1.6 переводы каталога расписания были зарегистрированы
|
|
84
|
+
под именем `schedule.manager`, тогда как читает их `schedule.catalog`: проверка покрытия
|
|
85
|
+
по ключам при этом рапортовала «всё переведено»;
|
|
86
|
+
- **пары форм числа** — множества `.one`/`.other` должны совпадать, потому что клиент
|
|
87
|
+
выбирает форму как `count === 1 ? '.one' : '.other'`.
|
|
88
|
+
|
|
89
|
+
Число покрытия выводится, но не является гейтом: точный список «добавить / удалить»
|
|
90
|
+
требует сравнения ключей **по namespace** (это делает релизная оснастка), а сравнение имён
|
|
91
|
+
ключей глобально отметило бы разделы, которые пакет сознательно оставляет на английском.
|
|
92
|
+
|
|
93
|
+
## Область покрытия: только официальный интерфейс
|
|
94
|
+
|
|
95
|
+
Пакет переводит **собственные namespace клиента харнеса** — 52 namespace, сверенные с
|
|
96
|
+
закреплённым тегом. Словари сторонних плагинов в область сознательно не входят, и вот
|
|
97
|
+
почему.
|
|
98
|
+
|
|
99
|
+
Мы проверили 17 самых скачиваемых плагинов экосистемы (npm, `keywords:dsh-plugin`,
|
|
100
|
+
скачивания за месяц) и посмотрели, какие из них вообще поставляют собственные строки:
|
|
101
|
+
|
|
102
|
+
| Что обнаружено | Плагинов |
|
|
103
|
+
|---|---|
|
|
104
|
+
| **Своих строк нет** — интерфейс берёт текст из namespace харнеса | **11 из 17** |
|
|
105
|
+
| Крупный собственный словарь (подтверждено вручную) | 2 — `dshmarket` (~1262 ключа), `@nanmicoder/dsh-agent-teams` (420) |
|
|
106
|
+
| Только типы `.d.ts`, строк нет | 1 — `dsh-plugin-model-proxy` |
|
|
107
|
+
| Требует отдельной проверки | 3 |
|
|
108
|
+
|
|
109
|
+
Практическое следствие: у **11 из 17** популярнейших плагинов русский интерфейс уже
|
|
110
|
+
работает — их текст лежит в общих namespace харнеса, которые пакет переводит полностью.
|
|
111
|
+
Отдельный словарь нужен только там, где плагин везёт свои строки, а такой словарь — это
|
|
112
|
+
обязательство следить за чужими релизами: у `dshmarket` уже версия 1.66.3, и её ключи
|
|
113
|
+
меняются независимо от харнеса.
|
|
114
|
+
|
|
115
|
+
Поэтому здесь один пакет и одно обязательство: **официальный интерфейс, переведённый и
|
|
116
|
+
проверенный**, без гонки за чужими словарями. Нужен конкретный плагин — заведите issue:
|
|
117
|
+
словарь для него возможен как отдельный слой, привязанный к версии, а не как молчаливое
|
|
118
|
+
обещание «покрыто всё».
|
|
119
|
+
|
|
57
120
|
## Разработка
|
|
58
121
|
|
|
59
122
|
```sh
|
|
@@ -66,7 +129,7 @@ npm pack # -> ragnoryok1-dsh-client-locale-ru-<version>.tgz
|
|
|
66
129
|
|
|
67
130
|
## Содержимое
|
|
68
131
|
|
|
69
|
-
- `src/client/dicts.ts` — русские словари (52 namespace,
|
|
132
|
+
- `src/client/dicts.ts` — русские словари (52 namespace, 2304 перевода, ~2700 строк). Это и есть основная ценность.
|
|
70
133
|
- `src/client/index.ts` — точка входа клиентского плагина (регистрация `ru` + словарей).
|
|
71
134
|
- `src/index.ts` — пустая host-половина (`apply()`).
|
|
72
135
|
- `cordis.patch.yml` — профиль-патч (`- insert:` клиентской строки `locale-ru`).
|
package/lib/client.js
CHANGED
|
@@ -2703,14 +2703,103 @@ window.__ModuleLoader__.load({
|
|
|
2703
2703
|
}
|
|
2704
2704
|
};
|
|
2705
2705
|
//#endregion
|
|
2706
|
+
//#region src/client/typography.ts
|
|
2707
|
+
/**
|
|
2708
|
+
* Russian screen typography for translated strings.
|
|
2709
|
+
*
|
|
2710
|
+
* The harness locale contract is a flat `Record<string, string>`, so a
|
|
2711
|
+
* language pack cannot reach the composed sentences a component builds from
|
|
2712
|
+
* several keys, and cannot ask the host to insert typography. The rendered DOM
|
|
2713
|
+
* is the one place where every translated string passes through, so this module
|
|
2714
|
+
* rewrites text nodes in place. The rules are deliberately conservative: they
|
|
2715
|
+
* only fire on unambiguous straight-quote and spaced-hyphen patterns, and the
|
|
2716
|
+
* DOM pass skips code, form fields and editable content.
|
|
2717
|
+
*/
|
|
2718
|
+
/** Straight double quotes around a span become Russian guillemets. */
|
|
2719
|
+
const QUOTED = /"([^"\n]{1,200})"/g;
|
|
2720
|
+
/** A spaced hyphen between two words is an em dash in Russian typography. */
|
|
2721
|
+
const SPACED_HYPHEN = /(?<=[\p{L}\p{N}»\)]) - (?=[\p{L}\p{N}«\(])/gu;
|
|
2722
|
+
/** Three dots in running text are an ellipsis. */
|
|
2723
|
+
const ELLIPSIS = /\.{3}(?=\s|$)/g;
|
|
2724
|
+
/** A digit followed by a lowercase letter keeps a non-breaking space. */
|
|
2725
|
+
const NUMBER_UNIT = /(\d) ([а-яё]{1,24})(?![а-яё])/giu;
|
|
2726
|
+
/** Non-breaking space keeps a number attached to a short unit-like token. */
|
|
2727
|
+
const NUMBER_SYMBOL = /(\d) ([%°₽$€£]|кб|КБ|МБ|ГБ)(?=\s|$)/giu;
|
|
2728
|
+
/** Guillemets, em dash, ellipsis, and non-breaking spaces in running text. */
|
|
2729
|
+
function applyRussianTypography(text) {
|
|
2730
|
+
return text.replace(QUOTED, "«$1»").replace(SPACED_HYPHEN, " — ").replace(ELLIPSIS, "…").replace(NUMBER_UNIT, "$1\xA0$2").replace(NUMBER_SYMBOL, "$1\xA0$2");
|
|
2731
|
+
}
|
|
2732
|
+
/** Elements whose text is code, data or user input and must stay verbatim. */
|
|
2733
|
+
const SKIP_SELECTOR = [
|
|
2734
|
+
"code",
|
|
2735
|
+
"pre",
|
|
2736
|
+
"kbd",
|
|
2737
|
+
"samp",
|
|
2738
|
+
"var",
|
|
2739
|
+
"textarea",
|
|
2740
|
+
"input",
|
|
2741
|
+
"select",
|
|
2742
|
+
"option",
|
|
2743
|
+
"[contenteditable]:not([contenteditable=\"false\"])",
|
|
2744
|
+
"[data-typography=\"off\"]"
|
|
2745
|
+
].join(",");
|
|
2746
|
+
/** Text nodes already rewritten, so a later pass leaves them alone. */
|
|
2747
|
+
const processed = /* @__PURE__ */ new WeakSet();
|
|
2748
|
+
/** Whether the node sits inside content that must not be rewritten. */
|
|
2749
|
+
function isSkipped(node) {
|
|
2750
|
+
const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
|
|
2751
|
+
return element === null || element.closest(SKIP_SELECTOR) !== null;
|
|
2752
|
+
}
|
|
2753
|
+
function rewriteNode(node) {
|
|
2754
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
2755
|
+
if (processed.has(node)) return;
|
|
2756
|
+
processed.add(node);
|
|
2757
|
+
const before = node.data;
|
|
2758
|
+
const after = applyRussianTypography(before);
|
|
2759
|
+
if (after !== before) node.data = after;
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
if (node.nodeType !== Node.ELEMENT_NODE || isSkipped(node)) return;
|
|
2763
|
+
for (const child of node.childNodes) rewriteNode(child);
|
|
2764
|
+
}
|
|
2765
|
+
/**
|
|
2766
|
+
* Start the typography pass on a document and keep it current as the app
|
|
2767
|
+
* renders. Returns a disposer that stops observing.
|
|
2768
|
+
*
|
|
2769
|
+
* @param doc - document whose body carries the rendered interface.
|
|
2770
|
+
* @returns disposer removing the observer.
|
|
2771
|
+
*/
|
|
2772
|
+
function startRussianTypography(doc) {
|
|
2773
|
+
const root = doc.body;
|
|
2774
|
+
if (root === null) return () => {};
|
|
2775
|
+
rewriteNode(root);
|
|
2776
|
+
const observer = new MutationObserver((records) => {
|
|
2777
|
+
for (const record of records) {
|
|
2778
|
+
if (record.type === "characterData") {
|
|
2779
|
+
if (isSkipped(record.target)) continue;
|
|
2780
|
+
rewriteNode(record.target);
|
|
2781
|
+
continue;
|
|
2782
|
+
}
|
|
2783
|
+
for (const added of record.addedNodes) rewriteNode(added);
|
|
2784
|
+
}
|
|
2785
|
+
});
|
|
2786
|
+
observer.observe(root, {
|
|
2787
|
+
childList: true,
|
|
2788
|
+
characterData: true,
|
|
2789
|
+
subtree: true
|
|
2790
|
+
});
|
|
2791
|
+
return () => observer.disconnect();
|
|
2792
|
+
}
|
|
2793
|
+
//#endregion
|
|
2706
2794
|
//#region src/client/index.ts
|
|
2707
2795
|
/** Required service: the locale registry that owns languages and dictionaries. */
|
|
2708
2796
|
const inject = ["locale"];
|
|
2709
2797
|
/**
|
|
2710
|
-
* Client plugin body: add `ru` to the selectable language catalog
|
|
2711
|
-
* a ru dictionary for every namespace this pack contributes
|
|
2712
|
-
* an effect, so the contributed
|
|
2713
|
-
*
|
|
2798
|
+
* Client plugin body: add `ru` to the selectable language catalog, register
|
|
2799
|
+
* a ru dictionary for every namespace this pack contributes, and start the
|
|
2800
|
+
* Russian typography pass. Registration is an effect, so the contributed
|
|
2801
|
+
* language, dictionaries and the typography observer are torn down with this
|
|
2802
|
+
* plugin's fiber.
|
|
2714
2803
|
* @param ctx - client root context.
|
|
2715
2804
|
*/
|
|
2716
2805
|
function apply(ctx) {
|
|
@@ -2720,6 +2809,7 @@ window.__ModuleLoader__.load({
|
|
|
2720
2809
|
fallback: "en"
|
|
2721
2810
|
}), "locale-ru: language");
|
|
2722
2811
|
for (const [ns, dict] of Object.entries(RU_DICTS)) ctx.effect(() => ctx.locale.register(ns, "ru", dict), `locale-ru: ${ns} dictionary`);
|
|
2812
|
+
ctx.effect(() => startRussianTypography(document), "locale-ru: typography");
|
|
2723
2813
|
}
|
|
2724
2814
|
//#endregion
|
|
2725
2815
|
exports.apply = apply;
|