@goodandready/dsh-russian-lang 0.3.17 → 0.3.19
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 +14 -6
- package/lib/client.js +14 -11
- package/lib/client.template.js +101 -6
- package/lib/locales/core.json +1 -1
- package/lib/locales/plugins/03-dshmarket.json +1 -1
- package/lib/locales/plugins/07-better-sidebar.json +1 -1
- package/lib/locales/plugins/17-dsh-context.json +1 -1
- package/lib/locales/plugins/20-skill-explorer.json +1 -1
- package/lib/locales/plugins/26-config-manager.json +1 -1
- package/lib/locales/plugins/27-config-manager-sync.json +1 -1
- package/lib/locales/plugins/29-config-manager-recovery.json +1 -1
- package/lib/locales/plugins/34-key-rotation.json +1 -1
- package/lib/locales/plugins/42-plugin-console.json +1 -1
- package/lib/locales/plugins/51-plugin-guard.json +1 -1
- package/lib/locales/plugins/68-image-gen.json +1 -1
- package/lib/locales/plugins/77-github-ops.json +1 -1
- package/lib/locales/plugins/81-ego-browser.json +1 -1
- package/lib/pure.js +7 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
<h3>Полная русская локализация, умная типографика и исправление раскладки клавиатуры для DeepSeek Harness</h3>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<a href="https://www.npmjs.com/package/@goodandready/dsh-russian-lang"><img src="https://img.shields.io/badge/npm-v0.3.
|
|
8
|
+
<a href="https://www.npmjs.com/package/@goodandready/dsh-russian-lang"><img src="https://img.shields.io/badge/npm-v0.3.18-6366f1.svg?style=for-the-badge&labelColor=1e1b4b" alt="npm version"></a>
|
|
9
9
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-10b981.svg?style=for-the-badge&color=10b981&labelColor=064e3b" alt="license"></a>
|
|
10
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
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
12
|
</p>
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
15
|
-
<img src="https://img.shields.io/badge/DSH-v0.
|
|
15
|
+
<img src="https://img.shields.io/badge/DSH-v0.2.0--rc.1%2B-blue.svg?style=for-the-badge&labelColor=1e1b4b" alt="DSH v0.1.7-rc.2+">
|
|
16
16
|
<img src="https://img.shields.io/badge/Покрытие-100%25-10b981.svg?style=for-the-badge&labelColor=064e3b" alt="Покрытие 100%">
|
|
17
|
-
<img src="https://img.shields.io/badge/Ключей_UI-
|
|
17
|
+
<img src="https://img.shields.io/badge/Ключей_UI-8523-6366f1.svg?style=for-the-badge&labelColor=1e1b4b" alt="8110 ключей">
|
|
18
18
|
<img src="https://img.shields.io/badge/Ручная_вычитка-100%25-0ea5e9.svg?style=for-the-badge&labelColor=082f49" alt="100% ручная вычитка">
|
|
19
19
|
</p>
|
|
20
20
|
|
|
@@ -52,9 +52,9 @@ graph LR
|
|
|
52
52
|
DSH[Web UI Интерфейс] --> Loader[Загрузчик локализации dsh-russian-lang]
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
subgraph TranslationLayer [Слой перевода и адаптации —
|
|
56
|
-
Loader --> CoreDict[59 Пространств имён ядра DSH /
|
|
57
|
-
Loader --> PluginDict[65+ Плагинов экосистемы DSH /
|
|
55
|
+
subgraph TranslationLayer [Слой перевода и адаптации — 8523 ключа / 100% вычитано]
|
|
56
|
+
Loader --> CoreDict[59 Пространств имён ядра DSH / 2421 ключ]
|
|
57
|
+
Loader --> PluginDict[65+ Плагинов экосистемы DSH / 6102 ключа]
|
|
58
58
|
Loader --> PluralEngine[Движок грамматических Plural-форм: 1/2/5]
|
|
59
59
|
end
|
|
60
60
|
|
|
@@ -189,6 +189,14 @@ russian-lang:
|
|
|
189
189
|
|
|
190
190
|
---
|
|
191
191
|
|
|
192
|
+
## 🛠 Разработка и зеркало репозитория
|
|
193
|
+
|
|
194
|
+
Публичный репозиторий на GitHub является дистрибутивным зеркалом релизов `@goodandready/dsh-russian-lang`. Основная разработка, сборочный пайплайн и полный тестовый контур ведутся в закрытом репозитории проекта.
|
|
195
|
+
|
|
196
|
+
Pull Requests и Issues на GitHub приветствуются и обрабатываются в обычном порядке! Для внесения правок в логику клиента используйте `lib/client.template.js`. Запуск `npm test` в клоне репозитория проверяет синтаксис бандла через `node --check`.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
192
200
|
## 📄 Лицензия
|
|
193
201
|
|
|
194
202
|
MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
|
package/lib/client.js
CHANGED
|
@@ -195,11 +195,12 @@ return count > 0 ? { status: 'full', count, label: `RU: ${count} строк` } :
|
|
|
195
195
|
const typoQuotes = (text) => text
|
|
196
196
|
.replace(/"([^"\n]{1,200})"/g, '«$1»').replace(/[“„]([^“”\n]{1,200})[”"]/g, '«$1»').replace(/[『「]([^』」\n]{1,200})[』」]/g, '«$1»')
|
|
197
197
|
const typoDash = (text) => text
|
|
198
|
-
.replace(/(^|[\s(\[«])--(?=\s|$)/g, '$1—').replace(/(
|
|
198
|
+
.replace(/(^|[\s(\[«])--(?=\s|$)/g, '$1—').replace(/(?<=[\p{L}\p{N}»\)]) - (?=[\p{L}\p{N}«\(])/gu, ' — ')
|
|
199
199
|
const typoPunct = (text) => text.replace(/\s+([,.:;!?])(?=\s|$)/g, '$1')
|
|
200
200
|
const TYPO_SHORT = new Set(['в', 'с', 'к', 'о', 'у', 'а', 'и', 'но', 'не', 'ни', 'на', 'по', 'до', 'из', 'за', 'от', 'об'])
|
|
201
|
-
const typoNbsp = (text) => text
|
|
202
|
-
))
|
|
201
|
+
const typoNbsp = (text) => text
|
|
202
|
+
.replace(/(^|[\s(\[«])([а-яё]{1,2})(\s+)/g, (match, lead, word) => (TYPO_SHORT.has(word) ? lead + word + ' ' : match
|
|
203
|
+
)).replace(/(\d) ([а-яё]{1,24})(?![а-яё])/giu, '$1 $2').replace(/(\d) ([%°₽$€£]|кб|КБ|МБ|ГБ)(?=\s|$)/giu, '$1 $2')
|
|
203
204
|
const YO_EDGE_L = '(?<![а-яёА-ЯЁ])'
|
|
204
205
|
const YO_EDGE_R = '(?![а-яёА-ЯЁ])'
|
|
205
206
|
const yoCase = (src, repl) => {if (src === src.toUpperCase() && src !== src.toLowerCase()) return repl.toUpperCase()
|
|
@@ -393,7 +394,8 @@ return results.sort((a, b) => b.score - a.score).map((r) => r.item)
|
|
|
393
394
|
}
|
|
394
395
|
const RU = {"common":{"back":"Назад","cancel":"Отмена","close":"Закрыть","collapse":"Свернуть","copied":"Скопировано","copy":"Копировать","delete":"Удалить","edit":"Изменить","expand":"Развернуть","loading":"Загрузка…","more":"Ещё","next":"Дальше","ok":"ОК","retry":"Повторить","save":"Сохранить","search":"Поиск","submit":"Отправить"},"dsh-cron":{"sidebar.label":"Задачи по расписанию"},"dsh-usage-stats":{"footer.todayLabel":"Сегодня"},"pluginMarket":{"trigger":"Магазин плагинов"}}
|
|
395
396
|
const ZH_RU = {}
|
|
396
|
-
RU['russian-lang'] = {"cardTitle":"Русская локализация","cardSub":"Язык интерфейса, типографика, раскладка","badgeRu":"🟢 RU активен","badgeEn":"⚪ EN активен","badgeCoverage":"🟢 100% (7,902 ключа)","badgeSmartUx":"⚡ Smart UX активен","secLanguage":"🌐 Язык интерфейса","secLanguageDesc":"Нативное переключение языка интерфейса DSH на русский без перезагрузки страницы.","enabled":"Русский язык включён","enabledDesc":"Переключает язык интерфейса DeepSeek Harness на русский.","quickSwitchNote":"Быстрый переключатель RU ⇄ EN доступен в шапке сессии рядом с кнопками диалога.","secTypography":"✍️ Умная типографика и ввод (Smart UX)","secTypographyDesc":"Нормы русской типографики во время диалога и набора.","typography":"Типографика вывода","typographyDesc":"Кавычки «», тире — вместо дефисов, неразрывные пробелы.","yo":"Буква «ё»","yoDesc":"Восстановление «ё» в частых словах (ещё, идёт и др.).","liveInput":"Живая типографика инпута","liveInputDesc":"Кавычки «», тире — и неразрывные пробелы перед отправкой.","slashAliases":"Русские алиасы команд","slashAliasesDesc":"Команды: /цель, /сжать, /план, /справка, /память.","altLHintText":"Конвертация раскладки текущего поля (ghbdtn ⇄ привет).","secAgentPrompt":"🤖 Системный промпт агента","secAgentPromptDesc":"Официальная секция расширения DSH systemPrompt для ведения диалога на русском языке.","agentPrompt":"Русский промпт агента","agentPromptDesc":"Добавляет в системный промпт инструкцию отвечать по-русски в выбранном стиле.","agentPromptPreset":"Стиль ответов агента","presetExpert":"Технический эксперт (строгая терминология, чистый код)","presetWriter":"Технический писатель (Markdown, таблицы, ГОСТ)","presetConcise":"Лаконичный режим (кратко, без лишней воды)","presetReviewer":"Код-ревьюер (поиск багов, безопасность, аудит)","presetArchitect":"Системный архитектор (контракты, масштабируемость)","presetTutor":"Наставник (понятные объяснения, пошаговый разбор)","secUpdater":"🔄 Обновление языкового пакета","secUpdaterDesc":"Проверка наличия новых релизов в реестре npm и обновление в один клик.","updaterCurrent":"Текущая версия: v{version}","updaterLatest":"Доступна новая версия: v{version}","updaterUpToDate":"Установлена актуальная версия","updaterChecking":"Проверка…","updaterCheckBtn":"Проверить обновления","updaterBtn":"Обновить до v{version} в 1 клик","updaterUpdating":"Установка обновления…","updaterSuccess":"✅ Плагин успешно обновлён! Перезапустите DSH для применения.","updaterFailed":"❌ Не удалось проверить/обновить плагин. Проверьте сеть или логи сервера.","badgeUpdateAvailable":"Доступно обновление","badgeUpToDate":"Актуальная версия","secTranslator":"🌐 Перевод сообщений ассистента","secTranslatorDesc":"Перевод ответов модели в чате на русский язык.","translateEngine":"Движок перевода","engineOff":"Выключен (по умолчанию, без сетевых вызовов)","engineLocal":"Локальный LibreTranslate (приватно, ~600 МБ RAM)","engineGoogle":"Google Translate (онлайн)","googleWarn":"⚠️ Текст сообщений передаётся на публичные серверы Google.","localStatusRunning":"🟢 Контейнер LibreTranslate активен","localStatusStopped":"⚪ Контейнер LibreTranslate не запущен","localStartBtn":"Запустить LibreTranslate в Docker (~600 МБ RAM)","localStarting":"Запуск контейнера…","localInfo":"LibreTranslate в Docker: изолированно, ~600 МБ RAM.","secOverrides":"✍️ Пользовательские переопределения (Custom Overrides)","secOverridesDesc":"Замена любой формулировки интерфейса DSH на собственную.","overrideKeyPlaceholder":"Ключ (например, common.back или settings.title)","overrideValuePlaceholder":"Ваш русский перевод","overrideAddBtn":"Добавить","overrideEmpty":"Переопределений пока нет.","overrideDelete":"Удалить","overrideTip":"💡 Зажмите Alt и кликните на элемент UI для быстрой правки.","secSupport":"📊 Покрытие экосистемы и поддержка","secSupportDesc":"100% UI-покрытие ядра и плагинов DSH
|
|
397
|
+
RU['russian-lang'] = {"cardTitle":"Русская локализация","cardSub":"Язык интерфейса, типографика, раскладка","badgeRu":"🟢 RU активен","badgeEn":"⚪ EN активен","badgeCoverage":"🟢 100% (7,902 ключа)","badgeSmartUx":"⚡ Smart UX активен","secLanguage":"🌐 Язык интерфейса","secLanguageDesc":"Нативное переключение языка интерфейса DSH на русский без перезагрузки страницы.","enabled":"Русский язык включён","enabledDesc":"Переключает язык интерфейса DeepSeek Harness на русский.","quickSwitchNote":"Быстрый переключатель RU ⇄ EN доступен в шапке сессии рядом с кнопками диалога.","secTypography":"✍️ Умная типографика и ввод (Smart UX)","secTypographyDesc":"Нормы русской типографики во время диалога и набора.","typography":"Типографика вывода","typographyDesc":"Кавычки «», тире — вместо дефисов, неразрывные пробелы.","yo":"Буква «ё»","yoDesc":"Восстановление «ё» в частых словах (ещё, идёт и др.).","liveInput":"Живая типографика инпута","liveInputDesc":"Кавычки «», тире — и неразрывные пробелы перед отправкой.","slashAliases":"Русские алиасы команд","slashAliasesDesc":"Команды: /цель, /сжать, /план, /справка, /память.","altLHintText":"Конвертация раскладки текущего поля (ghbdtn ⇄ привет).","secAgentPrompt":"🤖 Системный промпт агента","secAgentPromptDesc":"Официальная секция расширения DSH systemPrompt для ведения диалога на русском языке.","agentPrompt":"Русский промпт агента","agentPromptDesc":"Добавляет в системный промпт инструкцию отвечать по-русски в выбранном стиле.","agentPromptPreset":"Стиль ответов агента","presetExpert":"Технический эксперт (строгая терминология, чистый код)","presetWriter":"Технический писатель (Markdown, таблицы, ГОСТ)","presetConcise":"Лаконичный режим (кратко, без лишней воды)","presetReviewer":"Код-ревьюер (поиск багов, безопасность, аудит)","presetArchitect":"Системный архитектор (контракты, масштабируемость)","presetTutor":"Наставник (понятные объяснения, пошаговый разбор)","secUpdater":"🔄 Обновление языкового пакета","secUpdaterDesc":"Проверка наличия новых релизов в реестре npm и обновление в один клик.","updaterCurrent":"Текущая версия: v{version}","updaterLatest":"Доступна новая версия: v{version}","updaterUpToDate":"Установлена актуальная версия","updaterChecking":"Проверка…","updaterCheckBtn":"Проверить обновления","updaterBtn":"Обновить до v{version} в 1 клик","updaterUpdating":"Установка обновления…","updaterSuccess":"✅ Плагин успешно обновлён! Перезапустите DSH для применения.","updaterFailed":"❌ Не удалось проверить/обновить плагин. Проверьте сеть или логи сервера.","badgeUpdateAvailable":"Доступно обновление","badgeUpToDate":"Актуальная версия","secTranslator":"🌐 Перевод сообщений ассистента","secTranslatorDesc":"Перевод ответов модели в чате на русский язык.","translateEngine":"Движок перевода","engineOff":"Выключен (по умолчанию, без сетевых вызовов)","engineLocal":"Локальный LibreTranslate (приватно, ~600 МБ RAM)","engineGoogle":"Google Translate (онлайн)","googleWarn":"⚠️ Текст сообщений передаётся на публичные серверы Google.","localStatusRunning":"🟢 Контейнер LibreTranslate активен","localStatusStopped":"⚪ Контейнер LibreTranslate не запущен","localStartBtn":"Запустить LibreTranslate в Docker (~600 МБ RAM)","localStarting":"Запуск контейнера…","localInfo":"LibreTranslate в Docker: изолированно, ~600 МБ RAM.","secOverrides":"✍️ Пользовательские переопределения (Custom Overrides)","secOverridesDesc":"Замена любой формулировки интерфейса DSH на собственную.","overrideKeyPlaceholder":"Ключ (например, common.back или settings.title)","overrideValuePlaceholder":"Ваш русский перевод","overrideAddBtn":"Добавить","overrideEmpty":"Переопределений пока нет.","overrideDelete":"Удалить","overrideTip":"💡 Зажмите Alt и кликните на элемент UI для быстрой правки.","secSupport":"📊 Покрытие экосистемы и поддержка","secSupportDesc":"100% UI-покрытие ядра и плагинов DSH.","statNamespaces":"Пространств имён","statCoreKeys":"Ключей ядра","statPluginKeys":"Ключей плагинов","overridesCount":"Своих переопределений","statusLoading":"Настройки загружаются…","statusUnavailable":"Настройки недоступны на этом хосте","translateTurn":"Перевести на русский","reportIssue":"Сообщить о неточности перевода","exportMdHint":"Экспорт диалога в Markdown доступен по кнопке [ 📥 MD ] в шапке сессии.","translateTurnHint":"Перевод ответов ассистента на русский доступен по кнопке [ RU ↗ ] на блоках сообщений.","inspectorToggleOff":"🔍 Включить инспектор перевода (Alt+I)","inspectorToggleOn":"🔍 Инспектор перевода (включён)","exportJsonBtn":"📤 Экспорт JSON","importJsonBtn":"📥 Импорт JSON","overridesCopied":"Оверрайды скопированы в буфер обмена как JSON!","overridesImportPrompt":"Вставьте JSON с оверрайдами:","overridesImportError":"Ошибка разбора JSON: "}
|
|
398
|
+
const CARD_EN = {cardTitle: 'Russian localization',cardSub: 'Interface language, typography, keyboard layout',badgeRu: '🟢 RU active',badgeEn: '⚪ EN active',badgeCoverage: '🟢 100% (7,902 keys)',badgeSmartUx: '⚡ Smart UX active',secLanguage: '🌐 Interface language',secLanguageDesc: 'Switch the DSH interface language to Russian natively, without reloading the page.',enabled: 'Russian language enabled',enabledDesc: 'Switches the DeepSeek Harness interface to Russian.',quickSwitchNote: 'The RU ⇄ EN quick switch is available in the session header next to the dialog buttons.',secTypography: '✍️ Smart typography & input (Smart UX)',secTypographyDesc: 'Russian typography rules applied while you chat and type.',typography: 'Output typography',typographyDesc: 'Guillemets «», em dashes — instead of hyphens, non-breaking spaces.',yo: 'Letter «ё»',yoDesc: 'Restores «ё» in frequent words (ещё, идёт, etc.).',liveInput: 'Live input typography',liveInputDesc: 'Guillemets «», em dashes — and non-breaking spaces before sending.',slashAliases: 'Russian command aliases',slashAliasesDesc: 'Commands: /цель, /сжать, /план, /справка, /память.',altLHintText: 'Converts the keyboard layout of the current field (ghbdtn ⇄ привет).',secAgentPrompt: '🤖 Agent system prompt',secAgentPromptDesc: 'An official DSH systemPrompt extension section for conversing in Russian.',agentPrompt: 'Russian agent prompt',agentPromptDesc: 'Adds an instruction to reply in Russian in the selected style to the system prompt.',agentPromptPreset: 'Reply style',presetExpert: 'Technical expert (strict terminology, clean code)',presetWriter: 'Technical writer (Markdown, tables, GOST)',presetConcise: 'Concise mode (brief, no fluff)',presetReviewer: 'Code reviewer (bug hunting, security, audit)',presetArchitect: 'System architect (contracts, scalability)',presetTutor: 'Mentor (clear explanations, step-by-step)',secUpdater: '🔄 Language pack updates',secUpdaterDesc: 'Check for new releases in the npm registry and update in one click.',updaterCurrent: 'Current version: v{version}',updaterLatest: 'New version available: v{version}',updaterUpToDate: 'You are on the latest version',updaterChecking: 'Checking…',updaterCheckBtn: 'Check for updates',updaterBtn: 'Update to v{version} in 1 click',updaterUpdating: 'Installing update…',updaterSuccess: '✅ Plugin updated successfully! Restart DSH to apply.',updaterFailed: '❌ Failed to check or update the plugin. Check your network or server logs.',badgeUpdateAvailable: 'Update available',badgeUpToDate: 'Up to date',secTranslator: '🌐 Assistant message translation',secTranslatorDesc: 'Translate model replies in chat into Russian.',translateEngine: 'Translation engine',engineOff: 'Off (default, no network calls)',engineLocal: 'Local LibreTranslate (private, ~600 MB RAM)',engineGoogle: 'Google Translate (online)',googleWarn: '⚠️ Message text is sent to public Google servers.',localStatusRunning: '🟢 LibreTranslate container is running',localStatusStopped: '⚪ LibreTranslate container is not running',localStartBtn: 'Start LibreTranslate in Docker (~600 MB RAM)',localStarting: 'Starting container…',localInfo: 'LibreTranslate in Docker: isolated, ~600 MB RAM.',secOverrides: '✍️ Custom overrides',secOverridesDesc: 'Replace any DSH interface wording with your own.',overrideKeyPlaceholder: 'Key (e.g. common.back or settings.title)',overrideValuePlaceholder: 'Your translation',overrideAddBtn: 'Add',overrideEmpty: 'No overrides yet.',overrideDelete: 'Delete',overrideTip: '💡 Hold Alt and click a UI element for quick editing.',secSupport: '📊 Ecosystem coverage & support',secSupportDesc: '100% UI coverage of the DSH core and plugins.',inspectorToggleOff: '🔍 Enable translation inspector (Alt+I)',inspectorToggleOn: '🔍 Translation inspector (on)',exportJsonBtn: '📤 Export JSON',importJsonBtn: '📥 Import JSON',overridesCopied: 'Overrides copied to clipboard as JSON!',overridesImportPrompt: 'Paste overrides JSON:',overridesImportError: 'JSON parse error: ',statNamespaces: 'Namespaces',statCoreKeys: 'Core keys',statPluginKeys: 'Plugin keys',overridesCount: 'Custom overrides',statusLoading: 'Loading settings…',statusUnavailable: 'Settings unavailable on this host',translateTurn: 'Translate to Russian',reportIssue: 'Report a translation issue',exportMdHint: 'Export the dialog to Markdown via the [ 📥 MD ] button in the session header.',translateTurnHint: 'Translate assistant replies to Russian via the [ RU ↗ ] button on message blocks.',}
|
|
397
399
|
const SETTINGS_NS_NAME = 'russian-lang'
|
|
398
400
|
const FORM_NS = 'dsh-russian-lang'
|
|
399
401
|
const LEGACY_NS = 'russian-lang'
|
|
@@ -411,6 +413,7 @@ const scope = (rawScope && typeof rawScope.getSnapshot === 'function') ? rawScop
|
|
|
411
413
|
const legacyFlagMode = !(ctx && ctx.configForms)
|
|
412
414
|
for (const ns of Object.keys(RU)) {ctx.effect(() => {try { return ctx.locale.register(ns, 'ru', RU[ns]) } catch (err) { return () => {} }}, 'dsh-russian-lang: ' + ns)
|
|
413
415
|
}
|
|
416
|
+
ctx.effect(() => {try { return ctx.locale.register(SETTINGS_NS_NAME, 'en', CARD_EN) } catch (err) { return () => {} }}, 'dsh-russian-lang: russian-lang-en')
|
|
414
417
|
const registerDictMap = (dictMap) => {if (!dictMap || typeof dictMap !== 'object') return
|
|
415
418
|
for (const ns of Object.keys(dictMap)) {if (ns === 'zhRu' || ns === 'plugins' || ns === 'core') continue
|
|
416
419
|
const dict = dictMap[ns]
|
|
@@ -1129,7 +1132,7 @@ setTimeout(() => { copyKeyBtn.textContent = '📋' }, 1500)
|
|
|
1129
1132
|
}}
|
|
1130
1133
|
const copyReportBtn = modal.querySelector('.rl-copy-report-btn')
|
|
1131
1134
|
if (copyReportBtn) {copyReportBtn.onclick = () => {const snippet = typeof generateBugReportSnippet === 'function'
|
|
1132
|
-
? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.
|
|
1135
|
+
? generateBugReportSnippet({ key: k, ns, original: origText || rawText, current: rawText, override: textarea.value.trim(), pkgVersion: '0.3.19' })
|
|
1133
1136
|
: ('### Репорт: ' + (k || rawText) + '\n- Исходный: ' + (origText || rawText) + '\n- Перевод: ' + textarea.value.trim())
|
|
1134
1137
|
try { navigator.clipboard.writeText(snippet) } catch (err) { void err; }
|
|
1135
1138
|
copyReportBtn.textContent = '✓ Скопировано'
|
|
@@ -1417,7 +1420,7 @@ const [snap, setSnap] = React.useState(() => (scope && scope.getSnapshot ? scope
|
|
|
1417
1420
|
const [ruActive, setRuActive] = React.useState(() => { try { return runtime.getLocale().active === 'ru' } catch (e) { return false } })
|
|
1418
1421
|
const [typo, setTypoState] = React.useState(() =>
|
|
1419
1422
|
(snap.value && snap.value.typography) || {})
|
|
1420
|
-
const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.
|
|
1423
|
+
const [upStatus, setUpStatus] = React.useState({currentVersion: '0.3.19',latestVersion: undefined,updateAvailable: false
|
|
1421
1424
|
})
|
|
1422
1425
|
const [upLoading, setUpLoading] = React.useState(false)
|
|
1423
1426
|
const [upMsg, setUpMsg] = React.useState(null)
|
|
@@ -1510,7 +1513,7 @@ scope.set('agentPromptPreset', val)
|
|
|
1510
1513
|
} catch (err) { console.warn('dsh-russian-lang: set agentPromptPreset failed', err) }}},React.createElement('option', { value: 'technical_expert' }, t('presetExpert')),React.createElement('option', { value: 'tech_writer' }, t('presetWriter')),React.createElement('option', { value: 'concise' }, t('presetConcise')),React.createElement('option', { value: 'code_reviewer' }, t('presetReviewer')),React.createElement('option', { value: 'architect' }, t('presetArchitect')),React.createElement('option', { value: 'tutor' }, t('presetTutor')))),presetInfo ? React.createElement('div', { className: 'rl-preview-box' },React.createElement('div', { style: { fontWeight: 600, marginBottom: '4px', color: 'var(--dsw-alias-label-primary)' } }, '💬 ' + presetInfo.label + ':'),presetInfo.text
|
|
1511
1514
|
) : null
|
|
1512
1515
|
) : null
|
|
1513
|
-
),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secUpdater')),React.createElement('span', { className: 'rl-badge ' + (upStatus.updateAvailable ? 'rl-badge-warn' : 'rl-badge-ok') },upStatus.updateAvailable ? t('badgeUpdateAvailable') : t('badgeUpToDate'))),React.createElement('div', { className: 'rl-section-desc' }, t('secUpdaterDesc')),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '8px' } },React.createElement('div', null,React.createElement('div', { style: { fontWeight: 600, color: 'var(--dsw-alias-label-primary)' } },t('updaterCurrent').replace('{version}', upStatus.currentVersion || '0.3.
|
|
1516
|
+
),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secUpdater')),React.createElement('span', { className: 'rl-badge ' + (upStatus.updateAvailable ? 'rl-badge-warn' : 'rl-badge-ok') },upStatus.updateAvailable ? t('badgeUpdateAvailable') : t('badgeUpToDate'))),React.createElement('div', { className: 'rl-section-desc' }, t('secUpdaterDesc')),React.createElement('div', { className: 'rl-item-card' },React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '8px' } },React.createElement('div', null,React.createElement('div', { style: { fontWeight: 600, color: 'var(--dsw-alias-label-primary)' } },t('updaterCurrent').replace('{version}', upStatus.currentVersion || '0.3.19')),upStatus.updateAvailable
|
|
1514
1517
|
? React.createElement('div', { style: { color: 'var(--dsw-alias-state-warning-primary)', marginTop: '2px', fontWeight: 500 } },t('updaterLatest').replace('{version}', upStatus.latestVersion || ''))
|
|
1515
1518
|
: React.createElement('div', { style: { color: 'var(--dsw-alias-label-secondary)', marginTop: '2px' } },t('updaterUpToDate'))),upStatus.updateAvailable
|
|
1516
1519
|
? React.createElement('button', {type: 'button',className: 'rl-btn rl-btn-primary',disabled: upLoading,onClick: triggerUpdate,}, upLoading ? t('updaterUpdating') : t('updaterBtn').replace('{version}', upStatus.latestVersion || ''))
|
|
@@ -1541,14 +1544,14 @@ React.createElement('div', {key: k,style: { display: 'flex', justifyContent: 'sp
|
|
|
1541
1544
|
delete next[k]
|
|
1542
1545
|
try { scope.set('overrides', next) } catch (_e) { void _e }},}, t('overrideDelete'))))),React.createElement('div', { className: 'rl-hint-text', style: { marginTop: '8px' } }, t('overrideTip')),React.createElement('div', { style: { display: 'flex', gap: '8px', alignItems: 'center', flexWrap: 'wrap', marginTop: '10px' } },React.createElement('button', {type: 'button',className: 'rl-btn' + (isInspectorActive ? ' rl-btn-primary' : ''),onClick: () => {isInspectorActive = !isInspectorActive
|
|
1543
1546
|
updateInspectorPill()
|
|
1544
|
-
},}, isInspectorActive ? '
|
|
1545
|
-
alert('
|
|
1546
|
-
} catch (err) { void err; }}}, '
|
|
1547
|
+
},}, isInspectorActive ? t('inspectorToggleOn') : t('inspectorToggleOff')),React.createElement('button', {type: 'button',className: 'rl-btn',style: { fontSize: '11px' },onClick: () => {try {navigator.clipboard.writeText(JSON.stringify(overrides, null, 2))
|
|
1548
|
+
alert(t('overridesCopied'))
|
|
1549
|
+
} catch (err) { void err; }}}, t('exportJsonBtn')),React.createElement('button', {type: 'button',className: 'rl-btn',style: { fontSize: '11px' },onClick: () => {const input = typeof window !== 'undefined' && typeof window.prompt === 'function' ? window.prompt(t('overridesImportPrompt')) : null
|
|
1547
1550
|
if (!input) return
|
|
1548
1551
|
try {const parsed = JSON.parse(input)
|
|
1549
1552
|
if (parsed && typeof parsed === 'object') {const merged = Object.assign({}, overrides, parsed)
|
|
1550
1553
|
scope.set('overrides', merged)
|
|
1551
|
-
}} catch (err) { alert('
|
|
1554
|
+
}} catch (err) { alert(t('overridesImportError') + (err && err.message || err)) }}}, t('importJsonBtn')))),React.createElement('div', { className: 'rl-section-card' },React.createElement('div', { className: 'rl-section-title' },React.createElement('span', null, t('secSupport')),React.createElement('span', { className: 'rl-badge rl-badge-ok' }, '🟢 100.0%')),React.createElement('div', { className: 'rl-section-desc' }, t('secSupportDesc')),React.createElement('div', { className: 'rl-grid-3' },React.createElement('div', { className: 'rl-stat-box' },React.createElement('div', { className: 'rl-stat-val' }, '113'),React.createElement('div', { className: 'rl-stat-label' }, t('statNamespaces'))),React.createElement('div', { className: 'rl-stat-box' },React.createElement('div', { className: 'rl-stat-val' }, '1 225'),React.createElement('div', { className: 'rl-stat-label' }, t('statCoreKeys'))),React.createElement('div', { className: 'rl-stat-box' },React.createElement('div', { className: 'rl-stat-val' }, '5 924'),React.createElement('div', { className: 'rl-stat-label' }, t('statPluginKeys')))),React.createElement('div', { className: 'rl-actions-row' },React.createElement('a', {href: makeIssueUrl({}, '0.3.19'),target: '_blank',rel: 'noopener noreferrer',className: 'rl-btn rl-btn-primary'
|
|
1552
1555
|
}, '💬 ' + t('reportIssue')),React.createElement('div', { className: 'rl-hint-text', style: { flex: 1 } },t('exportMdHint')))))))
|
|
1553
1556
|
}
|
|
1554
1557
|
const RL_CSS = ['.rl-modal-mask{position:fixed;top:0;left:0;width:100vw;height:100vh;background:var(--dsw-alias-bg-mask);display:flex;align-items:center;justify-content:center;z-index:99999;backdrop-filter:blur(2px)}.rl-modal-box{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;width:calc(100vw - 40px);max-width:500px;box-shadow:var(--dsw-alias-shadow-l3);display:flex;flex-direction:column;gap:12px;padding:18px}.rl-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;overflow:hidden;transition:border-color .15s ease}.rl-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;display:flex;align-items:center;gap:12px;padding:14px 18px}.rl-head:hover{background:var(--dsw-alias-bg-layer-2)}.rl-head-main{flex:1;display:flex;flex-direction:column;gap:4px}.rl-title{color:var(--dsw-alias-label-primary);font-size:16px;font-weight:600;line-height:1.4;display:flex;align-items:center;flex-wrap:wrap;gap:8px}.rl-sub{color:var(--dsw-alias-label-secondary);font-size:13px}.rl-chev{margin-left:auto;flex:none;color:var(--dsw-alias-label-tertiary);display:inline-flex;transition:transform .16s}.rl-chev-open{transform:rotate(180deg)}.rl-body{border-top:1px solid var(--dsw-alias-border-l2);padding:18px}.rl-page{display:flex;flex-direction:column;gap:16px;max-width:960px}.rl-section-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:10px;padding:16px 18px;display:flex;flex-direction:column;gap:12px}.rl-section-title{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary);display:flex;align-items:center;justify-content:space-between;gap:8px}.rl-section-desc{font-size:12px;color:var(--dsw-alias-label-secondary);margin-top:-4px;line-height:1.4}.rl-grid-2{display:grid;grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));gap:10px}.rl-grid-3{display:grid;grid-template-columns:repeat(auto-fit, minmax(140px, 1fr));gap:10px}.rl-badge{font-size:11px;padding:2px 8px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);display:inline-flex;align-items:center;gap:4px;font-weight:500}.rl-badge-ok{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 8%, transparent)}.rl-badge-warn{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:color-mix(in srgb, var(--dsw-alias-state-warning-primary) 8%, transparent)}.rl-badge-dim{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-3)}.rl-item-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;padding:10px 12px;display:flex;flex-direction:column;gap:4px}.rl-item-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.rl-item-label{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary);display:flex;align-items:center;gap:8px;cursor:pointer}.rl-item-desc{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.4}.rl-stat-box{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;padding:10px 12px;display:flex;flex-direction:column;gap:2px}.rl-stat-val{font-size:16px;font-weight:700;color:var(--dsw-alias-label-primary)}.rl-stat-label{font-size:11px;color:var(--dsw-alias-label-secondary);letter-spacing:0.3px}.rl-hotkey-box{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;padding:10px 12px;font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.4;display:flex;align-items:flex-start;gap:10px}.rl-hotkey-tag{font-size:11px;font-weight:700;padding:2px 6px;border-radius:4px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);white-space:nowrap}.rl-preview-box{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;padding:10px 12px;font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.4}.rl-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:6px 12px;font-size:12px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font-weight:500;display:inline-flex;align-items:center;justify-content:center;gap:6px;transition:all .15s ease;text-decoration:none}.rl-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-1);border-color:var(--dsw-alias-label-secondary)}.rl-btn-primary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3);border-color:transparent}.rl-btn-primary:hover:not(:disabled){opacity:0.9}.rl-select{height:32px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:6px;padding:0 8px;font-size:12px;outline:none;width:100%;max-width:380px}.rl-select:focus{border-color:var(--dsw-alias-state-brand-primary)}.rl-check{width:16px;height:16px;accent-color:var(--dsw-alias-label-primary);cursor:pointer;flex-shrink:0}.rl-hint-text{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.4}.rl-actions-row{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding-top:4px}.rl-lang-chip{appearance:none;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary);font-size:11px;font-weight:700;padding:2px 7px;border-radius:6px;display:inline-flex;align-items:center;transition:all .15s;margin:0 4px}.rl-lang-chip:hover{border-color:var(--dsw-alias-label-primary);color:var(--dsw-alias-label-primary)}.rl-lang-chip-active{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.rl-action-btn{appearance:none;background:0 0;border:1px solid transparent;border-radius:4px;color:var(--dsw-alias-label-secondary);cursor:pointer;font-size:11px;padding:2px 5px;display:inline-flex;align-items:center;transition:all .15s}.rl-action-btn:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3)}.rl-action-btn-active{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l2)}.rl-turn-translation{margin:8px 0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:8px;font-size:13px;line-height:1.5}.rl-trans-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;border-bottom:1px solid var(--dsw-alias-border-l2);padding-bottom:4px}.rl-trans-title{font-size:11px;font-weight:600;color:var(--dsw-alias-label-secondary);text-transform:uppercase;letter-spacing:0.5px}.rl-trans-tools{display:flex;gap:4px}.rl-trans-btn{appearance:none;background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);cursor:pointer;padding:2px 7px;border-radius:4px;font-size:11px}.rl-trans-btn:hover{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}.rl-trans-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;user-select:text}.rl-export-md-btn{appearance:none;border:1px solid var(--dsw-alias-border-l2);height:32px;color:var(--dsw-alias-label-primary);cursor:pointer;background:transparent;border-radius:18px;justify-content:center;align-items:center;gap:4px;padding:6px 12px;font-size:13px;font-weight:500;display:inline-flex;white-space:nowrap;margin-left:6px;transition:all .15s ease}.rl-export-md-btn:hover{background:var(--dsw-alias-interactive-bg-hover)}.rl-inspector-hover{position:absolute;pointer-events:none;border:2px solid var(--dsw-alias-state-brand-primary);border-radius:4px;background:color-mix(in srgb,var(--dsw-alias-state-brand-primary) 12%,transparent);box-shadow:0 0 10px color-mix(in srgb,var(--dsw-alias-state-brand-primary) 30%,transparent);z-index:99998;transition:all .06s ease;display:none}.rl-inspector-badge{position:absolute;bottom:calc(100% + 4px);left:0;background:var(--dsw-alias-state-brand-primary);color:var(--dsw-alias-label-inverse);font-size:10px;font-weight:600;padding:2px 6px;border-radius:4px;white-space:nowrap;box-shadow:var(--dsw-alias-shadow-l2);pointer-events:none}.rl-inspector-pill{position:fixed;bottom:18px;right:18px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l1);border-radius:20px;padding:6px 12px;box-shadow:var(--dsw-alias-shadow-l3);display:none;align-items:center;gap:8px;font-size:12px;z-index:99990;user-select:none}.rl-inspector-pill-active{border-color:var(--dsw-alias-state-brand-primary);box-shadow:0 0 12px color-mix(in srgb,var(--dsw-alias-state-brand-primary) 35%,transparent)}'
|
package/lib/client.template.js
CHANGED
|
@@ -33,6 +33,94 @@ window.__ModuleLoader__.load({
|
|
|
33
33
|
// Подписи собственной карточки настроек (namespace russian-lang).
|
|
34
34
|
RU['russian-lang'] = /*__CARD_RU__*/{}
|
|
35
35
|
|
|
36
|
+
const CARD_EN = {
|
|
37
|
+
cardTitle: 'Russian localization',
|
|
38
|
+
cardSub: 'Interface language, typography, keyboard layout',
|
|
39
|
+
badgeRu: '🟢 RU active',
|
|
40
|
+
badgeEn: '⚪ EN active',
|
|
41
|
+
badgeCoverage: '🟢 100% (7,902 keys)',
|
|
42
|
+
badgeSmartUx: '⚡ Smart UX active',
|
|
43
|
+
secLanguage: '🌐 Interface language',
|
|
44
|
+
secLanguageDesc: 'Switch the DSH interface language to Russian natively, without reloading the page.',
|
|
45
|
+
enabled: 'Russian language enabled',
|
|
46
|
+
enabledDesc: 'Switches the DeepSeek Harness interface to Russian.',
|
|
47
|
+
quickSwitchNote: 'The RU ⇄ EN quick switch is available in the session header next to the dialog buttons.',
|
|
48
|
+
secTypography: '✍️ Smart typography & input (Smart UX)',
|
|
49
|
+
secTypographyDesc: 'Russian typography rules applied while you chat and type.',
|
|
50
|
+
typography: 'Output typography',
|
|
51
|
+
typographyDesc: 'Guillemets «», em dashes — instead of hyphens, non-breaking spaces.',
|
|
52
|
+
yo: 'Letter «ё»',
|
|
53
|
+
yoDesc: 'Restores «ё» in frequent words (ещё, идёт, etc.).',
|
|
54
|
+
liveInput: 'Live input typography',
|
|
55
|
+
liveInputDesc: 'Guillemets «», em dashes — and non-breaking spaces before sending.',
|
|
56
|
+
slashAliases: 'Russian command aliases',
|
|
57
|
+
slashAliasesDesc: 'Commands: /цель, /сжать, /план, /справка, /память.',
|
|
58
|
+
altLHintText: 'Converts the keyboard layout of the current field (ghbdtn ⇄ привет).',
|
|
59
|
+
secAgentPrompt: '🤖 Agent system prompt',
|
|
60
|
+
secAgentPromptDesc: 'An official DSH systemPrompt extension section for conversing in Russian.',
|
|
61
|
+
agentPrompt: 'Russian agent prompt',
|
|
62
|
+
agentPromptDesc: 'Adds an instruction to reply in Russian in the selected style to the system prompt.',
|
|
63
|
+
agentPromptPreset: 'Reply style',
|
|
64
|
+
presetExpert: 'Technical expert (strict terminology, clean code)',
|
|
65
|
+
presetWriter: 'Technical writer (Markdown, tables, GOST)',
|
|
66
|
+
presetConcise: 'Concise mode (brief, no fluff)',
|
|
67
|
+
presetReviewer: 'Code reviewer (bug hunting, security, audit)',
|
|
68
|
+
presetArchitect: 'System architect (contracts, scalability)',
|
|
69
|
+
presetTutor: 'Mentor (clear explanations, step-by-step)',
|
|
70
|
+
secUpdater: '🔄 Language pack updates',
|
|
71
|
+
secUpdaterDesc: 'Check for new releases in the npm registry and update in one click.',
|
|
72
|
+
updaterCurrent: 'Current version: v{version}',
|
|
73
|
+
updaterLatest: 'New version available: v{version}',
|
|
74
|
+
updaterUpToDate: 'You are on the latest version',
|
|
75
|
+
updaterChecking: 'Checking…',
|
|
76
|
+
updaterCheckBtn: 'Check for updates',
|
|
77
|
+
updaterBtn: 'Update to v{version} in 1 click',
|
|
78
|
+
updaterUpdating: 'Installing update…',
|
|
79
|
+
updaterSuccess: '✅ Plugin updated successfully! Restart DSH to apply.',
|
|
80
|
+
updaterFailed: '❌ Failed to check or update the plugin. Check your network or server logs.',
|
|
81
|
+
badgeUpdateAvailable: 'Update available',
|
|
82
|
+
badgeUpToDate: 'Up to date',
|
|
83
|
+
secTranslator: '🌐 Assistant message translation',
|
|
84
|
+
secTranslatorDesc: 'Translate model replies in chat into Russian.',
|
|
85
|
+
translateEngine: 'Translation engine',
|
|
86
|
+
engineOff: 'Off (default, no network calls)',
|
|
87
|
+
engineLocal: 'Local LibreTranslate (private, ~600 MB RAM)',
|
|
88
|
+
engineGoogle: 'Google Translate (online)',
|
|
89
|
+
googleWarn: '⚠️ Message text is sent to public Google servers.',
|
|
90
|
+
localStatusRunning: '🟢 LibreTranslate container is running',
|
|
91
|
+
localStatusStopped: '⚪ LibreTranslate container is not running',
|
|
92
|
+
localStartBtn: 'Start LibreTranslate in Docker (~600 MB RAM)',
|
|
93
|
+
localStarting: 'Starting container…',
|
|
94
|
+
localInfo: 'LibreTranslate in Docker: isolated, ~600 MB RAM.',
|
|
95
|
+
secOverrides: '✍️ Custom overrides',
|
|
96
|
+
secOverridesDesc: 'Replace any DSH interface wording with your own.',
|
|
97
|
+
overrideKeyPlaceholder: 'Key (e.g. common.back or settings.title)',
|
|
98
|
+
overrideValuePlaceholder: 'Your translation',
|
|
99
|
+
overrideAddBtn: 'Add',
|
|
100
|
+
overrideEmpty: 'No overrides yet.',
|
|
101
|
+
overrideDelete: 'Delete',
|
|
102
|
+
overrideTip: '💡 Hold Alt and click a UI element for quick editing.',
|
|
103
|
+
secSupport: '📊 Ecosystem coverage & support',
|
|
104
|
+
secSupportDesc: '100% UI coverage of the DSH core and plugins.',
|
|
105
|
+
inspectorToggleOff: '🔍 Enable translation inspector (Alt+I)',
|
|
106
|
+
inspectorToggleOn: '🔍 Translation inspector (on)',
|
|
107
|
+
exportJsonBtn: '📤 Export JSON',
|
|
108
|
+
importJsonBtn: '📥 Import JSON',
|
|
109
|
+
overridesCopied: 'Overrides copied to clipboard as JSON!',
|
|
110
|
+
overridesImportPrompt: 'Paste overrides JSON:',
|
|
111
|
+
overridesImportError: 'JSON parse error: ',
|
|
112
|
+
statNamespaces: 'Namespaces',
|
|
113
|
+
statCoreKeys: 'Core keys',
|
|
114
|
+
statPluginKeys: 'Plugin keys',
|
|
115
|
+
overridesCount: 'Custom overrides',
|
|
116
|
+
statusLoading: 'Loading settings…',
|
|
117
|
+
statusUnavailable: 'Settings unavailable on this host',
|
|
118
|
+
translateTurn: 'Translate to Russian',
|
|
119
|
+
reportIssue: 'Report a translation issue',
|
|
120
|
+
exportMdHint: 'Export the dialog to Markdown via the [ 📥 MD ] button in the session header.',
|
|
121
|
+
translateTurnHint: 'Translate assistant replies to Russian via the [ RU ↗ ] button on message blocks.',
|
|
122
|
+
}
|
|
123
|
+
|
|
36
124
|
const SETTINGS_NS_NAME = 'russian-lang'
|
|
37
125
|
// Namespace формы настроек: на 0.1.7+ это id записи загрузчика
|
|
38
126
|
// ('dsh-russian-lang' из cordis.patch.yml), а не имя, выбранное плагином
|
|
@@ -71,6 +159,13 @@ window.__ModuleLoader__.load({
|
|
|
71
159
|
}, 'dsh-russian-lang: ' + ns)
|
|
72
160
|
}
|
|
73
161
|
|
|
162
|
+
// 1-en. Английский фолбэк собственной карточки (см. CARD_EN выше):
|
|
163
|
+
// пара ('russian-lang', 'en') никем не занята, конфликта с 'ru' нет.
|
|
164
|
+
ctx.effect(() => {
|
|
165
|
+
try { return ctx.locale.register(SETTINGS_NS_NAME, 'en', CARD_EN) }
|
|
166
|
+
catch (err) { return () => {} }
|
|
167
|
+
}, 'dsh-russian-lang: russian-lang-en')
|
|
168
|
+
|
|
74
169
|
// 1a. Двухфазная быстрая загрузка и клиентский кэш словарей (CacheStorage / localStorage) (Issue #284, #285)
|
|
75
170
|
const registerDictMap = (dictMap) => {
|
|
76
171
|
if (!dictMap || typeof dictMap !== 'object') return
|
|
@@ -2575,7 +2670,7 @@ window.__ModuleLoader__.load({
|
|
|
2575
2670
|
isInspectorActive = !isInspectorActive
|
|
2576
2671
|
updateInspectorPill()
|
|
2577
2672
|
},
|
|
2578
|
-
}, isInspectorActive ? '
|
|
2673
|
+
}, isInspectorActive ? t('inspectorToggleOn') : t('inspectorToggleOff')),
|
|
2579
2674
|
React.createElement('button', {
|
|
2580
2675
|
type: 'button',
|
|
2581
2676
|
className: 'rl-btn',
|
|
@@ -2583,16 +2678,16 @@ window.__ModuleLoader__.load({
|
|
|
2583
2678
|
onClick: () => {
|
|
2584
2679
|
try {
|
|
2585
2680
|
navigator.clipboard.writeText(JSON.stringify(overrides, null, 2))
|
|
2586
|
-
alert('
|
|
2681
|
+
alert(t('overridesCopied'))
|
|
2587
2682
|
} catch (err) { /* bestEffort */ void err; }
|
|
2588
2683
|
}
|
|
2589
|
-
}, '
|
|
2684
|
+
}, t('exportJsonBtn')),
|
|
2590
2685
|
React.createElement('button', {
|
|
2591
2686
|
type: 'button',
|
|
2592
2687
|
className: 'rl-btn',
|
|
2593
2688
|
style: { fontSize: '11px' },
|
|
2594
2689
|
onClick: () => {
|
|
2595
|
-
const input = typeof window !== 'undefined' && typeof window.prompt === 'function' ? window.prompt('
|
|
2690
|
+
const input = typeof window !== 'undefined' && typeof window.prompt === 'function' ? window.prompt(t('overridesImportPrompt')) : null
|
|
2596
2691
|
if (!input) return
|
|
2597
2692
|
try {
|
|
2598
2693
|
const parsed = JSON.parse(input)
|
|
@@ -2600,9 +2695,9 @@ window.__ModuleLoader__.load({
|
|
|
2600
2695
|
const merged = Object.assign({}, overrides, parsed)
|
|
2601
2696
|
scope.set('overrides', merged)
|
|
2602
2697
|
}
|
|
2603
|
-
} catch (err) { alert('
|
|
2698
|
+
} catch (err) { alert(t('overridesImportError') + (err && err.message || err)) }
|
|
2604
2699
|
}
|
|
2605
|
-
}, '
|
|
2700
|
+
}, t('importJsonBtn'))
|
|
2606
2701
|
)
|
|
2607
2702
|
),
|
|
2608
2703
|
|