dsh-bilingual-helper:在每条对话下面放一个 EN 翻译按钮

前言

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 定价页。

这个路由有两个约束:

  1. 必须携带当前会话 id。浏览器半边从 Chat 节点的标准 props 里读取 sessionId,随请求一起发给 Host 半边。
  2. 会话必须处于活跃状态。历史或已归档会话里点击会失败,面板显示 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。

启用步骤:

  1. 在 Settings → Plugins 中确认 @local/dsh-bilingual-helper 已列出并启用。
  2. 无需重启,装好后浏览器页面会自动加载。目录在含 & 的路径下(Agent&AI_dev),shell 里操作记得加引号。
  3. 打开任意活跃会话,点提问或回答下方的 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 的注意点:

  1. 目录移动后必须重新安装并更新链接,profile 只保存一个 link: 依赖和一条 node_modules 软链。
  2. 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

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