docs: describe both translation seams and the merged dictionary order

This commit is contained in:
spearzificial 2026-10-15 23:44:53 +05:30
parent 68a7fc608d
commit b865825c51

View file

@ -2,23 +2,29 @@
> Renders Chinese UI text in [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) third-party plugins as English.
Most community plugins ship hardcoded Chinese inside their client bundles. Those
strings never reach DSH's locale dictionaries, so they stay Chinese even when the
interface language is English. This plugin translates them at the transport
layer, before any of that code runs.
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
Plugin bundles are served by DSH's `clientModules` service over `/plugins/...`.
This plugin wraps `clientModules.bundleResource`, decodes each JavaScript
response, rewrites the Chinese string literals it can translate, and returns the
result. Because interception happens server-side, it covers every plugin at once,
including combination bundles that carry dozens of them, and it runs before any
plugin code is evaluated.
The text arrives by two routes, so there are two seams.
Two deliberate choices:
**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
@ -28,6 +34,13 @@ Two deliberate choices:
- **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.
@ -36,17 +49,15 @@ The client half does one thing: it selects the `en` locale through
## What it does not cover
Translation works on string literals inside served bundles. Text that arrives
another way stays as it is:
- 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.
- Plugin **descriptors** in `package.json` and `README.md` files (marketplace
listings, plugin titles).
- **Server-side API prose** that is fetched as JSON at runtime.
- The long tail of one-off literals that 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.
The shipped dictionary holds roughly 1,200 entries and covers about 45% of the
Chinese literals in a typical plugin set. Coverage is concentrated on strings
that appear repeatedly; rare one-off sentences are usually left in Chinese.
---
@ -70,11 +81,13 @@ 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:
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, merged last so they win.
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`.