polytypo 1.3.1 → 1.4.1

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/polytypo/data/README.md +15 -0
  3. data/lib/polytypo/data/VERSION +1 -1
  4. data/lib/polytypo/data/fixtures/cs.json +1 -1
  5. data/lib/polytypo/data/fixtures/de-CH.json +1 -1
  6. data/lib/polytypo/data/fixtures/de-DE.json +1 -1
  7. data/lib/polytypo/data/fixtures/el.json +1 -1
  8. data/lib/polytypo/data/fixtures/en-GB.json +44 -1
  9. data/lib/polytypo/data/fixtures/en-US.json +18 -1
  10. data/lib/polytypo/data/fixtures/es.json +1 -1
  11. data/lib/polytypo/data/fixtures/fi.json +1 -1
  12. data/lib/polytypo/data/fixtures/fr-CA.json +9 -1
  13. data/lib/polytypo/data/fixtures/fr.json +50 -1
  14. data/lib/polytypo/data/fixtures/it.json +17 -1
  15. data/lib/polytypo/data/fixtures/locale-resolution.json +1 -1
  16. data/lib/polytypo/data/fixtures/nl.json +17 -1
  17. data/lib/polytypo/data/fixtures/pl.json +1 -1
  18. data/lib/polytypo/data/fixtures/pt-BR.json +9 -1
  19. data/lib/polytypo/data/fixtures/pt-PT.json +9 -1
  20. data/lib/polytypo/data/fixtures/ru.json +1 -1
  21. data/lib/polytypo/data/fixtures/sv.json +1 -1
  22. data/lib/polytypo/data/fixtures/uk.json +1 -1
  23. data/lib/polytypo/data/locales/cs.json +42 -57
  24. data/lib/polytypo/data/locales/de-CH.json +39 -44
  25. data/lib/polytypo/data/locales/de-DE.json +37 -48
  26. data/lib/polytypo/data/locales/el.json +6 -52
  27. data/lib/polytypo/data/locales/en-GB.json +8 -50
  28. data/lib/polytypo/data/locales/en-US.json +8 -62
  29. data/lib/polytypo/data/locales/es.json +34 -67
  30. data/lib/polytypo/data/locales/fi.json +12 -78
  31. data/lib/polytypo/data/locales/fr-CA.json +55 -52
  32. data/lib/polytypo/data/locales/fr.json +60 -59
  33. data/lib/polytypo/data/locales/it.json +26 -59
  34. data/lib/polytypo/data/locales/nl.json +33 -49
  35. data/lib/polytypo/data/locales/pl.json +33 -62
  36. data/lib/polytypo/data/locales/pt-BR.json +32 -47
  37. data/lib/polytypo/data/locales/pt-PT.json +32 -49
  38. data/lib/polytypo/data/locales/registry.json +1 -1
  39. data/lib/polytypo/data/locales/ru.json +35 -46
  40. data/lib/polytypo/data/locales/sv.json +9 -64
  41. data/lib/polytypo/data/locales/uk.json +32 -56
  42. data/lib/polytypo/data/rules/order.json +2 -2
  43. data/lib/polytypo/data/schema/locale.schema.json +23 -2
  44. data/lib/polytypo/engine/rules/quote_ambiguity.rb +68 -0
  45. data/lib/polytypo/engine/rules/quotes.rb +10 -6
  46. data/lib/polytypo/version.rb +1 -1
  47. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f23f9a5d1868ea452ed02c3becffa73160c432d050f2af45ce54199a25b3064c
4
- data.tar.gz: a9195694fa18f75875bb6dd20d5dccffc90c8f3272dd880ad0b11f3164d64b2d
3
+ metadata.gz: 666e8d6abb66f68efd99b02224e7a7a344933e5707f9cb32fc2169e615bb1d15
4
+ data.tar.gz: bd1a363b0c0a29be241a7455af03699f0f4c76f37ad24c4685d64a8ccad247e8
5
5
  SHA512:
