No description
Find a file
spearzificial 64831fe317 feat: translate 100% of served UI literals, including template literals
The remaining Chinese was one measurement bug and one structural gap.

Measurement: the harvester and every earlier gap report walked the profile
tree only. 55 of the 58 served client bundles live in the dsh install tree,
so whole sentences were never in scope and the strict coverage rule left
them in Chinese. Scanning the tree the browser actually receives showed
74.27% coverage, not the ~100% previously claimed.

Structure: rewriteScript skipped every backtick literal, and skipped any
literal containing ${. That is where user-visible text like
`剩余 ${hours} 小时 ${minutes} 分` lived, so it could never be translated.

- scripts/analyze.ts walks the resolved served-bundle set (exported to
  ~/.cache/c2e/served.json by scripts/served.mjs) instead of a plugin root.
- src/dictionary.residual.json: 1,452 entries covering the measured gap.
- src/rewrite.ts: substituteInterpolated handles template literals. Only an
  exact dictionary hit is accepted, because segmenting a template rebuilds
  the sentence from word fragments (`已退出 ${n} 个网页端会话` became
  `Exited ${n} itemsWeb pageSessions`). A translation that changes the
  number of interpolations is rejected.
- src/strings.ts: scanLiteralEnd tracks ${ depth through nested strings and
  nested template literals. Previously `` `a ${c ? `b` : ""}` `` was cut at
  the inner backtick and rewritten as a fragment.
- scripts/merge-dict.mjs: bilingual keys (`中文 / English`) resolve to their
  human-written tail instead of being machine-translated. Also merges the
  residual dictionary.
- scripts/mt-fill.mjs: guards templates by interpolation count, skips
  bilingual keys, and walks both plugin roots.

Verified over all 78 served bundles via the runtime entry point
translateScript: 54/54 bundles rewritten, residual Chinese in display
literals 9 -> 2 strings (2 CJK chars), both inside CSS comments within a
stylesheet string, so no user-visible text remains. All 5,437 Chinese-bearing
display literals now resolve.
2026-10-18 20:22:35 +05:30
lib feat: translate 100% of served UI literals, including template literals 2026-10-18 20:22:35 +05:30
scripts feat: translate 100% of served UI literals, including template literals 2026-10-18 20:22:35 +05:30
src feat: translate 100% of served UI literals, including template literals 2026-10-18 20:22:35 +05:30
.gitignore fix: translate plugin bundles at the transport layer 2026-10-15 22:53:20 +05:30
cordis.patch.yml chore: rename package to chinese2english 2026-10-11 18:33:25 +05:30
dshmarket_dict.json feat: implement direct plugin bundle patching with comprehensive 500+ DSH dictionary 2026-10-12 15:44:54 +05:30
LICENSE feat: initial commit of dsh-plugin-english 2026-10-11 16:57:03 +05:30
locale-pairs.json fix: translate the API copy that actually renders 2026-10-15 23:44:17 +05:30
package.json feat: translate 100% of served UI literals, including template literals 2026-10-18 20:22:35 +05:30
patch-plugins.py feat: implement direct plugin bundle patching with comprehensive 500+ DSH dictionary 2026-10-12 15:44:54 +05:30
README.md docs: describe both translation seams and the merged dictionary order 2026-10-15 23:44:53 +05:30

chinese2english

Renders Chinese UI text in DeepSeek Harness third-party plugins as English.

Most community plugins ship hardcoded Chinese, and the interface language setting does not reach it. This plugin rewrites that text server-side, before any of it reaches the browser.


How it works

The text arrives by two routes, so there are two seams.

Plugin bundles. clientModules serves every plugin bundle over /plugins/.... Wrapping clientModules.bundleResource decodes each JavaScript response, rewrites the Chinese string literals it can translate, and returns the result. One interception covers every plugin, including combination bundles that carry dozens, and it runs before any plugin code is evaluated.

API copy. Plugin descriptors, model and provider names, and settings descriptions never appear in bundles at all: they reach the browser as JSON over /api. The gateway resolves its own dispatchRpc per request, so overriding that instance method intercepts those responses without claiming the interceptor slot the gateway already owns.

Three deliberate choices:

  • All-or-nothing literals. Chinese has no word boundaries, so replacing known words wherever they appear corrupts larger words (当前工作区 would become Saat IniWorkspace). A literal is translated only when every Chinese character in it is covered by a dictionary entry; anything else is left in Chinese, since half-translated text reads worse than untranslated text.
  • A compile gate. Each rewritten bundle is parsed with new Function before it is served. If a rewrite ever produced invalid JavaScript, the original bytes are served instead, so a bad dictionary entry cannot break the UI.
  • An endpoint allowlist. Only endpoints returning server-authored copy are rewritten. /api/session/list, for example, returns your own prompts and transcripts, and is left untouched.

Where an author already wrote English, that English is used rather than the dictionary: locale maps ({ zh, en }) resolve to the English sibling, and bilingual descriptors joined by · keep their English half.

The client half does one thing: it selects the en locale through ctx.locale.setLocale, which persists the preference. It never touches the DOM.


What it does not cover

  • Text rendered inside an <img>, canvas, or other non-text payload.
  • Content fetched from a third-party server at runtime (announcements, remote catalogs) rather than from DSH itself.
  • The long tail of one-off strings the dictionary does not yet cover.

The shipped dictionary holds roughly 1,570 entries. Coverage is concentrated on strings that appear repeatedly; rare one-off sentences are usually left in Chinese, which is why the all-or-nothing rule matters.


Installation

dsh plugin --profile web add chinese2english

Remove it with dsh plugin --profile web remove chinese2english.

Restart DSH after installing, so the plugin is loaded and its transport wrapper is installed before the first bundle request.


Development

npm run dict    # rebuild src/dictionary.json from its sources
npm run build   # bundle the server and client halves into lib/

Dictionary sources, merged in this order (later wins):

  1. dshmarket_dict.json — harvested pairs.
  2. UI_TRANSLATIONS in patch-plugins.py — harvested pairs.
  3. src/dictionary.extra.json — curated entries.
  4. locale-pairs.json — author-written { zh, en } pairs lifted from plugin locale maps; highest quality, so it overrides everything above.

Add or fix translations in src/dictionary.extra.json, then run npm run dict && npm run build.


License

MIT © oiupoyt