Search
Search documentation
Natural language plugins
Updated Nov 1, 2026
Natural-language plugins parse prose into nlcst and check or rewrite it. The checks report messages on the file and leave the text as it is. Reach them from Markdown through md.toNaturalLanguage or from HTML through html.toNaturalLanguage.
nl.parse
Parses natural language into an nlcst tree of paragraphs, sentences, words and punctuation.
from textlane import nl, textlane
file = (
textlane()
.use(nl.parse())
.use(nl.smartypants())
.use(nl.stringify())
.process("\"Hi\" -- she said...")
)
file["value"]
# “Hi” — she said…Follows retext-english.
| Option | Type | Default | Description |
|---|---|---|---|
language | Language | The rules sentences and words are found by. |
Language
The language nl.parse reads: parse-latin's rules, or parse-english's on top of them.
Takes Literal["latin", "english"].
nl.equality
Warns about insensitive, inconsiderate wording.
from textlane import nl, textlane
file = (
textlane()
.use(nl.parse())
.use(nl.equality())
.use(nl.stringify())
.process("Hey guys, the chairman is here.")
)
file["messages"][0]["reason"]
# Unexpected potentially insensitive use of `guys`, in somes cases `people`, ` …Follows retext-equality.
| Option | Type | Default | Description |
|---|---|---|---|
ignore | Optional[List[str]] | None | Phrases not to warn about. |
binary | Optional[bool] | None | Whether a phrase used next to its counterpart (he or she) is fine. |
nl.passive
Warns about the passive voice.
from textlane import nl, textlane
file = (
textlane()
.use(nl.parse())
.use(nl.passive())
.use(nl.stringify())
.process("The cake was eaten.")
)
file["messages"][0]["reason"]
# Unexpected use of the passive voiceFollows retext-passive.
| Option | Type | Default | Description |
|---|---|---|---|
ignore | Optional[List[str]] | None | Participles not to warn about. |
nl.readability
Warns about sentences too hard to read for a target age.
from textlane import nl, textlane
file = (
textlane()
.use(nl.parse())
.use(nl.readability({"age": 12}))
.use(nl.stringify())
.process("The constitutionally mandated intergovernmental reconciliation procedures necessitate comprehensive multilateral deliberation.")
)
file["messages"][0]["reason"]
# Unexpected hard to read sentence, according to 6 out of 7 algorithmsFollows retext-readability.
| Option | Type | Default | Description |
|---|---|---|---|
age | Optional[float] | None | The age of the target audience: 16 by default. |
threshold | Optional[float] | None | The share of algorithms that must find a sentence too hard: 4/7 by default. |
minWords | Optional[float] | None | The fewest words a sentence needs to be checked: 5 by default. |
nl.repeatedWords
Warns about a word written twice in a row.
from textlane import nl, textlane
file = (
textlane()
.use(nl.parse())
.use(nl.repeated_words())
.use(nl.stringify())
.process("It is the the best.")
)
file["messages"][0]["reason"]
# Unexpected repeated `the`, remove one occurrenceFollows retext-repeated-words.
No options.
nl.sentenceSpacing
Warns about the spaces between sentences: one by default.
from textlane import nl, textlane
file = (
textlane()
.use(nl.parse())
.use(nl.sentence_spacing())
.use(nl.stringify())
.process("One. Two.")
)
file["messages"][0]["reason"]
# Unexpected 2 spaces between sentence, expected 1 spaceFollows retext-sentence-spacing.
| Option | Type | Default | Description |
|---|---|---|---|
preferred | Optional[Spacing] | The spacing expected between sentences: 1 or "space" (the default), 2 or "double-space", 0 or "newline". |
Spacing
A spacing between sentences, as a count of spaces or by name.
Takes Union[float, str].
nl.stringify
Serializes nlcst back into text.
from textlane import nl, textlane
file = (
textlane()
.use(nl.parse())
.use(nl.stringify())
.process("Same text out.")
)
file["value"]
# Same text out.Follows retext-stringify.
No options.
nl.smartypants
Turns straight quotes, dashes, dots and backticks into their typographic forms.
from textlane import nl, textlane
file = (
textlane()
.use(nl.parse())
.use(nl.smartypants())
.use(nl.stringify())
.process("\"It's\" -- fine...")
)
file["value"]
# “It’s” — fine…Follows retext-smartypants.
| Option | Type | Default | Description |
|---|---|---|---|
backticks | Backticks | Turn double backticks and double straight single quotes into double quotes; all turns single ones into single quotes too, and needs quotes: false. | |
closingQuotes | Optional[QuoteCharacters] | The closing quotes to use: ” and ’ by default. | |
dashes | Dashes | Turn two dashes into an em dash; oldschool and inverted treat three too. | |
ellipses | Ellipses | Turn three dots into an ellipsis, with spaces between them or without. | |
openingQuotes | Optional[QuoteCharacters] | The opening quotes to use: “ and ‘ by default. | |
quotes | Optional[bool] | True | Turn straight quotes into smart ones. |
Backticks
Takes Union[bool, Literal["all"]].
QuoteCharacters
The quote characters to use for double and single quotes.
| Option | Type | Default | Description |
|---|---|---|---|
double | str | ||
single | str |
Dashes
Takes Union[bool, Literal["oldschool", "inverted"]].
Ellipses
Takes Union[bool, Literal["spaced", "unspaced"]].
nl.quotes
Warns about quotes and apostrophes that are not the preferred kind, smart by default.
from textlane import nl, textlane
file = (
textlane()
.use(nl.parse())
.use(nl.quotes())
.use(nl.stringify())
.process("He said \"hi\".")
)
file["messages"][0]["reason"]
# Unexpected straight quote `"`, expected `“`Follows retext-quotes.
| Option | Type | Default | Description |
|---|---|---|---|
preferred | Optional[QuoteStyle] | The style of quotes and apostrophes to prefer: smart by default. | |
smart | Optional[List[str]] | None | Smart quotes by nesting level, each one character or an opening and closing pair: ["“”", "‘’"] by default. |
straight | Optional[List[str]] | None | Straight quotes by nesting level: ["\"", "'"] by default. |
QuoteStyle
A style of quotes: straight (") or smart (“”).
Takes Literal["smart", "straight"].
nl.contractions
Warns about missing or misplaced apostrophes in contractions.
from textlane import nl, textlane
file = (
textlane()
.use(nl.parse())
.use(nl.contractions())
.use(nl.stringify())
.process("Dont do that.")
)
file["messages"][0]["reason"]
# Unexpected missing apostrophe in `Dont`, expected `Don’t`Follows retext-contractions.
| Option | Type | Default | Description |
|---|---|---|---|
allowLiterals | Optional[bool] | False | Also check contractions written as literals, such as “hasnt”. |
straight | Optional[bool] | False | Expect straight apostrophes (') instead of smart ones (’). |
nl.indefiniteArticle
Warns about a and an used before the wrong sound.
from textlane import nl, textlane
file = (
textlane()
.use(nl.parse())
.use(nl.indefinite_article())
.use(nl.stringify())
.process("It is a apple.")
)
file["messages"][0]["reason"]
# Unexpected article `a` before `apple`, expected `an`Follows retext-indefinite-article.
No options.
Created with ❤ and Livemark