6
- metadata.gz: 3dc6343031405d5195562a8b561395fa111bb664c05649d6859f850f8df5a7d6f3e3945f52793d0adfbe910845cb34032376dd7edb281954273415ea3d0e2487
7
- data.tar.gz: 05f095ad58cbc58c2247053345675c19e914aba0bc36163ebe108bc0b7751e7905774aa4a7fd3b18b052a1865025bedb30572be0b2b04506cd1e3b919290c0a9
6
+ metadata.gz: 451be400df665a42f7ebf3ef9ca20a16016544d404649446c39040382570311428d2eac903a3b3042929bcd25917f4fffd79d7cf173671be5e112fb9f97d58c7
7
+ data.tar.gz: 88706e664349141c734be770fbab45208ce806c2b7a6d8b37982b34d40ff7eed81fe3167cbbe7681451e50fc0487fb63ed762e53b7603c84f991cc3e883bf698
@@ -18,3 +18,18 @@ canonical's `spec/` changes, re-copy the affected files here. How this vendoring
18
18
  long-term (submodule, per-ecosystem spec package, or something else) is an open decision tracked
19
19
  in `polytypo/polytypo`'s roadmap; this is the interim, manually-synced form — the same status
20
20
  every other port's vendored copy has.
21
+
22
+ ## `locales/*.json` here carry no `sources`
23
+
24
+ The canonical files do, and mandatorily — `spec/schema/locale.schema.json` makes `sources`
25
+ required with `minItems: 1`, and `validate-spec.mjs` fails a locale without a citation. This copy
26
+ drops that one field, because these exact files are what ships: no rule reads the citations, and
27
+ they are 94% of the locale payload by raw bytes (193 KB of 206 KB, against 13 KB of everything the
28
+ engine actually consults). Keeping them here would put a quarter-megabyte of citation prose in
29
+ every install of this package.
30
+
31
+ So this is one field narrower than the canonical file, deliberately, and it is not drift: the
32
+ directory was always "the subset it needs" (see the paragraph above). **The citations are
33
+ evidence and they are not weakened — read them in `polytypo/polytypo`'s own `spec/locales/`, or
34
+ on the project's Locales page, which renders them from those files.** Re-syncing this directory
35
+ means copying the canonical files and dropping `sources` again.
@@ -1 +1 @@
1
- 1.3.1
1
+ 1.4.0
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "cs",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "de-CH",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "de-DE",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "el",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "en-GB",
4
4
  "cases": [
5
5
  {
@@ -1296,6 +1296,49 @@
1296
1296
  "ranges": true
1297
1297
  },
1298
1298
  "note": "ranges.md §3.2a (spec 1.3.0): U+00A3, pinned in the locale that actually writes it. The closed-up walk is locale-independent shape logic; `en-GB` shares `en-US`'s `range: \"en-tight\"`."
1299
+ },
1300
+ {
1301
+ "id": "en-gb-markdown-commonmark-span-boundary-possessive-declined",
1302
+ "rule": "quotes",
1303
+ "mode": "markdown",
1304
+ "dialect": "commonmark",
1305
+ "in": "He says 'avoid `x`'s printer.' Done.\n",
1306
+ "out": "He says ‘avoid `x`’s printer.’ Done.\n",
1307
+ "note": "Canonical issue #53's headline case. quotes.md §3.2's span-boundary elision veto (spec 1.4.0): the possessive sits flush against a code span, so modes.md §3.2's marker occupies the position the x would hold and the medial-elision veto cannot fire. The cited quotes.elisionClitics.after entry s declines the mark, apostrophe (order 50) renders it U+2019 by its case 4 across the marker, and the author's own pair keeps the primary glyphs. Through spec 1.3.1 this produced ’avoid `x`‘s printer.’ — the possessive opening the quotation and the real opening mark demoted to a closing glyph — and it was a fixed point, so a second pass never repaired it."
1308
+ },
1309
+ {
1310
+ "id": "en-gb-html-span-boundary-possessive-declined",
1311
+ "rule": "quotes",
1312
+ "mode": "html",
1313
+ "in": "He says 'avoid <code>x</code>'s printer.' Done.",
1314
+ "out": "He says ‘avoid <code>x</code>’s printer.’ Done.",
1315
+ "note": "The same shape in html rather than markdown. modes.md §3.2's marker model is mode-independent, so the two agree on identical prose split the same way (modes.md §7.4)."
1316
+ },
1317
+ {
1318
+ "id": "en-gb-markdown-commonmark-span-boundary-possessive-strong",
1319
+ "rule": "quotes",
1320
+ "mode": "markdown",
1321
+ "dialect": "commonmark",
1322
+ "in": "He says 'avoid **x**'s printer.' Done.\n",
1323
+ "out": "He says ‘avoid **x**’s printer.’ Done.\n",
1324
+ "note": "Strong emphasis behaves as a code span does: the veto reads the marker, not what kind of span produced it."
1325
+ },
1326
+ {
1327
+ "id": "en-gb-markdown-commonmark-span-boundary-plural-possessive-not-closed",
1328
+ "rule": "quotes",
1329
+ "mode": "markdown",
1330
+ "dialect": "commonmark",
1331
+ "in": "He says 'avoid `xs`' printer.' Done.\n",
1332
+ "out": "He says ‘avoid `xs`’ printer.' Done.\n",
1333
+ "note": "Known non-closure, pinned so it stays visible: a PLURAL possessive after a span has the marker on its left and a space on its right, so its attaching run is empty and no entry can match. The same three code points are also a quotation's closing mark after a span, which is a modes.md §3.3 row, so no test over these neighbours could separate them. The second mark takes the pairing and the third is abandoned as U+0027. quotes.md §4 and §7 item 10; tracked as issue #54."
1334
+ },
1335
+ {
1336
+ "id": "en-gb-html-span-boundary-quotation-not-declined",
1337
+ "rule": "quotes",
1338
+ "mode": "html",
1339
+ "in": "'a'<code>x</code>'b'",
1340
+ "out": "‘a’<code>x</code>‘b’",
1341
+ "note": "The negative control, and the reason the veto reads a cited fragment rather than the marker itself. modes.md §3.3 puts the marker in OPENISH precisely so the second pair here can open flush after a span; letting the marker satisfy the medial-elision veto's ALNUM test would have broken this row and en-us-markdown-commonmark-boundary-nested-quotes with it. The run b is not a listed fragment, so the quotation stands."
1299
1342
  }
