@goodandready/dsh-russian-lang 0.3.0 → 0.3.3
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 +20 -5
- package/README.ru.md +20 -5
- package/bin/dsh-i18n.mjs +247 -0
- package/lib/client.js +2932 -2360
- package/lib/index.js +89 -15
- package/lib/locales/core.json +1 -1
- package/lib/locales/plugins/03-dshmarket.json +1 -1
- package/lib/locales/plugins/17-dsh-context.json +1 -1
- package/lib/locales/plugins/26-config-manager.json +1 -1
- package/lib/locales/plugins/29-config-manager-recovery.json +1 -1
- package/lib/locales/plugins/32-vision-bridge.json +1 -1
- package/lib/locales/plugins/33-messenger-gateway.json +1 -1
- package/lib/locales/plugins/34-key-rotation.json +1 -1
- package/lib/locales/plugins/35-gitea.json +1 -1
- package/lib/locales/plugins/36-cost-meter.json +1 -1
- package/lib/locales/plugins/37-time-machine.json +1 -1
- package/lib/locales/plugins/40-clinebot.json +1 -1
- package/lib/locales/plugins/41-lanmode.json +1 -1
- package/lib/locales/plugins/42-plugin-console.json +1 -1
- package/lib/locales/plugins/48-task-tracker.json +1 -1
- package/lib/locales/plugins/49-dsml-guard.json +1 -1
- package/lib/locales/plugins/54-workshop-market.json +1 -1
- package/lib/locales/plugins/56-cron.json +1 -1
- package/lib/locales/plugins/58-grok-xsearch.json +1 -1
- package/lib/locales/plugins/59-goal.json +1 -1
- package/lib/locales/plugins/60-model-sync.json +1 -1
- package/lib/locales/plugins/61-issue-reporter.json +1 -1
- package/lib/locales/plugins/62-model-search.json +1 -1
- package/lib/locales/plugins/63-agent-orchestrator.json +1 -1
- package/lib/locales/plugins/65-subscriptions.json +1 -1
- package/lib/locales/plugins/67-voice.json +1 -1
- package/lib/locales/plugins/68-image-gen.json +1 -1
- package/lib/locales/plugins/70-hypotheses.json +1 -1
- package/lib/locales/plugins/72-server-monitor.json +1 -1
- package/lib/locales/plugins/73-agentrouter.json +1 -1
- package/lib/locales/plugins/77-github-ops.json +1 -0
- package/lib/locales/plugins/78-web-gateway.json +1 -0
- package/lib/locales/plugins/79-approval-gate.json +1 -0
- package/lib/locales/plugins/80-ip-pool.json +1 -0
- package/lib/locales/plugins/81-ego-browser.json +1 -0
- package/lib/locales/plugins/82-effort-slider.json +1 -0
- package/lib/locales/zh-ru.json +1 -1
- package/lib/locales.js +50 -12
- package/lib/pure.js +306 -14
- package/lib/translator.js +55 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
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.
|
|
8
|
+
<a href="https://www.npmjs.com/package/@goodandready/dsh-russian-lang"><img src="https://img.shields.io/badge/npm-v0.3.3-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>
|
|
@@ -137,10 +137,25 @@ graph LR
|
|
|
137
137
|
* Показывает ненавязчивую плавающую подсказку над полем ввода;
|
|
138
138
|
* При нажатии горячей клавиши <kbd>Alt+L</kbd> мгновенно конвертирует набранный текст в правильную кириллицу.
|
|
139
139
|
|
|
140
|
-
### 6.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
140
|
+
### 6. 🔍 Интерактивный Инспектор перевода и живые оверрайды (<kbd>Alt+Click</kbd> / <kbd>Alt+I</kbd>)
|
|
141
|
+
Мощный встроенный инструмент для инспекции строк интерфейса и мгновенного редактирования переводов на лету:
|
|
142
|
+
* **Точечная инспекция (<kbd>Alt+Click</kbd>)**: зажмите клавишу <kbd>Alt</kbd> и кликните по любому элементу интерфейса (кнопке, пункту меню, лейблу), чтобы открыть модальное окно инспектора.
|
|
143
|
+
* **Постоянный режим (<kbd>Alt+I</kbd>)**: переключает интерфейс в интерактивный режим исследования — наведение курсора подсвечивает элементы аккуратной рамкой с бейджем источника (`Core`, `Plugin`, `DOM`, `Untranslated`), а в правом нижнем углу экрана отображается плавающий статус-виджет с кнопкой быстрого выхода.
|
|
144
|
+
* **Модальное окно инспектора**:
|
|
145
|
+
* Точное определение системного ключа, пространства имён и типа источника.
|
|
146
|
+
* Быстрое копирование ключа или готового стандартизированного Markdown-сниппета для баг-репорта в 1 клик.
|
|
147
|
+
* Поле ввода собственного перевода (оверрайда) с кнопкой **«Применить»**: мгновенно изменяет текст в DOM и сохраняет оверрайд в настройки (`russian-lang.overrides`) без перезагрузки страницы. Все аналогичные элементы интерфейса обновляются автоматически.
|
|
148
|
+
* Кнопка **«Сбросить»** для мгновенного отката к стандартному словарному переводу.
|
|
149
|
+
* **Управление в карточке настроек (Раздел 4.5)**:
|
|
150
|
+
* Кнопка быстрого включения/выключения инспектора (<kbd>Alt+I</kbd>).
|
|
151
|
+
* Кнопки **«Экспорт JSON»** и **«Импорт JSON»** для резервного копирования, переноса и синхронизации списка ваших оверрайдов между браузерами и рабочими профилями.
|
|
152
|
+
|
|
153
|
+
### 7. ⚡ Оптимизации производительности и архитектурная безопасность
|
|
154
|
+
* **Пакетная обработка MutationObserver**: оптимизированный наблюдатель мутаций интерфейса с адаптивным debounce (16 мс) предотвращает лавинообразные перерисовки и перерасчёт макета (reflow) при интенсивном рендеринге списков и стриминге LLM-токенов.
|
|
155
|
+
* **Защита HTTP-маршрутов словарей (405 Method Not Allowed)**: серверные эндпоинты `/dict/core`, `/dict/plugins`, `/dict/all`, `/dict/search` строго защищены и принимают только безопасные методы GET/HEAD.
|
|
156
|
+
* **Клиентский бандл с запасом**: оптимизированная архитектура и компактный бутстрап обеспечивают размер клиентского скрипта 153.7 KiB (запас более 10 KiB до предельного лимита).
|
|
157
|
+
|
|
158
|
+
### 8. 🔤 Нативная интеграция в ядро
|
|
144
159
|
Плагин нативно регистрирует пункт **«Русский»** в меню **Настройки → Общие → Язык**, переключает атрибут `<html lang="ru-RU">` и активирует встроенный браузерный словарь проверки орфографии (`spellcheck`).
|
|
145
160
|
|
|
146
161
|
---
|
package/README.ru.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
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.2
|
|
8
|
+
<a href="https://www.npmjs.com/package/@goodandready/dsh-russian-lang"><img src="https://img.shields.io/badge/npm-v0.3.2-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>
|
|
@@ -137,10 +137,25 @@ graph LR
|
|
|
137
137
|
* Показывает ненавязчивую плавающую подсказку над полем ввода;
|
|
138
138
|
* При нажатии горячей клавиши <kbd>Alt+L</kbd> мгновенно конвертирует набранный текст в правильную кириллицу.
|
|
139
139
|
|
|
140
|
-
### 6.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
140
|
+
### 6. 🔍 Интерактивный Инспектор перевода и живые оверрайды (<kbd>Alt+Click</kbd> / <kbd>Alt+I</kbd>)
|
|
141
|
+
Мощный встроенный инструмент для инспекции строк интерфейса и мгновенного редактирования переводов на лету:
|
|
142
|
+
* **Точечная инспекция (<kbd>Alt+Click</kbd>)**: зажмите клавишу <kbd>Alt</kbd> и кликните по любому элементу интерфейса (кнопке, пункту меню, лейблу), чтобы открыть модальное окно инспектора.
|
|
143
|
+
* **Постоянный режим (<kbd>Alt+I</kbd>)**: переключает интерфейс в интерактивный режим исследования — наведение курсора подсвечивает элементы аккуратной рамкой с бейджем источника (`Core`, `Plugin`, `DOM`, `Untranslated`), а в правом нижнем углу экрана отображается плавающий статус-виджет с кнопкой быстрого выхода.
|
|
144
|
+
* **Модальное окно инспектора**:
|
|
145
|
+
* Точное определение системного ключа, пространства имён и типа источника.
|
|
146
|
+
* Быстрое копирование ключа или готового стандартизированного Markdown-сниппета для баг-репорта в 1 клик.
|
|
147
|
+
* Поле ввода собственного перевода (оверрайда) с кнопкой **«Применить»**: мгновенно изменяет текст в DOM и сохраняет оверрайд в настройки (`russian-lang.overrides`) без перезагрузки страницы. Все аналогичные элементы интерфейса обновляются автоматически.
|
|
148
|
+
* Кнопка **«Сбросить»** для мгновенного отката к стандартному словарному переводу.
|
|
149
|
+
* **Управление в карточке настроек (Раздел 4.5)**:
|
|
150
|
+
* Кнопка быстрого включения/выключения инспектора (<kbd>Alt+I</kbd>).
|
|
151
|
+
* Кнопки **«Экспорт JSON»** и **«Импорт JSON»** для резервного копирования, переноса и синхронизации списка ваших оверрайдов между браузерами и рабочими профилями.
|
|
152
|
+
|
|
153
|
+
### 7. ⚡ Оптимизации производительности и архитектурная безопасность
|
|
154
|
+
* **Пакетная обработка MutationObserver**: оптимизированный наблюдатель мутаций интерфейса с адаптивным debounce (16 мс) предотвращает лавинообразные перерисовки и перерасчёт макета (reflow) при интенсивном рендеринге списков и стриминге LLM-токенов.
|
|
155
|
+
* **Защита HTTP-маршрутов словарей (405 Method Not Allowed)**: серверные эндпоинты `/dict/core`, `/dict/plugins`, `/dict/all`, `/dict/search` строго защищены и принимают только безопасные методы GET/HEAD.
|
|
156
|
+
* **Клиентский бандл с запасом**: оптимизированная архитектура и компактный бутстрап обеспечивают размер клиентского скрипта 153.7 KiB (запас более 10 KiB до предельного лимита).
|
|
157
|
+
|
|
158
|
+
### 8. 🔤 Нативная интеграция в ядро
|
|
144
159
|
Плагин нативно регистрирует пункт **«Русский»** в меню **Настройки → Общие → Язык**, переключает атрибут `<html lang="ru-RU">` и активирует встроенный браузерный словарь проверки орфографии (`spellcheck`).
|
|
145
160
|
|
|
146
161
|
---
|
package/bin/dsh-i18n.mjs
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const ROOT = path.resolve(__dirname, '..')
|
|
8
|
+
|
|
9
|
+
function getPkgVersion() {
|
|
10
|
+
try {
|
|
11
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'))
|
|
12
|
+
return pkg.version || '0.0.0'
|
|
13
|
+
} catch (_) {
|
|
14
|
+
return '0.0.0'
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function printHelp() {
|
|
19
|
+
console.log(`
|
|
20
|
+
dsh-i18n (v${getPkgVersion()}) — Инструмент локализации для плагинов DeepSeek Harness (DSH)
|
|
21
|
+
|
|
22
|
+
Использование:
|
|
23
|
+
dsh-i18n <команда> [опции]
|
|
24
|
+
|
|
25
|
+
Команды:
|
|
26
|
+
extract <каталог> Извлечь ключи локализации (t('...'), translate('...')) из исходников
|
|
27
|
+
validate <файл.json> Проверить файл словаря на синтаксис, плейсхолдеры и глоссарий
|
|
28
|
+
scaffold <имя-плагина> Создать структуру и стартовый шаблон локализации для плагина
|
|
29
|
+
|
|
30
|
+
Опции:
|
|
31
|
+
-o, --out <путь> Путь для сохранения результата
|
|
32
|
+
-n, --namespace <имя> Пространство имён по умолчанию (для extract)
|
|
33
|
+
-g, --glossary <путь> Пользовательский файл глоссария (для validate)
|
|
34
|
+
-h, --help Справка по командам
|
|
35
|
+
-v, --version Версия утилиты
|
|
36
|
+
|
|
37
|
+
Примеры:
|
|
38
|
+
dsh-i18n extract src/ -n my-plugin -o ru/my-plugin.json
|
|
39
|
+
dsh-i18n validate ru/my-plugin.json
|
|
40
|
+
dsh-i18n scaffold dsh-my-plugin --out ru/
|
|
41
|
+
`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function extractKeys(targetDir, defaultNs = 'common') {
|
|
45
|
+
const result = { [defaultNs]: {} }
|
|
46
|
+
const extensions = new Set(['.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx'])
|
|
47
|
+
|
|
48
|
+
function walk(dir) {
|
|
49
|
+
if (!fs.existsSync(dir)) return
|
|
50
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
const fullPath = path.join(dir, entry.name)
|
|
53
|
+
if (entry.isDirectory()) {
|
|
54
|
+
if (['node_modules', '.git', 'dist', 'build', '.worktrees'].includes(entry.name)) continue
|
|
55
|
+
walk(fullPath)
|
|
56
|
+
} else if (entry.isFile() && extensions.has(path.extname(entry.name))) {
|
|
57
|
+
const code = fs.readFileSync(fullPath, 'utf8')
|
|
58
|
+
const tMatches = code.matchAll(/\bt\(\s*['"`]([a-zA-Z0-9_.-]+)['"`]\s*\)/g)
|
|
59
|
+
for (const m of tMatches) {
|
|
60
|
+
result[defaultNs][m[1]] = result[defaultNs][m[1]] || ''
|
|
61
|
+
}
|
|
62
|
+
const transMatches = code.matchAll(/\btranslate\(\s*['"`]([a-zA-Z0-9_.-]+)['"`]\s*,\s*['"`]([a-zA-Z0-9_.-]+)['"`]/g)
|
|
63
|
+
for (const m of transMatches) {
|
|
64
|
+
const ns = m[1]
|
|
65
|
+
const key = m[2]
|
|
66
|
+
if (!result[ns]) result[ns] = {}
|
|
67
|
+
result[ns][key] = result[ns][key] || ''
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
walk(targetDir)
|
|
74
|
+
return result
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function validateDict(filePath, glossaryPath) {
|
|
78
|
+
if (!fs.existsSync(filePath)) {
|
|
79
|
+
console.error(`Ошибка: Файл не найден: ${filePath}`)
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let data
|
|
84
|
+
try {
|
|
85
|
+
data = JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.error(`Ошибка синтаксиса JSON в ${filePath}: ${err.message}`)
|
|
88
|
+
return false
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let errors = []
|
|
92
|
+
let warnings = []
|
|
93
|
+
let totalKeys = 0
|
|
94
|
+
|
|
95
|
+
let glossary = null
|
|
96
|
+
const gPath = glossaryPath || path.join(ROOT, 'glossary.json')
|
|
97
|
+
if (fs.existsSync(gPath)) {
|
|
98
|
+
try {
|
|
99
|
+
glossary = JSON.parse(fs.readFileSync(gPath, 'utf8')).terms || {}
|
|
100
|
+
} catch (_) {}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function checkString(ns, key, val) {
|
|
104
|
+
totalKeys++
|
|
105
|
+
if (typeof val !== 'string') {
|
|
106
|
+
errors.push(`[${ns}:${key}] Значение должно быть строкой, получено: ${typeof val}`)
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (/\{[\s]*\}/.test(val)) {
|
|
111
|
+
errors.push(`[${ns}:${key}] Обнаружен пустой плейсхолдер: "{}"`)
|
|
112
|
+
}
|
|
113
|
+
const openCount = (val.match(/\{/g) || []).length
|
|
114
|
+
const closeCount = (val.match(/\}/g) || []).length
|
|
115
|
+
if (openCount !== closeCount) {
|
|
116
|
+
errors.push(`[${ns}:${key}] Несовпадение количества открывающих и закрывающих фигурных скобок ({: ${openCount}, }: ${closeCount})`)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (glossary) {
|
|
120
|
+
const lower = val.toLowerCase()
|
|
121
|
+
for (const [term, info] of Object.entries(glossary)) {
|
|
122
|
+
if (Array.isArray(info.forbidden)) {
|
|
123
|
+
for (const forbidden of info.forbidden) {
|
|
124
|
+
const fLow = forbidden.toLowerCase()
|
|
125
|
+
const re = new RegExp(`\\b${fLow}\\b`, 'i')
|
|
126
|
+
if (re.test(lower)) {
|
|
127
|
+
warnings.push(`[${ns}:${key}] Запрещённый термин по глоссарию "${forbidden}" (рекомендуется: "${info.canonical}")`)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (const ns of Object.keys(data)) {
|
|
136
|
+
const section = data[ns]
|
|
137
|
+
if (typeof section === 'object' && section !== null) {
|
|
138
|
+
for (const [k, v] of Object.entries(section)) {
|
|
139
|
+
checkString(ns, k, v)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
console.log(`Проверено ключей: ${totalKeys}`)
|
|
145
|
+
if (warnings.length) {
|
|
146
|
+
console.warn(`Предупреждения (${warnings.length}):`)
|
|
147
|
+
for (const w of warnings) console.warn(` ⚠️ ${w}`)
|
|
148
|
+
}
|
|
149
|
+
if (errors.length) {
|
|
150
|
+
console.error(`Ошибки (${errors.length}):`)
|
|
151
|
+
for (const e of errors) console.error(` ❌ ${e}`)
|
|
152
|
+
return false
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log(`✓ Файл ${filePath} прошёл проверку валидности!`)
|
|
156
|
+
return true
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function scaffoldPlugin(pluginName, outDir = '.') {
|
|
160
|
+
const cleanName = pluginName.replace(/^@goodandready\//, '').replace(/^dsh-/, '')
|
|
161
|
+
const ns = cleanName
|
|
162
|
+
const template = {
|
|
163
|
+
[ns]: {
|
|
164
|
+
title: 'Название плагина',
|
|
165
|
+
description: 'Описание назначения и возможностей плагина',
|
|
166
|
+
settings: 'Настройки',
|
|
167
|
+
status: 'Статус',
|
|
168
|
+
save: 'Сохранить',
|
|
169
|
+
cancel: 'Отмена'
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const dir = path.resolve(outDir)
|
|
174
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
175
|
+
const targetFile = path.join(dir, `${cleanName}.json`)
|
|
176
|
+
|
|
177
|
+
fs.writeFileSync(targetFile, JSON.stringify(template, null, 2) + '\n', 'utf8')
|
|
178
|
+
console.log(`✓ Шаблон локализации создан: ${targetFile}`)
|
|
179
|
+
console.log(`
|
|
180
|
+
Пример подключения в коде плагина (Cordis / DSH):
|
|
181
|
+
|
|
182
|
+
import dictRu from './ru/${cleanName}.json' assert { type: 'json' }
|
|
183
|
+
|
|
184
|
+
export function apply(ctx) {
|
|
185
|
+
ctx.effect(() => {
|
|
186
|
+
return ctx.locale.register('${ns}', 'ru', dictRu['${ns}'])
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
`)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const args = process.argv.slice(2)
|
|
193
|
+
if (args.length === 0 || args.includes('-h') || args.includes('--help')) {
|
|
194
|
+
printHelp()
|
|
195
|
+
process.exit(0)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (args.includes('-v') || args.includes('--version')) {
|
|
199
|
+
console.log(getPkgVersion())
|
|
200
|
+
process.exit(0)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const command = args[0]
|
|
204
|
+
if (command === 'extract') {
|
|
205
|
+
const target = args[1] || '.'
|
|
206
|
+
let ns = 'common'
|
|
207
|
+
let out = null
|
|
208
|
+
for (let i = 2; i < args.length; i++) {
|
|
209
|
+
if ((args[i] === '-n' || args[i] === '--namespace') && args[i + 1]) ns = args[++i]
|
|
210
|
+
if ((args[i] === '-o' || args[i] === '--out') && args[i + 1]) out = args[++i]
|
|
211
|
+
}
|
|
212
|
+
const extracted = extractKeys(path.resolve(target), ns)
|
|
213
|
+
const jsonStr = JSON.stringify(extracted, null, 2)
|
|
214
|
+
if (out) {
|
|
215
|
+
fs.mkdirSync(path.dirname(path.resolve(out)), { recursive: true })
|
|
216
|
+
fs.writeFileSync(path.resolve(out), jsonStr + '\n', 'utf8')
|
|
217
|
+
console.log(`✓ Извлечено ключей: ${Object.keys(extracted[ns] || {}).length} -> ${out}`)
|
|
218
|
+
} else {
|
|
219
|
+
console.log(jsonStr)
|
|
220
|
+
}
|
|
221
|
+
} else if (command === 'validate') {
|
|
222
|
+
const file = args[1]
|
|
223
|
+
if (!file) {
|
|
224
|
+
console.error('Ошибка: Укажите путь к JSON файлу словаря (dsh-i18n validate <file.json>)')
|
|
225
|
+
process.exit(1)
|
|
226
|
+
}
|
|
227
|
+
let glossary = null
|
|
228
|
+
for (let i = 2; i < args.length; i++) {
|
|
229
|
+
if ((args[i] === '-g' || args[i] === '--glossary') && args[i + 1]) glossary = args[++i]
|
|
230
|
+
}
|
|
231
|
+
const ok = validateDict(path.resolve(file), glossary ? path.resolve(glossary) : null)
|
|
232
|
+
process.exit(ok ? 0 : 1)
|
|
233
|
+
} else if (command === 'scaffold') {
|
|
234
|
+
const name = args[1]
|
|
235
|
+
if (!name) {
|
|
236
|
+
console.error('Ошибка: Укажите имя плагина (dsh-i18n scaffold <plugin-name>)')
|
|
237
|
+
process.exit(1)
|
|
238
|
+
}
|
|
239
|
+
let out = '.'
|
|
240
|
+
for (let i = 2; i < args.length; i++) {
|
|
241
|
+
if ((args[i] === '-o' || args[i] === '--out') && args[i + 1]) out = args[++i]
|
|
242
|
+
}
|
|
243
|
+
scaffoldPlugin(name, out)
|
|
244
|
+
} else {
|
|
245
|
+
console.error(`Неизвестная команда: "${command}". Запустите "dsh-i18n --help" для справки.`)
|
|
246
|
+
process.exit(1)
|
|
247
|
+
}
|