Quotes
How the quotes rule turns straight and foreign quotation marks into each locale's primary and secondary pairs, resolves nesting, and leaves unmatched marks alone.
The quotes rule finds quotation marks in the text and replaces them with the pair of glyphs the locale prescribes. A quotation inside a quotation gets the locale's secondary pair, so one input turns into different output in English, German, French or Polish. It also removes stray spaces just inside a pair where the locale writes none.
It runs fifth in the pipeline (order 40), before apostrophe, and is on by default in every mode.
How it works #
The rule looks at every quote mark in the text: straight (" and ') and typographic (« » “ ” „ ‘ ’ ‚ ‹ › and a few more), whether they belong to the locale or to another one. A mark that is already curly is not trusted to be right. «Wort» in German text is re-typeset to „Wort“, and “hi” in British text becomes ‘hi’.
- Classify each mark. A mark can open when it follows a space, an opening bracket, a dash, another quote mark or the start of the text, and is followed by non-space content. It can close when it follows non-space content and is followed by a space, punctuation, a dash, another quote mark or the end of the text. The space just inside the mark is ignored for this decision, so
" hello"still opens. The space on the outside is evidence and is read literally:"hi" to mecloses because a space follows it. - Pair marks. Marks are paired like brackets, using one stack for double-width marks (
"«“„and so on) and one for single-width marks ('‘’‹›). A closing mark takes the nearest open mark on its own stack. A pair that encloses nothing but spaces is not formed. - Assign glyphs by depth. The depth of a pair is one plus the number of pairs that enclose it. Odd depth takes the locale's primary pair, even depth the secondary pair, alternating further down. Depth decides the glyphs, not the width of the mark the author typed, so an outer pair typed with single marks still receives the primary pair.
- Check the result. The rule renders the pairing it intends to apply, runs the pairing again over that rendering, and drops pairs until the second pass reproduces exactly the pairs it started from. This is what keeps idempotency intact when converting marks changes how their neighbours read. In ordinary text the first check passes and nothing is dropped.
- Emit edits. Only marks whose glyph changes are edited, and only inner spaces that should go are deleted.
Examples #
Straight marks, one level of nesting, in en-US. The inner pair is the secondary pair, and the apostrophe in don't is left for the next rule:
Input
Output
Depth, not the typed width, picks the glyphs. The outer single marks become the primary pair and the inner double marks become the secondary pair:
Input
Output
The same sentence in German. The primary pair is „ “ and the secondary pair is ‚ ‘:
Input
Output
Swiss German uses guillemets for the primary pair and single guillemets for the secondary pair, with no spaces inside:
Input
Output
Polish nests with „ ” on the outside and » « on the inside, the reverse of the French-style guillemets:
Input
Output
Russian uses « » outside and „ “ inside:
Input
Output
British English reverses the English roles: single quotes are primary and double quotes are secondary:
Input
Output
Existing curly marks are candidates too. Here a correct American pair sits in British text and is re-typeset to the local primary pair:
Input
Output
Guillemets from another language convert to the locale's own pair. Spaces just inside the old marks are removed, because German writes none:
Input
Output
French sets its primary guillemets with a space inside. quotes places the glyphs and nbsp supplies the no-break space, so a quotation typed with straight marks and one typed with loose spaces both end up the same:
Input
Output
Locale data #
The rule reads these fields from the locale:
quotes.primaryandquotes.secondary, each with anopenglyph, acloseglyph and aninnerSpacevalue.quotes.elisionIdioms, a short list of fixed phrases whose single quotes are elisions, such asrock 'n' rollin en-US.quotes.elisionClitics, lists of word fragments (s,l,qu,jusquand similar) used when a mark sits against the edge of an inline span in HTML or Markdown.
innerSpace is none in most locales and nbsp for the French primary pair. When it is none, quotes deletes spaces just inside the pair. When it is anything else, quotes does not touch them and nbsp inserts or converts them.
The glyph conventions differ enough to be worth comparing:
| Locale | Primary | Secondary |
|---|---|---|
| en-US | “ ” | ‘ ’ |
| en-GB | ‘ ’ | “ ” |
| de-DE | „ “ | ‚ ‘ |
| de-CH | « » | ‹ › |
| fr | « » with no-break spaces inside | “ ” |
| pl | „ ” | » « |
| ru | « » | „ “ |
| fi, sv | ” ” | ’ ’ |
Finnish and Swedish use the same glyph on both sides of a pair. The pairing, not the glyph, tells the rule which mark opens and which closes, so ”Hän sanoi ’moi’” is a stable result in fi and the rule does not mistake it for something to fix.
Edge cases #
- Apostrophes inside words. A single-width mark with a letter or digit on both sides (
don't,l'été,O'Brien,1990's) is never part of a quotation. The apostrophe rule converts it afterwards. rock 'n' roll. A pair of single marks around exactly one letternorN, with a space outside each mark, is treated as elision in every locale, and both marks become apostrophes. The cost is stated:The letter 'n' is common.is read the same way. A quotednfollowed by a comma is not affected.
Input
Output
- Elisions that find no partner.
'90sand a trailing possessive such asthe dogs' bowlsare left as typed by this rule and handed toapostrophe. A lone leading elision can still be paired with a distant closing mark in the same text, because nothing local distinguishes them. - Unmatched marks. The rule converts what it can pair and leaves the rest. A single stray mark stays exactly as typed:
Input
Output
- Foot and inch marks.
6' 2"have nothing to pair with, so they are not changed.
Input
Output
- Runs of identical marks.
"",'''and««are left alone. So is a pair that encloses nothing but spaces («»," ").
Input
Output
- Spaces next to the marks. Spaces inside the pair go when the locale's
innerSpaceisnone, and only when the next character is a letter, a digit or another quote mark. A space in front of a dash is kept, so" --x"loses no space and the glyphs still convert. A line break is never deleted or crossed by a space-skipping check, so a mark at the end of a line does not open across it. Outer spaces are never touched. - Pairs across blocks. Pairing runs over the whole text unit. A quotation can span a blank line in
textmode, and an unclosed mark can pair with one much further on. Inhtmlandmarkdownmodes a quotation can open before an inline element and close after it. - Possessives next to markup. In
htmlandmarkdown, a mark written flush against an inline span, such as `x'sor<code>x</code>'s, is read through the locale'selisionClitics` list, so the possessive stays an apostrophe and does not open or close a quotation:
Input
Output
- Plural possessives next to markup. `
xs' printer` has the same shape as a closing mark after a span. The rule cannot tell them apart, and the pairing it chooses stands. - Primes and look-alikes. U+2032, U+2033, U+02BC, the grave accent and the acute accent are not quote marks and are never converted.
- Skipped regions. Code, URLs, attribute values and other regions skipped by the mode adapter are not examined. See the span model.
Related #
- nbsp: inserts the no-break space inside French guillemets
- apostrophe: converts the straight apostrophes this rule declines to pair
- Rule order
- Idempotency
- Locale resolution
Normative text: spec/rules/quotes.md.