1300
1343
  ]
1301
1344
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "en-US",
4
4
  "cases": [
5
5
  {
@@ -2513,6 +2513,23 @@
2513
2513
  "in": "description: one...two\n three...four\n",
2514
2514
  "out": "description: one...two\n three...four\n",
2515
2515
  "note": "modes.md §3.8.6: a multi-line plain scalar's folding is a construct this scan does not claim, so the whole value yields no spans — including its first line."
2516
+ },
2517
+ {
2518
+ "id": "en-us-markdown-commonmark-span-boundary-possessive-declined",
2519
+ "rule": "quotes",
2520
+ "mode": "markdown",
2521
+ "dialect": "commonmark",
2522
+ "in": "He says 'avoid `x`'s printer.' Done.\n",
2523
+ "out": "He says “avoid `x`’s printer.” Done.\n",
2524
+ "note": "Canonical issue #53's headline case in a locale whose primary pair is WIDE, where the inversion was loudest: through spec 1.3.1 the possessive opened a double-quoted quotation, giving ’avoid `x`“s printer.”. quotes.md §3.2's span-boundary elision veto (spec 1.4.0) declines the mark on the cited quotes.elisionClitics.after entry s."
2525
+ },
2526
+ {
2527
+ "id": "en-us-html-span-boundary-listed-fragment-quoted",
2528
+ "rule": "quotes",
2529
+ "mode": "html",
2530
+ "in": "He said <em>'s'</em> loudly.",
2531
+ "out": "He said <em>’s’</em> loudly.",
2532
+ "note": "Accepted false positive, recorded in the suite rather than left in someone's content: a quotation inside an inline span whose content is EXACTLY a listed fragment is read as an elision. Strictly narrower than, and the same family as, the universal medial-n veto's accepted The letter 'n' is common. — it needs the span boundary as well as the single-fragment content. '<em>x</em>', '<em>no</em>' and '<em>fine</em>' in the same position are unaffected. quotes.md §3.2."
2516
2533
  }
2517
2534
  ]
2518
2535
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "es",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "fi",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "fr-CA",
4
4
  "cases": [
5
5
  {
@@ -312,6 +312,14 @@
312
312
  "in": "le n° cinq",
313
313
  "out": "le n° cinq",
314
314
  "note": "nbsp.md §3.11 N9 étape 4 : il faut un DIGIT après le séparateur. Un nombre écrit en toutes lettres n'en est pas un, l'espace reste sécable."
315
+ },
316
+ {
317
+ "id": "fr-ca-quotes-span-boundary-elision-declined",
318
+ "rule": "quotes",
319
+ "mode": "html",
320
+ "in": "Il dit 'l'<em>idée</em> est bonne.' Fin.",
321
+ "out": "Il dit « l’<em>idée</em> est bonne. » Fin.",
322
+ "note": "quotes.md §3.2's span-boundary elision veto (spec 1.4.0), on the identical OQLF citation fr carries — the OQLF is Quebec's own authority, so for fr-CA the evidence is direct rather than a substitute. No retrieved source states any France/Quebec divergence on elision, so the two lists are identical."
315
323
  }
316
324
  ]
