- Home
- Plugin
- Miglioramenti UI
- dsh-client-locale-ru
dsh-client-locale-ru
ragnoryok1/dsh-client-locale-ru
Russian (ru) language pack for the dsh web GUI: 584 namespaces and 67724 strings, including 520 third-party plugin dictionaries and Russian plural forms.
Installazione
dsh plugin --profile web add github:ragnoryok1/dsh-client-locale-ruREADME
@ragnoryok1/dsh-client-locale-ru

Русский (ru) язык для веб-интерфейса DeepSeek Harness (dsh). Пакет регистрирует ru как выбираемый язык клиента через сервис локали (ctx.locale.addLanguage) и везёт по словарю на каждый namespace — 584 namespace, 67 666 строк, сборка под dsh 0.2.1-rc.2. Установка: dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru, затем Русский в Settings → General. Недостающие ключи падают в английский, поэтому более новый харнесс продолжает работать. Область — собственный интерфейс харнесса (64 namespace и 2 996 строк) и словари 521 стороннего плагина, переведённые с английского, который публикует сам плагин. MIT, поддерживается сообществом, не связан с DeepSeek.
If the pack was useful to you, please star the repository. It is the only signal that a translation is wanted, and it is what decides which parts get worked on next.
Полный список переведённого — в NAMESPACES.md: 584 namespace, 67 666 строк, словари плагинов помечены отдельно.
Плагины за пределами дерева харнесса переводятся так же, но их английский текст берётся из собственной публикации: plugins/<namespace>.json хранит пакет, версию, лицензию и строки, выгруженные из бандла. Что осталось английским в вашей установке, покажет npm run report:plugins: он обходит node_modules профиля, находит у каждого плагина его собственный словарь и сверяет ключи с нашими, а плагин без словаря так и помечает — переводить нечего, пока он не спросит перевод.
Отдельно стоит сказать про семейство @linxin666: тринадцать словарей, и каждый переведён целиком — от нескольких десятков строк до четырёх сотен.
| Словарь | Пакет | Строк |
|---|---|---|
update | @linxin666/dsh-update | 43 |
git-graph | @linxin666/dsh-client-ui-git-graph | 63 |
dsh-web-ui-usage | @linxin666/dsh-usage | 67 |
dsh-web-ui-session-archive | @linxin666/dsh-session-archive | 124 |
pet | @linxin666/dsh-pet | 81 |
remote | @linxin666/dsh-remote-web-ui | 117 |
dsh-skill-explorer | @linxin666/dsh-client-ui-skill-explorer | 114 |
dsh-web-ui-market | @linxin666/dsh-client-ui-market | 106 |
liangshen | @linxin666/dsh-liangshen | 42 |
dsh-web-ui-preset-center | @linxin666/dsh-client-ui-preset-center | 46 |
dsh-ssh | @linxin666/dsh-ssh | 158 |
task-board | @linxin666/dsh-client-ui-task-board | 439 |
task-board-github | @linxin666/dsh-client-ui-task-board-github | 113 |
| Итого | 13 словарей | 1 513 |
Названия и описания самих плагинов на странице «Плагины» пакет перевести не может: харнесс хранит их в locale/<язык>.json внутри каждого пакета и собирает из них карту перевода, а resolveText в сервисе локали словари не спрашивает вовсе. У 221 пакета есть locale/en.json, у 219 — locale/zh.json, и ни у одного нет locale/ru.json: эти 261 строка остаются английскими при любом языковом пакете. Разрыв считает scripts/descriptor-coverage.mjs, разбор механизма и 32 готовых перевода — в docs/descriptor-upstream.md.
Чужой перевод мы не копируем: сверять его было бы не с чем, а аудит сравнивает наше значение с английским шаблоном того же плагина. И одно предупреждение на будущее: если в профиле окажется второй пакет, который сам регистрирует русский, язык достанется тому, кто зарегистрировал его первым, — второй молча пропустит свои namespace, и в интерфейсе будет чужой перевод.
Как это выглядит
Русский в списке языков и переведённые разделы настроек (снимки сделаны в сборке 0.1.8:
тот же экран, но формулировки и часть разделов с тех пор изменились — например, подпись
размера шрифта теперь «Влияет только на содержимое диалога»):


