Skip to content
微信公众号二维码

Bob 官方公众号

Bob 1.21.0 插件翻译结果支持 Markdown ​

Bob 1.21.0 的翻译结果支持按 Markdown 渲染,内置的 AI 翻译服务已经默认启用。插件通过新的 content 字段声明译文格式,也能让译文以 Markdown 展示。本文介绍 content 字段的用法、Markdown 里引用插件自带图片的方式,以及对已有插件的兼容说明。

背景 ​

此前插件回传译文只有 toParagraphs 一种方式:一个 string 数组,Bob 展示时在元素之间插入空行。AI 类插件为了保留模型输出的格式,通常只给数组设置一个元素,把完整文本原样回传。但模型输出的 Markdown 语法(标题、列表、代码块、表格等)会原样显示成 #、*、| 这些符号,可读性很差。

1.21.0 的结果卡片可以直接渲染 Markdown,支持标题、列表、引用、代码块(带语法高亮)、表格、链接、图片、数学公式、Mermaid 图表和分割线,流式输出过程中也能边收边渲染。Markdown 渲染需要 macOS 13 及以上,macOS 12 上译文按纯文本显示,Markdown 标记原样保留。如果渲染出现异常,用户可以在「偏好设置 - 翻译 - Markdown」中关闭渲染,回到纯文本显示。

借这次改动,译文的回传结构也做了收敛:新增 content 对象,用 format 告诉 Bob 应该怎么理解 text,不再靠数组元素个数暗示格式。

content 对象 ​

translate result 新增 content 属性,存在时 Bob 以它为准,忽略 toParagraphs。

属性类型说明
formatstring译文格式,决定 Bob 怎么理解 text。可选值 plain、markdown、lines,不传按 plain 处理。
textstring译文文本。

三种格式的用法如下。

plain:纯文本整篇显示 ​

text 原样按纯文本显示,不拆合段落、不裁剪,换行就是换行。

js
query.onCompletion({
    result: {
        from: "en",
        to: "zh-Hans",
        content: {
            format: "plain",
            text: "第一行译文\n第二行译文"
        }
    }
});

markdown:整篇按 Markdown 渲染 ​

text 原样按 Markdown 渲染。AI 类插件让模型输出 Markdown 后直接透传即可,不需要做任何处理。

js
query.onCompletion({
    result: {
        from: "en",
        to: "zh-Hans",
        content: {
            format: "markdown",
            text: "## 摘要\n\n- 第一点\n- 第二点\n\n| 列 A | 列 B |\n| --- | --- |\n| 1 | 2 |"
        }
    }
});

译文里的软换行(单个换行符)会显示成段内换行,不会像通用 Markdown 渲染器那样合并成空格。翻译场景下换行结构来自用户原文(字幕、歌词、代码注释),保留换行更符合预期。

lines:逐行译文,由 Bob 按原文结构拼回 ​

传统机器翻译服务(DeepL、Google、百度这类)用这个格式。text 里每一行是一行译文,Bob 会按原文的换行结构把译文拼回去:原文两行之间是单换行,译文也是单换行;原文是空行,译文也是空行。

接口的返回形态不同,处理方式也不同:

  • 接口把整段译文作为一个字符串返回,原封不动放进 text 即可,不需要做任何处理
  • 接口按数组逐行返回,用换行把数组连成一个字符串再放进 text
js
// 接口整段返回
content: { format: "lines", text: resp.translatedText }

// 接口按数组返回
content: { format: "lines", text: resp.lines.join("\n") }

拼回的工作由 Bob 完成,插件不需要自己对齐原文。有几点需要知道:

  • Bob 按「第 i 行译文对应原文第 i 个非空行」拼回,只含空白的行会被忽略,所以译文里不要用空行占位
  • 每行内容原样保留,Bob 不做 trim,插件也不需要
  • 译文行数与原文对不上时,Bob 用单换行拼接、不会加空行,不会报错
  • lines 按纯文本显示,不解析 Markdown

认不出的 format ​

如果 format 是 Bob 当前版本不认识的值,Bob 不会猜、也不会当纯文本显示,而是在正文位置提示「当前版本不支持该译文格式」。这样以后再新增格式时,还没升级的用户能知道需要升级,而不是看到一堆原始标记。

流式输出 ​

onStream 回调同样传 content,text 传累计的全文。同一次 translate 调用内,某一帧没带 format 时会沿用上一帧的值,避免卡片在纯文本和 Markdown 之间来回切换。

与 thinkInfo 的配合 ​

thinkInfo.splitThinkTag 设为 true 时,Bob 会从 content.text 里拆出 <think></think> 标签的内容作为思考过程,只对 plain 和 markdown 生效。lines 不支持拆标签,思考内容请放到 thinkInfo.content。

Markdown 中引用插件自带的图片 ​

Markdown 译文里的图片支持 http(s) 远程地址、data: 内联和本地文件。为了让插件能引用自己包内的图片,新增了 bob-plugin:// 地址:

bob-plugin://<插件 identifier>/<路径>
  • 路径以 $sandbox/ 开头时指向插件沙盒目录,与 $file 的约定一致
  • 其余路径指向插件安装目录,相对于插件根目录
md
![图标](bob-plugin://com.example.plugin/assets/logo.png)
![缓存图](bob-plugin://com.example.plugin/$sandbox/cache/chart.png)

这个地址只在图片位置有意义,放在链接位置点击不会有任何动作。插件未安装、文件不存在或路径越出插件目录时,图片位置会显示 alt 文本。

兼容说明 ​

toParagraphs 仍然可用,但已废弃 ​

toParagraphs 继续可用,Bob 会把它归到 content 的两种格式里:

  • 丢掉空字符串元素后只有一个元素,按 format: plain 整篇原样显示
  • 多个元素,用换行连起来按 format: lines 处理

新插件请直接使用 content。

多段译文的显示方式变了 ​

这是对已有插件影响最大的一条。此前 toParagraphs 有多个元素时,Bob 在元素之间插入空行;1.21.0 起改为按原文的换行结构拼回,原文是单换行的地方译文也是单换行。已发布的插件不改代码,显示效果也会跟着变,这是有意为之:译文的换行结构应该跟原文一致,而不是一律空一行。

query.text 保留原文的换行结构 ​

与上一条配套,Bob 的文本预处理现在保留原文的换行结构:单换行保持单换行,空行保持空行,连续多个空行压成一个。此前所有换行都会被处理成空行。如果插件对 query.text 按 \n\n 拆段,需要留意这个变化。

同时兼容新老版本 Bob ​

content 和 toParagraphs 可以同时传:1.21.0 及以上版本以 content 为准,更早的版本不认识 content,会读取 toParagraphs。

js
query.onCompletion({
    result: {
        content: { format: "markdown", text: markdownText },
        toParagraphs: [markdownText]
    }
});

如果不准备兼容老版本,请将 info.json 的 minBobVersion 提升到 1.21.0,appcast.json 的 minBobVersion 也需同步修改。

相关文档 ​