317
325
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "fr",
4
4
  "cases": [
5
5
  {
@@ -879,6 +879,55 @@
879
879
  "in": "description: |\n Il a dit \"bonjour\n monsieur\" et il est parti\n",
880
880
  "out": "description: |\n Il a dit « bonjour\n monsieur » et il est parti\n",
881
881
  "note": "modes.md §3.2 model C in a locale whose quotation output is guillemets plus U+00A0 rather than curly quotes — structurally different from the en-US case of the same shape."
882
+ },
883
+ {
884
+ "id": "fr-quotes-span-boundary-elision-declined",
885
+ "rule": "quotes",
886
+ "mode": "html",
887
+ "in": "Il dit 'l'<em>idée</em> est bonne.' Fin.",
888
+ "out": "Il dit « l’<em>idée</em> est bonne. » Fin.",
889
+ "note": "quotes.md §3.2's span-boundary elision veto (spec 1.4.0): the elided l' stands flush against the emphasis boundary, so modes.md §3.2's marker occupies the position the attaching word would hold and the medial-elision veto cannot fire. The cited quotes.elisionClitics.before entry l declines the mark, apostrophe (order 50) renders it U+2019 by its case 3 across the marker, and the author's own pair keeps the guillemets. Before spec 1.4.0 this produced « l » around the single letter and abandoned the real closing mark as U+0027 (canonical issue #53)."
890
+ },
891
+ {
892
+ "id": "fr-quotes-span-boundary-elision-multi-letter-fragment",
893
+ "rule": "quotes",
894
+ "mode": "html",
895
+ "in": "Il dit 'jusqu'<em>ici</em> tout va bien.' Fin.",
896
+ "out": "Il dit « jusqu’<em>ici</em> tout va bien. » Fin.",
897
+ "note": "Why quotes.elisionClitics carries jusqu and not only qu: the veto compares the MAXIMAL LETTER run, which is jusqu here, so an entry qu could not match this at all. lorsqu, puisqu and quoiqu are listed for the same reason."
898
+ },
899
+ {
900
+ "id": "fr-quotes-span-boundary-elision-caps-not-folded",
901
+ "rule": "quotes",
902
+ "mode": "html",
903
+ "in": "Il dit 'QU'<em>il</em> vienne.' Fin.",
904
+ "out": "Il dit « QU »<em>il</em> vienne.' Fin.",
905
+ "note": "Accepted limitation, pinned rather than left to be rediscovered: the veto folds only the run's FIRST code point, and only ASCII A-Z, so the all-caps QU' does not match the entry qu and the inversion survives. Same leniency, and same limit, as the listed elision veto's left/right words. quotes.md §7 item 10."
906
+ },
907
+ {
908
+ "id": "fr-quotes-span-boundary-quotation-not-declined",
909
+ "rule": "quotes",
910
+ "mode": "html",
911
+ "in": "Il dit <em>'oui'</em> ici.",
912
+ "out": "Il dit <em>« oui »</em> ici.",
913
+ "note": "The negative control the whole mechanism is shaped around. A quotation wholly inside an emphasis span has the marker flush against its opening mark too, exactly as an elision does — what separates them is that the LETTER run oui is not a listed fragment. This is why the marker itself must not be allowed to satisfy the medial-elision veto's ALNUM test, and why the run is compared whole rather than as a prefix."
914
+ },
915
+ {
916
+ "id": "fr-quotes-span-boundary-elision-markdown",
917
+ "rule": "quotes",
918
+ "mode": "markdown",
919
+ "dialect": "commonmark",
920
+ "in": "Il dit 'l'*idée* est bonne.' Fin.\n",
921
+ "out": "Il dit « l’*idée* est bonne. » Fin.\n",
922
+ "note": "The same shape in markdown emphasis rather than an HTML element: modes.md §3.2's marker model is mode-independent, so the two agree on identical characters split the same way (modes.md §7.4)."
923
+ },
924
+ {
925
+ "id": "fr-quotes-span-boundary-listed-fragment-quoted",
926
+ "rule": "quotes",
927
+ "mode": "html",
928
+ "in": "Il dit <em>'l'</em> ici.",
929
+ "out": "Il dit <em>’l’</em> ici.",
930
+ "note": "The accepted false positive in the locale with the widest exposure to it: fr lists thirteen fragments, eight of them a single letter, so a quotation whose whole content is one of them is read as an elision here more readily than anywhere else. Same class as en-us-html-span-boundary-listed-fragment-quoted and as the universal medial-n veto's accepted The letter 'n' is common. — pinned in both locales so the exposure sits in the suite rather than in someone's content. quotes.md §3.2, §6 row S7."
882
931
  }
883
932
  ]