Оба снимка обрезаны по левой границе панели: в кадр не попадают названия рабочих пространств и сессий.
Пакет — не только словарь. Сверх перевода он делает три вещи: приводит текст к русской экранной типографике («ёлочки», тире с пробелами, одно многоточие вместо трёх точек), выбирает формы числа по Intl.PluralRules('ru') вместо нейтральных подписей и подставляет русские названия тем плагинам, чей пакет не везёт своей локали.
Каждый проход можно выключить, не трогая перевод: в строке профиля cordis.yml достаточно config: { typography: false }, так же отключаются plurals и pluginNames. Пропущенный переключатель считается включённым, поэтому существующий профиль править не нужно.
Типографика — единственный проход, который переписывает введённый вами текст. Если она мешает в поле ввода, выключайте именно её: остальное останется русским.
Установка
Это гибридный плагин (dsh.bundle + dsh.client), поэтому он ставится как обычный профиль-бандл через dsh plugin:
dsh plugin --profile web add @ragnoryok1/dsh-client-locale-ru
Дальше при следующем старте dsh web в настройках появится Русский. Отдельно pnpm add в собранный dsh web не нужен — плагин тянет штатную локаль через сервис locale.
Если pnpm отвечает ERR_PNPM_NO_MATURE_MATCHING_VERSION — это не поломка пакета, а
политика зрелости: профиль не ставит версии, опубликованные меньше 48 часов назад.
Подождите сутки-двое (имя версии в сообщении показывает, сколько осталось) или снимите
ограничение minimumReleaseAge в настройках профиля.
Манифест проверяется по правилам харнесса, а не на глаз (npm run check:manifest, входит в
verify). Правила вычитаны из его исходников и каждое — это отказ, который иначе увидел бы
пользователь: parseDshClient принимает platform любой строкой, но потребители отбирают
только platform === 'web', поэтому опечатка 'Web' молча выключила бы клиентскую
половину; declared dsh.client без экспорта ./client заставляет client-modules бросить
ошибку; dsh.bundle.patch обязан указывать на существующий файл.
Установка из GitHub тоже работает: dsh plugin --profile web add Ragnoryok1/dsh-client-locale-ru (в таком виде команду генерирует каталог плагинов, без @-скоупа) ставит архив репозитория, а собранный lib/ лежит в git — сборка на вашей машине не запускается и не требуется. Поэтому npm run check:built (входит в verify) пересобирает lib/ и сверяет его с коммитом: устаревший бандл в git иначе получил бы именно пользователь, а аудит этого не увидел бы — он читает исходники. Публикуемый пакет занимает 2013 КБ: баннер отдаётся JPEG, а иконка и карта исходников в npm не входят.
Почему гибрид
dsh web берёт клиентские (UI) плагины только из workspace-пакетов, а внешний npm-пакет с одним dsh.client не подхватывается. Чтобы внешний плагин с UI-частью был устанавливаемым, он должен объявлять dsh.bundle (профиль-слой), чей cordis.patch.yml через - insert: добавляет клиентскую строку locale-ru в браузерный ростер. Так устроены и другие сторонние плагины (например @zseven-w/dsh-android).
Как это работает
- Хост-половина (
lib/index.js) — пустойapply(): языковому плагину не нужна серверная логика, она лишь даёт корректную cordis-строку для Loader. - Браузерная половина (
lib/client.js) — регистрируетruи словари через сервисlocale(инжект изctx). Рантайм-импортов фреймворка нет — только сервис. - Рантайм-зависимостей нет —
lib/client.jsне импортирует другие пакеты, поэтому не тянет тяжёлых зависимостей. - Эффекты — язык и словари регистрируются через
ctx.effect(...), поэтому выгружаются вместе с плагином (HMR-безопасно). - Плюралы — контракт локали не несёт правил плюрализации: компоненты выбирают форму
как
count === 1 ? 'ключ.one' : 'ключ.other', а русскому нужны три (1 вызов / 2 вызова / 5 вызовов). Пакет берёт это на себя: рядом с.oneи.otherв словарях лежат.fewи.many, а обёртка надtranslateрантайма пересчитывает форму черезIntl.PluralRules('ru')для любого вызова с числовымcountилиn. Форму решает число, а не ключ, который выбрал компонент, поэтому «2 вызова инструментов» и «5 вызовов инструментов» получаются из одного и того же места вызова. Формы есть не только у парных ключей: если харнесс передаёт число ключу без пары (terminal.expandс{n}), пакет добавляет к нему свои.one/.few/.many— «Показать ещё 1 строку / 3 строки / 7 строк», — а обёртка ищет форму и для таких ключей. Там, где форма неуместна (в предложении несколько чисел), значение переписано нейтрально к числу: «Символов: {total}» вместо «{total} символов», «файлов: {files}» вместо «{files} файлов». Проверяется скриптом:npm run check:pluralна счетах 0, 1, 2, 3, 5 и 22, на ключе без пары, на дробном числе и на чужом языковом пакете, чьи формы наши правила не переписывают. - Фолбэк — отсутствующий русский ключ падает на
en(настроенныйfallback).
Типографика
Пакет приводит уже отрисованный русский текст к типографским правилам: «ёлочки» вместо
прямых кавычек, длинное тире вместо дефиса между словами, многоточие вместо трёх точек,
неразрывный пробел между числом и следующим словом или единицей измерения (чтобы «187
плагинов» не разрывалось на две строки) и такой же пробел после короткого слова — предлога,
союза или частицы: «в режим» переносится целиком, а не оставляет «в» висеть в конце строки.
Правила намеренно осторожны: срабатывают только на однозначных шаблонах, а код, поля ввода,
редактируемый текст и элементы с data-typography="off" не трогаются вообще. Повторный
проход ничего не меняет.
Сам проход видит только текстовые узлы, а значение из словаря попадает и в атрибуты —
title, aria-label, placeholder, — до которых он не достаёт. Поэтому значения
проверяются отдельно: в пакете не может быть прямых кавычек, дефиса вместо тире, тире без
пробела или трёх точек. Иначе одна и та же строка выглядела бы по-разному в зависимости от
того, куда её поставили, а в подсказке и в подписи для скринридера правила бы не работали
вовсе.
Текст, который меняется после первого прохода — счётчики токенов, таймеры, прогресс, —
обрабатывается заново: живое «4 с» сохраняет неразрывный пробел. Это проверяется на настоящем
документе, а не только на строках: npm run check:typography:dom (jsdom, шесть случаев —
первый проход, изменение текста, добавленное поддерево, остановка при выгрузке, чужой язык и
переключение языка).
Проход включается только пока активен русский: правила русские, и применять их к другому
языку значило бы править текст, который пакету не принадлежит — в английском интерфейсе
"quotes" превратились бы в «quotes». Пакет слушает locale/change и запускает или
останавливает проход при переключении, а не при установке.
Правило для числовых диапазонов («5–10» через среднее тире) сознательно не добавлено:
единственная строка с дефисом между числами во всём пакете — пример cron-выражения
0 9 * * 1-5, и тире его испортило бы. Измерено, а не предположено.
Правила проверяются скриптом, а не на глаз: npm run check:typography.
Имена плагинов
Название плагина приходит из его собственного файла locale/<язык>.json, а служба локализации
разрешает эту карту, не обращаясь к зарегистрированным словарям: её собственная документация
говорит «maps do not consult registered dictionaries». Поэтому словарём название плагина не
перевести — у пакета нет доступа к чужой карте, и английское название остаётся английским,
пока сам плагин не привезёт locale/ru.json.
Пакет несёт эти названия сам: названия 32 плагинов харнесса и названия с описаниями 33
сторонних плагинов, чьи словари он переводит. У каждого — английский текст, из которого сделан
перевод, и русский. Отрисованный интерфейс проходит через проход, который подставляет
русский текст на место английского: в карточках плагинов, в строках состава bundle, на
странице плагина и в реестре настроек. Подменяется и имя для скринридера (aria-label),
чтобы карточка читалась по-русски целиком.
Проход берёт только точное совпадение: строка целиком равна названию или описанию из пакета. Предложение, в котором название лишь упомянуто, остаётся как было; код, поля ввода и переписка не трогаются. Как и типографский проход, он работает только пока активен русский.
Те же 32 названия уходят в харнесс отдельным патчем (docs/harness-descriptors-ru.patch): в
интерфейсе самого харнесса плагины называются по-русски, и патч этот до начала. Патч —
собираемый артефакт: он строится из переводов и дерева харнесса, а гейт
npm run check:descriptor-patch падает, когда собранный и лежащий в репозитории разошлись.
Сторонние названия лежат в src/client/plugin-names-ru.json: файл собирается из
выгруженных словарей плагинов (npm run build:plugin-names), а гейт
npm run check:plugin-names:map падает, когда карта разошлась со словарями. Одно и то же
английское название не может быть переведено дважды — по-разному у харнесса и у плагина.
npm run check:plugin-names доказывает на настоящем документе (jsdom, 12 случаев), что проход
переводит название, описание, имя для скринридера и строку bundle — и не трогает то, что не
должен.
Проверки
npm run build # сборка трёх выходов
npm run check:typography # 18 правил типографики, включая «не трогать»
npm run check:locale # сосуществование словарей: 6 проверок
npm run check:plural # формы числа: 1 вызов / 2 вызова / 5 вызовов
npm run check:composition # собранные фразы: «Пн, Ср в 09:00», «Раз в 2 дня»
npm run check:namespaces # NAMESPACES.md совпадает с собранным бандлом
npm run check:tarball # что установят: архив распаковывается и проверяется
npm run check:workflows # оба workflow запускают всю цепочку verify
npm run check:readmes:pairs # три README двигаются вместе (хеши разделов)
npm run check:plugin-names # названия плагинов по-русски на настоящем документе
npm run check:descriptor-patch # патч для харнесса совпадает с переводами
npm run audit -- <дерево харнеса> # аудит локали
npm run verify -- <дерево харнеса> # всё вместе
npm run namespaces # перегенерировать NAMESPACES.md и pack-count.json
npm run readmes:record # перезаписать хеши разделов после правки трёх README
npm run diff:harness -- <старое> <новое> # что харнесс переформулировал
Отдельная проверка складывает фразы так, как это делает харнесс, и читает результат
целиком. Аудит сверяет значение с его собственным шаблоном, поэтому фраза, верная в
одиночку, но сломанная подстановкой, для него невидима: frequency.weekly был переведён
как «По {weekdays} в {time}», а дни приходят как «Пн, Ср» — получалось «По Пн, Ср в 09:00»,
чего не увидит ни одна построчная проверка. Так же проверяются «Каждые 2 дня» (теперь «Раз в
2 дня»), «Каждый день в 09:00 (Europe/Moscow)» и составной интервал с формами числа.
Третье, что нашлось этим же способом: time.locale — не текст, а тег для Intl, и он был
en. Правило с ограничением по месяцу показывало «Каждый день в January», а подпись
часового пояса — «Moscow Standard Time» в русском интерфейсе. Теперь тег ru (у китайского
словаря харнесса точно так же zh-CN), а месяц встаёт в скобки: «Каждый день (январь,
март)».
Тот же разбор по частям нашёлся в описании cron. Там были сокращения («Каждые {step} мин», «Каждые {count} ч») и книжные обороты («в минуту {minutes}», «минуты {minutes} в часы {hours}»). Счётные части получили формы числа — «Каждые 3 минуты» / «Каждые 5 минут», «Каждые 2 часа» / «Каждые 6 часов», — а остальные части переписаны в один читаемый вид с метками: «Каждый час, минуты: 5, 30», «минуты: 5, часы: 09–17». Вся фраза расписания складывается проверкой целиком: «Пн–Пт каждые 5 минут». Тем же способом закреплены даты: отметка «5 окт. 2026 г., 15:51» и подпись пояса «Москва, стандартное время» — из Intl с нашим тегом, без единого латинского слова.
Аудит выполняется в CI (.github/workflows/verify.yml) против закреплённого тега
харнеса, а не против случайного локального чекаута. Раз в неделю тот же прогон запускается
по расписанию: перевод гниёт и без наших правок — харнесс уезжает вперёд, локально всё
зелено, а расхождение всплывает только при сборке у пользователя. Гейтами проверяются
полнота (ключи и namespace), формы числа, плейсхолдеры, остатки английского и китайского,
терминология, латиница в прозе, гигиена текста, краевые пробелы, стиль, типографика значений,
«ё», составные фразы, длина подписей в боковой панели и заявленные числа — и каждая из этих
проверок уже ловила настоящие дефекты. Рядом аудит печатает справочные
отчёты, которые гейтом не являются, потому что требуют решения человека: сколько
namespace сверено и какие не удалось привязать, остались ли места «число + склоняемое
слово», и не выросли ли короткие строки больше чем в 2,8 раза от английского (риск для
вёрстки — «Effort» → «Уровень рассуждения» это нормальная длина русского, а не дефект).
Шаблоны харнесса аудит читает в том виде, в каком их пишут: строкой, массивом абзацев
(так хранятся руководства режимов) и ссылкой на константу ('hint.plan': PLAN_NEXT_ACTION_EN). Пока читался только первый вид, десять ключей — восемь из них целые
руководства — молча выпадали из сверки. И наоборот: пустой файл политики (keep-english.json,
пустой раздел terminology.json) теперь валит аудит, а не ослабляет гейт незаметно. Так же
сверяются и числа, которые README о себе заявляют: устаревший счётчик — это просто неверное
утверждение, и аудит читает его из бейджа, описания и текста всех трёх README, а также из
описания в package.json — того, что npm показывает в поиске и на странице пакета (там
дольше всех жило «55 namespaces»), включая
разряды («2,542 строки») — именно эта запись дольше всех держала в себе число 2,489, пока
гейт видел только форму без разрядов. Там же сверяется и фраза о том, против какой версии
харнесса пакет собран: она сравнивается с тегом, закреплённым в workflow, потому что
проверяет пакет именно он (оба README обещали 0.2.1-alpha.2, когда CI уже стоял на
dsh-v0.2.1-rc.2). Там же меряется и размер публикуемого пакета: npm run check:size
собирает архив и сравнивает заявленные килобайты с настоящими (допуск в один килобайт — gzip
на разных сборках Node сжимает чуть иначе, а сама фраза округлена до целых килобайт).
- мёртвые namespace — каждый namespace, который регистрирует пакет, обязан
существовать в харнессе. В 0.1.6 переводы каталога расписания были зарегистрированы
под именем
schedule.manager, тогда как читает ихschedule.catalog: проверка покрытия по ключам при этом рапортовала «всё переведено»; - пары форм числа — множества
.one/.otherдолжны совпадать, потому что клиент выбирает форму какcount === 1 ? '.one' : '.other'. Этого мало для русского: каждой паре нужны ещё.fewи.many, иначе обёртка молча вернётся к нейтральному.otherи вместо «2 дня» покажет «2 д». Проверка сверяет это по namespace — обёртка ищет форму в том namespace, который спросил, поэтому пара, забытая в одном экране при наличии в другом, иначе осталась бы незамеченной. Так нашлись четыре единицы времени (unit.day/hour/minute/secondвschedule.catalog) иtriggerв менеджере задач; заодно единицы перестали быть сокращениями («д», «ч», «мин») и получили полные формы в нужном падеже — «через 2 дня», «через 5 часов», а «Каждые 2 дня» стало «Раз в 2 дня». То же требование действует и для ключей, которые пакет добавил сам (terminal.expandи родственные: харнесс передаёт им счёт, но пары не заводит) — их формы держатся только нами, и потеря одной ничем больше не обнаружилась бы; - плейсхолдеры — русское значение обязано нести ровно те
{name}, что и английский шаблон ключа: лишний плейсхолдер попадает на экран буквально (Заняло {duration}7мин 26с, исправлено в #2), а потерянный молча убирает подставленное значение. Для пакетов, которые везут только китайский словарь, шаблоном служитzh. Наши собственные формы числа — добавленные.few/.manyи.oneу ключа, который харнесс не паровал, — в харнессе отсутствуют, поэтому сверяются с шаблоном соседней формы того же слова (.one/.other) или самого ключа. Без этого 52 значения не проверялись вовсе: именно так из формы может пропасть{count}, и на экране останется фраза без числа. - остатки — значение обязано быть русским: точная копия английского шаблона, китайский
иероглиф или пустая строка там, где шаблон не пуст, — это ошибка. Копии английского
допускаются только по решению, записанному в
scripts/keep-english.json(адреса, форматные строки без слов, названия протоколов и инструментов, аббревиатуры), поэтому новая недопереведённая строка не проедет молча. Сами решения тоже проверяются: запись, которая больше ничего не разрешает, валит сборку — либо ключа в пакете уже нет, либо значение перевели и решение устарело. Так нашлась запись проdeepSeekBaseUrl: английский шаблон обзавёлся пояснением проDEEPSEEK_BASE_URL, а подсказка в поле ввода — пример адреса, поэтому пояснение на экране было бы мусором внутри поля. Запись убрана, значение оставлено. Решение может называть и целый закрытый список:chat · app.*разрешает тридцать названий приложений одной строкой — словарь имён приложений и есть список имён. А значения без слов вне плейсхолдеров ({from}–{to},{percent}%,,) решения не требуют вовсе: переводить в них нечего, и политика остаётся про слова, а не про пунктуацию. Сверка остатков читает все шаблоны ключа, а не только блокen: раньше 60 значений в словарях с неопознанным блоком не проверялись вовсе, теперь сверено 2911 значения, 68 из них покрыты решениями (38 записей, одна из них — шаблон), и зона исчезла как класс. Шире от этого правило не стало: пустое значение — ошибка только там, где ни один шаблон не пуст, иначе русский справедливо опускает префикс, который английский тоже опускает (message.stepProcess.sharedPrefix). И точное совпадение — не единственный вид копии: значение, отличающееся от шаблона только регистром или краевыми пробелами (Cancelпри шаблонеcancel), — это тот же непереведённый текст, и оно тоже требует решения. Иначе такая строка читалась бы как английская, а все проверки оставались бы зелёными. - мёртвые ключи — ключ, которого нет ни в одном словаре харнесса и на который не ссылается ни один его исходник, — это остаток от прежней версии: он ничего не показывает и только путает. Такие ключи валят сборку. Ключ, который компонент всё ещё спрашивает, остаётся в справочном отчёте: его значение — единственное, что может попасть на экран.
- терминология — одно понятие переводится одним способом. Запрещённые варианты («кеш»
вместо «кэш», «сеанс» вместо «сессия», «этап» вместо «шаг») валят сборку, а
scripts/terminology.jsonхранит и решение, и счётчики выбранных форм, чтобы дрейф был виден раньше, чем станет дефектом. Проверка сразу нашла ключconfirm.description, переведённый в двух namespace по-разному — «шаги» и «этапы». Слово не может писаться с «ё» и без него: «ещё» и «еще» в одном пакете читаются как машинный перевод, поэтому «ё» внутри проверки сводится к «е», и два написания одной формы валят сборку. Исключения — настоящие разные слова вроде «все» и «всё»: они объявлены вscripts/yo-pairs.json, а запись, чьи написания из пакета ушли, тоже валит сборку. Там же проверяется латиница внутри русских фраз: имя продукта, команды или код-токен допустимы, простое английское слово в предложении — нет. Название продукта обязано писаться так, как его пишет харнесс: «Github Desktop» вместо «GitHub Desktop» читается как опечатка в имени продукта, и появляется она только у нас. Ориентир — английские шаблоны; слово, которое харнесс пишет только строчными («cursor», «desktop»), не считается — там заглавная буква как раз и означает имя продукта. Рядом проверяется механическая гигиена: двойные пробелы, пробел перед знаком препинания, непарные «ёлочки» и скобки, неразрывный пробел внутри значения, а также краевые пробелы — значение, которое начинается или кончается пробелом, компонент склеивает с соседним, поэтому оно обязано совпадать с английским шаблоном своего namespace. Тем же шаблоном сверяется стиль: если английский кончается знаком, русский не должен его терять (на этом проверка нашлаregistryCustomHintбез точки), а регистр первой буквы обязан совпадать — кроме сокращений вродеJSONи шаблонов, начинающихся с плейсхолдера. Отдельно проверяется обращение к пользователю: внутри фразы русский интерфейс пишет «вы» со строчной, а «Вы» с заглавной — только в начале предложения, где это просто орфография. Наконец, пакет сверяется сам с собой: одну и ту же английскую фразу он обязан переводить одним способом. Если два namespace дают разные русские предложения (считаются шаблоны от четырёх слов — короткая подпись законно зависит от контекста), сборка падает. Так нашёлсяconfirm.description: один вариант обещал «пропускать шаги подтверждения», другой — «уменьшать число шагов», а английское «subsequent tasks» во втором стало «текущей задачей». Намеренное расхождение объявляется вscripts/one-rendering.jsonвместе с причиной, и устаревшее исключение тоже валит сборку — иначе список решений тихо расширял бы проверку. Обратное направление печатается справочно: одинаковый русский ответ на разные английские фразы от четырёх слов — это след копипасты (подсказка, приехавшая от соседнего ключа) либо два разных факта, слитых в одну фразу. Гейтом это быть не может: русский законно сливает слова, которые английский различает, — поэтому обе стороны печатаются рядом, и решение принимает человек.
За свежестью тега следит отдельный workflow: watch-upstream раз в неделю сравнивает
закреплённый тег с тегами харнесса, при новом открывает issue со списком шагов и красит
прогон — чтобы отставание находилось по расписанию, а не по чужому скриншоту. Сравнение
версий проверяется без сети (npm run check:upstream: порядок предрелизов, равенство версий,
переход между линиями) и входит в verify:
релиз должен быть новее собственных предрелизов, иначе дозор срабатывал бы вечно.
Число покрытия не является гейтом: часть разделов пакет сознательно оставляет на английском.
Рядом печатается отчёт по namespace: аудит восстанавливает связь «namespace → словарь»
по тому, кто его регистрирует (NS или литерал в locale.register; bind и PropsLocale
только читают namespace), и показывает ключи, которых нет ни в своём namespace, ни в общем
common, куда рантайм падает при поиске. Глобальное сравнение имён такие дыры не видит:
перевод, лежащий под чужим namespace, считается покрытием, а экран в это время показывает
английский. Отчёт справочный — одинаковое имя в разных namespace может значить разное
(list.aria — «Активные напоминания» в каталоге и «Фоновые задачи» в задачах), поэтому
перевод переносится в чужой namespace только там, где оба namespace обслуживает один и
тот же словарь. На закреплённом теге отчёт пуст: каждая строка, которую харнесс рисует в
нашем namespace, лежит именно в нём. Сверяются все 584 namespace пака — привязка строится и по
импорту словаря рядом с регистрацией, и по именам, которые передаёт сама регистрирующая
строка (locale.register(NS, { zh, en })); без второго правила common и settings.locale,
чьи словари лежат в соседнем каталоге, оставались бы вне сверки. Обратная сторона проверяется
жёстко: namespace, который харнесс регистрирует и наполняет ключами, а пак не регистрирует, —
это отказ сборки, потому что такой экран остаётся английским, а покрытие по именам ключей
этого не видит. Так нашёлся settings.sessionLog — строка «Загружать журнал сессии» на
странице общих настроек.
Область покрытия: харнесс и словари плагинов
Пакет переводит собственные namespace клиента харнесса — 64 namespace сверенных с закреплённым тегом, — и словари сторонних плагинов: 520 плагинов, чей английский текст выгружен из их собственной публикации вместе с версией и лицензией.
Поиск по реестру npm — шесть запросов вместо одного «dsh» — нашёл ещё 40 плагинов со своим
словарём. 39 из них переведены; три имени (dsh-automation, remote, settings.ip-pool)
уже заняты словарями других плагинов и оставлены прежним владельцам, чтобы не подменить
перевод чужого продукта. Итог на 22 октября 2026: 584 namespace, 67 666 значений,
520 словарей, 47 700 различных русских строк в собранном пакете. Замер конкурентов тем же
методом — docs/competitors.md.
Раньше словари плагинов в область не входили, и на то была причина: у dshmarket уже
версия 1.66.44, её ключи меняются независимо от харнесса, а молчаливое обещание «покрыто
всё» — это не обещание, а долг, который нельзя предъявить. Ответ оказался простым: чужой
английский текст выгружается (npm run fetch:plugins) и хранится рядом с переводом —
с именем пакета, версией и лицензией, а аудит сверяет наш перевод именно с этим снимком, а
не с чужим русским. Мы проверили 17 самых скачиваемых плагинов экосистемы (npm,
keywords:dsh-plugin, скачивания за месяц) и посмотрели, какие из них вообще поставляют
собственные строки:
| Что обнаружено | Плагинов |
|---|---|
| Своих строк нет — интерфейс берёт текст из namespace харнесса | 11 из 17 |
| Крупный собственный словарь (подтверждено вручную) | 2 — dshmarket (733 ключа), @nanmicoder/dsh-agent-teams (420) |
Только типы .d.ts, строк нет | 1 — dsh-plugin-model-proxy |
| Требует отдельной проверки | 3 |
У 11 из 17 популярнейших плагинов русский интерфейс уже работает без отдельного словаря: их текст лежит в общих namespace харнесса, которые пакет переводит полностью. Словари есть там, где плагин везёт свои строки, — их 32, и у каждого зафиксирована версия, с которой выгружен английский текст.
Такой словарь — обязательство следить за чужими релизами, но теперь оно проверяемое: новая версия плагина с другими ключами видна как расхождение снимка, а не как тишина. Нужен конкретный плагин — заведите issue: словарь для него возможен как отдельный слой, привязанный к версии, а не как молчаливое обещание «покрыто всё».
Разработка
pnpm install
pnpm run build # tsdown -> lib/index.js (node half) + lib/client.js (client bundle)
npm pack # -> ragnoryok1-dsh-client-locale-ru-<version>.tgz
lib/client.js собирается в формате, который понимает клиентский загрузчик dsh (__ModuleLoader__.load), и экспортирует inject/apply.
Релизы
Публикация в npm выполняется workflow publish по тегу — учётные данные лежат в секрете
репозитория (NPM_TOKEN), поэтому токен не нужно ни вводить в терминал, ни пересылать.
Ручная публикация с машины возможна, но не нужна.
npm version 0.3.0 --no-git-tag-version # поднять версию в package.json
# …и синхронно обновить версию в блоке deepseek-harness-meta в этом README
git commit -am "release: 0.3.0" && git tag v0.3.0 && git push origin v0.3.0
Перед публикацией workflow проверяет:
- версия в теге совпадает с версией в
package.json; - версия в блоке
deepseek-harness-metaсовпадает сpackage.json— маркетплейс берёт её оттуда, причём читает только при первой индексации, поэтому расхождение не исправляется само; - типографику (18 правил);
- аудит локализации против закреплённого тега харнесса — мёртвые namespace, дубли и пары форм числа упадут и остановят публикацию;
- совместимость со всеми версиями из
compatible_versions: для каждой скачивается её рантайм локали с npm и против него прогоняются три набора — сосуществование языков, формы числа и собранные фразы (npm run check:compat, входит в CI) — обещание в meta-блоке проверяется, а не подразумевается.
Отдельно проверяется само объявление совместимости (npm run check:peers, входит в
verify): харнесс читает наши peerDependencies для @deepseek-ai/dsh* не как версии пакетов,
а как диапазон версий рантайма и с includePrerelease — и если диапазон не удовлетворит
работающий dsh, пользователь увидит предупреждение о несовместимости и запрос на исключение.
Скрипт прогоняет это правило харнесса по всем заявленным версиям: сейчас проходят все восемь.
Проверить всё то же локально, без публикации:
npm run verify -- путь/к/дереву/харнесса
Для workflow есть ручной запуск с переключателем «только проверки, без публикации»
(workflow_dispatch → dry-run).
Содержимое
src/client/dicts.ts— русские словари: 584 namespace и 67 666 строк, файл 71 453 строки. Это и есть основная ценность.src/client/index.ts— точка входа клиентского плагина (регистрацияru+ словарей).src/index.ts— пустая host-половина (apply()).cordis.patch.yml— профиль-патч (- insert:клиентской строкиlocale-ru).
Что переведено
Актуальный набор — по тегам харнеса: ключи сняты с профильного коммита, а добавленные
позже падают на en при поиске, поэтому пакет не ломает интерфейс на новых версиях.
0.1.7 — исправлен мёртвый namespace, переведён менеджер задач, переписаны числительные
Главное в релизе — исправление ошибки из 0.1.6. Тогда было записано, что namespace
schedule.catalog упразднён, а его место занял schedule.manager. На деле в харнесе
оба namespace живут и по отдельности: schedule.catalog — это каталог напоминаний в
шапке сессии с частотами и единицами, schedule.manager — новый менеджер задач
автоматизации. Из-за неверного вывода ~30 строк переводов (напоминания, частота, единицы
времени) были зарегистрированы под именем, которое никто не читает, и никогда не
показывались в интерфейсе. Теперь каталог зарегистрирован как schedule.catalog.
- исправлено: переводы каталога расписания снова видны —
trigger.*,list.*,delete.*,frequency.*,cron.*,unit.*,relative.*,mark.aria,hover.more; - добавлен
schedule.manager(188 ключей) — менеджер задач автоматизации целиком на русском: панель и каталог задач, поиск, фильтр состояний, сведения о задаче и её вкладки, журнал запусков с правилами хранения, редактор расписания (дата, время, час, минута, секунда, часовой пояс, интервал повтора), правила повтора (разово, каждые N минут/часов/ секунд, ежедневно, пн–пт, еженедельно, свой cron), cron-форма с выбором дат и минуты, удаление с подтверждением, уведомления и карточки; - числительные переписаны (17 пар ключей): формы, которые ломались на числах 2–4,
заменены на корректные при любом
n ≥ 2— например «2 субагентов» → «Субагентов: 2», «каждые 2 часов» → «каждые 2 ч». Ещё 8 строк с{count}перед склоняемым словом переведены на сокращения: «ещё 3 строк» → «ещё 3 стр.»; - добавлено 7 ключей, которые отсутствовали:
blocked.composer,defaultWorkspace.title,schedule.active,settings.transcript.expanded,status.scheduled,status.overdueи namespaceshortcuts.layout(подпись команды показа левой панели); - новая проверка в проекте: аудит имён namespace. Прежняя проверка покрытия
сверяла ключи внутри namespace, но не сверяла сами имена, поэтому целый namespace
мог быть зарегистрирован под несуществующим именем и тихо не работать. Теперь имена
извлекаются из всех способов их объявления (
const NS/SETTINGS_NS/namespace,locale.register,locale.bind,PropsLocale<…>) и сравниваются множествами.
В 0.1.2 добавлены разделы, которых не было в 0.1.1: open-in-app («Открыть в
приложении»), sidebarFiles, sidebarRight (правый сайдбар с файлами) и
sidebarCodePreview (предпросмотр кода). Также обновлены ключи, изменившиеся в
харнесе 0.1.5, и удалены 21 ключ, которых в нём больше нет.
В 0.1.3 пакет догнал харнес 0.1.6-alpha.1: добавлено 139 ключей и 7 новых
разделов — sidebarTerminal (терминал в правом сайдбаре), settings.archivedSessions
(архивные сессии) и семейство предпросмотра документов (sidebarDocumentPreview,
documentHtml, sidebarImage, documentMarkdown, sidebarPdf). Кроме этого
переведена экспериментальная «Автопроверка» (permission.access: auto.*),
подписи и токены палитры команд, поля эндпоинтов в настройках моделей и
изменения в conversation, trajectory, chat, session-log-download.
28 ключей, исчезнувших из харнеса, удалены.
Проверка покрытия выполняется по исходникам харнеса: для каждого namespace сравниваются множества ключей, поэтому следующий выпуск начинается с точного списка «добавить / проверить / удалить».
В 0.1.6 пакет догнал харнес 0.1.7-rc.2: добавлено 252 ключа, 18 обновлены, 26 удалены, добавлены два новых namespace. Изменения:
shortcuts(новый namespace, 56 ключей) — редактор горячих клавиш: запись сочетания, конфликты, зарезервированные комбинации, подсказки для macOS и Windows, восстановление значений по умолчанию;schedule.manager(70 ключей на тот момент) — менеджер задач автоматизации: список, удаление, повтор, история запусков, выражения Cron. Ошибочно было записано, что namespaceschedule.catalogпри этом упразднён; в 0.1.7 это исправлено — оба namespace существуют параллельно;settings.account(50 ключей) — первый запуск приложения: приветствие, выбор назначения и глубины показа хода работы, пополнение баланса, выход из учётной записи во время выполнения задач;- добавлены подписи добавления и удаления инструментов на ходу
(
message.tools*,trajectory.layout.*), команды правой боковой панели (sidebarRight), состояния «сначала выберите сессию» в панелях, обновлено название раздела настроек: «Инструменты для кода» вместо «Инструментов разработчика»; - 18 ключей обновлены под изменившийся английский текст, 26 удалены. Единый термин «рабочая папка» вместо «рабочей области» в одном месте.
В 0.1.5 пакет догнал харнес 0.1.7-rc.1: добавлено 49 ключей, 13 обновлены,
16 удалены, namespace directory-browser убран (в rc.1 его больше нет). Появились
подписи шагов работы инструментов (message.stepProcess.prepare.* и done.* —
чтение изображений, запись файлов, подготовка к поиску по коду, веб-поиску,
координации субагентов и другие), просмотр изображений во весь экран
(image.*), режимы показа ходов «Стандартный» и «Подробный», установка плагинов
из GitHub с таймаутом и китайским зеркалом, предупреждение о несовместимой версии
плагина, выбор источника загрузки моделей для голосового ввода и сообщения
команды Cordis. Уточнены формулировки, где upstream заменил «teammate/agent»
на «subagent», а «Beta» — на «Experimental».
Также в 0.1.5 исправлена единица счёта в инвентаре плагинов: было «187 плагины»,
стало «187 плагинов». Переведены и встроенные снимки интерфейса, добавлены npm
keywords и исправлен peer-диапазон @deepseek-ai/dsh-client-locale
(>=0.1.0-rc.2 <0.2.0) — раньше ^0.1.0 не разрешался, потому что пакет
публикуется только под prerelease-версиями.
В 0.1.4 пакет догнал харнес 0.1.7-alpha.2 — самый крупный скачок: добавлено 905 ключей, 29 изменены, 126 удалены. Новые разделы:
pluginManager— встроенная панель плагинов: установка, удаление, включение и отключение компонентов, версии, реестры и подсказки по установке;settings.account— учётная запись DeepSeek: вход, баланс, расход, API-ключ;- отдельные разделы настроек, на которые разъехался прежний
settings.plugins:settings.agentLoop,settings.shell,settings.subagent,settings.webSearch; sidebarBrowser— встроенный браузер в правой панели (адрес, песочница, восстановление страницы);sidebarExcelиsidebarOffice— предпросмотр таблиц и документов Office; масштабирование (zoom.*) добавлено в PDF и изображения;voice-input— экспериментальный голосовой ввод.
Также переведены новые тексты в conversation (+194), chat (+53),
workspace (+43: архив сессий, закрепление), deliverables (+31: обзор
изменений хода), job (+24: живой вывод фоновых задач), settings (+22:
обновление десктопного приложения), plan, open-in-app, trajectory,
model, reference, session-log-download и в руководствах по режимам
агента (settings.agentPreset, 12 длинных текстов). Раздел
settings.archivedSessions удалён: в 0.1.7 upstream убрал этот пакет, а архив
сессий переехал в workspace.
Лицензия
MIT
История изменений
CHANGELOG.md — что было в каждом выпуске.
Plugin correlati
dsh-web (dsh-task-board)
zhu1090093659/dsh-web
dsh-web (dsh-web-all)
zhu1090093659/dsh-web
dsh-web-ui (dsh-task-board)
zhu1090093659/dsh-web-ui
dsh-web-ui (dsh-web-ui-all)
zhu1090093659/dsh-web-ui