ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Vale:为文档引入静态检查的开源Linter,从术语到CI全覆盖

Vale:为文档引入静态检查的开源Linter,从术语到CI全覆盖 写文档的人都有一种复杂情绪代码有 ESLint、Checkstyle 这类工具兜底缩进不对、命名不规范、一行超过 80 个字符CI 直接变红可轮到 README、接口文档、错误码说明、PR 描述这些同样每天都在产出的“文字资产”整个团队基本靠人眼 Review。更麻烦的是A 同学写文档习惯用 “login”B 同学写接口描述用 “sign in”C 同学在发布公告里写 “Login”。三个人写得都对但放在一起文档就是不专业。Vale 就是来解决这个问题的。它是一个开源、免费、用 Go 编写的命令行 Linter专门针对自然语言文本做静态检查。把它理解成“ESLint但用在文字上”基本不会偏差。它的工作方式不是靠 AI 猜而是靠一套可编程、可版本化、可嵌入 CI 的规则把文档里的术语、大小写、禁词、风格偏好全部变成机器可检查的约束。我的判断是Vale 不是又一个“写作辅助工具”而是文档工程化链条里最重要、也最容易被低估的一环。它不承诺让文章写得更好但它能保证团队里的文档在术语、大小写、风格上保持一致。这种一致性恰恰是文档从“个人作品”变成“团队资产”的分水岭。这篇文章会从 Vale 的核心概念讲起然后完整演示安装、配置、编写自定义规则、接入编辑器和 CI最后给出常见问题排查和工程化落地建议。读完你会知道它适合什么团队不适合什么场景以及怎么用最小的规则量先把流程跑起来。1. 这篇文章真正要解决的问题过去几年DevOps 和工程化理念已经把代码生产的每个环节都武装到了牙齿代码有 linter提交信息有 commitlint依赖有漏洞扫描合并请求有自动化测试。但文档几乎是自动化检查的盲区。一篇文档合不合规范往往取决于 Reviewer 今天的心情和精力。这种状态带来的问题很具体也很常见。首先是术语不一致。接口文档里写的是 “API”内部 Wiki 里写的是 “api”错误提示文案里写的是 “Application Programming Interface”。如果团队有搜索引擎收录、对外文档、开发者门户这些场景这种不一致会直接影响专业度和检索效果。其次是禁词和风格偏好无法执行。很多团队有“写作规范”比如不要用 “obviously”“simply”不要用 “utilize” 而用 “use”但规范文件躺在 Wiki 里真正写文档的时候没人记得住。再就是新人成本。新同学刚加入团队光是要搞清楚哪些词能用、哪些不能、大小写怎么约定就需要翻很多文档、问很多人。Vale 的定位就是把这套“约定”变成代码。它通过规则文件描述“什么是好文案”然后在命令行、编辑器、CI 三个层面同时生效。有了它Reviewer 不再需要为了一个大小写问题专门打回 PR提交者也不需要靠记忆去遵守规范。机器先筛一遍人只负责判断机器筛不出来的内容。这篇文章适合这几类读者正在维护大量 Markdown 文档的研发团队负责技术文档、开发者文档、API 描述文案的技术写作人员做开发者门户或对外文档平台希望规范内容质量的后端工程师以及所有被“文档 Review 全靠肉眼”折磨过的开源项目维护者。需要提前说明的是Vale 并不适合当作“作文批改工具”它解决不了“这句话读起来不顺”“这段逻辑不清晰”这类主观问题。它的边界非常明确只做机器擅长的一致性检查把人的精力留给真正需要人的地方。2. Vale 是什么给自然语言做静态检查的引擎Vale 是一个命令行程序核心功能是读取你指定的文件解析出其中的文本内容然后按照规则逐条匹配最后把命中的问题以告警形式输出。整个流程和 ESLint 对 JavaScript 的处理方式几乎一样输入文件、加载配置、运行规则、输出问题列表。有一个容易混淆的点Vale 不是 Grammarly 之类的 AI 语法检查工具。Grammarly 这类产品基于语言模型会判断“这个句子是不是通顺”“这个表达是不是地道”它解决的是“质”的问题。而 Vale 解决的是“规”的问题。它不判断句子好不好只判断你有没有违反团队事先定好的规则。这听起来好像很“低级”但恰恰是这种可预测、可解释、可配置的特性让它适合进入工程流水线。因为规则是确定的所以运行结果可以复现因为规则写在 YAML 里所以可以纳入版本控制因为命令有退出码所以可以和 CI 集成。这些特性AI 语法检查工具反而不一定有。从实现层面看Vale 用 Go 编写分发形式是单二进制文件没有运行时依赖安装成本很低。它原生支持 Markdown、HTML、reStructuredText、AsciiDoc、LaTeX 以及纯文本等多种格式。读取这些文件时Vale 会先解析文档结构把代码块、行内代码、链接地址等内容和正文区分开避免把代码或 URL 当成普通文字来检查。这也是它和“用正则字符串匹配整个文件”的简单脚本最本质的区别。另一个值得关注的特点是速度。Go 编译出来的单文件程序扫描一个几百篇文档的仓库通常只需要几秒钟。这意味着它可以被放进 pre-commit 钩子也可以每次保存文件时在编辑器里自动运行不会让人觉得“卡”。对于团队工具来说速度往往是决定工具能不能被日常使用的关键因素。3. 核心概念Style、Rule、Scope 与告警级别要真正用明白 Vale需要先理解四个核心概念Style、Rule、Scope、告警级别。这四个概念和 ESLint 里的 plugin、rule、文件作用域、error 级别几乎一一对应。Style 是一组规则的集合。你可以把它理解成一个插件或一个风格包。Vale 官方发布包会附带若干现成风格比如面向技术文档的 Microsoft 风格、Google 风格以及整合了常见写作问题的 proselint、write-good、alex 等。这些风格各有侧重Microsoft 风格关注术语规范和技术写作习惯write-good 关注常见的弱表达和赘词alex 关注文字中的不当语气。团队使用 Vale 的第一步通常就是启用其中一两个风格先看效果再决定要不要定制。Rule 是 Vale 的最小检查单元每个规则就是一个 YAML 文件。官方规则体系里有几种常用的规则类型它们的检查逻辑各不相同。我整理了一个表格方便对比。规则类型检查逻辑典型使用场景existence判断某个或某些 token 是否出现禁用词、敏感词、不礼貌表达substitution判断某个词是否出现并提示替换词统一术语、替换复杂表达occurrence统计某个 token 的出现次数并设置上限限制 TODO 数量、限制感叹号数量repetition检测连续重复的单词检测 “the the”“and and”consistency检测同一概念的不同写法是否混用统一 “e-mail” 和 “email”、“API” 和 “api”spelling基于词表或词典做拼写检查检查专业术语拼写、品牌名大小写Scope 表示规则作用于文档的哪个部分。比如一条规则只想检查标题不想检查正文只想检查正文不想检查注释或代码块。Vale 在解析 Markdown 等格式时会把文档分成不同的文本区域规则可以通过配置来决定检查范围。默认情况下代码块和行内代码不会被当作普通正文处理这一点对技术文档尤其重要。告警级别则决定了问题的严重程度。Vale 支持三个级别suggestion、warning、error。suggestion 适合提示性建议不应当阻断 CIwarning 表示明显不符合规范可以在 PR 中提示error 表示严重问题可以直接让 CI 变红。合理的用法是把“必须遵守”的硬性规则设为 error把“建议优化”的软性规则设为 suggestion避免一开始就上太多 error 导致团队抵触。4. 环境准备安装与验证Vale 的安装方式非常轻量因为它是一个 Go 编译的单文件程序。macOS 用户可以直接使用 Homebrew 安装命令非常简单。brew install valeLinux 环境更推荐直接访问 GitHub Releases 页面下载对应平台架构的压缩包解压后把二进制文件放到 PATH 目录中。例如将 vale 放到 /usr/local/bin然后验证版本。Windows 用户可以通过包管理器搜索 vale 进行安装也可以下载 Windows 二进制如果公司安全策略限制较多使用 WSL 中的 Linux 版本也是很常见的做法。安装完成后在终端执行以下命令确认安装成功。vale --version正常输出会显示类似 “vale version 3.x.x” 的版本信息。需要注意的是Vale 的各版本之间配置文件格式基本稳定但新版本会不断增加规则类型和命令参数。具体版本号请以官方 Release 页面为准本文演示的是通用思路不绑定某个特定版本。确认命令可用后可以先找一篇 Markdown 文档试跑一次。即使还没有任何配置文件Vale 也会给出提示告诉你缺少配置。这个阶段不需要追求结果只需要确认二进制能正常工作。接下来需要做的是创建配置文件目录结构让 Vale 知道规则放在哪里。5. .vale.ini 配置一次讲清楚关键项Vale 的配置入口是一个名为 .vale.ini 的文件使用 INI 格式通常放在仓库根目录。这个文件虽然不长但它是整个工具的行为总开关。一个最小配置包含两部分全局设置和文件范围设置。先说全局设置。StylesPath 指定样式目录的路径Vale 会从这个目录加载所有风格包和规则。MinAlertLevel 设置最低告警级别低于这个级别的检查结果不会输出。Vocab 指定团队词表词表里维护的是团队自定义的术语Vale 在检查拼写时会把这些词视为合法。文件范围设置则使用方括号加通配符例如 [.md] 表示只对 Markdown 文件生效[] 表示对所有文件生效。最常见的配置是在 Markdown 文件范围内声明 BasedOnStyles意思是启用哪些风格包。下面是一个典型的最小配置。# 文件路径.vale.ini StylesPath .vale/styles MinAlertLevel suggestion Vocab TeamTerms [*.md] BasedOnStyles TeamStyle, Microsoft这个配置的含义是样式目录在 .vale/styles所有 Markdown 文件启用 TeamStyle 和 Microsoft 两套风格团队词表叫 TeamTerms。目录结构对应如下。仓库根目录 ├── .vale.ini └── .vale └── styles ├── TeamStyle │ ├── WeakWords.yml │ └── PlainEnglish.yml └── Vocab └── TeamTerms └── Accept.txt这里容易踩坑的地方是目录名必须严格对应。ini 里写的是 TeamStyle那么 .vale/styles 下就必须有一个名为 TeamStyle 的目录。如果目录名写错规则不会报错只是静默不生效。排查此类问题时要先检查目录名是否和配置一致。Vocab 词表是 Vale 比较有特色的机制。在实际项目中品牌名、产品名、团队内部术语往往不在标准拼写词典里如果不用词表维护这些词会被拼写规则反复标记为错误。你只需要在 .vale/styles/Vocab/TeamTerms/Accept.txt 里每行写一个词Vale 就会在检查时把所有这些词视为合法。# 文件路径.vale/styles/Vocab/TeamTerms/Accept.txt CDN DevOps Kubernetes 微服务配置完成后在仓库根目录执行 vale README.md就能看到第一条检查结果。如果你之前从来没有配置过任何规则这个阶段建议先启用一个现成风格比如 Microsoft跑一遍看效果再逐步加自己的规则。6. 编写自定义规则从 3 个示例开始团队真正开始依赖 Vale通常是从编写自定义规则开始的。因为现成风格解决的是通用问题而团队内部约定只有自己知道。下面用三个最常用的规则类型演示从零写规则到跑通检查的完整流程。第一个是 existence 类型用于禁用某些词。很多团队都有“写作黑名单”比如不要让文档里出现 “obviously”“simply” 这类容易显得傲慢或者没有信息量的词。新建一个 YAML 文件。# 文件路径.vale/styles/TeamStyle/WeakWords.yml extends: existence message: 尽量少用弱化表达%s。 ignorecase: true level: warning tokens: - obviously - simply - easily - just这个规则的逻辑是在文本里查找 tokens 列表中的词找到之后按 message 提示级别是 warning。ignorecase 表示忽略大小写这样 “Obviously” 和 “obviously” 都会被命中。第二个是 substitution 类型用于统一术语和替换复杂表达。比如团队规定不要用 “utilize”统一用 “use”。这类规则的好处是它不仅能检查错误还能给出正确的替换建议Reviewer 不需要再手动写评论。# 文件路径.vale/styles/TeamStyle/PlainEnglish.yml extends: substitution message: 建议用 %s 代替 %s。 ignorecase: true level: suggestion swap: utilize: use a lot of: many in order to: tosubstitution 规则的 swap 字段是键值对前一个是不推荐的写法后一个是推荐写法。命中之后消息模板里的第一个 %s 是推荐词第二个 %s 是被替换的词。级别设为 suggestion表示这属于优化建议不建议阻断 CI。第三个是 occurrence 类型用于限制某个词的出现次数。比如团队不希望文档里出现太多 TODO因为 TODO 往往意味着内容没写完。可以这样配置。# 文件路径.vale/styles/TeamStyle/TodoCount.yml extends: occurrence message: TODO 在一篇文章中不要超过 2 次。 level: warning scope: text max: 2 token: TODO这个规则会统计文本中出现 TODO 的次数超过 2 次就触发告警。scope 设置为 text表示只在正文范围检查。三个规则文件创建好之后还需要确认 .vale.ini 里已经启用了 TeamStyle。然后在仓库根目录执行检查。vale README.md如果 README.md 里出现了 obviously、utilize、多个 TODO终端会输出类似下面的结果。README.md 3:5 warning Try to avoid using obviously. WeakWords.yml 7:2 warning Use use instead of utilize. PlainEnglish.yml 12:1 warning TODO must not occur more than 2 times. TodoCount.yml ✖ 3 errors, 0 warnings and 0 suggestions in 1 file.输出中的每一行都包含文件、行号、列号、告警消息和命中规则的文件名。这种格式和编译器输出很像开发者的接受成本很低。如果需要在脚本或工具链里解析检查结果可以使用 JSON 输出格式。vale --outputJSON README.mdJSON 输出会包含文件的完整路径、规则的元数据以及每个告警的精确位置适合接入自定义报告系统或者数据统计。7. 接入编辑器与 CI让检查进入日常流程命令行对于 CI 场景足够用但要真正改变团队写作习惯还得让检查发生在“下笔的时候”而不是“提交之后”。在 VS Code 扩展市场搜索 Vale安装扩展后只要本机已经安装 vale 命令扩展会自动读取 .vale.ini并在打开 Markdown 文档时实时标记问题。需要提醒的是扩展依赖命令行工具如果修改了 PATH建议重启编辑器让扩展重新加载环境变量。编辑器接入带来的是即时反馈。写文档的人不需要等 CI 报错在编辑过程中就能看到波浪线和告警提示。这看起来是小改进实际上极大降低了规则的“对抗感”。规则不再像是一个事后找麻烦的检查官而像一个随时提醒你的写作助手。CI 接入才是 Vale 真正发挥工程价值的地方。因为 Vale 命令的退出码是有意义的如果存在达到告警级别的问题命令会返回非零退出码。这意味着不需要任何额外适配只需要在 CI 脚本里运行 Vale 就能实现“文档不合规范流水线变红”。vale --minAlertLevelwarning docs/这条命令的意思是检查 docs 目录下的文档只要出现 warning 及以上级别的问题就返回非零。如果只想让 error 级别阻断 CI可以把级别改成 error。这种分级策略非常重要它决定了工具是“合作者”还是“警察”。如果团队使用 GitHub可以直接在 Actions 里接入。下面是一个典型的 workflow 示例具体版本和输入参数以官方仓库 README 为准。# 文件路径.github/workflows/vale.yml name: prose-lint on: pull_request: paths: - docs/** jobs: prose: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Run Vale uses: errata-ai/vale-actionv2 with: files: docs/**/*.md env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}这个 workflow 的含义是只有当 PR 修改了 docs 目录下的文件时才触发运行 Vale 检查这些文件并在 PR 评论中报告问题。配置完成后文档就成了被自动化守护的“代码”这和团队保护核心业务代码的方式完全一致。团队落地的时候我建议分三步走。第一步先在 CI 里以 suggestion 级别运行只输出提示不阻断任何提交让团队先看一段时间收集大家对误报的反馈。第二步根据反馈维护词表和规则把高频误报的词加入 Accept.txt。第三步逐步把告警级别提升到 warning 或 error让规则真正产生约束力。这比第一天就上 error 级别要温和得多团队接受度和最终效果反而更好。8. 常见问题与排查思路Vale 本身设计得比较简单但实际落地时还是会遇到一些问题。下面整理了一份排查表。问题现象可能原因排查方式解决方案运行 vale 提示找不到配置文件当前目录不在仓库根目录查看报错提示中的路径切换到仓库根目录执行或用 --config 指定配置路径自定义规则没有任何输出样式目录名和 BasedOnStyles 不一致检查 .vale/styles 下的目录名统一目录名和配置中的值中文文本基本没有检查结果内置规则大多面向英文确认规则中是否包含中文 token为中文团队编写自定义规则或补充中文词表检查整个仓库很慢范围过大包含构建产物或第三方目录用 glob 限定文件范围在命令中指定 docs/**避免扫描无关目录VS Code 扩展不显示检查结果扩展找不到 vale 命令在终端执行 vale --version将 vale 加入 PATH然后重启编辑器误报太多团队词表没有维护看告警内容判断是规则问题还是用词问题使用 Accept.txt 加白名单或直接下线不合适的规则我在实际使用中遇到最多的是中文场景的问题。Vale 的很多现成风格例如 Microsoft、Google、write-good都是针对英文写作设计的。中文文本没有被命中并不代表 Vale 有问题而是这些规则里根本没有中文 token。如果团队以中文文档为主有两个可行的方向。一是完全自己写规则用 existence 和 substitution 检查中文禁词、统一中文术语例如把“登入”和“登录”统一。二是维护一个完善的中文词表让拼写检查不至于把所有中文术语都报成错误。这两个方向都不复杂但需要花时间沉淀属于典型的“越用越准”的工具。另一个容易被忽略的坑是编码问题。Vale 对 UTF-8 支持正常但如果文档是 GBK 或者 GB2312 编码就可能出现乱码或者检查不到内容。建议团队统一要求所有文档使用 UTF-8 编码这既是 Markdown 的默认要求也是 Vale 正常工作的前提。9. 最佳实践在团队里优雅落地 Vale工具落地最大的敌人不是技术难度而是团队抗拒。Vale 这种工具尤其如此因为它检查的是每个人写的东西很容易被理解为“被机器挑毛病”。从工程实践角度看有几个原则能显著降低这种抗拒感。第一术语词表先行。不要一上来就启用一堆现成风格而是先花一小时整理团队最常用的术语例如产品名、品牌名、大小写约定放进 Accept.txt。词表是团队自己的语言资产维护好词表之后再启用现成风格的拼写检查误报率会大幅下降。第二规则渐进式增加。第一次接入只启用一个现成风格比如 Microsoft并且只观察不阻断。运行一周后查看哪些规则命中率高、哪些规则误报多再决定保留或关闭。自定义规则从最痛的三个问题开始例如团队反复出现的大小写不一致、禁词、术语混用不要试图一次性把规范写全。第三告警级别分级管理。suggestion 只提示warning 在 PR 里提醒error 才阻断提交。一个常见的反模式是把所有规则都设为 error结果 CI 整天红团队反而习惯于无视红色。分级的核心目标是让规则集中在真正重要的问题上。第四配置必须入库。.vale.ini、.vale/styles 目录、词表文件都要提交到版本控制。文档规范和代码规范一样应该是版本的、可追溯的。任何人修改规则都应该走代码评审流程这样才能保证规则的变更是有意的而不是某个人本地随手改的。第五明确 Vale 的边界。Vale 解决一致性不解决表达质量。文章结构清不清楚、逻辑顺不顺、描述准不准这些需要人来判断。好的做法是让 Vale 处理“机器能判断的 20%”把人的精力集中在“机器判断不了的 80%”。如果团队期望 Vale 能替代 Review那大概率会失望如果团队期望 Vale 能减少 Review 中的琐碎争吵那它会非常出色。第六定期用 JSON 输出做统计。可以写一个小脚本把 vale --outputJSON 的结果按规则分组统计看看哪些规则命中最多哪些规则几乎没有命中。命中多且合理的规则是团队的硬规范应该保留命中极少的规则说明大家已经形成了习惯可以考虑降级或删除保持规则集精简。规则越多维护成本越高噪音越大越容易被忽视。10. 写在最后Vale 的价值不在于它有多聪明而在于它把“写作规范”从一段躺在 Wiki 里的文字变成了可执行的代码。代码 Review 能守住代码质量文档 Review 同样需要工具来守住内容质量。Vale 是这条路上成熟度最高的开源选择之一。建议你先找一个小型文档仓库安装 Vale启用一个现成风格再写两三条团队自己的规则跑一周看看效果。重点观察两个指标误报率是否可接受团队是否愿意在编辑器里接受实时提示。如果这两点都成立再推 CI 阻断和更多规则。工具是越用越准的Vale 尤其如此。规则和词表只有跟着团队的真实需求迭代才会变成真正有价值的团队资产。如果你正在维护对外文档、开发者门户或者开源项目的 README这套流程尤其值得一试。文档工程化不需要一步到位从一条规则开始就能让文档质量在版本控制的守护下一点一点变得可靠。
返回列表