884
933
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "it",
4
4
  "cases": [
5
5
  {
@@ -156,6 +156,22 @@
156
156
  "in": "Copyright (c) 2026, formato 40x60 cm",
157
157
  "out": "Copyright © 2026, formato 40×60 cm",
158
158
  "note": "symbols.md: «(c)» diventa U+00A9 e la «x» fra cifre U+00D7. L'U+00A0 davanti a «cm» è opera di nbsp N5, non di symbols: i due casi stanno insieme per mostrare che le regole si compongono."
159
+ },
160
+ {
161
+ "id": "it-quotes-span-boundary-elision-declined",
162
+ "rule": "quotes",
163
+ "mode": "html",
164
+ "in": "Lui dice 'l'<em>idea</em> è buona.' Fine.",
165
+ "out": "Lui dice «l’<em>idea</em> è buona.» Fine.",
166
+ "note": "quotes.md §3.2's span-boundary elision veto (spec 1.4.0), on the Crusca-cited entry l. Italian elision is not a closed set, so this field evidences membership per fragment rather than the completeness of the list — the same discipline quotes.md §3.2 states for each elisionIdioms triple."
167
+ },
168
+ {
169
+ "id": "it-quotes-span-boundary-elision-feminine-article",
170
+ "rule": "quotes",
171
+ "mode": "html",
172
+ "in": "Lui dice 'un'<em>idea</em> è buona.' Fine.",
173
+ "out": "Lui dice «un’<em>idea</em> è buona.» Fine.",
174
+ "note": "The entry un covers the feminine indefinite un' only; Crusca is categorical that the masculine un takes no apostrophe. The mechanism cannot tell the two apart, which is harmless here because it only ever declines a pairing."
159
175
  }
160
176
  ]
