ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

KOReader 词典发音功能全解:TTS 语音合成到底有没有,发音从哪来

KOReader 词典发音功能全解:TTS 语音合成到底有没有,发音从哪来

KOReader 词典发音功能全解:TTS 语音合成到底有没有,发音从哪来

【免费下载链接】koreaderAn ebook reader application supporting PDF, DjVu, EPUB, FB2 and many more formats, running on Cervantes, Kindle, Kobo, PocketBook and Android devices项目地址: https://gitcode.com/GitHub_Trending/ko/koreader

用 KOReader 读英文书时,你长按选中一个生词,弹出来的词典窗口里只有释义——想听发音却发现根本没有发音按钮,这是很多读者的第一个卡点。翻遍界面找不到入口,原因其实很简单:KOReader 词典本身没有内置 TTS 语音合成(Text-to-Speech,把文字转成声音的技术),它的"发音"能力来自词典数据本身,外加设备系统的配合。下面把这条链路拆开讲清楚,你能知道发音到底从哪来、哪里能配上、哪里就是配不上。

🔍 原理拆解:从选中单词到发音窗口的完整数据流

这一节回答"我点了查词之后,事情是怎么一步步发生的"。

选中的单词是怎么被清洗并送去查询的

一切从 frontend/apps/reader/modules/readerdictionary.lua 里的onLookupWord开始。你选中的文字往往带着逗号、引号之类的杂质,cleanSelection先把它们剥掉,再决定用不用模糊匹配(fuzzy search,拼写不完全对也能查到)。随后调用stardictLookup走真正的查询。如果当前设备支持外部词典查询且你在设置里开了它,这一步会直接转交给系统处理,本地引擎不参与——这是 Kindle 和 Kobo 上听系统词典发音的通道。

sdcv 命令行引擎是如何查本地词典的

查询由rawSdcv执行:拼出一条 sdcv 命令(StarDict 的命令行查询工具,随 KOReader 一起打包),带上--json-output、--exact-search之类的参数,再按你的词典优先级逐个加上-u选项,最后把单词交给它执行。sdcv 扫的是data/dict目录下的 StarDict 词典包(以.ifo为标志文件),结果以 JSON 形式吐回 Lua 解析。值得注意的是源码里的注释:StarDict 规范的res/子目录允许存放图片、音频文件等资源——发音条目、例句音频就住在这里,只是播放与否取决于后面两步。

结果窗口和按钮是怎么渲染出来的

拿到结果后showDict创建DictQuickLookup窗口(frontend/ui/widget/dictquicklookup.lua)把 HTML 释义渲染出来。窗口底部有一排按钮,默认是"上一条/高亮/下一条/Wikipedia/搜索/关闭",布局可在设置里自定义。这排按钮里同样没有"发音"——因为发音不是按钮,而是藏在释义 HTML 的链接里的一条链接。

bword 链接怎么被拦截成第二次查词

HTML 词典条目里可以写bword://word这样的特殊协议链接。onHtmlDictionaryLinkTapped专门认这种链接:剥掉bword://前缀拿到单词,然后再次调用stardictLookup。这就是"发音词典当第二跳"的机制——释义词典和发音词典是两个独立的 StarDict 包,前者条目里用 bword 链接指向后者,你点一下链接,第二个窗口就带出发音内容。整条链路是:选词 → 清洗 → sdcv 查包 → 渲染 HTML → 点 bword 链接 → 二次查询。

⚙️ 上手实操:5 步把发音链路配起来

  • 先备词典。"更多"→ 设置 → 词典设置 → 下载词典,挑一本释义词典;带音频或发音条目的 StarDict 包放进data/dict后,重启应用即被扫描到。
  • 长按选词查词,确认窗口能正常显示释义,按钮布局符合习惯(词典设置 → 自定义按钮可调)。
  • 如果释义条目里有 bword 链接(通常是加粗的单词),点它触发第二跳,打开发音词条。
  • 用 Kindle 或 Kobo 的话,在词典设置里勾选"Use external dictionary",让查词交给系统词典,由它处理发音。
  • 模糊匹配没查到结果时,按提示用 fuzzy search 重查一次,常见于选词多带了标点。

📖 关键代码走读:两个函数看懂发音入口

最值得看的是 frontend/apps/reader/modules/readerdictionary.lua 里的onLookupWord,它是整条链路的入口:

function ReaderDictionary:onLookupWord(word, is_sane, boxes, highlight, link, dict_close_callback) word = self:cleanSelection(word, is_sane) -- 清洗选中文字 Trapper:wrap(function() self:stardictLookup(word, self.enabled_dict_names, not disable_fuzzy_search, boxes, link, dict_close_callback) end) -- 包裹后发起查询 return true end

再看同一文件里的onHtmlDictionaryLinkTapped,它只截取关键行:

local url_prefix = "bword://" if link.uri:sub(1, url_prefix:len()) == url_prefix then word = link.uri:sub(url_prefix:len() + 1) -- 取出链接里的单词 elseif link.uri:find("://") then return -- 非 bword 协议一律不处理 end -- 随后 Trapper:wrap 内再次 stardictLookup,完成第二跳

两段代码合起来就是全部真相:KOReader 把发音交给词典作者用 bword 链接表达,自己不发声。

🧩 边界与扩展:哪些设备听得到,插件能做什么

先说限制。仓库里找不到 espeak、flite 之类的 TTS 引擎,也没有音频播放器调用——所以没有内置语音合成。发音是否可用取决于两件事:词典包里有没有发音资源,以及设备有没有发声能力。Android 上系统 TTS 和扬声器齐全,是体验最好的平台;Kindle、Kobo 的扬声器与系统词典 API 各不相同,能走"外部词典"通道的设备才真正听得到;部分老型号干脆外放受限。语言支持则完全跟随词典数据,你想听哪个语种,就得备哪个语种的发音词典。

扩展方向倒是有现成的钩子:

  • 写一个 koplugin 插件监听WordLookedUp事件(stardictLookup开头就会派发),在回调里调用设备 TTS,等于给所有平台补上发音按钮。
  • 利用ReaderDictionary:addToDictButtons这个公开 API 往结果窗口注入自定义按钮,把插件的发音动作做成可见入口。
  • 把发音词典组织成 bword 第二跳,零代码成本用上仓库原生支持的路径。

想动手的话,先读 doc/Development_guide.md 了解 KOReader 的架构,词典的全部逻辑都集中在 frontend/apps/reader/modules/readerdictionary.lua 这一个文件里,翻一遍就能摸透。

【免费下载链接】koreaderAn ebook reader application supporting PDF, DjVu, EPUB, FB2 and many more formats, running on Cervantes, Kindle, Kobo, PocketBook and Android devices项目地址: https://gitcode.com/GitHub_Trending/ko/koreader

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进