polytypo 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -0
  3. data/lib/polytypo/data/.not-vendored +11 -0
  4. data/lib/polytypo/data/README.md +16 -11
  5. data/lib/polytypo/data/VERSION +1 -1
  6. data/lib/polytypo/data/fixtures/cs.json +1 -1
  7. data/lib/polytypo/data/fixtures/de-CH.json +1 -1
  8. data/lib/polytypo/data/fixtures/de-DE.json +14 -1
  9. data/lib/polytypo/data/fixtures/el.json +1 -1
  10. data/lib/polytypo/data/fixtures/en-GB.json +1 -1
  11. data/lib/polytypo/data/fixtures/en-US.json +143 -1
  12. data/lib/polytypo/data/fixtures/es.json +1 -1
  13. data/lib/polytypo/data/fixtures/fi.json +1 -1
  14. data/lib/polytypo/data/fixtures/fr-CA.json +1 -1
  15. data/lib/polytypo/data/fixtures/fr.json +13 -1
  16. data/lib/polytypo/data/fixtures/it.json +1 -1
  17. data/lib/polytypo/data/fixtures/locale-resolution.json +1 -1
  18. data/lib/polytypo/data/fixtures/nl.json +1 -1
  19. data/lib/polytypo/data/fixtures/pl.json +1 -1
  20. data/lib/polytypo/data/fixtures/pt-BR.json +1 -1
  21. data/lib/polytypo/data/fixtures/pt-PT.json +1 -1
  22. data/lib/polytypo/data/fixtures/ru.json +1 -1
  23. data/lib/polytypo/data/fixtures/sv.json +1 -1
  24. data/lib/polytypo/data/fixtures/tr.json +1 -1
  25. data/lib/polytypo/data/fixtures/uk.json +1 -1
  26. data/lib/polytypo/data/locales/cs.json +56 -37
  27. data/lib/polytypo/data/locales/de-CH.json +43 -34
  28. data/lib/polytypo/data/locales/de-DE.json +47 -32
  29. data/lib/polytypo/data/locales/el.json +51 -1
  30. data/lib/polytypo/data/locales/en-GB.json +56 -4
  31. data/lib/polytypo/data/locales/en-US.json +68 -4
  32. data/lib/polytypo/data/locales/es.json +66 -29
  33. data/lib/polytypo/data/locales/fi.json +77 -7
  34. data/lib/polytypo/data/locales/fr-CA.json +63 -36
  35. data/lib/polytypo/data/locales/fr.json +70 -41
  36. data/lib/polytypo/data/locales/it.json +65 -22
  37. data/lib/polytypo/data/locales/nl.json +61 -29
  38. data/lib/polytypo/data/locales/pl.json +61 -28
  39. data/lib/polytypo/data/locales/pt-BR.json +53 -28
  40. data/lib/polytypo/data/locales/pt-PT.json +55 -28
  41. data/lib/polytypo/data/locales/registry.json +1 -1
  42. data/lib/polytypo/data/locales/ru.json +45 -30
  43. data/lib/polytypo/data/locales/sv.json +63 -4
  44. data/lib/polytypo/data/locales/tr.json +72 -32
  45. data/lib/polytypo/data/locales/uk.json +55 -27
  46. data/lib/polytypo/data/rules/modes.md +243 -11
  47. data/lib/polytypo/data/rules/order.json +1 -1
  48. data/lib/polytypo/data/rules/quotes.md +27 -9
  49. data/lib/polytypo/data/schema/fixtures.schema.json +12 -1
  50. data/lib/polytypo/engine/pipeline.rb +29 -0
  51. data/lib/polytypo/modes/markdown.rb +51 -0
  52. data/lib/polytypo/modes/runner.rb +33 -3
  53. data/lib/polytypo/version.rb +1 -1
  54. data/lib/polytypo.rb +27 -12
  55. metadata +2 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf167fa991d92f3a4e63e03b4b4f96882a5842a2c80759941533cde64b0cf5a4
4
- data.tar.gz: 8337a2ad5508d5d698b5130329753eacf8534ec5b7ead365981f8d42fa85931f
3
+ metadata.gz: 406f25967b3adc0d3dca2c77838e8e59d861ede74e42959c01efcb732d08c419
4
+ data.tar.gz: '03389980598eee3542f48d15f23b1501a83cfd88e79bcc56ad2044db034ba40b'
5
5
  SHA512:
