前言¶
DSH 的会话界面里,中文提问和中文回答都在同一屏显示。做双语阅读练习的人常见的做法是把整段文本复制出去再翻译,或者开另一个翻译窗口逐句对照,过程中原文容易被覆盖、也容易对错行。实际翻译时还有一个问题:整条消息一次性送翻译,长回答不仅慢,还会把代码块一起改掉。
下面介绍的插件处理的正是这个问题。它由 SCUklr 维护,在社区目录的「联网工具」分类下,包名 @local/dsh-bilingual-helper,仓库地址见文末。它的形态是 DSH bundle plugin:在每个提问和每个回答下方各加一个 EN 按钮,点一下就在原消息下方展开该条消息的完整英文翻译,再点一次收起,原文不动。
它解决什么问题¶
定位一句话:给 DSH 会话加一层「按需展开」的中英对照面板,翻译只针对当前这条消息,而不是整个会话或整篇导出。
目前已核实的行为:
- 中文提问下方、中文回答下方出现
EN 英文翻译按钮;已经是英文的消息不出现按钮(没有可翻译内容)。 - 每行独立展开、独立缓存,折叠后再展开是瞬时的,不会重复请求。
- 代码块(``` 围起来的部分)原样保留、不送翻译,只翻正文。
- 长消息自动切块,逐块翻译后再按原文顺序拼回。
- 深色 / 浅色主题自动适配,只用主题 token。
- 网络或服务错误时在面板内以红色文案显示,按钮变为「重试」,不会静默失败。
翻译从哪来¶
点击按钮时,该消息正文发给 DSH 内置的翻译服务(ctx.translator),返回纯文本译文。默认走 deepseek-official 付费路由:deepseek-flash 模型、thinking 关闭、单条文本、固定直译提示词(无对话历史、无工具),按输入 / 输出 token 计费。README 给出的量级是约 0.5–1.5 分 / 条,具体见 DeepSeek 定价页。
这个路由有两个约束:
- 必须携带当前会话 id。浏览器半边从 Chat 节点的标准 props 里读取
sessionId,随请求一起发给 Host 半边。 - 会话必须处于活跃状态。历史或已归档会话里点击会失败,面板显示
TRANSLATION_SESSION_INACTIVE之类的错误,需要回到活跃会话再点。
隐私方面:点击按钮等于把该消息正文发送给 DeepSeek;不点击就不发送任何内容。
如果想回到不花钱的路线,把 cordis.patch.yml(以及 profile 里 cordis.patch.yml 的覆盖行)中两处 provider: deepseek-official 改成 bing 或 google。匿名接口不消耗 token、不需要会话 id,但译文质量是 NMT 直译。
启用与使用¶
插件的目录目前在 ~/Agent&AI_dev/dsh-plugins/en-translation,profile 只保存一个 link: 依赖和一条 node_modules 软链。README 未给出独立的安装命令,安装路径是 profile 里的 link: 依赖注册,包名为 @local/dsh-bilingual-helper。
启用步骤:
- 在 Settings → Plugins 中确认
@local/dsh-bilingual-helper已列出并启用。 - 无需重启,装好后浏览器页面会自动加载。目录在含
&的路径下(Agent&AI_dev),shell 里操作记得加引号。 - 打开任意活跃会话,点提问或回答下方的
EN 英文翻译;点「收起翻译」折叠;点「重试」重新请求。
配置项在 cordis.patch.yml 中本插件行的 config:
config:
targetLanguage: en # 目标语言,BCP 47,如 en、ja
provider: deepseek-official # 付费路由;也可改 bing / google 走匿名 NMT
换 provider 时两处都要改(bundle 配置和 profile 覆盖行),否则实际生效的可能仍是缓存的旧配置。
目录结构与调用方式¶
仓库很小,主要文件职责如下:
dsh-plugins/en-translation/
├── package.json bundle 清单:dsh.bundle.patch + dsh.client(web, immediately)
├── cordis.patch.yml 插入两行:translator(内置翻译服务)+ 本插件
├── index.js Host 半边:在同源 Web 上注册 /dsh-en-translation/* 路由
├── client.js 浏览器半边:Chat 行定义 + 渲染器 + 按钮 UI
├── locale/{zh,en}.json 插件面板里的标题与说明
└── icon.svg 原创图标
行从哪来:浏览器半边注册两个 Conversation Definition(en-translation-question 匹配 user/message,en-translation-answer 匹配 assistant/message),各自发布 kind: "en-translation" 的 Chat Node,锚在该消息自己的 seq 上,因此行落在消息正下方。
桥接部分值得说几句,因为 bundle 的浏览器半边不能直接调用 Host 服务,也没有 host.call。Host 半边改用 ctx.webServer 注册同源 JSON 端点,浏览器只做 fetch:
GET /dsh-en-translation/health → 翻译服务是否可用、可用 provider
POST /dsh-en-translation/translate {text, sessionId?} → {ok, translation, chunks, elapsedMs}
POST /dsh-en-translation/debug → 仅开发用,DEBUG=true 时把诊断写进系统临时目录
译文按会话持久化,同一会话内同一段文本重复展开不再请求;页面内存里另有一份缓存,最多 200 条。
已知限制¶
这些限制来自 README,先看再决定要不要开付费路由:
- 付费路由比匿名 NMT 慢:实测 270 字约 1.5 s、800 字约 3 s、接近 3000 字的单块约 10–20 s。超长回答会被切成多块串行翻译,前端总超时 90 s,极长消息可能失败,面板会提示原因。
- 付费路由绑定活跃会话,历史 / 已归档会话里点翻译会失败。
- 切到付费路由后每条新消息都会消耗 token(同一会话内重复展开命中缓存则免费),会话日志里会新增
plugin:translator/request|result记录。 - 机器翻译会改写 Markdown 排版与专有名词;原文始终保留在上方,可对照阅读。
- 一条回复如果分多个 assistant 消息(多步工具调用),中文的每一步都有自己的按钮。
- 翻译结果不落盘、不跨页面持久化,刷新页面后需重新点一次。
搬迁与重装¶
两条来自 README 的注意点:
- 目录移动后必须重新安装并更新链接,profile 只保存一个
link:依赖和一条node_modules软链。 dsh-client-modules会把「Loader 行名 + 根目录」对应的包元数据缓存到进程重启为止(sourceKey = baseUrl + '\0' + loaderName)。所以「删掉同名包 → 原地改名或换路径重装」时,前端会静默不加载,而 Host 半边正常。解决办法二选一:装机时换一个新包名(当前做法是@local/dsh-bilingual-helper),或者重启 DSH 应用。原地改文件内容、路径不变则不受影响。
适合谁¶
适合在 DSH 会话里做中英对照阅读、需要按句子核对译文、又不想离开聊天界面的人。插件以当前 DSH 进程的权限运行,并在点击时把消息正文发送给翻译服务方,安装前应先读一遍源码和许可证(MIT,© 2026 SCUklr),再决定是否信任这条数据通路。
仓库:https://github.com/SCUklr/dsh-bilingual-helper
目录页:https://www.skillhub.cn/plugins/SCUklr/dsh-bilingual-helper