写文档的人都有一种复杂情绪:代码有 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 适合提示性建议,不应当阻断 CI;warning 表示明显不符合规范,可以在 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": "to"substitution 规则的 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 --output=JSON README.mdJSON 输出会包含文件的完整路径、规则的元数据以及每个告警的精确位置,适合接入自定义报告系统或者数据统计。
7. 接入编辑器与 CI:让检查进入日常流程
命令行对于 CI 场景足够用,但要真正改变团队写作习惯,还得让检查发生在“下笔的时候”,而不是“提交之后”。在 VS Code 扩展市场搜索 Vale,安装扩展后,只要本机已经安装 vale 命令,扩展会自动读取 .vale.ini,并在打开 Markdown 文档时实时标记问题。需要提醒的是,扩展依赖命令行工具,如果修改了 PATH,建议重启编辑器让扩展重新加载环境变量。
编辑器接入带来的是即时反馈。写文档的人不需要等 CI 报错,在编辑过程中就能看到波浪线和告警提示。这看起来是小改进,实际上极大降低了规则的“对抗感”。规则不再像是一个事后找麻烦的检查官,而像一个随时提醒你的写作助手。
CI 接入才是 Vale 真正发挥工程价值的地方。因为 Vale 命令的退出码是有意义的:如果存在达到告警级别的问题,命令会返回非零退出码。这意味着不需要任何额外适配,只需要在 CI 脚本里运行 Vale 就能实现“文档不合规范,流水线变红”。
vale --minAlertLevel=warning 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/checkout@v4 - name: Run Vale uses: errata-ai/vale-action@v2 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 --output=JSON 的结果按规则分组统计,看看哪些规则命中最多,哪些规则几乎没有命中。命中多且合理的规则是团队的硬规范,应该保留;命中极少的规则说明大家已经形成了习惯,可以考虑降级或删除,保持规则集精简。规则越多,维护成本越高,噪音越大,越容易被忽视。
10. 写在最后
Vale 的价值不在于它有多聪明,而在于它把“写作规范”从一段躺在 Wiki 里的文字,变成了可执行的代码。代码 Review 能守住代码质量,文档 Review 同样需要工具来守住内容质量。Vale 是这条路上成熟度最高的开源选择之一。
建议你先找一个小型文档仓库,安装 Vale,启用一个现成风格,再写两三条团队自己的规则,跑一周看看效果。重点观察两个指标:误报率是否可接受,团队是否愿意在编辑器里接受实时提示。如果这两点都成立,再推 CI 阻断和更多规则。工具是越用越准的,Vale 尤其如此。规则和词表只有跟着团队的真实需求迭代,才会变成真正有价值的团队资产。
如果你正在维护对外文档、开发者门户或者开源项目的 README,这套流程尤其值得一试。文档工程化不需要一步到位,从一条规则开始,就能让文档质量在版本控制的守护下,一点一点变得可靠。