6
- metadata.gz: d408de52fe21124d92a29ef3492fccaeca452ae11c80924a2e96dd6f69509d23b21263efb4635db3d7b41b79d02798eb261eb8be87b72d24ad8f1f139a13cb59
7
- data.tar.gz: a9a451994c2b99626c5eaa702c50bd2d6b4cb2c68ee754b9711343cb51a0cf59893c3e9443203483f33adf5513a3e5431cc72d274f089004759034570610084b
6
+ metadata.gz: ab38ef48178c5d64b8261830d13d96b66b2941ebf229f6bcc8161ef66ee94f6bbbce4479d2ed98183530aea9f67a945383e7feaeb1d4b69923f4ead8dc4bea9b
7
+ data.tar.gz: a3545f797663af489eba433bf8a6d30cd0e23af1bde436834c502f746ddafd31e5caee4eda322d01b175c3de3d1faf6f18fd54616dd9fe81c40032a6a2760a36
data/README.md CHANGED
@@ -85,6 +85,31 @@ chomping indicator are never decoded and rewritten — the file is located, not
85
85
  trailing newlines of a `|+` block come back exactly as you wrote them. An empty `keys:` array is
86
86
  legal and processes nothing, and `yaml` mode needs no parser at all.
87
87
 
88
+ In `markdown` mode frontmatter is skipped whole by default, delimiters included — it is a
89
+ machine-read block, and `fr` would put a narrow no-break space in front of the colon of every
90
+ field in it. On a site whose frontmatter carries the headline that leaves the most visible string
91
+ on the page untouched, so name the keys you want processed:
92
+
93
+ ```ruby
94
+ Polytypo.transform(
95
+ %(---\ntitle: He said "hello" once\nslug: "he-said-hello"\n---\n\nThe body was typeset all along.\n),
96
+ locale: "en-US", mode: "markdown", dialect: "commonmark", frontmatter_keys: ["title"]
97
+ )
98
+ # ---
99
+ # title: He said “hello” once
100
+ # slug: "he-said-hello"
101
+ # ---
102
+ #
103
+ # The body was typeset all along.
104
+ ```
105
+
106
+ Without `frontmatter_keys:` nothing in the block changes, and a key you do not name never does. The
107
+ block is read with the same scan `yaml` mode uses, so it refuses the same constructs — a
108
+ single-quoted scalar containing `''` among them, which is how an apostrophe is written inside single
109
+ quotes, so `title: 'It''s a test'` comes back untouched. The block is also processed separately from
110
+ the body, which shows up in exactly one place: an unbalanced quotation mark in `title` cannot pair
111
+ with a mark in your first paragraph.
112
+
88
113
  `require "polytypo"` never loads the native `commonmarker` extension unless you actually call
89
114
  `Polytypo.transform` with `mode: "markdown"` — the require happens lazily, inside the markdown
