KOReader 词典查询功能深度解析:划词到释义背后,3 个齿轮如何咬合工作?
【免费下载链接】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 词典查询体验的全部诉求。它不依赖网络、不跳出阅读界面,把「查词」变成了和「翻页」一样顺手的小动作。
它到底解决了什么
一句话:把「离线查词典」变成阅读器内置的零摩擦动作,查词、释义、换词典、顺带查 Wikipedia 全部在一次抬手内完成。
为什么需要它?纸质书的词典要翻页查找,手机查词要切 App 断上下文,而 KOReader 跑在 Kindle/Kobo 这类墨水屏设备上时,屏幕刷新还偏慢——任何一个多余步骤都会被放大成烦躁。所以这个功能的设计目标是:一次触发,就地出结果。
原理拆解:3 个咬合的齿轮
整个功能集中在 frontend/apps/reader/modules/readerdictionary.lua 里的ReaderDictionary模块(约 2000 行),可以拆成三段流水线:
齿轮 1:把选中的文本「洗」成能查的词
onLookupWord是入口,它做的第一件事不是查词,而是调用cleanSelection给文本做预处理。想象你在 PDF 里选中的其实是qu'autrefois,——带引号、带逗号、带不可断空格。cleanSelection会依次:
- 把不可断空格(U+00A0)换成普通空格,掐掉首尾空白;
- 把花引号换成普通引号,剥掉首尾标点;
- 删掉音节分隔符
·、|、重音符ˈˌ; - 去掉英语所有格
's和法语缩合前缀l' s' t'、qu'。
洗完之后qu'autrefois,变成autrefois,才真正拿去查。这一步是「查不到」的第一大杀手来源——选得脏,查得白。
齿轮 2:把词「喂」给 sdcv 这个外部工具
KOReader 自己不做词典检索,它把一个命令行工具sdcv(Stardict 的命令行查询器)当外援。rawSdcv拼出一条命令,核心参数如下(逐行注释版):
local args = { "./sdcv", -- 查询器本体(Android 上是 libsdcv.so) "--utf8-input", -- 输入按 UTF-8 解析,避免中文/法语乱码 "--json-output", -- 结果输出成 JSON,方便 Lua 直接解析 "--non-interactive", -- 不要交互式提问,跑完就退出 "--data-dir", dict_dir, -- 指向 data/dict 词典目录 }几个值得注意的咬合细节:
- 模糊搜索是开关:不传
--exact-search时,词形不对也能「猜」(比如查going可能命中go);但代价是慢,几十个词典加模糊匹配时单次查询能拖到 10 秒左右; - 双词典目录兜底:除了
data/dict,还会查data/dict_ext——官方注释明说,可以往里放「词量大但释义差」的兜底词典,避免它们掩盖主词典的好结果; - 等待时能取消:查询走
Trapper:dismissablePopen执行,屏幕上那个「Searching dictionary for: xxx」提示气泡本身就是一个可点按的陷阱——嫌慢?点它一下,进程直接终止。 - 结果去重展平:sdcv 可能对同一词先精确查再模糊查,
startSdcv用词典名+词+释义作哈希把重复条目滤掉。
齿轮 3:浮出一张「速查卡片」
结果回来后,showDict实例化 frontend/ui/widget/dictquicklookup.lua 里的DictQuickLookup窗口。它是个带底部按钮池的浮层,默认布局:
| 第一排 | 第二排 |
|---|---|
prev_dict/highlight/next_dict | wikipedia/search/close |
也就是说:左右切不同词典的结果、一键高亮原文、直接追问 Wikipedia、在释义里再查词、关闭卡片,五个动作都摆在指尖。释义 HTML 还能挂自定义样式——和词典.ifo文件同名的.css会被自动加载(见readDictionaryCss),.lua文件则提供「结果清洗钩子」,修补渲染引擎吃不下的高质量 HTML。
卡片里还有个隐藏彩蛋:如果词典条目本身写了bword://word这种链接,点它会触发onHtmlDictionaryLinkTapped,就地把链接里的词当新词再查一轮——相当于「释义里的词继续查」,不依赖任何额外按钮。
眼见为实:一次查词的完整旅程
- 选中:长按或双击划出单词(触摸位置参考 KOReader 的触摸区划分,选词区域在页面中部之外):
- 等待:屏幕下方浮出
Searching dictionary for: xxx气泡;模糊搜索下词多典多时可能要等几秒,点气泡可随时打断。 - 出卡:释义以 HTML 卡片形式弹出,词典名显示在卡头;连续点
next_dict可以在不同词典间轮播同一词的释义。 - 追问:点
wikipedia直接拉在线释义(离线则优雅失败);点search可输入新词再查。 - 留痕:每次查询都会触发
WordLookedUp事件并写入查询历史(可在设置里关闭),插件世界靠这个事件「偷看」你的查词行为。
顺带一提,Android 桌面和 SDL 模拟器还支持「外部词典应用」:开启external_dict_lookup后,查词会调用 frontend/device/android/device.lua 里的doExternalDictLookup,把词交给手机上的第三方词典 App,回来时自动恢复阅读状态——这是官方留的「把查词外包出去」的口子。
边界与坑:新手最常踩的 5 个雷
- 没有预装词典:KOReader 本体不带任何词典,
data/dict为空时查询只会返回「No dictionaries installed」提示。词典是 Stardict 格式(.ifo索引文件),可离线下载,见 frontend/ui/data/dictionaries.lua 里的下载源清单。 - 模糊搜索的 CJK 陷阱:如果所有候选词都含中日韩字符,代码会强制关闭模糊搜索——因为 sdcv 对 CJK 的模糊匹配会返回上百条垃圾结果(
startSdcv里的注释原话)。所以「日英词典查不出来」有时是保护机制在工作。 - 选词带脏字符:
cleanSelection只处理常见情况。德语变音字母、复杂组合字符这类场景,官方代码里甚至有被注释掉、待验证的去音标逻辑——选词越「国际化」,越可能需要手动改词再查。 - 外部查询不是全平台可用:
doExternalDictLookup只在 Android 和 SDL 桌面端实现了canExternalDictLookup = yes,Kindle/Kobo/PocketBook 等真机上这个开关根本不会出现。 - 没有内置的独立 TTS 发音引擎:仓库里没有 eSpeak/系统 TTS 的调用代码。所谓「听发音」走的是两条外围路径:词典自带音频资源(sdcv 查到的条目里可能含音频链接,由条目内容承载),或者 Android 上用外部词典 App 兜底。指望阅读器本身朗读单词,目前没有现成模块。
往前多想一步:插件可以插在哪
给二开同学标好三个「接口螺丝口」:
WordLookedUp事件:每次查词都会携带单词和书名广播,写个.koplugin挂监听就能做查词统计、生词本、发音触发——不用动一行核心代码。- 插件按钮池:
DictQuickLookup:populatePluginButtons会把插件贡献的按钮塞进卡片按钮池(参考 frontend/ui/plugin/switch_plugin.lua 的注册套路),等于给速查卡片直接加个「🔊 朗读」按钮的合法位置。 - 设备抽象层:frontend/device/ 下每个平台一个
device.lua,canXxx/doXxx成对出现的模式(如canExternalDictLookup/doExternalDictLookup)就是标准范式——想给某个平台接 TTS 引擎,照这个形状抄一份canSpeakWord/doSpeakWord就行,上层代码零改动。
另有两个现成的扩展素材:data/dict_ext目录可以直接丢入兜底大词典改善命中率;词典.lua清洗钩子允许你针对某个「脏 HTML」词典写专属修正函数。词典功能的骨架是「文本→sdcv→卡片」这条极简流水线,真正让它好用的,是围绕这三个齿轮的取消机制、去重、预设切换(Presets)与事件广播——二开时顺着这些咬合点下手,比另起炉灶省力得多。
【免费下载链接】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),仅供参考