From b865825c514e146af02b4535b58210106512076b Mon Sep 17 00:00:00 2001 From: spearzificial <342299391+spearzificial@users.noreply.github.com> Date: Thu, 15 Oct 2026 23:44:53 +0530 Subject: [PATCH] docs: describe both translation seams and the merged dictionary order --- README.md | 57 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 77da482..f53318f 100644 --- a/README.md +++ b/README.md @@ -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 ``, 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`.