90
115
  pipeline only. A single `Polytypo.transform` module method (not separate
@@ -0,0 +1,11 @@
1
+ # Paths under canonical polytypo's spec/ that this repository deliberately does not vendor, so
2
+ # check-vendored-spec.sh must not expect them here. Every other file canonical has at tag
3
+ # spec-v$(cat VERSION) must be present here, or the check fails naming it — which is how a
4
+ # vendored file that was deleted, or a canonical file that was never copied, is caught.
5
+ #
6
+ # An entry canonical does not have at that tag is an error, and so is an entry that is also
7
+ # present here: this list records deliberate omissions, not exemptions.
8
+ #
9
+ # CONFORMANCE.md is canonical's own conformance matrix, written after a release. Nothing here
10
+ # reads it, and it is the one canonical file a released spec version may still gain.
11
+ CONFORMANCE.md
@@ -18,17 +18,22 @@ one copy, shipped as part of the gem's own `lib/` payload and loaded via `File.r
18
18
  Editing a file here does not change the spec; it only drifts this copy from canonical. When
19
19
  canonical's `spec/` changes, re-copy the affected files here.
20
20
 
21
- CI checks that it was done. `script/check-vendored-spec.sh` compares every file in this
22
- directory against canonical `polytypo/polytypo` at tag `spec-v` + this directory's own
23
- `VERSION`, and fails on any difference. Three details. The files this repository authors
24
- itself are listed in `.not-canonical` and skipped — and a listed path that canonical *does*
25
- have is an error, so that list cannot be used to keep a forked copy of a canonical file. A
26
- `locales/*.json` file is compared in full when it carries its `sources` array, and against
27
- canonical minus that array when it does not, which is the shipped form three of the five
28
- runtimes vendor; the script reads which case it is off the file rather than being told. And a
29
- file here with no canonical counterpart is a failure, so a canonical rename cannot pass
30
- unnoticed. Completeness is deliberately not checked — each runtime vendors its own subset,
31
- and the subsets differ.
21
+ CI checks that it was done. `script/check-vendored-spec.sh` compares every file in this directory
22
+ against canonical `polytypo/polytypo` at tag `spec-v` + this directory's own `VERSION`, and fails
23
+ on any difference. Five details. The files this repository authors itself are listed in
24
+ `.not-canonical` and skipped — and a listed path that canonical *does* have is an error, so that
25
+ list cannot be used to keep a forked copy of a canonical file. A `locales/*.json` file is compared
26
+ in full when it carries its `sources` array, and against canonical minus that array when it does
27
+ not, which is the shipped form three of the five runtimes vendor; the script reads which case it
28
+ is off the file rather than being told. A file here with no canonical counterpart is a failure, so
29
+ a canonical rename cannot pass unnoticed. Completeness is checked from canonical's side rather
30
+ than this one: every file canonical has at that tag must be here unless `.not-vendored` names it,
31
+ so a vendored file that was deleted, or a canonical file that arrived later and was never copied,
32
+ fails naming itself, and an entry canonical does not have at that tag, or one that is also present
33
+ here, fails too. Here that list names `CONFORMANCE.md`, and nothing else. And the version itself
34
+ is checked: a tree faithful to the tag it claims while canonical has tagged a newer one is a
35
+ warning in CI and, with `--require-current`, a refusal in the release job, because a package must
36
+ not be published claiming a spec version canonical has moved past.
32
37
 
33
38
  Before that check existed, this half of the tree went stale unnoticed in four of the five
34
39
  ports at once: the data half is proved by the test suite, and nothing at all read the prose.
@@ -1 +1 @@
1
- 1.6.2
1
+ 1.7.0
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "cs",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "de-CH",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "de-DE",
4
4
  "cases": [
5
5
  {
@@ -763,6 +763,19 @@
763
763
  "in": "description: a---#b\n",
764
764
  "out": "description: a---#b\n",
765
765
  "note": "modes.md §3.8.6 with §3.4's character clause: without it this became `description: a – #b`, and everything from the hash became a comment — the value silently truncated to `a –`."
766
+ },
767
+ {
768
+ "id": "de-de-markdown-commonmark-frontmatter-keys",
769
+ "rule": "quotes",
770
+ "mode": "markdown",
771
+ "dialect": "commonmark",
772
+ "frontmatterKeys": [
773
+ "title",
774
+ "summary"
775
+ ],
776
+ "in": "---\ntitle: Er sagte \"hallo\" laut\nsummary: Noch \"eins\" hier\nslug: \"er-sagte-hallo\"\n---\n\nIm Text \"so\" hier.\n",
777
+ "out": "---\ntitle: Er sagte „hallo“ laut\nsummary: Noch „eins“ hier\nslug: \"er-sagte-hallo\"\n---\n\nIm Text „so“ hier.\n",
778
+ "note": "modes.md §3.7.4: two listed plain scalars in a locale whose quote glyphs differ from English, with an unlisted slug between the block and the body. Each listed scalar is its own span and the marks pair inside it, so the German pair appears three times and the slug keeps its straight quotes."
766
779
  }
767
780
  ]
768
781
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "el",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "en-GB",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "en-US",
4
4
  "cases": [
5
5
  {
@@ -1903,6 +1903,148 @@
1903
1903
  "out": "---\ntitle: “Unclosed”\n\nBody “quotes” here.\n",
1904
1904
  "note": "modes.md §3.7.3 skips a frontmatter block; an opening delimiter with no closing one is not a block. The leading --- is then an ordinary thematic break and everything after it is prose, so the metadata-looking line converts like any other paragraph. Pinned so the skip cannot be widened into \"anything after a leading ---\"."
1905
1905
  },
1906
+ {
1907
+ "id": "en-us-markdown-commonmark-frontmatter-keys",
1908
+ "rule": "quotes",
1909
+ "mode": "markdown",
1910
+ "dialect": "commonmark",
1911
+ "frontmatterKeys": [
1912
+ "title"
1913
+ ],
1914
+ "in": "---\ntitle: \"He said 'hi' twice\"\nslug: \"he-said-hi\"\n---\n\nBody has \"quotes\" here.\n",
1915
+ "out": "---\ntitle: \"He said “hi” twice\"\nslug: \"he-said-hi\"\n---\n\nBody has “quotes” here.\n",
1916
+ "note": "modes.md §3.7.4: the option names title, so that scalar is processable and everything else in the block — the unlisted slug, both delimiters, every key — is not. Compare en-us-markdown-commonmark-frontmatter, the same shape with the option absent."
1917
+ },
1918
+ {
1919
+ "id": "en-us-markdown-commonmark-frontmatter-keys-empty",
1920
+ "rule": "quotes",
1921
+ "mode": "markdown",
1922
+ "dialect": "commonmark",
1923
+ "frontmatterKeys": [],
1924
+ "in": "---\ntitle: \"He said 'hi' twice\"\n---\n\nBody has \"quotes\" here.\n",
1925
+ "out": "---\ntitle: \"He said 'hi' twice\"\n---\n\nBody has “quotes” here.\n",
1926
+ "note": "modes.md §3.7.4: an empty list is legal and yields no spans, as it does for keys in yaml mode. Process nothing is a choice, not an error, and the output is identical to the option being absent."
1927
+ },
1928
+ {
1929
+ "id": "en-us-markdown-commonmark-frontmatter-keys-unlisted-only",
1930
+ "rule": "dashes",
1931
+ "mode": "markdown",
1932
+ "dialect": "commonmark",
1933
+ "frontmatterKeys": [
1934
+ "title"
1935
+ ],
1936
+ "in": "---\ndescription: \"Fast - reliable\"\n---\n\nBody - here.\n",
1937
+ "out": "---\ndescription: \"Fast - reliable\"\n---\n\nBody—here.\n",
1938
+ "note": "modes.md §3.7.4: a block containing none of the listed keys is left exactly as the skip left it. The same dash converts in the body, so the case pins that the block was not processed rather than that the rule declined."
1939
+ },
1940
+ {
1941
+ "id": "en-us-markdown-commonmark-frontmatter-keys-toml",
1942
+ "rule": "quotes",
1943
+ "mode": "markdown",
1944
+ "dialect": "commonmark",
1945
+ "frontmatterKeys": [
1946
+ "title"
1947
+ ],
1948
+ "in": "+++\ntitle = \"He said 'hi'\"\n+++\n\nBody has \"quotes\" here.\n",
1949
+ "out": "+++\ntitle = \"He said 'hi'\"\n+++\n\nBody has “quotes” here.\n",
1950
+ "note": "modes.md §3.7.4: a TOML block yields no spans with the option given. TOML quoting is a second grammar the scan does not claim (§7.13), so +++ keeps §3.7.3's skip-whole treatment whatever the caller lists."
1951
+ },
1952
+ {
1953
+ "id": "en-us-markdown-commonmark-frontmatter-keys-unterminated",
1954
+ "rule": "quotes",
1955
+ "mode": "markdown",
1956
+ "dialect": "commonmark",
1957
+ "frontmatterKeys": [
1958
+ "title"
1959
+ ],
1960
+ "in": "---\ntitle: \"Unclosed\"\n\nBody \"quotes\" here.\n",
1961
+ "out": "---\ntitle: “Unclosed”\n\nBody “quotes” here.\n",
1962
+ "note": "modes.md §3.7.4: an opening delimiter with no closing one is not a block, so the option has nothing to name and adds no spans. The line converts anyway — as ordinary prose, per en-us-markdown-commonmark-frontmatter-unterminated — which is why the option must not be credited with it."
1963
+ },
1964
+ {
1965
+ "id": "en-us-markdown-commonmark-frontmatter-keys-escape-bail",
1966
+ "rule": "quotes",
1967
+ "mode": "markdown",
1968
+ "dialect": "commonmark",
1969
+ "frontmatterKeys": [
1970
+ "title"
1971
+ ],
1972
+ "in": "---\ntitle: \"She said \\\"hi\\\" to me\"\n---\n\nBody has \"quotes\" here.\n",
1973
+ "out": "---\ntitle: \"She said \\\"hi\\\" to me\"\n---\n\nBody has “quotes” here.\n",
1974
+ "note": "modes.md §3.7.4 inherits every bail of §3.8.6: a double-quoted scalar whose content contains U+005C yields no spans, because the source spells the content with more characters than it has. The listed key is not enough on its own."
1975
+ },
1976
+ {
1977
+ "id": "en-us-markdown-commonmark-frontmatter-keys-single-quote-bail",
1978
+ "rule": "apostrophe",
1979
+ "mode": "markdown",
1980
+ "dialect": "commonmark",
1981
+ "frontmatterKeys": [
1982
+ "title"
1983
+ ],
1984
+ "in": "---\ntitle: 'It''s a test'\n---\n\nBody it's here.\n",
1985
+ "out": "---\ntitle: 'It''s a test'\n---\n\nBody it’s here.\n",
1986
+ "note": "modes.md §3.7.4, and the cost §7.13 measures: a single-quoted scalar containing two consecutive U+0027 yields no spans, and an apostrophe in a single-quoted scalar is written exactly that way. Half the prose values of the §3.7.4.1 corpus bail here when re-emitted in this style."
1987
+ },
1988
+ {
1989
+ "id": "en-us-markdown-commonmark-frontmatter-keys-separate-unit",
1990
+ "rule": "quotes",
1991
+ "mode": "markdown",
1992
+ "dialect": "commonmark",
1993
+ "frontmatterKeys": [
1994
+ "title"
1995
+ ],
1996
+ "in": "---\ntitle: He said \"hello\n---\n\nworld\" she said\n",
1997
+ "out": "---\ntitle: He said \"hello\n---\n\nworld\" she said\n",
1998
+ "note": "modes.md §3.7.4: the block is its own text unit, so the unbalanced mark in title cannot pair with the one in the body. This case discriminates: run as one unit the pair converts — measured, text mode over the same characters gives “hello … world” — and under two units both marks stay straight. A runtime that concatenates the block with the body passes every other frontmatter case and fails this one."
1999
+ },
2000
+ {
2001
+ "id": "en-us-markdown-commonmark-frontmatter-keys-nested",
2002
+ "rule": "quotes",
2003
+ "mode": "markdown",
2004
+ "dialect": "commonmark",
2005
+ "frontmatterKeys": [
2006
+ "title"
2007
+ ],
2008
+ "in": "---\nseo:\n title: He said \"hi\"\nslug: \"x\"\n---\n\nBody \"quotes\" here.\n",
2009
+ "out": "---\nseo:\n title: He said “hi”\nslug: \"x\"\n---\n\nBody “quotes” here.\n",
2010
+ "note": "modes.md §3.7.4 with §3.8.2's matching: a listed key is processable at any depth and by bare name only, so a nested title converts while the sibling slug does not. §7.12's accepted cost is the same one — a caller cannot say seo.title and not title."
2011
+ },
2012
+ {
2013
+ "id": "en-us-markdown-commonmark-frontmatter-keys-block-scalar",
2014
+ "rule": "dashes",
2015
+ "mode": "markdown",
2016
+ "dialect": "commonmark",
2017
+ "frontmatterKeys": [
2018
+ "summary"
2019
+ ],
2020
+ "in": "---\nsummary: |\n One line - here.\n Another - line.\n---\n\nBody - here.\n",
2021
+ "out": "---\nsummary: |\n One line—here.\n Another—line.\n---\n\nBody—here.\n",
2022
+ "note": "modes.md §3.7.4 reuses §3.8.5: each non-blank content line of a listed block scalar is one span, the header and the indentation stay outside every span, and the gap between two content lines is a −2 marker."
2023
+ },
2024
+ {
2025
+ "id": "en-us-markdown-commonmark-frontmatter-keys-crlf",
2026
+ "rule": "quotes",
2027
+ "mode": "markdown",
2028
+ "dialect": "commonmark",
2029
+ "frontmatterKeys": [
2030
+ "title"
2031
+ ],
2032
+ "in": "---\r\ntitle: \"He said 'hi'\"\r\n---\r\n\r\nBody has \"quotes\" here.\r\n",
2033
+ "out": "---\r\ntitle: \"He said “hi”\"\r\n---\r\n\r\nBody has “quotes” here.\r\n",
2034
+ "note": "modes.md §3.7.4: a U+000D before the terminator belongs to the terminator, so a CRLF document gives the same block content as the same bytes with LF and the delimiters stay outside every span."
2035
+ },
2036
+ {
2037
+ "id": "en-us-markdown-mdx-frontmatter-keys",
2038
+ "rule": "quotes",
2039
+ "mode": "markdown",
2040
+ "dialect": "mdx",
2041
+ "frontmatterKeys": [
2042
+ "title"
2043
+ ],
2044
+ "in": "---\ntitle: \"He said 'hi'\"\n---\n\n<Callout>Body \"quotes\" here</Callout>\n",
2045
+ "out": "---\ntitle: \"He said “hi”\"\n---\n\n<Callout>Body “quotes” here</Callout>\n",
2046
+ "note": "modes.md §3.7.4 applies to both dialects: YAML frontmatter is the same construct in mdx as in commonmark, and §3.7.3 lists it once for both."
2047
+ },
1906
2048
  {
1907
2049
  "id": "en-us-ranges-closed-up-currency",
1908
2050
  "rule": "ranges",
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "es",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "fi",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "fr-CA",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "fr",
4
4
  "cases": [
5
5
  {
@@ -747,6 +747,18 @@
747
747
  "out": "---\ntitle: Une note ; suite\n---\n\n<Callout>Est-ce vrai ?</Callout>\n",
748
748
  "note": "modes.md §3.7.3: the same guarantee under \"mdx\", where frontmatter plus JSX is the ordinary document shape. This is the case the polytypo-js source comment above its frontmatter skip entry described as a spec gap."
749
749
  },
750
+ {
751
+ "id": "fr-markdown-commonmark-frontmatter-keys-colon",
752
+ "rule": "nbsp",
753
+ "mode": "markdown",
754
+ "dialect": "commonmark",
755
+ "frontmatterKeys": [
756
+ "title"
757
+ ],
758
+ "in": "---\ntitle: \"Chapitre 1: le début\"\ndate: \"2026-09-25\"\n---\n\nVoici: le corps.\n",
759
+ "out": "---\ntitle: \"Chapitre 1 : le début\"\ndate: \"2026-09-25\"\n---\n\nVoici : le corps.\n",
760
+ "note": "modes.md §3.7.4 with the locale that made §3.7.3 skip the block in the first place: inside a listed quoted scalar a colon is content, so fr inserts its narrow no-break space, while the unlisted date field and the key itself keep every byte. That contrast is the whole argument for a caller-named list."
761
+ },
750
762
  {
751
763
  "id": "fr-nbsp-character-reference-numeric",
752
764
  "rule": "nbsp",
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "it",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "cases": [
4
4
  {
5
5
  "id": "exact-en-us",
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "nl",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "pl",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "pt-BR",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "pt-PT",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "ru",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "sv",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "tr",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.6.2",
2
+ "spec": "1.7.0",
3
3
  "locale": "uk",
4
4
  "cases": [
5
5
  {
@@ -33,43 +33,62 @@
33
33
  "nbsp": {
34
34
  "beforePunctuation": [],
35
35
  "narrowBeforePunctuation": [],
36
- "afterShortWords": [
37
- "a",
38
- "i",
39
- "k",
40
- "o",
41
- "s",
42
- "u",
43
- "v",
44
- "z"
45
- ],
46
- "abbreviations": [
47
- "a. s.",
48
- "s. r. o."
49
- ],
50
- "beforeUnits": [
51
- "%",
52
- "°C",
53
- "ha",
54
- "kg",
55
- "km",
56
- "cm",
57
- "mm",
58
- "m²",
59
- "kWh",
60
- "km/h",
61
- "str.",
62
- "hod."
63
- ],
36
+ "afterShortWords": ["a", "i", "k", "o", "s", "u", "v", "z"],
37
+ "abbreviations": ["a. s.", "s. r. o."],
38
+ "beforeUnits": ["%", "°C", "ha", "kg", "km", "cm", "mm", "m²", "kWh", "km/h", "str.", "hod."],
64
39
  "beforeNumber": [],
65
- "beforeWord": [
66
- "tj.",
67
- "tzv.",
68
- "tzn."
69
- ],
70
- "afterSymbols": [
71
- "§"
72
- ],
40
+ "beforeWord": ["tj.", "tzv.", "tzn."],
41
+ "afterSymbols": ["§"],
73
42
  "initialBinding": "single"
74
- }
43
+ },
44
+ "sources": [
45
+ {
46
+ "rule": "quotes",
47
+ "cite": "Ústav pro jazyk český AV ČR, Internetová jazyková příručka, «Uvozovky»: «V češtině užíváme různé varianty uvozovek: dvojité „ “, jednoduché ‚ ‘ a boční » «»; «Jako základní se doporučují uvozovky typu 99 66, tj. dvojité „ “»; «Ostatní typy uvozovek (v první řadě jednoduché, až v druhé řadě boční) se užívají především tehdy, pokud potřebujeme do uvozeného textu vložit ještě další uvozovky»; «Uvozovky přiléhají vždy těsně k výrazům, které ohraničují (tedy „takto“)»",
48
+ "url": "https://prirucka.ujc.cas.cz/?id=162",
49
+ "note": "První stupeň: U+201E otevírací, U+201C zavírací — NIKOLI U+201D jako v polštině, a právě to je rozdíl, který se v sazbě přehlédne. Kódy nebyly odečteny z markdownové konverze stránky, která se u uvozovek ukázala jako nespolehlivá; rozhodující je vlastní označení zdroje «uvozovky typu 99 66»: první znaménko má tvar devítky u paty řádku (U+201E), druhé tvar šestky vyzdvižený nahoru (U+201C). Polský pár PWN naproti tomu označuje jako 99 99. Druhý stupeň: jednoduché uvozovky, protože zdroj je výslovně řadí «v první řadě» pro uvozovky v uvozovkách. OMEZENÁ JISTOTA, řečená naplno: označení «99 66» je ve zdroji uvedeno pouze pro dvojité uvozovky, takže kódy jednoduchých (U+201A … U+2018) vycházejí ze stejného vzorce a z vysázených glyfů, ne z doslovného tvrzení. Rozhodla by tabulka H.1 normy ČSN 01 6910:2014, která je však placená (ČSN online od 1 000 Kč/rok). ROZHODNUTÍ OPERÁTORA, 18. 9. 2026: leží-li pravidlo za placenou normou, projekt je formuluje podle převládajícího úzu a označí to jako rozhodnutí, nikoli jako citaci — stejně jako u německého «Nr.». Padne-li placená stěna, nahradí znění normy tento řádek. Q-W platí v obou případech: U+201A, U+2018 i U+2019 patří do NARROW. Ověřeno 18. 9. 2026."
50
+ },
51
+ {
52
+ "rule": "dashes",
53
+ "cite": "Ústav pro jazyk český AV ČR, Internetová jazyková příručka, «Pomlčka»: «Pomlčku oddělujeme z obou stran mezerami, komplikovanější situace je pouze tehdy, pokud toto znaménko vystupuje ve funkci výrazů a, až, od … do … nebo proti (versus)»; «pomlčka místo čárky: Tato kniha – vydaná ještě před válkou – je opravdu úžasná»; «Pomlčka (–) je dlouhá vodorovná čárka»; «Vedle krátké pomlčky (–) se v textu někdy používá také pomlčka dlouhá (—)»",
54
+ "url": "https://prirucka.ujc.cas.cz/?id=165",
55
+ "note": "Odůvodňuje dash.parenthetical = «en-spaced». Vsuvka není žádná ze čtyř vyjmenovaných výjimek, takže platí obecné pravidlo o mezerách z obou stran. DÉLKA je na rozdíl od polštiny rozhodnuta samotným zdrojem: definiční glyf stránky je krátká pomlčka U+2013 a dlouhá U+2014 je popsána jako to, co se používá «někdy» navíc. U polštiny musel o délce rozhodnout operátor, protože PWN si v korpusu protiřečil; tady stačí číst. PŘÍPUSTNÁ VARIANTA, zaznamenána: U+2014. Ověřeno 18. 9. 2026."
56
+ },
57
+ {
58
+ "rule": "ranges",
59
+ "cite": "Ústav pro jazyk český AV ČR, Internetová jazyková příručka, «Pomlčka», vyjádření rozsahu (s významem ‚až‘ či ‚od … do‘): «strana 23–26, v letech 1945–1948, 9–16 h»; ÚJČ (P. Lozan, M. Pravdová), «Otázky a odpovědi k ČSN 01 6910 (2014)», otázka 2: «norma umožňuje i psaní mezery kolem pomlčky v rozsazích a ve významu „a“ a „versus“, jestliže je alespoň jeden z výrazů kolem pomlčky víceslovný»",
60
+ "url": "https://prirucka.ujc.cas.cz/?id=165",
61
+ "note": "Odůvodňuje dash.range = «en-tight»: krátká pomlčka U+2013 bez mezer. Potvrzeno ze dvou stran: příručka sází rozsahy těsně, a dokument ÚJČ k normě popisuje mezery v rozsahu jako povolenou VÝJIMKU pro víceslovné výrazy, což těsnou sazbu předpokládá jako pravidlo. Text samotné ČSN 01 6910 čten nebyl (je placený); citován je výklad jejích vlastních zpracovatelů, což je nejsilnější veřejně dostupná náhrada. Stránka rovněž uvádí, že pomlčka vyjadřující rozsah nemá stát na konci ani na začátku řádku — požadavek, pro který locale.schema.json nemá pole. Pravidlo ranges je ve výchozím stavu vypnuté (spec 0.5.0). Ověřeno 18. 9. 2026."
62
+ },
63
+ {
64
+ "rule": "ellipsis",
65
+ "cite": "Ústav pro jazyk český AV ČR, Internetová jazyková příručka, «Tři tečky»: «jedná se totiž o jeden znak, nikoli tři samostatné tečky»; «Za třemi tečkami často následují další interpunkční znaménka, která se za tři tečky připojují bez mezery, ale nikdy se však nepřipojuje další tečka (nikoli tedy čtyři tečky vedle sebe)»",
66
+ "url": "https://prirucka.ujc.cas.cz/?id=166",
67
+ "note": "Odůvodňuje abbreviatedAfterTerminal = false. Výpustka je jeden znak U+2026 a otazník či vykřičník stojí ZA ní — ruská dvoutečková forma «?..» není v žádném konzultovaném českém zdroji předvídána. Bylo to ověřeno záměrně: ukrajinština, zkoumaná v témže průchodu, tu formu MÁ, takže analogie mezi slovanskými jazyky by tu byla svůdná a mylná. Ověřeno 18. 9. 2026."
68
+ },
69
+ {
70
+ "rule": "hyphen",
71
+ "cite": "Ústav pro jazyk český AV ČR, Internetová jazyková příručka, «Spojovník»: «píše se bez mezer mezi výrazy, které spojuje»; «Spojovník na začátku dalšího řádku neopakujeme, naznačuje-li rozdělení slova (např. žong- | -lér)»; táž příručka, «Zalomení řádků a nevhodné výrazy na jejich konci», jejíž uzavřený výčet míst, kde k zalomení dojít nemá, spojovník neuvádí",
72
+ "url": "https://prirucka.ujc.cas.cz/?id=164",
73
+ "note": "Odůvodňuje tři prázdné seznamy. Čeština spojovník na konci řádku ani nezakazuje, ani — na rozdíl od polštiny — dělení v jeho místě nepředepisuje; neexistuje uzavřený normativní seznam tvarů, jejichž spojovník se nesmí lámat. To, že jej třináctibodový výčet stránky o zalomení řádků neobsahuje, je doložená nepřítomnost, ne nenalezená citace — a je to zároveň rozdíl proti ukrajinštině, kde § 64 п. 4 takový seznam má. Ověřeno 18. 9. 2026."
74
+ },
75
+ {
76
+ "rule": "nbsp",
77
+ "cite": "Ústav pro jazyk český AV ČR, Internetová jazyková příručka, «Zalomení řádků a nevhodné výrazy na jejich konci»: k zalomení nemá dojít «ve spojení neslabičných předložek k, s, v, z s následujícím slovem, např. k mostu, s bratrem, v Plzni, z nádraží»; «ve spojení slabičných předložek o, u a spojek a, i s výrazem, který po nich následuje, např. u babičky, o páté»; «ve složených zkratkách a kódech, např. a. s., s. r. o.»; «mezi zkratkami tj., tzv., tzn. a následujícím výrazem»; «mezi zkratkou jména a příjmením»; «mezi číslem a značkou, např. 50 %, § 23»; «V textovém procesoru vkládáme tam, kde nemá dojít k zalomení řádku, místo běžné mezislovní mezery mezeru pevnou»",
78
+ "url": "https://prirucka.ujc.cas.cz/?id=880",
79
+ "note": "Pět polí z jednoho uzavřeného výčtu, který se odvolává na ČSN 01 6910. (1) afterShortWords: osm jednopísmenných výrazů, citovaných doslova. Na rozdíl od polštiny je zákaz v prameni BEZPODMÍNEČNÝ — není vázán na šířku sloupce ani na to, zda jde o titulek — takže zde nebylo co rozhodovat. (2) abbreviations: «a. s.» a «s. r. o.». Interakce s N3 ověřena a je opačná než u polského «i in.»: N3 vyžaduje, aby po jednopísmenném výrazu následovala mezera, a po «a» následuje tečka, takže N3 index nenárokuje a obě mezery dostane N4. «ČSN 01 6910» z výčtu vypuštěno: číslo normy není fakt o jazyce. (3) beforeWord: «tj.», «tzv.», «tzn.». Bod o «zkratce titulu» populován není, protože zdroj u něj žádný seznam neuvádí. (4) afterSymbols = [«§»]. Ze stejného citovaného bodu byly ZÁMĚRNĚ vynechány «*», «†» a «#»: řetězec «* 1921» je bajt po bajtu totožný s odrážkou seznamu následovanou číslicí, takže v režimu markdown by šlo o falešný zásah bez vyvažujícího přínosu. (5) initialBinding = «single»: pramen váže «zkratku jména» k příjmení a jeho vlastní příklady jsou Fr. Daneš a M. Těšitelová, tedy JEDNA zkrácená křestní jméno; «chain» by na ani jednom z nich nezabral a citované pravidlo by zůstalo nenaplněné. Expozici, kterou schéma u «single» samo pojmenovává, přijímáme na základě citace, stejně jako u fr. beforeNumber zůstává prázdné: jediným kandidátem je «strana 2», ale «strana» je víceznačné plnovýznamové slovo, nikoli zkratka — týž argument, jakým ru.json vylučuje «г.». Ověřeno 18. 9. 2026."
80
+ },
81
+ {
82
+ "rule": "nbsp",
83
+ "cite": "Ústav pro jazyk český AV ČR, Internetová jazyková příručka, «Značky, čísla a číslice»: «Značky se od číselné hodnoty oddělují mezerou, číslo a značka se umísťují na stejný řádek», «např. 10 ha = 10 hektarů, 3 kg = 3 kilogramy, 14 % = 14 procent», «100 kWh», «rychlost 50 km/h», «teplota 12–15 °C»; BIPM, The International System of Units (SI), 9th ed., concise summary: «A single space is always left between the number and the unit»",
84
+ "url": "https://prirucka.ujc.cas.cz/?id=785",
85
+ "note": "Rozdělení rolí jako v en-US, fi, sv, es, pt a pl: BIPM říká, které řetězce jsou značkami jednotek SI, ÚJČ říká, jaká je česká konvence. České znění je přitom silnější, než nbsp.md §2.1 od locale vyžaduje: uvádí nejen mezeru, ale i to, že «číslo a značka se umísťují na stejný řádek». PŘÍPUSTNÁ VARIANTA, zaznamenána: «V případě, že pomocí číslice a značky vyjadřujeme přídavné jméno, mezeru nevkládáme: 8km = 8kilometrový, 20% = 20procentní». Rozpor není třeba řešit: N5 existující mezeru pouze nahrazuje a nikdy ji nevkládá (nbsp.md §3.7), takže «14 %» dostane U+00A0 a «20%» zůstane nedotčeno. Jednoznakové značky vypuštěny, a pro češtinu z konkrétního důvodu, ne ze stylové opatrnosti: «s» je zároveň vyjmenovaná jednopísmenná předložka, takže ve větě «Přišel v 5 s bratrem» by se «5» svázala se «s». Ověřeno 18. 9. 2026."
86
+ },
87
+ {
88
+ "rule": "nbsp",
89
+ "cite": "Ústav pro jazyk český AV ČR, Internetová jazyková příručka, «Otazník»: «Stejně jako naprostá většina interpunkčních znamének se připojuje k předcházejícímu slovu (zkratce, značce) bez mezery, za ním následuje mezera»; táž příručka, «Tři tečky»: «tři tečky následující za slovem se připojují bez mezery»",
90
+ "url": "https://prirucka.ujc.cas.cz/?id=171",
91
+ "note": "Odůvodňuje prázdné beforePunctuation i narrowBeforePunctuation. Čeština před «:», «;», «!», «?» nestaví ani U+00A0, ani U+202F, a zdroj jim odstup výslovně upírá — je to zápor, ne mlčení. Q-P je tím splněno triviálně. Ověřeno 18. 9. 2026."
92
+ }
93
+ ]
75
94
  }