• All Posts
  • Code
  • Design
  • Process
  • Speaking
  • Poetry
  • About
D.

June 03, 2022 Integrate vale into an open source project

This week my friend Bekah introduced me to vale.sh, an open-source spelling and grammer checker written in golang and configurable to your project’s needs.

Think grammarly, but as a CLI tool.

You can even add it to your CI/CD build.

  • Installing vale
  • Configuring vale
  • Using vale
    • Accepting proper nouns and domain specific language
    • Disabling linting rules with markdwn comments
      • Disable one rule
      • Disable all validation
    • Set up guard for rapid feedback
    • Add a make target
  • Conclusion

Installing vale

The vale.sh site includes a friendly Get Started button which takes you to some Installation docs.

On my M1 Mac this was as simple as using brew:

brew install vale

This installed the vale CLI and made it avaialble in my PATH, so I could open a terminal and use it right away:

❯ which vale
/opt/homebrew/bin/vale
❯ vale
vale - A command-line linter for prose.

Usage:	vale [options] [input...]
	vale myfile.md myfile1.md mydir1
	vale --output=JSON [input...]

Vale is a syntax-aware linter for prose built with speed and extensibility in
mind. It supports Markdown, AsciiDoc, reStructuredText, HTML, and more.

To get started, you'll need a configuration file (.vale.ini):

Example:

	StylesPath = a/path/to/your/styles
	MinAlertLevel = suggestion

	[*]
	BasedOnStyles = Vale

See https://vale.sh for more setup information.

(Or use vale --help for a listing of all CLI options.)

Configuring vale

Configuring vale took me some time as I learned a few things about the tool:

  • it requires a configuration file to work (default file is .vale.ini in whatever directory you are currently in) and this can be set per-project and checked in under source control
  • it requires a set of yaml styles to be available on your local machine
  • it includes a sync subcommand to download styles based on your configuration file

Here is what I recommend:

  1. Visit this vale Config Generator and experiment with the dropdown boxes to generate a .vale.ini file.

    Here is the one I selected for my project:

     StylesPath = .styles
    
     MinAlertLevel = suggestion
     Vocab = Base
    
     Packages = Google, write-good
    
     [*]
     BasedOnStyles = Vale, Google, write-good
    

    Note that I changed the default StylesPath setting from styles to .styles. This means that vale will look for styles yaml files inside the .styles folder of my project.

    Prefixing a file or folder name with a . means that the file or folder will be hidden from a normal directory listing, similar to the .git folder.

    I made this choice because I don’t want to distract first time contributors with a styles folder that is part of my project’s standards but not part of my project’s content.

  2. Save this file as .vale.ini in the root of your project folder.

     ❯ cd my-project
    
     ❯ ls .vale.ini
     .vale.ini
    
     ❯ cat .vale.ini
     StylesPath = .styles
        
     MinAlertLevel = suggestion
     Vocab = Base
    
     Packages = Google, write-good
        
     [*]
     BasedOnStyles = Vale, Google, write-good
    
     ❯
    
  3. Open a terminal, change directory into the root of your project folder, and run this

     vale sync
    

    Here’s what it looked like for me with the .vale.ini file above:

     ❯ cd my-project
    
     ❯ vale sync
     SUCCESS  Downloaded package 'Google'
     SUCCESS  Downloaded package 'write-good'
     Downloading packages [2/2]
    
     ❯
    

This uses the settings in your project’s .vale.ini to download styles yaml files and put them in your local StylesPath.

With the .vale.ini file in my project folder that looks like this:

❯ cd my-project
❯ ls .
.styles   .vale.ini
❯ tree .styles
.styles
├── Google
│   ├── AMPM.yml
│   ├── Acronyms.yml
│   ├── Colons.yml
│   ├── Contractions.yml
│   ├── DateFormat.yml
│   ├── Ellipses.yml
│   ├── EmDash.yml
│   ├── EnDash.yml
│   ├── Exclamation.yml
│   ├── FirstPerson.yml
│   ├── Gender.yml
│   ├── GenderBias.yml
│   ├── HeadingPunctuation.yml
│   ├── Headings.yml
│   ├── Latin.yml
│   ├── LyHyphens.yml
│   ├── OptionalPlurals.yml
│   ├── Ordinal.yml
│   ├── OxfordComma.yml
│   ├── Parens.yml
│   ├── Passive.yml
│   ├── Periods.yml
│   ├── Quotes.yml
│   ├── Ranges.yml
│   ├── Semicolons.yml
│   ├── Slang.yml
│   ├── Spacing.yml
│   ├── Spelling.yml
│   ├── Units.yml
│   ├── We.yml
│   ├── Will.yml
│   ├── WordList.yml
│   ├── meta.json
│   └── vocab.txt
├── Vocab
│   └── Base
│       ├── accept.txt
│       └── reject.txt
└── write-good
    ├── Cliches.yml
    ├── E-Prime.yml
    ├── Illusions.yml
    ├── Passive.yml
    ├── README.md
    ├── So.yml
    ├── ThereIs.yml
    ├── TooWordy.yml
    ├── Weasel.yml
    └── meta.json

