news 2026/8/31 10:44:29

Vale:面向自然语言文本的Linter,用规则统一技术文档风格

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vale:面向自然语言文本的Linter,用规则统一技术文档风格

Vale 是一个面向自然语言文本的 Linter,英文定位就叫 Linter for Prose。简单说,它用命令行方式帮你检查散文、技术文档、博客文章里的用词、术语、一致性和风格问题,而不是检查语法错误或者做排版。这个定位让它和拼写检查器、Markdown 格式化工具完全不一样。如果你写英文技术文档,或者团队有明确的中英文写作规范,又不想每次 review 都靠人工去抓“这个术语前后不一致”“这个词不该用”“那个表达太口语化”这类问题,Vale 值得认真了解一下。

这篇文章按我自己的落地顺序写:先讲它到底适合解决什么问题,再讲怎么安装、怎么配置、怎么写自己的规则,最后是批量任务和实测中最容易踩到的坑。我不会把项目 README 复述一遍,只会写我实际用下来觉得关键的环节。

1. 先搞清楚 Vale 到底解决写作里的什么问题

1.1 它不是拼写检查器,也不是排版神器

很多人第一次接触 Vale 会误以为它是“高级版拼写检查”。实际上它的定位更接近“基于规则的文本检查器”。

拼写检查器解决的是“这个单词是否拼错了”,排版工具解决的是“代码块缩进是否统一、空行是否一致”。Vale 更关心的是:这句话里是不是用了某个团队禁用的词,某个术语的大小写是不是统一,某个动词的搭配是否更符合你所在领域的惯例。

举几个典型场景:

  • 文档里既有JavaScript又出现Javascript,规则可以自动标记后者。
  • 团队要求避免使用utilize这类词,统一写成use,规则可以提醒。
  • 文案里出现whether ifin 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 vale

Windows 如果使用 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.md

test.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:最低告警级别,可以是suggestionwarningerror
  • 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 = Demo

Vale 会加载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 = MyDocs

accept.txt里放允许出现的词:

VSCode TypeScript JavaScript GitHub

reject.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

这种规则表示:只要文本中出现veryreallybasically这些词,就产生一条 warning 级别的提示。message里的%s会被替换成实际匹配到的文本。

字段含义:

  • extends:规则类型。
  • message:告警时输出的提示文字。
  • level:告警级别,可以是suggestionwarningerror
  • ignorecase:匹配时是否忽略大小写。
  • tokens:需要匹配的词或正则表达式。
  • scope:检查范围,常见取值有textsentenceheading等。

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里的一个映射,积累一段时间后,一份很厚的术语表就变成了一套可自动执行的规则。

existencesubstitution是写规则时最常用的两种,先把它们用熟,比堆很多复杂类型更有价值。

4.3 用 occurrence、conditional 处理上下文

existence只能判断是否存在,无法判断“出现几次”和“前后文关系”。

如果你希望限制某个词的出现次数,比如一个句子里however最多出现一次,可以写occurrence类型规则:

extends: occurrence message: "不要在一句话里使用超过一次 '%s'。" level: warning scope: sentence max: 1 tokens: - however

occurrence会统计tokens在指定scope内出现的次数,超过max时触发提示。这类规则适合处理“用词重复”或者“某一类连接词过密”的问题。

如果你需要处理“前面出现了某个词,后面就不能出现另一个词”的场景,可以看conditional类型规则。它的用途是表达上下文限制,比如“如果标题里出现了will,后面就不要跟着be able to”。

这类规则的字段会比 existence 多一点,常见的是firstsecondexceptions。实际使用中,我会先手写一个简单的 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 强制执行,本地跑不跑全看个人自觉。

基础流程是:

  1. 安装 Vale。
  2. 拉取或检查样式包。
  3. 运行 Vale 检查文档目录。
  4. 根据退出码判断是否中断流水线。

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 的报错一般分几类:配置找不到、样式目录不存在、规则文件名对不上、规则语法错误。

我的排查顺序是:

  1. 先确认vale --version正常。
  2. 检查当前目录是否存在.vale.ini,以及字段拼写是否正确。
  3. 确认StylesPath指向的目录真实存在。
  4. 运行单个测试文件,并用--output=JSON看完整输出。
  5. 如果规则没有生效,检查规则文件名和你配置里引用的名字是否完全一致。
  6. 如果规则报语法错误,单独打开.yml文件检查 YAML 缩进和引号。

一个很常见的坑是:在.vale.ini里写了BasedOnStyles = Demo,但实际目录名是demo,大小写不一致。Vale 在区分大小写的系统上会直接找不到样式。

另一个常见问题是:规则文件里用到了正则特殊字符,比如*.,结果匹配范围比预期大很多。此时建议在 tokens 里给特殊字符加转义,或者先用一个非常简单的 token 验证规则路径通不通。

记得:先造一个一定能命中的测试文本,再验证规则是否真正触发。很多时候规则没报错,不是配置问题,而是测试文本里根本没有规则要匹配的词。

7. 更进一步的规则:把团队自己的写作规范沉淀成 Vale 样式

7.1 从风格手册到 YAML 规则

团队如果已经有人工维护的文档风格手册,那 Vale 规则可以直接从手册里提取。

比如手册里写着“不要使用 please kindly 这种过于客套的表达”,就可以转成一条 existence 规则。写着“产品名称统一使用DataSync,不要使用Datasyncdata 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 的价值不在于规则多,而在于每一条规则都稳定、可解释、真的对团队有帮助。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 10:42:58

深信服C/C++开发岗笔试D卷全解析:考点、编程题与避坑指南

秋招那阵子,深信服的C/C软件开发岗位笔试是我们宿舍讨论最多的一场。网申投完没两天就收到了在线笔试通知,点进去一看是D卷,当时还在牛客上搜了一圈,发现考过的人说法五花八门,有人说偏基础,有人说算法题很…

作者头像 李华
网站建设 2026/8/31 10:39:40

用了半年察元:被同事问最多的十个问题

察元AI文档助手在我这台机器上跑了半年,从看客变成部门"人肉接口人",被问的问题重复率极高。挑十个最高频的整理成问答,答案都是我实际踩过验证过的,不是手册复读。排错密度最高的那两周,我几乎每天都要口头…

作者头像 李华
网站建设 2026/8/31 10:36:22

家政O2O系统三端源码解析:仿阿姨帮58到家的上门平台搭建指南

简介:这是一套面向PHP开发者与O2O创业团队的高仿上门服务系统源码,基于BAOCMS二次开发,完整复刻阿姨帮、58到家核心业务逻辑,适用于搭建家政、跑腿、外卖、酒店、农家乐等多场景本地生活服务平台。资源包共2000个文件,…

作者头像 李华
网站建设 2026/8/31 10:35:33

LibTV导演台实战:从零制作1分钟AI真人短剧全流程

在实际 AI 短视频创作里,LibTV 经常被当作一个“导演台”来使用:先确定剧本,再固定角色和场景,然后逐镜头生成图片和视频,最后合成成片。这种工作流很适合 AI 真人短剧,因为真人风格的角色最怕前后不一致&a…

作者头像 李华
网站建设 2026/8/31 10:32:47

查询单据--凭证 关系记录表

QFilter botpFilternew QFilter("voucherid",QFilter.in,voucherids);//voucherids为凭证idString billtrackerFields"billtype.number,sourcebillid,voucherid";DataSet billTrackerDataSet QueryServiceHelper.queryDataSet("daptracker",&qu…

作者头像 李华