@goodandready/dsh-russian-lang 0.1.23 → 0.1.25

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/README.md CHANGED
@@ -1,108 +1,134 @@
1
- # @goodandready/dsh-russian-lang
2
-
3
- Русская локализация веб-интерфейса DeepSeek Harness.
4
-
5
- ## Что делает
6
-
7
- - Регистрирует русские словари для namespace'ов ядра (`common`,
8
- `conversation`, `settings`, `settings.models`, `workspace`, `subagent`,
9
- `reference` и другие — **30 namespace'ов ядра, 722 ключа, 100% покрытия**
10
- релизной поверхности DSH 0.1.1-rc.2) и для ряда сторонних плагинов.
11
- - Добавляет **«Русский» третьей позицией** в родной список языков
12
- (**Настройки → Общие → Язык**). Выбор сохраняется штатным механизмом
13
- настроек DSH и переживает браузеры.
14
- - Синхронизирует `<html lang="ru-RU">` при активном русском.
15
-
16
- Непереведённые ключи показываются по-английски (штатный fallback ядра), пустых
17
- мест в интерфейсе не появляется.
18
-
19
- ### Помимо словарей
20
-
21
- - **Русские plural-формы.** Обёртка `translate` резолвит `one/few/many` для
22
- счётных ключей (как с суффиксом `.one/.other`, так и голых через
23
- `X.one/X.few/X.many`).
24
- - **Пользовательские переопределения.** Пространство настроек
25
- `russian-lang.overrides` — `{ ключ: формулировка }`, применяется верхним
26
- слоем поверх словарей.
27
- - **Типографика вывода** (`russian-lang.typography`). Идемпотентный
28
- постпроцессор текстовых узлов: кавычки-«ёлочки», тире, неразрывные пробелы
29
- после коротких слов, опциональная ё (флаг `yo`). Код, ссылки и поля ввода не
30
- трогаются.
31
- - **Орфография** браузерный `spellcheck` на текстовых полях при активном
32
- русском; моноширинные поля (код, команды) исключаются.
33
- - **Фикс неправильной раскладки.** Если текст набран латиницей вместо
34
- кириллицы, над инпутом появляется подсказка с превью конвертации; клик
35
- заменяет. Конверсия кириллицы→латиницы предлагается только для
36
- slash-команд. Ручной конверт текущего инпута — `Alt+L`.
37
-
38
- ## Как это работает
39
-
40
- Реестр локалей живёт в браузере: плагин докладывает русские словари в чужие
41
- namespace'ы через `ctx.locale.register(ns, 'ru', dict)`. Namespace'ы, которые
42
- плагины уже локализуют сами на русский, пропускаются (иначе загрузчик падает с
43
- `already has locale ru`).
44
-
45
- Список языков штатного селектора (`Настройки → Общие → Язык`) живёт в snapshot
46
- локаль-runtime; меню строится из него, и `setLocale()` валидирует выбор по
47
- нему же. На версиях ядра, где `ru` ещё не в списке (DSH 0.1.1-rc.2 из
48
- коробки), плагин расширяет snapshot пунктом «Русский» — файлы ядра не
49
- правятся. Если ядро когда-нибудь узнает `ru` само, расширение не происходит
50
- и всё работает штатно. Запись выбора в настройки идёт через собственный
51
- namespace `russian-lang` (хост-схема ядра знает только `zh/en` и не
52
- принимает `ru`).
53
-
54
- Ничего в установке DSH не патчится; удаление плагина полностью возвращает
55
- интерфейс к исходному состоянию.
56
-
57
- ## Установка
58
-
59
- ```bash
60
- dsh plugin --profile web add @goodandready/dsh-russian-lang
1
+ # 📦 @goodandready/dsh-russian-lang
2
+
3
+ <div align="center">
4
+
5
+ <h3>Полная русская локализация, умная типографика и исправление раскладки клавиатуры для DeepSeek Harness</h3>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@goodandready/dsh-russian-lang"><img src="https://img.shields.io/npm/v/@goodandready/dsh-russian-lang.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
9
+ <a href="LICENSE"><img src="https://img.shields.io/github/license/GooDAnDReaDY/dsh-russian-lang.svg?style=for-the-badge&color=10b981&labelColor=064e3b" alt="license"></a>
10
+ <a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
11
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
12
+ </p>
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ ## ⚡ Обзор
19
+
20
+ **`dsh-russian-lang`** — официальный, всеобъемлющий пакет русской локализации для веб-интерфейса [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh).
21
+
22
+ Пакет не просто переводит интерфейс ядра и популярных плагинов, но и добавляет интеллектуальные функции для комфортной русскоязычной работы: **грамматические формы множественного числа (plural)**, автоматическую **расстановку типографики** («ёлочки», длинные тире, неразрывные пробелы) и **исправление ошибочно набранного текста** на неверной раскладке по горячей клавише <kbd>Alt+L</kbd>.
23
+
24
+ ```mermaid
25
+ graph LR
26
+ subgraph DSHCore [Ядро DeepSeek Harness]
27
+ DSH[Web UI Интерфейс] --> Loader[Загрузчик локализации dsh-russian-lang]
28
+ end
29
+
30
+ subgraph TranslationLayer [Слой перевода и адаптации]
31
+ Loader --> CoreDict[30+ Пространств имён ядра DSH / 720+ ключей]
32
+ Loader --> PluginDict[10+ Плагинов экосистемы DSH]
33
+ Loader --> PluralEngine[Движок грамматических Plural-форм]
34
+ end
35
+
36
+ subgraph SmartFeatures [Интеллектуальные модули]
37
+ Loader --> Typo[Модуль типографики: кавычки-«ёлочки» и длинные тире]
38
+ Loader --> LayoutFix[Детектор неверной раскладки + Hotkey Alt+L]
39
+ Loader --> Spell[Активация русской проверки орфографии]
40
+ Loader --> Overrides[Пользовательские переопределения ключей]
41
+ end
42
+
43
+ subgraph Result [Результат]
44
+ CoreDict --> NativeUI[Нативный русский интерфейс]
45
+ PluginDict --> NativeUI
46
+ PluralEngine --> NativeUI
47
+ Typo --> NativeUI
48
+ LayoutFix --> NativeUI
49
+ Spell --> NativeUI
50
+ Overrides --> NativeUI
51
+ end
52
+
53
+ style DSHCore fill:#1e1e2e,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4
54
+ style TranslationLayer fill:#181825,stroke:#cba6f7,stroke-width:2px,color:#cdd6f4
55
+ style SmartFeatures fill:#11111b,stroke:#a6e3a1,stroke-width:2px,color:#cdd6f4
56
+ style Result fill:#181825,stroke:#f38ba8,stroke-width:2px,color:#cdd6f4
61
57
  ```
62
58
 
63
- Затем: **Настройки → Общие → Язык → Русский**.
59
+ ---
64
60
 
65
- ## Разработка и проверка
61
+ ## Полный обзор возможностей
66
62
 
67
- ```bash
68
- python3 extract-dicts.py <node_modules ядра> [node_modules профиля] # обновить *-en.json
69
- python3 check-coverage.py # что не переведено / устарело
70
- python3 build.py # собрать lib/client.js
71
- python3 tools/mt_fallback.py --dry-run # непереведённые ключи
72
- python3 tools/mt_fallback.py --review # очередь ручной выверки MT
73
- python3 tools/merge_freq.py # обновить freq-словарь для фикса раскладки
74
- python3 tools/upstream_check.py # ночной апстрим-детектор новых ключей
75
- python3 tools/top100.py <профиль>/node_modules # отчёт покрытия плагинов
76
- python3 test/test_extract.py # smoke для extract-dicts/check-coverage
77
- python3 test/smoke.py # E2E против запущенного контура
78
- ```
63
+ ### 1. 🌐 100% Покрытие ядра DSH (30+ Пространств имён, 720+ ключей)
64
+ * **Диалоги и переписка (`conversation`)**: лента сообщений, ветки обсуждений, карточки вызова инструментов, статус генерации и рассуждений (thinking traces).
65
+ * **Модели и провайдеры (`settings.models`)**: каталог моделей, параметры инференса, управление токенами и ключами API.
66
+ * **Настройки ядра (`settings`, `common`)**: интерфейс настроек, горячие клавиши, системные уведомления, модальные окна и подтверждения.
67
+ * **Рабочие области (`workspace`, `subagent`)**: дерево файлов, управление контекстом, запуск и координация субагентов.
68
+ * **Системные статусы (`status`, `reference`)**: индикаторы сетевого подключения, загрузка файлов и ошибки.
69
+
70
+ ### 2. 🧩 Встроенный перевод 10+ плагинов экосистемы DSH
71
+ Локализация автоматически распространяется на сторонние и экосистемные плагины:
72
+ * `token-dashboard` аналитика и графики расхода токенов;
73
+ * `subscriptions` (`dsh-subscriptions`) карточки подключения персональных подписок;
74
+ * `dshmarket` и `dshmarket-1.13` — маркетплейс моделей и каталоги провайдеров;
75
+ * `better-sidebar` — улучшенное боковое меню навигации;
76
+ * `task-board` и `kanban` — доски управления задачами и канбан-планировщик;
77
+ * `im-hub` — шлюзы интеграции с мессенджерами;
78
+ * `context-doctor` и `plugins-store` — диагностика контекста и магазин расширений.
79
+
80
+ ### 3. 🔢 Русские грамматические формы числительных (Plural)
81
+ Английский язык различает только `1` и `other`. Русский язык требует 3 грамматические формы числительных (`1 файл`, `2 файла`, `5 файлов` / `1 секунда`, `3 секунды`, `10 секунд`). Движок локализации автоматически подставляет правильные окончания в зависимости от числового значения.
82
+
83
+ ### 4. ✍️ Интеллектуальный модуль типографики (`russian-lang.typography`)
84
+ Автоматически приводит тексты сообщений и описаний к правилам русской экранной типографики:
85
+ * Заменяет прямые "лапки" на правильные кавычки-«ёлочки» (а вложенные — на „лапки“);
86
+ * Заменяет дефисы между словами на длинные тире (—);
87
+ * Добавляет неразрывные пробелы после коротких союзов и предлогов, предотвращая висячие строки;
88
+ * **Безопасность кода**: типографика строго изолирована и никогда не затрагивает блоки исходного кода, формулы LaTeX и моноширинные поля ввода.
79
89
 
80
- MT-переводы непереведённых ключей (`mt_fallback.py --apply`) пишутся в
81
- `mt-registry.json` как обычные строки; ручной перевод в `ru/*.json` всегда
82
- имеет приоритет. Очередь ручной выверки `upstream/review-queue.md`.
90
+ ### 5. ⌨️ Исправление ошибочной раскладки клавиатуры (<kbd>Alt+L</kbd>)
91
+ Если вы забыли переключить раскладку и набрали текст латиницей (например, `ghjtrn d gzkmt` вместо `проект в пальто`), плагин мгновенно:
92
+ * Определяет ошибочный ввод в реальном времени;
93
+ * Показывает ненавязчивую плавающую подсказку над полем ввода;
94
+ * При нажатии горячей клавиши <kbd>Alt+L</kbd> мгновенно конвертирует набранный текст в правильную кириллицу.
83
95
 
84
- `smoke.py` требует `websocket-client` (`pip install websocket-client`) и Chrome
85
- на `/usr/bin/google-chrome`. Адрес переопределяется через `DSH_URL`.
96
+ ### 6. ⚙️ Пользовательские переопределения (`russian-lang.overrides`)
97
+ Вы можете переопределить абсолютно любой системный ключ перевода под терминологию вашей команды в **Настройки → Плагины → Russian Lang → Переопределения** без редактирования исходного кода.
86
98
 
87
- ## Скриншоты
99
+ ### 7. 🔤 Нативная интеграция в ядро
100
+ Плагин нативно регистрирует пункт **«Русский»** в меню **Настройки → Общие → Язык**, переключает атрибут `<html lang="ru-RU">` и активирует встроенный браузерный словарь проверки орфографии (`spellcheck`).
88
101
 
89
- | Что | Файл |
90
- |-----|------|
91
- | Интерфейс на русском | `docs/media/ui-russian.png` |
92
- | Список языков в Настройки → Общие | `docs/media/language-selector.png` |
102
+ ---
93
103
 
94
- Скриншоты обновляются через `python3 test/screenshots.py`.
104
+ ## 📦 Установка
95
105
 
96
- ## Ограничения
106
+ ```bash
107
+ dsh plugin --profile web add @goodandready/dsh-russian-lang
108
+ ```
109
+
110
+ После установки откройте веб-интерфейс, перейдите в **Настройки → Общие → Язык**, выберите **«Русский»** и обновите страницу.
111
+
112
+ ---
113
+
114
+ ## ⚙️ Параметры конфигурации (`settings.yaml`)
115
+
116
+ ```yaml
117
+ dsh-russian-lang:
118
+ typography:
119
+ enabled: true
120
+ quotes: true # Преобразование кавычек в «ёлочки»
121
+ dashes: true # Длинные тире
122
+ nbsp: true # Неразрывные пробелы
123
+ layoutFix:
124
+ enabled: true
125
+ hotkey: "Alt+KeyL" # Горячая клавиша исправления раскладки
126
+ spellcheck: true # Браузерная проверка правописания
127
+ overrides: {} # Пользовательские переопределения ключей
128
+ ```
97
129
 
98
- - Переводится только веб-интерфейс. Описания тулов и системные промпты агента
99
- — не locale, плагин их не трогает.
100
- - Плагины, которые рисуют текст инлайном без locale-слотов, не переводятся.
101
- - Текст, добавленный сторонним плагином без `ctx.locale.register`, остаётся на
102
- языке плагина — для перевода нужно обновить сам плагин.
103
- - Фикс раскладки опирается на встроенный частотный словарь (~9k слов) и может
104
- не распознать редкую терминологию; ложных замен нет — только подсказка.
130
+ ---
105
131
 
106
- ## Лицензия
132
+ ## 📄 Лицензия
107
133
 
108
- MIT
134
+ MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
package/lib/client.js CHANGED
@@ -1217,6 +1217,229 @@ window.__ModuleLoader__.load({
1217
1217
  "settings.unavailable": "Обвязка еще не опубликовала настройки этого плагина. Если она только что перезапустилась, раздел появится сам через несколько секунд.",
1218
1218
  "settings.unsaved": "Несохранённые изменения"
1219
1219
  },
1220
+ "dsh-kanban": {
1221
+ "archive.empty": "В архиве пусто.",
1222
+ "archive.hint": "Карточки, отстоявшие свой срок в «Выполнено». Ничего не удалено: возвращённая карточка встаёт в ту же колонку, откуда ушла.",
1223
+ "archive.restore": "Вернуть на доску",
1224
+ "archive.since": "в архиве {ago}",
1225
+ "batch.hint": "Одна сессия на все. Задачи приходят по очереди: агент берёт следующую, закончив текущую. В «В работе» уезжает только первая — остальные ждут своей очереди там, где лежат.",
1226
+ "batch.start": "Запустить",
1227
+ "batch.title": "Запустить {n} задач в одном чате",
1228
+ "board.allRepos": "Все репозитории",
1229
+ "board.archive": "Архив",
1230
+ "board.back": "В чат",
1231
+ "board.backHint": "Закрыть доску и вернуться в разговор",
1232
+ "board.clearFilters": "Снять отборы",
1233
+ "board.collapse": "Свернуть колонку",
1234
+ "board.empty": "Задач пока нет.",
1235
+ "board.expand": "Развернуть колонку",
1236
+ "board.limit": "предел {n}",
1237
+ "board.loading": "Доска загружается…",
1238
+ "board.newTask": "Задача",
1239
+ "board.noMatch": "Ничего не найдено.",
1240
+ "board.noRepo": "без проекта",
1241
+ "board.pickArchive": "В архив",
1242
+ "board.pickMove": "Перенести…",
1243
+ "board.pickNone": "Снять выбор",
1244
+ "board.pickStart": "Запустить выбранные",
1245
+ "board.pickUnqueue": "Снять с очереди",
1246
+ "board.pickedCount": "выбрано {n}",
1247
+ "board.refresh": "Обновить",
1248
+ "board.repo": "репозиторий",
1249
+ "board.search": "Поиск",
1250
+ "board.shown": "показано {shown} из {total}",
1251
+ "board.someFailed": "Сделано у {done} из {total}, остальные отказали.",
1252
+ "board.sync": "Сверить с Gitea",
1253
+ "board.syncing": "Сверяю…",
1254
+ "board.waitCount": "Ждут ответа: {n}",
1255
+ "board.waitCountHint": "Задачи, где агент задал вопрос или просит разрешения.",
1256
+ "board.waiting": "нужен человек",
1257
+ "boardName.main": "Проектная доска",
1258
+ "boardName.simple": "Простая доска",
1259
+ "card.move": "перенести",
1260
+ "card.pack": "пачка {at} из {total}",
1261
+ "card.packHint": "Запущены вместе, одной сессией; агент берёт их в этом порядке.",
1262
+ "card.pick": "Отметить для группового запуска",
1263
+ "card.queue": "в очередь...",
1264
+ "card.stop": "остановить",
1265
+ "card.toChat": "перейти в чат",
1266
+ "card.unqueue": "снять с очереди",
1267
+ "chip.moveTo": "Перенести в",
1268
+ "chip.note": "Оставить заметку",
1269
+ "chip.noteHint": "Заметка дописывается в тело задачи. Тело уходит агенту первым сообщением, поэтому заметка до запуска попадёт в работу, а после запуска — нет.",
1270
+ "chip.noteSave": "Дописать",
1271
+ "chip.openCard": "Перейти к карточке",
1272
+ "column.backlog": "Бэклог",
1273
+ "column.cleanup": "Уборка",
1274
+ "column.deploy": "Выкатка",
1275
+ "column.done": "Выполнено",
1276
+ "column.in-progress": "В работе",
1277
+ "column.review": "Ревью",
1278
+ "dialog.archived": "архив",
1279
+ "dialog.bodyHint": "Уйдёт агенту первым сообщением при запуске задачи.",
1280
+ "dialog.cancel": "Отмена",
1281
+ "dialog.create": "Создать",
1282
+ "dialog.fromGitea": "Из Gitea",
1283
+ "dialog.imported": "уже на доске",
1284
+ "dialog.kind": "Какая это задача",
1285
+ "dialog.kindPlain": "Не проект",
1286
+ "dialog.kindPlainHint": "Заметка, не относящаяся ни к какому репозиторию. Живёт на простой доске и в Gitea не попадает.",
1287
+ "dialog.kindProject": "Проект",
1288
+ "dialog.kindProjectHint": "В Gitea заводится issue, карточка привязывается к нему и живёт на проектной доске.",
1289
+ "dialog.labelsHint": "через запятую",
1290
+ "dialog.loadingRepos": "Репозитории загружаются…",
1291
+ "dialog.noIssues": "Открытых issue не найдено.",
1292
+ "dialog.noRepos": "Репозиториев нет",
1293
+ "dialog.own": "Своя задача",
1294
+ "dialog.pickRepo": "Выберите репозиторий",
1295
+ "dialog.repo": "Репозиторий",
1296
+ "dialog.repoNamePlaceholder": "имя нового репозитория, латиницей",
1297
+ "dialog.repoNew": "— новый репозиторий —",
1298
+ "dialog.repoNewHint": "Будет создан в организации: приватный и пустой, без README и заготовок. Создание репозитория отсюда не отменить.",
1299
+ "dialog.taskBody": "Описание",
1300
+ "dialog.taskColumn": "Колонка",
1301
+ "dialog.taskLabels": "Метки",
1302
+ "dialog.taskTitle": "Заголовок",
1303
+ "dialog.title": "Новая задача",
1304
+ "discard": "Отменить",
1305
+ "error.bad-repo-name": "В имени репозитория допустимы только латинские буквы, цифры, точка, дефис и подчёркивание.",
1306
+ "error.crossSite": "Запрос отклонён как межсайтовый.",
1307
+ "error.giteaAbsent": "Плагин Gitea не установлен, импорт недоступен.",
1308
+ "error.giteaUnconfigured": "Сначала настройте Gitea в карточке плагина Gitea.",
1309
+ "error.issue-not-created": "Issue не заведён. Если репозиторий был новым, он остался — ничего не удалено.",
1310
+ "error.issueNotFound": "Issue не найден.",
1311
+ "error.modelNotSelected": "Модель не выбрана — задайте её в настройках или укажите здесь.",
1312
+ "error.no-live-sessions": "Нет ни одной идущей сессии, в очередь ставить некуда.",
1313
+ "error.permission-refused": "Уровень доступа не принят, задача не запущена.",
1314
+ "error.permission-unavailable": "В этой сборке уровней доступа нет — задача не запущена, чтобы не пойти с бо́льшими правами, чем просили.",
1315
+ "error.repo-not-created": "Репозиторий не создан; больше ничего не сделано.",
1316
+ "error.repo-required": "Выберите репозиторий или назовите новый.",
1317
+ "error.session-not-live": "Эта сессия больше не идёт.",
1318
+ "error.sessionNotOpened": "Сессия поднята ({id}), но эта сборка не умеет в неё переключиться. Найдите её в списке сессий.",
1319
+ "error.startFailed": "Не удалось поднять сессию.",
1320
+ "error.task-not-queued": "Задача не стоит в очереди.",
1321
+ "error.taskHasNoIssue": "За этой задачей нет issue в Gitea.",
1322
+ "error.taskNotFound": "Задача не найдена.",
1323
+ "error.titleRequired": "Нужен заголовок.",
1324
+ "error.unknown": "Запрос не выполнен.",
1325
+ "facet.author": "Автор",
1326
+ "facet.priority": "Приоритет",
1327
+ "facet.release": "Выпуск",
1328
+ "facet.repo": "Репозиторий",
1329
+ "facet.risk": "Риск",
1330
+ "facet.scope": "Область",
1331
+ "facet.signal": "Сигнал",
1332
+ "facet.status": "Статус",
1333
+ "facet.type": "Тип",
1334
+ "field.archiveAfterDays": "Убирать в архив из «Выполнено» через, дней",
1335
+ "field.boardToolEnabled": "Разрешить агенту двигать карточки",
1336
+ "field.columnNames": "Свои названия колонок",
1337
+ "field.defaultProjectRoot": "Корень проектов",
1338
+ "field.giteaOwner": "Организация Gitea",
1339
+ "field.giteaTokenRef": "Имя учётной записи",
1340
+ "field.giteaUrl": "Адрес инстанса",
1341
+ "field.pushToGitea": "Закрывать issue в Gitea",
1342
+ "field.replyInstruction": "Постоянная приписка",
1343
+ "field.staleAfterMin": "Считать задачу молчащей после, минут",
1344
+ "field.startPrompt": "Шаблон первого сообщения",
1345
+ "field.syncIntervalSec": "Сверяться с Gitea раз в, секунд",
1346
+ "field.watchRepos": "Сузить подхват до репозиториев",
1347
+ "field.webhookSecretRef": "Имя учётной записи для вебхука",
1348
+ "field.wipInProgress": "Предел «В работе»",
1349
+ "field.wipReview": "Предел «Ревью»",
1350
+ "group.general": "Общее",
1351
+ "group.gitea": "Gitea",
1352
+ "group.limits": "Пределы колонок",
1353
+ "group.sync": "Автоперемещение",
1354
+ "hint.archiveAfterDays": "Считается с попадания карточки в «Выполнено». Ноль — оставлять всё на доске.",
1355
+ "hint.boardToolEnabled": "Выключено, пока скилл воркфлоу запрещает этому CLI трогать канбан.",
1356
+ "hint.columnNames": "Пары «идентификатор=Название» через запятую, например «backlog=Идеи, review=Проверка». Пусто — обычные названия.",
1357
+ "hint.defaultProjectRoot": "Где лежат рабочие копии проектов. Пусто — рабочая папка харнесса.",
1358
+ "hint.giteaOwner": "Чьи репозитории смотрит доска. Пусто — определится сама, пока у токена ровно одна организация.",
1359
+ "hint.giteaTokenRef": "ИМЯ учётной записи DSH, в которой лежит токен. Сам токен сюда не вводить.",
1360
+ "hint.giteaUrl": "Например https://gitea.example.com. Пусто — импорт недоступен.",
1361
+ "hint.label": "Пусто — метка для этой колонки не ставится.",
1362
+ "hint.limit": "Ноль — без предела. Превышение подсвечивается, но не запрещается.",
1363
+ "hint.pushToGitea": "Карточка, дошедшая до «Выполнено», закрывает свой issue. Меток доска не ставит: стадия и так видна по ветке и pull request.",
1364
+ "hint.replyInstruction": "Дописывается к каждому первому сообщению — например язык ответа. Пусто — ничего не добавляется.",
1365
+ "hint.staleAfterMin": "Помечаются только задачи с сессией. Ноль отключает отметку.",
1366
+ "hint.startPrompt": "Шаблон первого сообщения агенту. Пусто — встроенный шаблон.",
1367
+ "hint.syncIntervalSec": "Ноль отключает сверку. Меньше пятнадцати секунд поднимается до пятнадцати.",
1368
+ "hint.watchRepos": "Через запятую, «владелец/репо» или просто «репо». Пусто — все репозитории организации, где есть открытые задачи.",
1369
+ "hint.webhookSecretRef": "ИМЯ учётной записи DSH с секретом вебхука Gitea. Пусто — остаётся только опрос.",
1370
+ "move.backlog": "Работа по задаче останавливается: идущий ход агента будет прерван. Если сессии нет, карточка просто переедет.",
1371
+ "move.cleanup": "Агент удалит ветку в Gitea, worktree и локальную ветку и запишет это в issue.",
1372
+ "move.confirm": "Перенести",
1373
+ "move.deploy": "Агент вольёт pull request и выкатит. Сам перенос в эту колонку И ЕСТЬ ваше «ок» на deploy — второй раз агент спрашивать не станет.",
1374
+ "move.done": "Задача считается завершённой. Это решение человека: сам агент карточку сюда не переносит.",
1375
+ "move.in-progress": "Агент получит команду начать или продолжить реализацию.",
1376
+ "move.review": "Агент доведёт работу до ревью: снимет с pull request пометку черновика и запросит проверку.",
1377
+ "move.title": "Перенести в «{column}»?",
1378
+ "order.manual": "Как разложено",
1379
+ "order.new": "Сначала новые",
1380
+ "order.old": "Сначала старые",
1381
+ "panel.agentPreset": "Профиль агента",
1382
+ "panel.archive": "В архив",
1383
+ "panel.author": "завёл {who}",
1384
+ "panel.bodyLoading": "читаю описание…",
1385
+ "panel.close": "Закрыть",
1386
+ "panel.continue": "Продолжить",
1387
+ "panel.continueHint": "У задачи один чат: открывается прежняя сессия, а не новая.",
1388
+ "panel.created": "заведена {ago} назад",
1389
+ "panel.delete": "Удалить",
1390
+ "panel.deleteConfirm": "Удалить «{title}»? Отменить будет нельзя.",
1391
+ "panel.issue": "Issue",
1392
+ "panel.log": "Переходы",
1393
+ "panel.message": "Сообщение агенту",
1394
+ "panel.messageHint": "Правьте перед отправкой. Пустое — уйдёт встроенный текст.",
1395
+ "panel.model": "Модель",
1396
+ "panel.noLog": "Переходов пока нет.",
1397
+ "panel.noModels": "У этого провайдера нет моделей",
1398
+ "panel.noProviders": "Провайдеров нет",
1399
+ "panel.openChat": "Открыть чат",
1400
+ "panel.permission": "Уровень доступа",
1401
+ "panel.pickModel": "Выберите модель",
1402
+ "panel.pickProvider": "Выберите провайдера",
1403
+ "panel.pickProviderFirst": "Сначала выберите провайдера",
1404
+ "panel.presetDefault": "Как в харнессе по умолчанию",
1405
+ "panel.provider": "Провайдер",
1406
+ "panel.refresh": "Обновить из Gitea",
1407
+ "panel.start": "Начать",
1408
+ "panel.startHint": "Откроется отдельная сессия агента по этой задаче. Ветку и worktree заводит агент после preflight, а не отсюда.",
1409
+ "panel.starting": "Запускается…",
1410
+ "panel.updated": "последнее изменение {ago} назад",
1411
+ "plan.counter": "{done} из {total}",
1412
+ "plan.title": "План агента",
1413
+ "queue.add": "В очередь",
1414
+ "queue.hint": "Задача встанет в очередь сессии: агент возьмёт её, закончив текущую работу. До этого карточка остаётся там, где лежит.",
1415
+ "queue.option": "{title} - задач: {n}",
1416
+ "queue.session": "Сессия",
1417
+ "queue.title": "Поставить в очередь идущей сессии",
1418
+ "save": "Сохранить",
1419
+ "section.label": "Канбан",
1420
+ "settings.loading": "Настройки загружаются…",
1421
+ "settings.readOnly": "Настройки в этой сессии доступны только для чтения.",
1422
+ "settings.saveFailed": "Не сохранены поля: {fields}",
1423
+ "settings.saved": "Сохранено.",
1424
+ "settings.unavailable": "Хост не знает пространство настроек dsh-kanban. Перезапустите веб-интерфейс после установки плагина.",
1425
+ "state.queued": "в очереди · {n}",
1426
+ "state.running": "в работе",
1427
+ "state.stopped": "остановился",
1428
+ "stop.idle": "Агент не работал, останавливать было нечего.",
1429
+ "stop.no-session": "У задачи нет сессии.",
1430
+ "subtitle": "Доска задач: issue из Gitea и отдельная сессия под задачу",
1431
+ "sync.failed": "сверка не проходит",
1432
+ "sync.lastSeen": "последняя удачная сверка {ago} назад",
1433
+ "sync.never": "сверки с Gitea ещё не было",
1434
+ "sync.neverSeen": "удачной сверки с запуска харнесса не было",
1435
+ "sync.okAgo": "сверено {ago} назад",
1436
+ "sync.running": "сверяемся с Gitea…",
1437
+ "time.day": "{n} дн",
1438
+ "time.hour": "{n} ч",
1439
+ "time.min": "{n} мин",
1440
+ "time.now": "только что",
1441
+ "title": "Канбан"
1442
+ },
1220
1443
  "dsh-market": {
1221
1444
  "actWhy": "Почему не активен?",
1222
1445
  "agentBusyInstall": "Агент в настоящее время работает — дождитесь завершения (или отмените его) перед установкой. Установка изменяет файлы плагина, поэтому работающий агент может завершиться с ошибкой в процессе.",
package/lib/index.js CHANGED
@@ -4,7 +4,6 @@
4
4
  // нечего, но у выбора языка есть сохраняемая настройка: регистрируем её схему
5
5
  // в собственном namespace, чтобы запись переживала браузеры и машины через
6
6
  // штатный механизм настроек DSH.
7
- import { settingsNamespace } from '@deepseek-ai/dsh-settings'
8
7
  import z from '@deepseek-ai/schemastery'
9
8
 
10
9
  export const name = '@goodandready/dsh-russian-lang'
@@ -21,14 +20,14 @@ const RussianLangSettingsSchema = z.object({
21
20
 
22
21
  export function apply(ctx) {
23
22
  ctx.inject(['settings'], (settingsCtx) => {
24
- settingsCtx.settings.register(settingsNamespace('russian-lang'), RussianLangSettingsSchema)
23
+ settingsCtx.settings.register('russian-lang', RussianLangSettingsSchema)
25
24
  })
26
25
 
27
26
  // #68: русский системный промпт агента (опционально). Регистрируем секцию
28
27
  // промпта, которая просит отвечать по-русски, когда включён флаг
29
28
  // russian-lang.agentPrompt. Секция — официальная точка расширения ядра.
30
29
  ctx.inject(['settings', 'systemPrompt'], (settingsCtx, prompt) => {
31
- const NS = settingsNamespace('russian-lang')
30
+ const NS = 'russian-lang'
32
31
  const RU_SECTION = 'dsh-russian-lang-agent-prompt'
33
32
  const RU_TEXT = 'Отвечай пользователю на русском языке. Если пользователь пишет на другом языке, отвечай на его языке.'
34
33
  let current = false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-russian-lang",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "Russian localization for the DeepSeek Harness web UI: adds Русский as the third option in the native Settings - General - Language menu. Translates the full core surface (100% coverage of DSH 0.1.1-rc.2) plus 16 community plugin namespaces - 52 namespaces, 2795 strings (many machine-drafted, queued for manual review), with Russian plural forms, count-key plurals, a typography pass (guillemets, em dash, nbsp), a wrong-layout input hint and browser spellcheck. The locale runtime snapshot is extended at runtime on unpatched cores; no files in the DSH installation are modified. The choice is persisted through the plugin-owned russian-lang namespace.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "peerDependencies": {
59
59
  "@deepseek-ai/cordis": "^4.0.1",
60
- "@deepseek-ai/dsh-settings": "^0.1.1-rc.2"
60
+ "@deepseek-ai/dsh-settings": "^0.1.2-alpha.2"
61
61
  },
62
62
  "repository": {
63
63
  "type": "git",
@@ -0,0 +1,225 @@
1
+ {
2
+ "dsh-kanban": {
3
+ "title": "Канбан",
4
+ "subtitle": "Доска задач: issue из Gitea и отдельная сессия под задачу",
5
+ "section.label": "Канбан",
6
+ "group.gitea": "Gitea",
7
+ "group.general": "Общее",
8
+ "field.giteaUrl": "Адрес инстанса",
9
+ "field.giteaTokenRef": "Имя учётной записи",
10
+ "hint.giteaUrl": "Например https://gitea.example.com. Пусто — импорт недоступен.",
11
+ "hint.giteaTokenRef": "ИМЯ учётной записи DSH, в которой лежит токен. Сам токен сюда не вводить.",
12
+ "group.limits": "Пределы колонок",
13
+ "group.sync": "Автоперемещение",
14
+ "field.syncIntervalSec": "Сверяться с Gitea раз в, секунд",
15
+ "field.staleAfterMin": "Считать задачу молчащей после, минут",
16
+ "field.giteaOwner": "Организация Gitea",
17
+ "hint.giteaOwner": "Чьи репозитории смотрит доска. Пусто — определится сама, пока у токена ровно одна организация.",
18
+ "field.columnNames": "Свои названия колонок",
19
+ "hint.columnNames": "Пары «идентификатор=Название» через запятую, например «backlog=Идеи, review=Проверка». Пусто — обычные названия.",
20
+ "field.watchRepos": "Сузить подхват до репозиториев",
21
+ "hint.watchRepos": "Через запятую, «владелец/репо» или просто «репо». Пусто — все репозитории организации, где есть открытые задачи.",
22
+ "field.archiveAfterDays": "Убирать в архив из «Выполнено» через, дней",
23
+ "hint.archiveAfterDays": "Считается с попадания карточки в «Выполнено». Ноль — оставлять всё на доске.",
24
+ "field.boardToolEnabled": "Разрешить агенту двигать карточки",
25
+ "field.pushToGitea": "Закрывать issue в Gitea",
26
+ "field.webhookSecretRef": "Имя учётной записи для вебхука",
27
+ "hint.pushToGitea": "Карточка, дошедшая до «Выполнено», закрывает свой issue. Меток доска не ставит: стадия и так видна по ветке и pull request.",
28
+ "hint.webhookSecretRef": "ИМЯ учётной записи DSH с секретом вебхука Gitea. Пусто — остаётся только опрос.",
29
+ "hint.syncIntervalSec": "Ноль отключает сверку. Меньше пятнадцати секунд поднимается до пятнадцати.",
30
+ "hint.staleAfterMin": "Помечаются только задачи с сессией. Ноль отключает отметку.",
31
+ "hint.boardToolEnabled": "Выключено, пока скилл воркфлоу запрещает этому CLI трогать канбан.",
32
+ "field.defaultProjectRoot": "Корень проектов",
33
+ "field.startPrompt": "Шаблон первого сообщения",
34
+ "field.replyInstruction": "Постоянная приписка",
35
+ "field.wipInProgress": "Предел «В работе»",
36
+ "field.wipReview": "Предел «Ревью»",
37
+ "hint.defaultProjectRoot": "Где лежат рабочие копии проектов. Пусто — рабочая папка харнесса.",
38
+ "hint.startPrompt": "Шаблон первого сообщения агенту. Пусто — встроенный шаблон.",
39
+ "hint.replyInstruction": "Дописывается к каждому первому сообщению — например язык ответа. Пусто — ничего не добавляется.",
40
+ "hint.label": "Пусто — метка для этой колонки не ставится.",
41
+ "hint.limit": "Ноль — без предела. Превышение подсвечивается, но не запрещается.",
42
+ "settings.loading": "Настройки загружаются…",
43
+ "settings.unavailable": "Хост не знает пространство настроек dsh-kanban. Перезапустите веб-интерфейс после установки плагина.",
44
+ "settings.readOnly": "Настройки в этой сессии доступны только для чтения.",
45
+ "settings.saveFailed": "Не сохранены поля: {fields}",
46
+ "settings.saved": "Сохранено.",
47
+ "save": "Сохранить",
48
+ "discard": "Отменить",
49
+ "column.backlog": "Бэклог",
50
+ "column.in-progress": "В работе",
51
+ "column.review": "Ревью",
52
+ "column.deploy": "Выкатка",
53
+ "column.cleanup": "Уборка",
54
+ "column.done": "Выполнено",
55
+ "board.loading": "Доска загружается…",
56
+ "board.empty": "Задач пока нет.",
57
+ "board.waiting": "нужен человек",
58
+ "board.sync": "Сверить с Gitea",
59
+ "board.syncing": "Сверяю…",
60
+ "board.noMatch": "Ничего не найдено.",
61
+ "board.search": "Поиск",
62
+ "board.limit": "предел {n}",
63
+ "board.newTask": "Задача",
64
+ "board.refresh": "Обновить",
65
+ "board.allRepos": "Все репозитории",
66
+ "board.repo": "репозиторий",
67
+ "dialog.title": "Новая задача",
68
+ "dialog.own": "Своя задача",
69
+ "dialog.fromGitea": "Из Gitea",
70
+ "dialog.repo": "Репозиторий",
71
+ "dialog.pickRepo": "Выберите репозиторий",
72
+ "dialog.loadingRepos": "Репозитории загружаются…",
73
+ "dialog.noRepos": "Репозиториев нет",
74
+ "dialog.taskTitle": "Заголовок",
75
+ "dialog.taskBody": "Описание",
76
+ "dialog.taskLabels": "Метки",
77
+ "dialog.taskColumn": "Колонка",
78
+ "dialog.bodyHint": "Уйдёт агенту первым сообщением при запуске задачи.",
79
+ "dialog.labelsHint": "через запятую",
80
+ "dialog.archived": "архив",
81
+ "dialog.create": "Создать",
82
+ "dialog.cancel": "Отмена",
83
+ "move.title": "Перенести в «{column}»?",
84
+ "board.expand": "Развернуть колонку",
85
+ "card.move": "перенести",
86
+ "card.toChat": "перейти в чат",
87
+ "card.pick": "Отметить для группового запуска",
88
+ "card.queue": "в очередь...",
89
+ "card.unqueue": "снять с очереди",
90
+ "queue.title": "Поставить в очередь идущей сессии",
91
+ "queue.hint": "Задача встанет в очередь сессии: агент возьмёт её, закончив текущую работу. До этого карточка остаётся там, где лежит.",
92
+ "queue.session": "Сессия",
93
+ "queue.option": "{title} - задач: {n}",
94
+ "queue.add": "В очередь",
95
+ "error.no-live-sessions": "Нет ни одной идущей сессии, в очередь ставить некуда.",
96
+ "error.session-not-live": "Эта сессия больше не идёт.",
97
+ "error.task-not-queued": "Задача не стоит в очереди.",
98
+ "batch.title": "Запустить {n} задач в одном чате",
99
+ "batch.hint": "Одна сессия на все. Задачи приходят по очереди: агент берёт следующую, закончив текущую. В «В работе» уезжает только первая — остальные ждут своей очереди там, где лежат.",
100
+ "batch.start": "Запустить",
101
+ "state.queued": "в очереди · {n}",
102
+ "board.pickedCount": "выбрано {n}",
103
+ "board.pickNone": "Снять выбор",
104
+ "board.pickStart": "Запустить выбранные",
105
+ "dialog.kind": "Какая это задача",
106
+ "dialog.kindProject": "Проект",
107
+ "dialog.kindPlain": "Не проект",
108
+ "dialog.kindProjectHint": "В Gitea заводится issue, карточка привязывается к нему и живёт на проектной доске.",
109
+ "dialog.kindPlainHint": "Заметка, не относящаяся ни к какому репозиторию. Живёт на простой доске и в Gitea не попадает.",
110
+ "dialog.repoNew": "— новый репозиторий —",
111
+ "dialog.repoNamePlaceholder": "имя нового репозитория, латиницей",
112
+ "dialog.repoNewHint": "Будет создан в организации: приватный и пустой, без README и заготовок. Создание репозитория отсюда не отменить.",
113
+ "error.repo-required": "Выберите репозиторий или назовите новый.",
114
+ "error.bad-repo-name": "В имени репозитория допустимы только латинские буквы, цифры, точка, дефис и подчёркивание.",
115
+ "error.repo-not-created": "Репозиторий не создан; больше ничего не сделано.",
116
+ "error.issue-not-created": "Issue не заведён. Если репозиторий был новым, он остался — ничего не удалено.",
117
+ "board.archive": "Архив",
118
+ "panel.archive": "В архив",
119
+ "archive.hint": "Карточки, отстоявшие свой срок в «Выполнено». Ничего не удалено: возвращённая карточка встаёт в ту же колонку, откуда ушла.",
120
+ "archive.empty": "В архиве пусто.",
121
+ "archive.since": "в архиве {ago}",
122
+ "archive.restore": "Вернуть на доску",
123
+ "sync.running": "сверяемся с Gitea…",
124
+ "sync.never": "сверки с Gitea ещё не было",
125
+ "sync.okAgo": "сверено {ago} назад",
126
+ "sync.failed": "сверка не проходит",
127
+ "sync.lastSeen": "последняя удачная сверка {ago} назад",
128
+ "sync.neverSeen": "удачной сверки с запуска харнесса не было",
129
+ "facet.repo": "Репозиторий",
130
+ "facet.author": "Автор",
131
+ "board.pickArchive": "В архив",
132
+ "board.pickUnqueue": "Снять с очереди",
133
+ "board.pickMove": "Перенести…",
134
+ "board.someFailed": "Сделано у {done} из {total}, остальные отказали.",
135
+ "card.stop": "остановить",
136
+ "card.pack": "пачка {at} из {total}",
137
+ "card.packHint": "Запущены вместе, одной сессией; агент берёт их в этом порядке.",
138
+ "stop.idle": "Агент не работал, останавливать было нечего.",
139
+ "stop.no-session": "У задачи нет сессии.",
140
+ "board.waitCount": "Ждут ответа: {n}",
141
+ "board.waitCountHint": "Задачи, где агент задал вопрос или просит разрешения.",
142
+ "order.manual": "Как разложено",
143
+ "order.old": "Сначала старые",
144
+ "order.new": "Сначала новые",
145
+ "facet.type": "Тип",
146
+ "facet.priority": "Приоритет",
147
+ "facet.status": "Статус",
148
+ "facet.scope": "Область",
149
+ "facet.risk": "Риск",
150
+ "facet.signal": "Сигнал",
151
+ "facet.release": "Выпуск",
152
+ "boardName.main": "Проектная доска",
153
+ "boardName.simple": "Простая доска",
154
+ "board.clearFilters": "Снять отборы",
155
+ "board.shown": "показано {shown} из {total}",
156
+ "board.noRepo": "без проекта",
157
+ "board.back": "В чат",
158
+ "board.backHint": "Закрыть доску и вернуться в разговор",
159
+ "panel.openChat": "Открыть чат",
160
+ "panel.bodyLoading": "читаю описание…",
161
+ "panel.author": "завёл {who}",
162
+ "panel.created": "заведена {ago} назад",
163
+ "panel.updated": "последнее изменение {ago} назад",
164
+ "time.now": "только что",
165
+ "time.min": "{n} мин",
166
+ "time.hour": "{n} ч",
167
+ "time.day": "{n} дн",
168
+ "board.collapse": "Свернуть колонку",
169
+ "plan.title": "План агента",
170
+ "plan.counter": "{done} из {total}",
171
+ "state.running": "в работе",
172
+ "state.stopped": "остановился",
173
+ "move.confirm": "Перенести",
174
+ "move.backlog": "Работа по задаче останавливается: идущий ход агента будет прерван. Если сессии нет, карточка просто переедет.",
175
+ "move.in-progress": "Агент получит команду начать или продолжить реализацию.",
176
+ "move.review": "Агент доведёт работу до ревью: снимет с pull request пометку черновика и запросит проверку.",
177
+ "move.deploy": "Агент вольёт pull request и выкатит. Сам перенос в эту колонку И ЕСТЬ ваше «ок» на deploy — второй раз агент спрашивать не станет.",
178
+ "move.cleanup": "Агент удалит ветку в Gitea, worktree и локальную ветку и запишет это в issue.",
179
+ "move.done": "Задача считается завершённой. Это решение человека: сам агент карточку сюда не переносит.",
180
+ "dialog.imported": "уже на доске",
181
+ "dialog.noIssues": "Открытых issue не найдено.",
182
+ "panel.close": "Закрыть",
183
+ "panel.issue": "Issue",
184
+ "panel.log": "Переходы",
185
+ "panel.noLog": "Переходов пока нет.",
186
+ "panel.refresh": "Обновить из Gitea",
187
+ "panel.delete": "Удалить",
188
+ "panel.deleteConfirm": "Удалить «{title}»? Отменить будет нельзя.",
189
+ "panel.start": "Начать",
190
+ "panel.agentPreset": "Профиль агента",
191
+ "panel.presetDefault": "Как в харнессе по умолчанию",
192
+ "panel.permission": "Уровень доступа",
193
+ "error.permission-unavailable": "В этой сборке уровней доступа нет — задача не запущена, чтобы не пойти с бо́льшими правами, чем просили.",
194
+ "error.permission-refused": "Уровень доступа не принят, задача не запущена.",
195
+ "panel.provider": "Провайдер",
196
+ "panel.model": "Модель",
197
+ "panel.pickProvider": "Выберите провайдера",
198
+ "panel.pickProviderFirst": "Сначала выберите провайдера",
199
+ "panel.pickModel": "Выберите модель",
200
+ "panel.noProviders": "Провайдеров нет",
201
+ "panel.noModels": "У этого провайдера нет моделей",
202
+ "panel.starting": "Запускается…",
203
+ "panel.continue": "Продолжить",
204
+ "panel.continueHint": "У задачи один чат: открывается прежняя сессия, а не новая.",
205
+ "panel.message": "Сообщение агенту",
206
+ "panel.messageHint": "Правьте перед отправкой. Пустое — уйдёт встроенный текст.",
207
+ "panel.startHint": "Откроется отдельная сессия агента по этой задаче. Ветку и worktree заводит агент после preflight, а не отсюда.",
208
+ "error.startFailed": "Не удалось поднять сессию.",
209
+ "error.sessionNotOpened": "Сессия поднята ({id}), но эта сборка не умеет в неё переключиться. Найдите её в списке сессий.",
210
+ "error.modelNotSelected": "Модель не выбрана — задайте её в настройках или укажите здесь.",
211
+ "chip.moveTo": "Перенести в",
212
+ "chip.openCard": "Перейти к карточке",
213
+ "chip.note": "Оставить заметку",
214
+ "chip.noteHint": "Заметка дописывается в тело задачи. Тело уходит агенту первым сообщением, поэтому заметка до запуска попадёт в работу, а после запуска — нет.",
215
+ "chip.noteSave": "Дописать",
216
+ "error.giteaAbsent": "Плагин Gitea не установлен, импорт недоступен.",
217
+ "error.giteaUnconfigured": "Сначала настройте Gitea в карточке плагина Gitea.",
218
+ "error.taskNotFound": "Задача не найдена.",
219
+ "error.taskHasNoIssue": "За этой задачей нет issue в Gitea.",
220
+ "error.issueNotFound": "Issue не найден.",
221
+ "error.titleRequired": "Нужен заголовок.",
222
+ "error.crossSite": "Запрос отклонён как межсайтовый.",
223
+ "error.unknown": "Запрос не выполнен."
224
+ }
225
+ }