Vale 是一个面向自然语言文本的 Linter,英文定位就叫 Linter for Prose。简单说,它用命令行方式帮你检查散文、技术文档、博客文章里的用词、术语、一致性和风格问题,而不是检查语法错误或者做排版。这个定位让它和拼写检查器、Markdown 格式化工具完全不一样。如果你写英文技术文档,或者团队有明确的中英文写作规范,又不想每次 review 都靠人工去抓“这个术语前后不一致”“这个词不该用”“那个表达太口语化”这类问题,Vale 值得认真了解一下。
这篇文章按我自己的落地顺序写:先讲它到底适合解决什么问题,再讲怎么安装、怎么配置、怎么写自己的规则,最后是批量任务和实测中最容易踩到的坑。我不会把项目 README 复述一遍,只会写我实际用下来觉得关键的环节。
1. 先搞清楚 Vale 到底解决写作里的什么问题
1.1 它不是拼写检查器,也不是排版神器
很多人第一次接触 Vale 会误以为它是“高级版拼写检查”。实际上它的定位更接近“基于规则的文本检查器”。
拼写检查器解决的是“这个单词是否拼错了”,排版工具解决的是“代码块缩进是否统一、空行是否一致”。Vale 更关心的是:这句话里是不是用了某个团队禁用的词,某个术语的大小写是不是统一,某个动词的搭配是否更符合你所在领域的惯例。
举几个典型场景:
- 文档里既有
JavaScript又出现Javascript,规则可以自动标记后者。 - 团队要求避免使用
utilize这类词,统一写成use,规则可以提醒。 - 文案里出现
whether if、in order to这类冗长或错误的表达,规则可以建议替换。 - 产品文档规定必须写
Windows 11,不能写成Win11,规则可以校验。
这类检查靠人工 review 也能做,但问题是文档一多、团队一大人,人工 review 很难保持标准一致。Vale 的价值就是把这些机械、可枚举的判断写成规则,提交代码时自动跑一遍。
顺便说一个它在工程上的好处:Vale 是本地执行的命令行工具,默认情况下检查的是本地文件,不会把文档内容上传到别的服务。对内部文档和隐私要求较高的项目,这个特性比在线语法检查工具更可控。
1.2 适合谁,不适合谁
如果你属于下面这几类人,Vale 会很有用:
- 技术文档工程师,需要维护大量 Markdown、HTML、reStructuredText 文档。
- 开源项目维护者,希望 contributors 提交的文档能和项目风格保持一致。
- 团队里有文档评审环节,经常因为术语不统一、禁用词反复提意见。
- 独立博主或写作者,希望给自己建立一个固定的写作用词规范。
如果你只是偶尔想检查一段英文的语法和拼写,那在线工具可能更省事。Vale 本身不提供 AI 式的句子改写,也不做语法树分析。它更像一个“白名单和黑名单管理器”,你把约定写成规则,它替你严格执行。
还有一点需要提前理解:Vale 默认只提供引擎,不提供现成规则。你可以通过内置的样式包机制拉取一些社区维护的规则,也可以自己写规则,但不能装完就直接用。这个设计既是门槛,也是它可定制的来源。
2. 在电脑上把 Vale 跑起来:安装和环境准备
2.1 Windows / macOS / Linux 安装
Vale 提供全局二进制,安装方式取决于你的系统。
macOS 且使用 Homebrew,可以直接安装:
brew install valeWindows 如果使用 Chocolatey 或 Scoop,也可以从包管理器安装,例如:
choco install vale不想用包管理器也没关系。去它的 GitHub Releases 页面下载对应系统的压缩包,解压后得到一个vale可执行文件,把它放到PATH目录,或者放指定目录后在命令里写全路径。
安装完成后先确认命令可用:
vale --version能正常打印版本号,说明环境没问题。后面所有配置都和这个命令联动,所以这一步别跳过。
2.2 验证命令和最小示例
Vale 不是装完就能检查文本的。它要求目录里有一个.vale.ini配置文件,并且配置文件里指定的样式目录存在。如果直接对没有配置的目录执行,会提示缺少配置或样式。
我建议把第一次尝试拆成三步:建目录、放配置、跑一个测试文件。
先创建一个临时目录:
mkdir vale-demo cd vale-demo在目录里新建一个最简单的.vale.ini:
StylesPath = styles MinAlertLevel = suggestion [*.md] BasedOnStyles = Demo再创建样式目录和测试文件:
mkdir -p styles/Demo touch styles/Demo/example.yml touch test.mdtest.md里随便写一段英文:
This is a demo document for testing Vale.然后运行:
vale test.md如果styles/Demo/example.yml是空文件,Vale 会认为该目录下没有可用的规则,输出会显示“没有发现问题”或者提示规则为空。这很正常,因为规则还没写。
2.3 文件格式和第一份配置
Vale 默认支持常见文档格式,包括 Markdown、HTML、LaTeX、AsciiDoc、reStructuredText、纯文本等。它会根据文件扩展名自动判断解析方式。
如果你项目的文档不是默认扩展名,可以在配置里加一个[formats]段做映射:
[formats] myext = md这样.myext文件会按 Markdown 解析。
这里有个容易忽略的点:Vale 的配置是分作用域的。[*.md]表示只对 Markdown 文件启用后面的规则,但如果不加[*.md]而直接写BasedOnStyles,通常对所有文件生效。实际项目中我建议按文件类型区分,因为技术文档和 HTML 页面的写法规范往往不一样。
.vale.ini里几个核心字段需要先理解:
StylesPath:规则目录路径,也就是存放样式文件的目录。MinAlertLevel:最低告警级别,可以是suggestion、warning、error。Vocab:项目词汇表,用于处理人名、产品名、专有名词。Packages:需要从远程拉取的样式包列表。[*.md]:glob 匹配模式,指定规则作用于哪些文件。
基础配置不需要一次全部写满,先跑通最小集更好。
3. 配置 .vale.ini:把规则目录和检查范围理清楚
3.1 StylesPath 和基础字段
StylesPath是 Vale 最核心的配置。它指向一个目录,里面放所有.yml规则文件和Vocab词汇目录。
路径可以用相对路径,也可以写绝对路径。我习惯用相对路径,比如项目根目录下的styles目录,这样对应仓库迁移时配置不会失效:
StylesPath = styles如果你的团队成员各自 clone 项目,路径是相对项目根的,维系列表就少踩坑。
MinAlertLevel的作用是过滤告警级别。比如:
MinAlertLevel = warning那么suggestion级别的提示就不会输出。这个字段很适合刚引入 Vale 时使用。团队第一次接入,可以先只输出error,避免一堆 suggestion 刷屏,等大家接受之后再把级别放开。
BasedOnStyles的作用是快速加载某个样式目录下的所有规则。比如:
[*.md] BasedOnStyles = DemoVale 会加载styles/Demo下的所有.yml文件。这个机制的好处是不用每条规则单独列;坏处是目录里的规则如果没整理好,可能误伤很多文档。
如果你只想启用某几条规则,不推荐无脑加载整个目录。可以改成:
[*.md] Demo.禁用词 = YES Demo.术语一致性 = NO这里Demo是样式目录名,禁用词和术语一致性是目录里的规则文件名。实际使用时,规则名建议用英文或拼音,避免不同终端在文件名处理上出问题。
3.2 按文件类型启用规则
我给文档项目做配置时,通常会区分几个文件类型:
[*.md] BasedOnStyles = Demo [*.html] BasedOnStyles = Demo Demo.html-specific = YES [*.txt] BasedOnStyles = Demo Demo.术语一致性 = NO这种写法适合一个仓库里同时存在多种文档格式的情况。比如md是给开发看的文档,html是给外部用户看的帮助页面,两者的用词和语气可以分开控制。
值得注意的是 glob 匹配是走 Vale 自己的文件匹配逻辑。[*.md]匹配根目录下的 md 文件,[docs/**/*.md]匹配docs目录下的 md 文件。如果你在子目录里跑vale .,全局文件都会按对应段配置检查。
配置的优先级是这个阶段最容易混乱的地方:同一类文件同时命中多个配置段时,更具体的匹配会覆盖通用配置。如果某条规则意外没生效,先看是否被更具体的配置段关闭了。
3.3 Vocab 处理人名、产品名和术语
Vocab是 Vale 处理专有名词和术语的机制。它解决的核心问题是:规则里标记了某个词是错的,但文档里确实需要出现这个词,怎么办。
词汇表目录结构如下:
styles/ Vocab/ MyDocs/ accept.txt reject.txt在.vale.ini里声明使用这个词汇表:
Vocab = MyDocsaccept.txt里放允许出现的词:
VSCode TypeScript JavaScript GitHubreject.txt里放强制不允许出现的词:
VSCode => Typescript Javascript Jscript这个机制配合spelling类规则很好用:内置的拼写规则会认为不在词典里的词是拼写错误,accept.txt相当于给拼写规则加白名单;而reject.txt明确列出即使拼写正确也不允许使用的形式。
实际项目中,我建议把产品名、人名、缩写、团队内部叫法都维护到accept.txt。这样规则报错时,你一眼就能看出是真正的错误还是专有名词没登记。
注意:Vocab 不是万能开关。如果你没有启用任何与拼写相关的规则,reject.txt 不会自动检测。它是在规则引擎读取单词时参与候选判断的,不是独立检查器。
4. 用手写一个样式文件来理解 Vale 的规则机制
4.1 YAML 规则的核心字段
规则文件是 YAML 格式,每个文件通常代表一条规则。一个最简单的existence规则长这样:
extends: existence message: "不要使用 '%s'。" level: warning ignorecase: true tokens: - very - really - basically这种规则表示:只要文本中出现very、really、basically这些词,就产生一条 warning 级别的提示。message里的%s会被替换成实际匹配到的文本。
字段含义:
extends:规则类型。message:告警时输出的提示文字。level:告警级别,可以是suggestion、warning、error。ignorecase:匹配时是否忽略大小写。tokens:需要匹配的词或正则表达式。scope:检查范围,常见取值有text、sentence、heading等。
scope的作用很关键。比如你想只检查标题里有没有某个词,就写:
extends: existence message: "标题里不要使用 '%s'。" level: error scope: heading tokens: - TODO这样正文里出现 TODO 不会报警,只有标题里出现才会提示。这种细化能让规则更精准,避免误报。
4.2 从 existence 到 substitution
existence只能检测某个词是否存在,适合“禁用词”场景。但更多时候你希望不仅提示“这个词不对”,还要告诉作者“应该改成什么”,这时候用substitution类型的规则。
一个典型例子:
extends: substitution message: "建议使用 '%s',不要使用 '%s'。" level: warning swap: "utilize": "use" "a lot of": "many" "in order to": "to"swap是一个映射表,左边是应该避免的词,右边是建议替换的词。Vale 在匹配到utilize时,会输出一条替换建议,且提示文本里会带上两个词。
这种规则特别适合团队术语表落地。比如:
- 禁止使用
click on,要求使用click。 - 禁止使用
login作为名词,要求使用log in。 - 禁止使用
info,要求使用information。
每一条都可以写成swap里的一个映射,积累一段时间后,一份很厚的术语表就变成了一套可自动执行的规则。
existence和substitution是写规则时最常用的两种,先把它们用熟,比堆很多复杂类型更有价值。
4.3 用 occurrence、conditional 处理上下文
existence只能判断是否存在,无法判断“出现几次”和“前后文关系”。
如果你希望限制某个词的出现次数,比如一个句子里however最多出现一次,可以写occurrence类型规则:
extends: occurrence message: "不要在一句话里使用超过一次 '%s'。" level: warning scope: sentence max: 1 tokens: - howeveroccurrence会统计tokens在指定scope内出现的次数,超过max时触发提示。这类规则适合处理“用词重复”或者“某一类连接词过密”的问题。
如果你需要处理“前面出现了某个词,后面就不能出现另一个词”的场景,可以看conditional类型规则。它的用途是表达上下文限制,比如“如果标题里出现了will,后面就不要跟着be able to”。
这类规则的字段会比 existence 多一点,常见的是first、second、exceptions。实际使用中,我会先手写一个简单的 existence 规则验证思路,再慢慢换成 conditional。原因是 conditional 涉及前后顺序和匹配范围,调起来更容易遇到边界问题。
把这个机制想明白之后,你就会发现 Vale 的规则不是一成不变的死字典,而是可以表达比较复杂判断的检查器。理解了这个,后面批量接入就不慌了。
5. 从单文件检查到批量文档与 CI 集成
5.1 命令行批量操作和输出格式
Vale 最简单的用法是检查单个文件:
vale test.md也可以一次检查多个文件:
vale docs/api.md docs/guide.md目录检查更常用:
vale docs/如果要匹配某个目录下的所有 Markdown 文件,建议加引号防止 shell 先展开:
vale "docs/**/*.md"批量任务里,我一般会在命令最后加上--no-wrap,避免输出被终端宽度自动换行,一方面看起来乱,另一方面不方便复制告警内容。
Vale 还支持不同输出格式。默认输出是适合人看的格式,但在脚本里解析不好用。如果你想把检查结果接到自己的流程里,可以输出 JSON:
vale --output=JSON docs/JSON 结果包含文件路径、行号、列号、规则名、消息、级别等信息。这样无论是给 CI 做统计,还是给内部工具做展示,都方便。
如果想在脚本里只关心错误级别,可以覆盖配置里的最低告警级别:
vale --min-alert-level=error docs/这样只输出 error 级别的告警,suggestion 和 warning 一律忽略。CI 阶段用这个命令最合适。
5.2 编辑器插件与本地反馈
命令行适合批量跑和 CI,但写文档时,最好还是在编辑器里直接看到提示。
Vale 官方提供 VS Code 扩展。安装后,它会在你打开项目时读取.vale.ini,并在编辑 Markdown 文件时不断给出告警。效果很像代码编辑器里的 lint 提示:哪一行有问题,鼠标放上去就能看到规则说明。
我用下来觉得编辑器插件的最大价值是:规则能从“CI 报错”变成“写的时候就知道”。尤其是新成员不熟悉团队写作规范时,边写边被提示能减少大量返工。
如果你用的不是 VS Code,也可以通过命令行和编辑器任务机制接入。具体能不能做到最低延迟,要看你编辑器的任务运行方式,但 Vale 的命令行接口足够小,接入并不复杂。
5.3 CI 中配置 Vale 的通用思路
把 Vale 接入 CI,是让团队统一写作规范的最关键一步。没有 CI 强制执行,本地跑不跑全看个人自觉。
基础流程是:
- 安装 Vale。
- 拉取或检查样式包。
- 运行 Vale 检查文档目录。
- 根据退出码判断是否中断流水线。
GitHub 项目里,官方提供errata-ai/vale-action,可以直接在 workflow 中使用。通用的做法是让 action 读取项目根目录下的.vale.ini,检查docs/目录,并将注释写回 PR。
如果你不用 GitHub,也完全可以自己在 Jenkins、GitLab CI 里跑命令。核心就三步:
vale sync vale docs/vale sync会根据.vale.ini里的Packages字段拉取远程样式包。如果团队不需要远程包,只使用本地 styles 目录,这一步可以跳过。
CI 接入有个建议:第一次不要开全部规则。哪怕你已经写了很多规则,也先只用warning级别跑几天,让团队成员理解和适应规则再慢慢收紧。规则质量远比规则数量重要,一堆误报很容易让大家对 Vale 失去信任。
6. 实测时最容易踩的几个坑
6.1 默认配置不够用,包也要有取舍
Vale 安装后并没有内置任何文本规则。这意味着你需要自己提供样式,或者从样式仓库拉取别人维护的规则。
常见的做法是在.vale.ini里配置Packages,然后执行vale sync拉取。比如引入一些社区维护的英文写作风格包。这些包的好处是开箱即用,坏处是规则噪音比想象中大。
我自己实测的感受是:不要一次引入太多包。两个风格包叠加,经常会出现同一句话报三四个不同建议,里面一半是“可以用更好表达”这类主观提醒,对团队没有实际约束力,反而让真正需要关注的 error 被淹没。
建议选一个包,跑完整批文档,把误报规则关掉或降级,再决定要不要引入第二个包。
6.2 中文场景的边界
Vale 面向英文散文设计,对中文的支持是有边界的。这一点在接入前就要想清楚。
中文文本的问题是:英文按空格分词,Vale 的很多默认 scope 和词边界逻辑依赖空格和标点。中文没有空格,句子拆分会变得不确定。如果你写一条规则要求“一句话中不能出现某个词超过一次”,对中文文档可能不会按预期触发。
但这不意味着中文场景完全不能用。像“禁用词”“术语统一”“需要改为指定写法”这类基于 token 的检查,中文也能跑。前提是规则里直接写中文 token,并保存为 UTF-8 编码:
extends: existence message: "文档中不要使用 '%s'。" level: error tokens: - 非常 - 我们 - 请注意还要注意,Vale 的正则引擎支持一些 Unicode 属性,但不要指望它做中文分词和句法分析。如果团队文档以中文为主,我建议把 Vale 定位成“术语和禁用词检查器”,不要拿它当完整的中文语法检查工具。
6.3 先看日志和退出码,再改规则
接入过程中遇到问题,先别急着改规则。Vale 的报错一般分几类:配置找不到、样式目录不存在、规则文件名对不上、规则语法错误。
我的排查顺序是:
- 先确认
vale --version正常。 - 检查当前目录是否存在
.vale.ini,以及字段拼写是否正确。 - 确认
StylesPath指向的目录真实存在。 - 运行单个测试文件,并用
--output=JSON看完整输出。 - 如果规则没有生效,检查规则文件名和你配置里引用的名字是否完全一致。
- 如果规则报语法错误,单独打开
.yml文件检查 YAML 缩进和引号。
一个很常见的坑是:在.vale.ini里写了BasedOnStyles = Demo,但实际目录名是demo,大小写不一致。Vale 在区分大小写的系统上会直接找不到样式。
另一个常见问题是:规则文件里用到了正则特殊字符,比如*或.,结果匹配范围比预期大很多。此时建议在 tokens 里给特殊字符加转义,或者先用一个非常简单的 token 验证规则路径通不通。
记得:先造一个一定能命中的测试文本,再验证规则是否真正触发。很多时候规则没报错,不是配置问题,而是测试文本里根本没有规则要匹配的词。
7. 更进一步的规则:把团队自己的写作规范沉淀成 Vale 样式
7.1 从风格手册到 YAML 规则
团队如果已经有人工维护的文档风格手册,那 Vale 规则可以直接从手册里提取。
比如手册里写着“不要使用 please kindly 这种过于客套的表达”,就可以转成一条 existence 规则。写着“产品名称统一使用DataSync,不要使用Datasync或data sync”,就可以转成 substitution 规则。
我建议按优先级分三批整理:
第一批:硬性错误。拼写不一致、产品名写错、严重禁用词。这些规则直接设为error。
第二批:风格偏好。冗长表达、口语化表达、建议替换的搭配。这些规则先设为warning,让作者自己决定是否修改。
第三批:需要上下文的检查。比如标题不要用某些词、正文某类词出现次数不能过多。这些规则比较敏感,需要更多测试再启用。
分批的好处是能控制告警数量。如果第一次就导入 80 条规则,文档满屏飘红,团队成员很难接受。
7.2 维护和版本管理
Vale 的配置和规则本质上是文本文件,完全可以纳入 Git 仓库管理。我建议把.vale.ini和整个styles/目录放在文档项目根目录,这样任何 clone 项目的人都能得到相同的规则。
当规则越来越复杂后,可以考虑为规则单独建立仓库,然后用 Vale 的Packages机制按版本拉取:
Packages = https://github.com/your-org/vale-styles然后执行:
vale sync这样文档项目只需要维护一份配置,规则升级走单独仓库,更适合中大型团队。
规则的变更也应该像代码一样走 review。每次增加或修改规则时,最好附上一条能命中的测试文本和一条不应该命中的文本。比如:
# good: Please read the guide. # bad: Please kindly read the guide.这种注释看起来简单,但后面维护的人看一眼就知道这条规则的意图和边界。
7.3 让 Vale 成为文档评审的一部分,而不是替代
最后说一个我比较深的体会:Vale 再强,也只能替代文档评审里的机械部分。
真正需要人判断的问题,比如结构是否合理、内容是否准确、读者是否能理解,这些它做不了。但把术语、拼写、禁用词、风格偏好这些是是而非的问题交给 Vale 处理之后,人工 review 的时间可以更集中在内容本身。
我实践下来的路径是:先跑一个最小配置,只检查最重要的几十个词;用几天时间观察误报率;然后把确定性的规则提升到error,接入 CI;再根据团队反馈慢慢扩充规则集。这个节奏比一开始就配一个超全的规则包要舒服很多。
如果你正准备在项目里引入 Vale,我建议你也从最小集开始,跑通一个文件再铺开。Linter 的价值不在于规则多,而在于每一条规则都稳定、可解释、真的对团队有帮助。