161
177
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "cases": [
4
4
  {
5
5
  "id": "exact-en-us",
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "nl",
4
4
  "cases": [
5
5
  {
@@ -116,6 +116,22 @@
116
116
  "in": "Copyright (c) 2026, formaat 40x60 cm",
117
117
  "out": "Copyright © 2026, formaat 40×60 cm",
118
118
  "note": "symbols.md: «(c)» wordt U+00A9 en de «x» tussen cijfers U+00D7. De U+00A0 vóór «cm» is het werk van nbsp N5."
119
+ },
120
+ {
121
+ "id": "nl-quotes-span-boundary-elision-declined",
122
+ "rule": "quotes",
123
+ "mode": "html",
124
+ "in": "Hij zegt 'ik kom <em>vroeg </em>'s avonds terug.' Klaar.",
125
+ "out": "Hij zegt “ik kom <em>vroeg </em>’s avonds terug.” Klaar.",
126
+ "note": "quotes.md §3.2's span-boundary elision veto (spec 1.4.0), read from quotes.elisionClitics.after rather than before: 's is a word-INITIAL omission (des avonds), so the letter run sits to the mark's right. Both lists are positional (quotes.md §2) — the fragment attaches forward to what follows, which is exactly why an attachment-based reading of the field would have missed it."
127
+ },
128
+ {
129
+ "id": "nl-quotes-span-boundary-elision-t",
130
+ "rule": "quotes",
131
+ "mode": "html",
132
+ "in": "Hij zegt 'ik kom <em>vroeg </em>'t is laat.' Klaar.",
133
+ "out": "Hij zegt “ik kom <em>vroeg </em>’t is laat.” Klaar.",
134
+ "note": "The same mechanism on the entry t ('t was), attested by the same Taalunie pair of pages."
119
135
  }
120
136
  ]
121
137
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "pl",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "pt-BR",
4
4
  "cases": [
5
5
  {
@@ -151,6 +151,14 @@
151
151
  "in": "pinga d'água",
152
152
  "out": "pinga d’água",
153
153
  "note": "U+0027 passa a U+2019. A regra não lê dados de locale; o caso certifica-a para esta locale."
154
+ },
155
+ {
156
+ "id": "pt-br-quotes-span-boundary-elision-declined",
157
+ "rule": "quotes",
158
+ "mode": "html",
159
+ "in": "Ele diz 'd'<em>Os Lusíadas</em> é bom.' Fim.",
160
+ "out": "Ele diz “d’<em>Os Lusíadas</em> é bom.” Fim.",
161
+ "note": "Same input and same Base XVIII citation as pt-PT, different primary pair — pt-BR opens with U+201C where pt-PT opens with a guillemet. Pinned in both files so a runtime cannot pass one and fail the other."
154
162
  }
155
163
  ]
156
164
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "pt-PT",
4
4
  "cases": [
5
5
  {
@@ -151,6 +151,14 @@
151
151
  "in": "pinga d'água",
152
152
  "out": "pinga d’água",
153
153
  "note": "U+0027 passa a U+2019. A regra não lê dados de locale; o caso certifica-a para esta locale."
154
+ },
155
+ {
156
+ "id": "pt-pt-quotes-span-boundary-elision-declined",
157
+ "rule": "quotes",
158
+ "mode": "html",
159
+ "in": "Ele diz 'd'<em>Os Lusíadas</em> é bom.' Fim.",
160
+ "out": "Ele diz «d’<em>Os Lusíadas</em> é bom.» Fim.",
161
+ "note": "quotes.md §3.2's span-boundary elision veto (spec 1.4.0), on Acordo Ortográfico Base XVIII 1.º a) — whose own examples are book titles, which is what makes this list load-bearing: a title set in emphasis rather than italics puts the span boundary immediately after the mark."
154
162
  }
155
163
  ]
156
164
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "ru",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "sv",
4
4
  "cases": [
5
5
  {
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "1.3.1",
2
+ "spec": "1.4.0",
3
3
  "locale": "uk",
4
4
  "cases": [
5
5
  {
@@ -12,7 +12,11 @@
12
12
  "close": "‘",
13
13
  "innerSpace": "none"
14
14
  },
15
- "elisionIdioms": []
15
+ "elisionIdioms": [],
16
+ "elisionClitics": {
17
+ "before": [],
18
+ "after": []
19
+ }
16
20
  },
17
21
  "dash": {
18
22
  "parenthetical": "en-spaced",
@@ -29,62 +33,43 @@
29
33
  "nbsp": {
30
34
  "beforePunctuation": [],
31
35
  "narrowBeforePunctuation": [],
32
- "afterShortWords": ["a", "i", "k", "o", "s", "u", "v", "z"],
33
- "abbreviations": ["a. s.", "s. r. o."],
34
- "beforeUnits": ["%", "°C", "ha", "kg", "km", "cm", "mm", "m²", "kWh", "km/h", "str.", "hod."],
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
+ ],
35
64
  "beforeNumber": [],
36
- "beforeWord": ["tj.", "tzv.", "tzn."],
37
- "afterSymbols": ["§"],
65
+ "beforeWord": [
66
+ "tj.",
67
+ "tzv.",
68
+ "tzn."
69
+ ],
70
+ "afterSymbols": [
71
+ "§"
72
+ ],
38
73
  "initialBinding": "single"
39
- },
40
- "sources": [
41
- {
42
- "rule": "quotes",
43
- "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“)»",
44
- "url": "https://prirucka.ujc.cas.cz/?id=162",
45
- "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."
46
- },
47
- {
48
- "rule": "dashes",
49
- "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á (—)»",
50
- "url": "https://prirucka.ujc.cas.cz/?id=165",
51
- "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."
52
- },
53
- {
54
- "rule": "ranges",
55
- "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ý»",
56
- "url": "https://prirucka.ujc.cas.cz/?id=165",
57
- "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."
58
- },
59
- {
60
- "rule": "ellipsis",
61
- "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)»",
62
- "url": "https://prirucka.ujc.cas.cz/?id=166",
63
- "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."
64
- },
65
- {
66
- "rule": "hyphen",
67
- "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í",
68
- "url": "https://prirucka.ujc.cas.cz/?id=164",
69
- "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."
70
- },
71
- {
72
- "rule": "nbsp",
73
- "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»",
74
- "url": "https://prirucka.ujc.cas.cz/?id=880",
75
- "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."
76
- },
77
- {
78
- "rule": "nbsp",
79
- "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»",
80
- "url": "https://prirucka.ujc.cas.cz/?id=785",
81
- "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."
82
- },
83
- {
84
- "rule": "nbsp",
85
- "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»",
86
- "url": "https://prirucka.ujc.cas.cz/?id=171",
87
- "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."
88
- }
89
- ]
74
+ }
90
75
  }