Back to Easydict

MDict

Easydict/Swift/Service/Dictionary/MDict/mdict-overview.md

2.20.06.3 KB
Original Source

MDict

MDict 实现用户导入的 MDX/MDD 离线词典查询能力。它负责管理词典文件、解析二进制索引、 查询条目内容、重写词典内资源链接,并把结果交给共享的词典 HTML 渲染层展示。

目录结构

MDict/
├── MDictService.swift                 # 查询服务入口和结果 HTML 包装
├── MDictConfigurationView.swift       # 设置页导入、启用、排序和删除 UI
├── MDictManager.swift                 # 导入记录持久化、加载和生命周期管理
├── MDictDictionary.swift              # 单本词典查询、MDD 资源解析和链接重写
├── MDictSearchIndex.swift             # 变形词、prefix、substring 和 fuzzy fallback
├── MDictReader/                       # MDX/MDD 二进制 reader、parser 和底层工具
├── mdict-overview.md                  # 本目录说明
└── mdict-architecture.svg

职责边界

  • MDictServiceQueryService 子类,负责读取启用词典、收集查询结果,并调用 DictionaryHTMLRenderer 生成结果面板 HTML。
  • MDictConfigurationView 是设置页 UI,负责触发 MDX/MDD 导入,并把启用、排序和删除操作 转发给 MDictManager
  • MDictManager 保存导入记录到 Defaults,查询时按需在后台加载启用的 MDictDictionary 实例,并在记录变化时发出通知。
  • MDictDictionary 表示一本 MDX 词典和它的 MDD 资源集合,负责查词、查资源、把图片、音频、 CSS 和脚本资源重写为 WebKit 可加载的形式;MDD reader 会在首次资源查询时懒加载,并缓存 常用 data URI、解析后的 CSS、MDX 同目录同名 CSS 文件和资源缺失结果。
  • MDictSearchIndex 在精确查词失败后按需建立轻量 headword 索引,提供变形词、前缀、词头 substring 和小编辑距离 fuzzy fallback,不索引正文 HTML;超大词库会跳过这个同步 fallback, 避免单次 miss 触发全量 key block 解析。
  • MDictReader/ 子目录只处理 MDX/MDD 二进制格式,不处理 UI、服务配置或结果面板样式。

主要流程

导入流程从 MDictConfigurationView 的文件选择器开始,MDictManager 根据扩展名导入 MDX 或匹配 MDD,合并同名资源文件并保存 MDictDictionaryRecord。MDD 匹配保留精确同名优先, 只把短数字后缀当作 multipart 资源后缀处理,避免年份等普通文件名误挂到无关 MDX。文件选择器 使用 MDX/MDD 扩展名类型并以通用 data 类型兜底,避免系统未注册自定义扩展名时没有可选类型。 首次查询启用词典时,MDictManager 在后台创建缺失的 MDictDictionary,重叠查询会复用同一个 in-flight 加载任务,避免重复解析同一 MDX;返回结果按 records 中的用户排序组织,保证查询 优先级和显示顺序一致。

查询流程从 MDictService.translate 开始。服务读取启用的 MDictDictionary,在调用方任务里 逐本调用 lookup(保留结构化并发取消语义,外层 translate 取消时下一本词典前会立即停止), 词典内部先通过 MDictReader 精确查找 key entry 和 record block;如果无结果,再尝试常见 英文变形词,并按需构建 headword 搜索索引用于 prefix、substring 和 fuzzy fallback。 超大词库只走精确查词和变形词路径,避免一次查询 miss 阻塞 UI。 命中后再把 HTML 中的本地资源链接改写为 data URI 或内部锚点。只有命中结果需要解析 MDD 资源时,词典才会创建对应的 resource reader。图片、音频和 CSS url 生成的 data URI 会按 LRU 缓存;外链 stylesheet 解析后的 CSS 也会缓存,并复用已缓存的内嵌资源。资源 key 查询会先尝试 词条原始路径,再生成反斜杠和前导斜杠变体,兼容不同 MDD 生成器的路径风格。CSS 和脚本等文本 资源优先使用 MDX header encoding 解码,再回退到常见 Unicode 编码;如果 CSS 内部资源受 单次 data URI 预算影响未完全替换,这份结果不会进入 stylesheet 缓存。最终服务把每本词典 的 HTML section 交给 DictionaryHTMLRenderer,由共享词典结果模板渲染。 如果 MDX 文件旁边存在同名 .css,例如 concise-enhanced.mdx 对应 concise-enhanced.cssMDictDictionary 会在首次命中 HTML 查询时读取并缓存该样式,然后 注入到词条内容前面,适配 MDict 词库常见的外置样式优化文件。 资源改写会优先处理脚本、stylesheet、图片和 CSS url,再处理音频链接;srcset 候选写入 data URI 时会把其中的 , 转义为 %2C,避免被浏览器误当作候选分隔符切碎。外链脚本被 改写成内联时会保留原 <script> 上的属性(如 type="module"defer),只剥离 src 和原有 nonce,再注入结果页 CSP 所需的 easydict-mdict nonce,并转义 </script 片段, 避免 HTML parser 提前截断脚本。普通 sound://mdict-sound:// 链接都会被改写为 data URI,避免 click handler 把无 scheme 处理器的 URL 喂给 new Audio(...) 而播放失败;负向前缀确保不会重复改写已被替换过的位置。 javascript:new Audio(...) 中的本地音频会先被改写为 data URI,并由结果页 click handler 拦截播放,避免被 CSP 当作导航阻断。 mdict-entry:// 跳转由结果面板按 href payload 查询,而不是依赖锚点展示文本。 单次查询有 data URI 数量和总字节预算,避免大词条因为数百个发音资源被同步内联而阻塞查询。

调试入口

  • 导入失败时,先检查文件扩展名、MDX/MDD 同名匹配,以及 MDictManager.loadErrors
  • 查询无结果时,检查 MDictManager.dictionariesForLookup()、词典大小写设置、key block 边界和 MDictSearchIndex fallback candidates。
  • 图片、音频或样式缺失时,优先检查 MDictDictionary 的 resource key candidates、同名 .css 文件路径、资源重写和 data URI/CSS cache 命中。
  • 解析、解压或加密相关错误,从 MDictReader/ 子目录里的 MDictReaderMDictBinaryMDictKeyBlocksMDictRecords 开始定位。
  • 结果面板样式或高度异常,回到 MDictService.wrapWithStyleDictionaryHTMLRenderer 排查。