Joplin v1.4 拼写检查器功能全解:从启用、语言切换到源码级实现原理
【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin
Joplin 是一款以隐私为核心的跨平台笔记应用(支持 Windows、macOS、Linux、Android 与 iOS)。本篇文章围绕 readme/news/20201126-114649.md 所记录的 v1.4 拼写检查器(Spell Checker)发布内容展开,系统讲解该功能的启用方式、两种编辑器(富文本编辑器 TinyMCE 与 Markdown 编辑器 CodeMirror)下的使用差异、语言管理与右键菜单的实战操作,并结合仓库中的SpellCheckerService、原生驱动与设置定义等源码,剖析其底层实现原理。读完本文,你将能熟练配置 Joplin 的拼写检查,并理解"启用开关—语言选择—编辑器联动"整条调用链是如何工作的。
一、背景:为什么拼写检查器是最受期待的功能
拼写检查器是 Joplin 社区投票需求(GitHub issue #275)中获得 340 票的最高呼声功能,于 v1.4 正式落地。根据原公告,这项功能长期难以实现,主要原因是 Joplin 使用了自研的富文本编辑器(Rich Text)与 Markdown 编辑器两套自定义编辑器,而拼写检查必须与编辑器底层输入模型深度耦合;直到 Electron 框架在相关能力上取得进展,加上 Caleb John 对 Markdown 编辑器的改进,才使该功能成为可能。
从源码看,这一"框架依赖"体现在两个层面:
- 底层拼写引擎由 Electron 提供:桌面端通过
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.ts中的SpellCheckerServiceDriverNative直接调用 Electron session 的内建拼写检查 API(setSpellCheckerLanguages、getSpellCheckerLanguages、addWordToSpellCheckerDictionary等),并不自带词典数据。 - 通用逻辑与编辑器解耦:
packages/lib/services/spellChecker/SpellCheckerService.ts中的SpellCheckerService定义了语言状态管理、历史记录、右键菜单等通用逻辑,通过抽象驱动基类packages/lib/services/spellChecker/SpellCheckerServiceDriverBase.ts屏蔽平台差异。
也就是说,Joplin 将"拼写检查策略"与"具体实现驱动"分层:SpellCheckerServiceDriverBase声明了availableLanguages、setLanguages、language、addWordToSpellCheckerDictionary等接口,而桌面端驱动是 Electron 原生实现;若未来有其他平台(如移动端)接入,只需提供新的驱动即可。
二、启用拼写检查器:地球图标与 Tools 菜单
在 v1.4 中,启用或禁用拼写检查非常简单:
- 点击 Joplin 主窗口右上角的地球图标(globe icon);
- 或者通过Tools(工具)菜单 → Spell Checker(拼写检查器)打开同一菜单;
- 在弹出的菜单中勾选"Use spell checker"复选框即可开启。
菜单底部是"Change language"(更改语言)子菜单,其中列出了操作系统 / Electron 提供的全部可用语言。由于某些操作系统上的语言列表可能非常庞大,Joplin 会把最近选过的语言直接置顶展示在 "Use spell checker" 复选框下方,方便快速切换回常用语言。
从源码可以印证菜单的结构与交互:
- 命令声明位于 showSpellCheckerMenu.ts,命令名为
showSpellCheckerMenu,图标为fas fa-globe——这正是"地球图标"的来源。 - 菜单项由
SpellCheckerService.spellCheckerConfigMenuItems()(见 SpellCheckerService.ts)构建,其结构依次为:Use spell checker复选框(调用toggleEnabled()切换spellChecker.enabled设置)→ 最近选择语言列表(前面带分隔线)→ 分隔线 →Change language子菜单(每个语言一个复选框项,点击调用setLanguage())。
值得一提的是,当拼写检查开启且已选择语言时,工具菜单中的该项标题会动态显示当前语言(例如 "en, fr"),该逻辑由showSpellCheckerMenu的mapStateToTitle实现——它会读取spellChecker.languages设置,去重语言代码前缀后用逗号拼接。
三、语言选择机制与"最近使用语言"历史
语言是拼写检查的核心配置。Joplin 的语言管理有几个关键行为:
- 默认语言为
en-US:若用户从未设置过语言(spellChecker.languages为空数组),SpellCheckerService.setupDefaultLanguage()会优先尝试使用驱动当前生效的语言;若该语言不在可用列表内,则回退到en-US。 - 多语言可同时启用:
setLanguage()采用"切换"语义——若某语言已在启用列表中则移除,否则追加,多个语言可以同时勾选。 - 最近选择历史:每次选择语言都会写入 KvStore 的
spellCheckerService.latestSelectedLanguages键;历史上限为 5 条,且已启用(enabled)的语言始终保留在历史中,超过上限时才淘汰已禁用的语言(见 SpellCheckerService.ts 中的addLatestSelectedLanguage与languagesHistorySizeMax = 5)。
这些行为对应的持久化设置定义在 builtInMetadata.ts:
| 设置键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
spellChecker.enabled | Bool | true | 拼写检查总开关 |
spellChecker.language | String | '' | 旧版单语言设置,已废弃 |
spellChecker.languages | Array | [] | 当前启用的语言代码列表(如["en-US","fr"]) |
其中spellChecker.language已在 Setting.ts 中标记为被spellChecker.languages取代(oldName→newName迁移映射)。spellChecker.enabled的默认值为true,但由于 Markdown 编辑器存在独立的 Beta 开关(见下文),新用户在 Markdown 编辑器中仍默认不会触发拼写检查。
四、Markdown 编辑器 Beta 拼写检查的独立开关
原公告特别强调:即使勾选了 "Use spell checker",Markdown 编辑器中默认也不会进行拼写检查,因为该编辑器下的功能处于 Beta 状态。原因在于,为了让拼写检查生效,Markdown 编辑器需要使用一种特殊模式(contenteditable),而该模式在过去曾引发过各种问题(如光标位置不稳定、编辑内容偶发不保存或未反映到预览等)。v1.4 中该模式已表现稳定,但官方仍将其标记为 Beta。
启用步骤:
- 打开General(常规)设置;
- 展开Show Advanced Settings(显示高级设置);
- 勾选"Enable spell checking in Markdown editor"选项。
对应源码中的设置定义为 builtInMetadata.ts 中的editor.spellcheckBeta:
| 设置键 | 类型 | 默认值 | 适用范围 | 说明 |
|---|---|---|---|---|
editor.spellcheckBeta | Bool | false | 仅桌面端(AppType.Desktop) | 是否在 Markdown 编辑器中启用拼写检查(Beta) |
五、源码级原理解析:拼写检查在两种编辑器中如何落地
5.1 原生驱动:Electron Session 层
桌面端的全部拼写能力最终都落在 SpellCheckerServiceDriverNative.ts 上:
availableLanguages直接返回session().availableSpellCheckerLanguages(由 Electron/系统提供语言列表);setLanguages()会逐一校验语言:先尝试原语言代码,再尝试只保留语言部分(languageCodeOnly),若仍失败则尝试同语言的其他 locale 变体(localesFromLanguageCode),直到找到一个可被setSpellCheckerLanguages接受的值;全部失败则跳过该语言;- 传入空数组即可禁用拼写检查(
setSpellCheckerEnabled(effectiveLanguages.length > 0)),这是复用 Electron 官方行为(见代码注释引用的 electron issue #25228); - 该驱动还在
initialize()中处理了一个 Electron 42 on Linux 的兼容性问题:先以空语言集合初始化一次,否则默认语言会被忽略。
5.2 通用服务:状态同步与右键菜单
SpellCheckerService负责把设置状态同步给驱动:applyStateToDriver()会在启用状态或语言列表变化时调用driver.setLanguages(...)——启用时传入语言列表,禁用时传入空数组,从而让 Electron 层开/关拼写检查。
当用户右键点击拼写错误的单词时,contextMenuItems()(SpellCheckerService.ts)会构建以下菜单项:
- 分词建议列表(每个建议点击后通过
replaceMisspelling命令替换单词); - 若无建议则显示灰色不可用的
(No suggestions); Add to dictionary(加入词典):调用driver.addWordToSpellCheckerDictionary(),在 Electron 驱动中所有语言共享同一个词典。
5.3 富文本编辑器(TinyMCE):默认即生效
富文本编辑器默认开启浏览器拼写检查:在 TinyMCE.tsx 中设置了browser_spellcheck: true。同时,为了不干扰代码片段,编辑器会对行内代码(inline code)显式禁用拼写检查——将spellcheck属性设为false(见 TinyMCE.tsx 与 TinyMCE.tsx),避免代码被误报为拼写错误。
5.4 Markdown 编辑器(CodeMirror):依赖 Beta 开关
Markdown 编辑器使用 CodeMirror 实现。拼写检查是否生效取决于editor.spellcheckBeta:
- CodeMirror 5 版本中(v5/Editor.tsx):当
editor.spellcheckBeta为真时,编辑器采用contenteditable输入模式(这是触发浏览器拼写检查的必要条件),并显式设置spellcheck: true;否则使用textarea模式,浏览器拼写检查不生效。这正是原公告所说的"必须启用特殊模式"。 - CodeMirror 6 版本中(v6/utils/useEditorSettings.ts):
spellcheckEnabled同样直接映射到editor.spellcheckBeta设置,并作为EditorSettings传入编辑器。
可以推断,当前仓库中的 Markdown 编辑器始终保留"默认关闭、需手动开启 Beta"的设计,这是为了避免contenteditable模式可能带来的编辑稳定性问题影响大多数用户。
六、常见问题与使用建议
- 勾选了 "Use spell checker" 但 Markdown 编辑器中仍无红色波浪线?这是预期行为。请前往 General → Advanced Settings,勾选 "Enable spell checking in Markdown editor" 后再试。
- 语言列表太长、找不到想要的方言?使用 "Change language" 子菜单搜索,或直接在最近使用列表中选取——系统会记住你最近选择的语言(最多 5 个)。
- 不想让代码块被检查?富文本编辑器中 Joplin 已自动对行内代码关闭拼写检查;Markdown 编辑器下建议对代码片段使用代码块语法,以减少误报。
- 关于 Beta 稳定性:原公告提示,Markdown 编辑器下的拼写检查依赖
contenteditable特殊模式,虽然当时未发现明显缺陷,但若遇到光标错位、编辑未保存或预览不同步等问题,可在论坛反馈(Help → Joplin Forum)。
七、总结
Joplin v1.4 的拼写检查器是一个典型的"框架能力 + 应用层策略"组合:Electron 原生拼写检查提供了词典与语言数据,SpellCheckerService负责语言状态、历史记录与菜单生成,两套编辑器则各自通过设置项决定是否启用浏览器拼写检查。理解这条调用链后,你不仅能在界面中熟练配置,也能在排查"拼写检查不生效"问题时快速定位是开关未开、语言未选,还是 Markdown 编辑器的 Beta 选项未启用。
【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考