4 directories, 46 files

Note: The tree command is a unix command to print out a folder structure as a tree shape. On MacOS the command is available via brew install tree

I checked in both .vale.ini and the entire .styles folder into source control so that anyone who clones or forks this project is using the same style files; there are lots of style files but yaml is pretty compact when stored in .git

Using vale

Now that you have the prerequisites installed:

  1. vale CLI
  2. a valid .vale.ini file
  3. style yaml files downloaded to the StylesPath configured in your .vale.ini file

you are ready to use vale to validate (valedate?) your content.

So far I have only used vale on Markdown files.

Running vale on my preject’s README originally looked like this:

 ❯ vale README.md

 README.md

 5:13     suggestion  Spell out 'GPL', if it's        Google.Acronyms
                      unfamiliar to the audience.
 13:20    warning     'go-git-mob' should use         Google.Headings
                      sentence-style capitalization.
 16:5     error       Did you really mean 'golang'?   Vale.Spelling
 16:24    error       Did you really mean 'nodejs'?   Vale.Spelling
 33:46    warning     'Table of Contents' should use  Google.Headings
                      sentence-style capitalization.
 50:4     warning     'About The Project' should use  Google.Headings
                      sentence-style capitalization.
 52:14    suggestion  Try to avoid using 'is'.        write-good.E-Prime
 52:54    suggestion  Feel free to use 'it's'         Google.Contractions
                      instead of 'It is'.
 52:54    warning     'It is' is too wordy.           write-good.TooWordy
 52:62    error       Did you really mean 'golang'?   Vale.Spelling
 52:81    error       Did you really mean 'nodejs'?   Vale.Spelling
 52:121   error       Did you really mean             Vale.Spelling
                      'complimenents'?
 53:52    suggestion  Try to avoid using 'be'.        write-good.E-Prime
 53:52    warning     'be detected' may be passive    write-good.Passive
                      voice. Use active voice if you
                      can.
 53:52    suggestion  In general, use active voice    Google.Passive
                      instead of passive voice ('be
                      detected').
 53:186   error       Did you really mean 'sesson'?   Vale.Spelling
 54:53    warning     Avoid using 'will'.             Google.Will
 54:58    suggestion  In general, use active voice    Google.Passive
                      instead of passive voice ('be
                      squashed').
 54:58    warning     'be squashed' may be passive    write-good.Passive
                      voice. Use active voice if you
                      can.
 54:58    suggestion  Try to avoid using 'be'.        write-good.E-Prime
 60:8     warning     Try to avoid using              Google.We
                      first-person plural like 'we'.
 60:26    warning     Try to avoid using              Google.We
                      first-person plural like 'we'.
 60:135   suggestion  Try to avoid using 'was'.       write-good.E-Prime
 60:277   warning     'only' is a weasel word!        write-good.Weasel
 60:285   suggestion  In general, use active voice    Google.Passive
                      instead of passive voice ('be
                      left').
 60:285   suggestion  Try to avoid using 'be'.        write-good.E-Prime
 60:285   warning     'be left' may be passive        write-good.Passive
                      voice. Use active voice if you
                      can.
 60:320   warning     'In addition' is too wordy.     write-good.TooWordy
 60:375   suggestion  In general, use active voice    Google.Passive
                      instead of passive voice ('was
                      introduced').
 60:375   suggestion  Try to avoid using 'was'.       write-good.E-Prime
 60:375   warning     'was introduced' may be         write-good.Passive
                      passive voice. Use active
                      voice if you can.
 60:467   suggestion  Try to avoid using 'be'.        write-good.E-Prime
 62:14    suggestion  In general, use active voice    Google.Passive
                      instead of passive voice ('was
                      created').
 62:14    warning     'was created' may be passive    write-good.Passive
                      voice. Use active voice if you
                      can.
 62:14    suggestion  Try to avoid using 'was'.       write-good.E-Prime
 62:105   suggestion  In general, use active voice    Google.Passive
                      instead of passive voice ('be
                      installed').
 62:105   warning     'be installed' may be passive   write-good.Passive
                      voice. Use active voice if you
                      can.
 62:105   suggestion  Try to avoid using 'be'.        write-good.E-Prime
 62:167   error       Did you really mean 'nodejs'?   Vale.Spelling
 62:205   error       Did you really mean 'repo'?     Vale.Spelling
 64:5     warning     'Built With' should use         Google.Headings
                      sentence-style capitalization.
 70:4     warning     'Getting Started' should use    Google.Headings
                      sentence-style capitalization.
 76:8     suggestion  Use parentheses judiciously.    Google.Parens
 76:9     warning     Try to avoid using              Google.We
                      first-person plural like 'we'.
 76:9     suggestion  Feel free to use 'we're'        Google.Contractions
                      instead of 'we are'.
 76:12    suggestion  Try to avoid using 'are'.       write-good.E-Prime
 83:5     warning     'Utility Commands' should use   Google.Headings
                      sentence-style capitalization.
 85:38    warning     'a number of' is too wordy.     write-good.TooWordy
 107:105  suggestion  Use parentheses judiciously.    Google.Parens
 128:12   error       Did you really mean 'Kotze'?    Vale.Spelling
 128:27   error       Did you really mean 'Ideler'?   Vale.Spelling
 128:149  error       Did you really mean 'nodejs'?   Vale.Spelling

✖ 11 errors, 20 warnings and 21 suggestions in 1 file.
 ❯ 

Wow that was a lot of feedback. In the end, however, it did not take me very long to work through each piece of feedback and resolve them.

Accepting proper nouns and domain specific language

Some of the errors were spelling errors with unrecognized proper nouns or technology keywords.

I learned that you can add such proper nouns and known words to your local styles files by adding them to a $StylesPath/Vocab/Base/accept.txt file.

Here is what mine looks like for the go-git-mob project:

Github
Make
make
Makefile
rbenv
nodenv
asdf
aruba
Golang
golang
nodejs
go-git-mob
git-mob
GPL
url
Kotze
Idele

These words are no longer flagged as Vale.Spelling errors.

Disabling linting rules with markdwn comments

Another neat trick is that vale supports disabling and enabling all validation or just specific rules using special comments in the same way that other linters (e.g. eslint, rubocop, etc) do.

Disable one rule

For example:

<!-- vale Google.Headings = NO -->
<h1 align="center">go-git-mob</h1>
<!-- vale Google.Headings = YES -->

disables the Google.Headings validation rule for that one top-level heading, which I do because I know that go-git-mob is the name of the project.

Disable all validation

Occasionally you may want to disable all validation, such as when including a blockquote.

You don’t want style/linting rules for your project to cause you to misquote someone else.

In this case you can temporarily disable all validation by surrounding your blockquote like this:

<!-- vale off -->
<blockquote>...</blockquote>
<!-- vale on -->

Set up guard for rapid feedback

I like rapid feedback, so I set up the guard command-line tool to re-run validation every time my README.md or CONTRIBUTING.md files chagne.

I added these two gems to my project’s Gemfile:

group :development do
  gem 'guard'
  gem 'guard-process'
end

And these lines to a ./Guardfile:

guard 'process', :name => 'Valedation', :command => 'vale README.md CONTRIBUTNG.md' do
  watch('README.md')
  watch('CONTRIBUTING.md')
end

Now I can cd into my project folder and run guard to enable this file watching behaviour:

❯ bundle exec guard
14:02:48 - INFO - Starting process Valedation
14:02:48 - INFO - Started process Valedation
14:02:48 - INFO - Guard is now watching at '/Users/dalpert/projects/go-git-mob'
[1] guard(main)> ✔ 0 errors, 0 warnings and 0 suggestions in 2 files.

And any time the guard tool detects a change to one of those markdown files it will automatically re-run validation:

14:04:38 - INFO - Stopping process Valedation
14:04:38 - INFO - Stopped process Valedation
14:04:38 - INFO - Starting process Valedation
14:04:38 - INFO - Started process Valedation
[1] guard(main)> ✔ 0 errors, 0 warnings and 0 suggestions in 2 files.

This was helpful when resolving the initial linting feedback but also when editing, to have updated linting output every time you save a file.

When resolving the first round of errors I ran guard in a terminal window inside VSCode and worked on the last errors/warnings first. As I fixed them and saved the file, the guard script would trigger and update the validation leaving new errors/warnings at the bottom of the list. This let me keep my terminal window small and the editing window large and usually focus on one or two rules at a time.

Add a make target

On this project I am using Make as a build tool.

Here is the Make target I created to wrap validation:

vale: ## run linting rules against markdown files
	vale README.md CONTRIBUTING.md # we don't valedate CHANGELOG.md as that reflects historical commit summaries

Now I can run make vale to run that validation and use the vale target as a prerequisite in my cit (CI build and Test) target to fail a build if any of the markdown validation rules fail.

cit: clean vale build test-unit test-features

Once I get PR builds working on this repo that vale feedback will be available to all contributors who make markdown changes.

Conclusion

Once I got over the initial setup challenges I found vale very easy to use.

Similar with other code quality and linting tools I’ve used (I’m looking at you rubocop) the first time I turned vale on my project’s README.md it was easy to get overwhelmed with the amount of feedback.

Now that I’ve worked through it however and having a passing mark on both README and CONTRIBUTING I find it easy to keep that validation on and deal with smaller more targeted feedback on only the content I’m changing.

A number of the initial pieces of feedback were challenging to resolve but eliminating passive voice really does make the content stronger.

I found that with the accept.txt file to expand my spelling dictionary and a few targeted markdown comment when a rule truely didn’t make sense I am now very happy to have vale running (and passing) against my project’s README.md and CONTRIBUTING.md files.


back to top

© David Alpert 2025