chaffjs 0.36.0 → 0.37.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 (257) hide show
  1. package/dist/detectors/acronym-expansion.d.ts.map +1 -1
  2. package/dist/detectors/acronym-expansion.js +10 -3
  3. package/dist/detectors/acronym-expansion.js.map +1 -1
  4. package/dist/detectors/acronym-link.d.ts +5 -0
  5. package/dist/detectors/acronym-link.d.ts.map +1 -1
  6. package/dist/detectors/acronym-link.js +33 -0
  7. package/dist/detectors/acronym-link.js.map +1 -1
  8. package/dist/detectors/acronym-mention.d.ts +5 -0
  9. package/dist/detectors/acronym-mention.d.ts.map +1 -0
  10. package/dist/detectors/acronym-mention.js +34 -0
  11. package/dist/detectors/acronym-mention.js.map +1 -0
  12. package/dist/detectors/acronym-name.js +2 -2
  13. package/dist/detectors/acronym-name.js.map +1 -1
  14. package/dist/detectors/addressee-title-edge.d.ts +21 -0
  15. package/dist/detectors/addressee-title-edge.d.ts.map +1 -0
  16. package/dist/detectors/addressee-title-edge.js +22 -0
  17. package/dist/detectors/addressee-title-edge.js.map +1 -0
  18. package/dist/detectors/capitalised-term.d.ts +4 -0
  19. package/dist/detectors/capitalised-term.d.ts.map +1 -1
  20. package/dist/detectors/capitalised-term.js +18 -1
  21. package/dist/detectors/capitalised-term.js.map +1 -1
  22. package/dist/detectors/caps-caption.d.ts +14 -4
  23. package/dist/detectors/caps-caption.d.ts.map +1 -1
  24. package/dist/detectors/caps-caption.js +36 -9
  25. package/dist/detectors/caps-caption.js.map +1 -1
  26. package/dist/detectors/char-shape.d.ts.map +1 -1
  27. package/dist/detectors/char-shape.js +37 -14
  28. package/dist/detectors/char-shape.js.map +1 -1
  29. package/dist/detectors/citation-sentence.d.ts +8 -0
  30. package/dist/detectors/citation-sentence.d.ts.map +1 -0
  31. package/dist/detectors/citation-sentence.js +40 -0
  32. package/dist/detectors/citation-sentence.js.map +1 -0
  33. package/dist/detectors/contraction-mention.d.ts +3 -0
  34. package/dist/detectors/contraction-mention.d.ts.map +1 -0
  35. package/dist/detectors/contraction-mention.js +22 -0
  36. package/dist/detectors/contraction-mention.js.map +1 -0
  37. package/dist/detectors/dash-break.d.ts +7 -0
  38. package/dist/detectors/dash-break.d.ts.map +1 -0
  39. package/dist/detectors/dash-break.js +30 -0
  40. package/dist/detectors/dash-break.js.map +1 -0
  41. package/dist/detectors/date-any-year.d.ts.map +1 -1
  42. package/dist/detectors/date-any-year.js +5 -3
  43. package/dist/detectors/date-any-year.js.map +1 -1
  44. package/dist/detectors/definition-use.d.ts.map +1 -1
  45. package/dist/detectors/definition-use.js +9 -4
  46. package/dist/detectors/definition-use.js.map +1 -1
  47. package/dist/detectors/document-shape.d.ts +5 -0
  48. package/dist/detectors/document-shape.d.ts.map +1 -1
  49. package/dist/detectors/document-shape.js +15 -1
  50. package/dist/detectors/document-shape.js.map +1 -1
  51. package/dist/detectors/duplicate-heading.d.ts +1 -0
  52. package/dist/detectors/duplicate-heading.d.ts.map +1 -1
  53. package/dist/detectors/duplicate-heading.js +25 -3
  54. package/dist/detectors/duplicate-heading.js.map +1 -1
  55. package/dist/detectors/email-letter.d.ts +9 -2
  56. package/dist/detectors/email-letter.d.ts.map +1 -1
  57. package/dist/detectors/email-letter.js +39 -8
  58. package/dist/detectors/email-letter.js.map +1 -1
  59. package/dist/detectors/fullwidth-alnum.d.ts.map +1 -1
  60. package/dist/detectors/fullwidth-alnum.js +7 -1
  61. package/dist/detectors/fullwidth-alnum.js.map +1 -1
  62. package/dist/detectors/gram-openers.d.ts +4 -3
  63. package/dist/detectors/gram-openers.d.ts.map +1 -1
  64. package/dist/detectors/gram-openers.js +26 -2
  65. package/dist/detectors/gram-openers.js.map +1 -1
  66. package/dist/detectors/heading-record-rhythm.d.ts +9 -0
  67. package/dist/detectors/heading-record-rhythm.d.ts.map +1 -0
  68. package/dist/detectors/heading-record-rhythm.js +57 -0
  69. package/dist/detectors/heading-record-rhythm.js.map +1 -0
  70. package/dist/detectors/kutoten-consistency.d.ts.map +1 -1
  71. package/dist/detectors/kutoten-consistency.js +7 -0
  72. package/dist/detectors/kutoten-consistency.js.map +1 -1
  73. package/dist/detectors/lexicon.d.ts.map +1 -1
  74. package/dist/detectors/lexicon.js +5 -2
  75. package/dist/detectors/lexicon.js.map +1 -1
  76. package/dist/detectors/mark-shape.d.ts +3 -1
  77. package/dist/detectors/mark-shape.d.ts.map +1 -1
  78. package/dist/detectors/mark-shape.js +20 -5
  79. package/dist/detectors/mark-shape.js.map +1 -1
  80. package/dist/detectors/name-variant.d.ts.map +1 -1
  81. package/dist/detectors/name-variant.js +6 -0
  82. package/dist/detectors/name-variant.js.map +1 -1
  83. package/dist/detectors/organisation-run.d.ts +0 -6
  84. package/dist/detectors/organisation-run.d.ts.map +1 -1
  85. package/dist/detectors/organisation-run.js +4 -1
  86. package/dist/detectors/organisation-run.js.map +1 -1
  87. package/dist/detectors/orthographic-variant.d.ts.map +1 -1
  88. package/dist/detectors/orthographic-variant.js +33 -4
  89. package/dist/detectors/orthographic-variant.js.map +1 -1
  90. package/dist/detectors/orthography.d.ts.map +1 -1
  91. package/dist/detectors/orthography.js +4 -2
  92. package/dist/detectors/orthography.js.map +1 -1
  93. package/dist/detectors/oxford-comma.d.ts.map +1 -1
  94. package/dist/detectors/oxford-comma.js +33 -3
  95. package/dist/detectors/oxford-comma.js.map +1 -1
  96. package/dist/detectors/range-notation.d.ts +2 -2
  97. package/dist/detectors/range-notation.d.ts.map +1 -1
  98. package/dist/detectors/range-notation.js +18 -7
  99. package/dist/detectors/range-notation.js.map +1 -1
  100. package/dist/detectors/requirement-smell.d.ts.map +1 -1
  101. package/dist/detectors/requirement-smell.js +10 -6
  102. package/dist/detectors/requirement-smell.js.map +1 -1
  103. package/dist/detectors/sentence-counts.d.ts +10 -2
  104. package/dist/detectors/sentence-counts.d.ts.map +1 -1
  105. package/dist/detectors/sentence-counts.js +34 -4
  106. package/dist/detectors/sentence-counts.js.map +1 -1
  107. package/dist/detectors/sentence-length.d.ts.map +1 -1
  108. package/dist/detectors/sentence-length.js +13 -4
  109. package/dist/detectors/sentence-length.js.map +1 -1
  110. package/dist/detectors/signals.d.ts.map +1 -1
  111. package/dist/detectors/signals.js +15 -3
  112. package/dist/detectors/signals.js.map +1 -1
  113. package/dist/detectors/superlative-name.d.ts +5 -0
  114. package/dist/detectors/superlative-name.d.ts.map +1 -1
  115. package/dist/detectors/superlative-name.js +9 -0
  116. package/dist/detectors/superlative-name.js.map +1 -1
  117. package/dist/detectors/token-shape.d.ts.map +1 -1
  118. package/dist/detectors/token-shape.js +3 -1
  119. package/dist/detectors/token-shape.js.map +1 -1
  120. package/dist/detectors/undefined-term.d.ts.map +1 -1
  121. package/dist/detectors/undefined-term.js +5 -0
  122. package/dist/detectors/undefined-term.js.map +1 -1
  123. package/dist/detectors/unknown-word.d.ts +4 -1
  124. package/dist/detectors/unknown-word.d.ts.map +1 -1
  125. package/dist/detectors/unknown-word.js +1 -1
  126. package/dist/detectors/unknown-word.js.map +1 -1
  127. package/dist/html/html-site-chrome.d.ts.map +1 -1
  128. package/dist/html/html-site-chrome.js +35 -12
  129. package/dist/html/html-site-chrome.js.map +1 -1
  130. package/dist/latin-word-boundary.d.ts +5 -0
  131. package/dist/latin-word-boundary.d.ts.map +1 -1
  132. package/dist/latin-word-boundary.js +17 -0
  133. package/dist/latin-word-boundary.js.map +1 -1
  134. package/dist/leading-label.d.ts +4 -0
  135. package/dist/leading-label.d.ts.map +1 -0
  136. package/dist/leading-label.js +14 -0
  137. package/dist/leading-label.js.map +1 -0
  138. package/dist/link-shape.d.ts +3 -2
  139. package/dist/link-shape.d.ts.map +1 -1
  140. package/dist/link-shape.js +12 -3
  141. package/dist/link-shape.js.map +1 -1
  142. package/dist/name-misspelt-usual.d.ts +5 -0
  143. package/dist/name-misspelt-usual.d.ts.map +1 -0
  144. package/dist/name-misspelt-usual.js +19 -0
  145. package/dist/name-misspelt-usual.js.map +1 -0
  146. package/dist/notice-header.d.ts +19 -0
  147. package/dist/notice-header.d.ts.map +1 -0
  148. package/dist/notice-header.js +31 -0
  149. package/dist/notice-header.js.map +1 -0
  150. package/dist/sentence-load.d.ts +2 -1
  151. package/dist/sentence-load.d.ts.map +1 -1
  152. package/dist/sentence-load.js +11 -3
  153. package/dist/sentence-load.js.map +1 -1
  154. package/dist/structure/attached-parts.d.ts +2 -1
  155. package/dist/structure/attached-parts.d.ts.map +1 -1
  156. package/dist/structure/attached-parts.js +22 -9
  157. package/dist/structure/attached-parts.js.map +1 -1
  158. package/dist/structure/definition-use.d.ts +5 -2
  159. package/dist/structure/definition-use.d.ts.map +1 -1
  160. package/dist/structure/definition-use.js +24 -7
  161. package/dist/structure/definition-use.js.map +1 -1
  162. package/dist/structure/gerund-phrase.d.ts +8 -0
  163. package/dist/structure/gerund-phrase.d.ts.map +1 -0
  164. package/dist/structure/gerund-phrase.js +30 -0
  165. package/dist/structure/gerund-phrase.js.map +1 -0
  166. package/dist/structure/universal.d.ts +7 -3
  167. package/dist/structure/universal.d.ts.map +1 -1
  168. package/dist/structure/universal.js +17 -5
  169. package/dist/structure/universal.js.map +1 -1
  170. package/dist/title-line.d.ts +2 -0
  171. package/dist/title-line.d.ts.map +1 -1
  172. package/dist/title-line.js +2 -1
  173. package/dist/title-line.js.map +1 -1
  174. package/dist/topic-clause.d.ts +7 -0
  175. package/dist/topic-clause.d.ts.map +1 -0
  176. package/dist/topic-clause.js +87 -0
  177. package/dist/topic-clause.js.map +1 -0
  178. package/dist/uncounted-particles.d.ts +18 -0
  179. package/dist/uncounted-particles.d.ts.map +1 -0
  180. package/dist/uncounted-particles.js +47 -0
  181. package/dist/uncounted-particles.js.map +1 -0
  182. package/package.json +3 -3
  183. package/rules/adversative-ga-repeat.yaml +1 -0
  184. package/rules/all-caps-shouting.yaml +2 -2
  185. package/rules/bare-url-mix.yaml +2 -2
  186. package/rules/concrete-evidence-density.yaml +1 -0
  187. package/rules/contraction-consistency.yaml +2 -2
  188. package/rules/duplicate-definition.yaml +2 -2
  189. package/rules/duplicate-heading.yaml +2 -2
  190. package/rules/fullwidth-alnum-consistency.yaml +2 -2
  191. package/rules/kutoten-consistency.yaml +2 -2
  192. package/rules/latin-spacing.yaml +2 -2
  193. package/rules/max-kanji-continuous.yaml +1 -1
  194. package/rules/max-sentence-length.yaml +2 -2
  195. package/rules/ngram-repetition.yaml +2 -2
  196. package/rules/orthographic-variant.yaml +2 -2
  197. package/rules/oxford-comma-consistency.yaml +2 -2
  198. package/rules/preamble-length.yaml +3 -2
  199. package/rules/range-notation-consistency.yaml +2 -2
  200. package/rules/requirement-smell.yaml +2 -2
  201. package/rules/title-length.yaml +2 -2
  202. package/rules/topic-predicate-distance.yaml +2 -2
  203. package/rules/undefined-acronym.yaml +3 -2
  204. package/rules/undefined-term.yaml +2 -2
  205. package/rules/unqualified-superlative.yaml +1 -1
  206. package/rules/unused-definition.yaml +2 -2
  207. package/rules/use-before-definition.yaml +2 -2
  208. package/src/detectors/acronym-expansion.ts +10 -3
  209. package/src/detectors/acronym-link.ts +38 -0
  210. package/src/detectors/acronym-mention.ts +44 -0
  211. package/src/detectors/acronym-name.ts +2 -2
  212. package/src/detectors/addressee-title-edge.ts +39 -0
  213. package/src/detectors/capitalised-term.ts +26 -2
  214. package/src/detectors/caps-caption.ts +43 -9
  215. package/src/detectors/char-shape.ts +55 -17
  216. package/src/detectors/citation-sentence.ts +44 -0
  217. package/src/detectors/contraction-mention.ts +24 -0
  218. package/src/detectors/dash-break.ts +45 -0
  219. package/src/detectors/date-any-year.ts +7 -3
  220. package/src/detectors/definition-use.ts +12 -8
  221. package/src/detectors/document-shape.ts +20 -1
  222. package/src/detectors/duplicate-heading.ts +26 -3
  223. package/src/detectors/email-letter.ts +50 -7
  224. package/src/detectors/fullwidth-alnum.ts +9 -1
  225. package/src/detectors/gram-openers.ts +36 -7
  226. package/src/detectors/heading-record-rhythm.ts +57 -0
  227. package/src/detectors/kutoten-consistency.ts +9 -0
  228. package/src/detectors/lexicon.ts +6 -2
  229. package/src/detectors/mark-shape.ts +30 -5
  230. package/src/detectors/name-variant.ts +6 -0
  231. package/src/detectors/organisation-run.ts +6 -1
  232. package/src/detectors/orthographic-variant.ts +45 -6
  233. package/src/detectors/orthography.ts +6 -2
  234. package/src/detectors/oxford-comma.ts +35 -3
  235. package/src/detectors/range-notation.ts +26 -6
  236. package/src/detectors/requirement-smell.ts +12 -8
  237. package/src/detectors/sentence-counts.ts +40 -4
  238. package/src/detectors/sentence-length.ts +17 -4
  239. package/src/detectors/signals.ts +18 -3
  240. package/src/detectors/superlative-name.ts +10 -0
  241. package/src/detectors/token-shape.ts +3 -1
  242. package/src/detectors/undefined-term.ts +8 -0
  243. package/src/detectors/unknown-word.ts +1 -1
  244. package/src/html/html-site-chrome.ts +70 -13
  245. package/src/latin-word-boundary.ts +18 -0
  246. package/src/leading-label.ts +15 -0
  247. package/src/link-shape.ts +15 -3
  248. package/src/name-misspelt-usual.ts +25 -0
  249. package/src/notice-header.ts +57 -0
  250. package/src/sentence-load.ts +14 -3
  251. package/src/structure/attached-parts.ts +24 -9
  252. package/src/structure/definition-use.ts +33 -7
  253. package/src/structure/gerund-phrase.ts +31 -0
  254. package/src/structure/universal.ts +22 -7
  255. package/src/title-line.ts +2 -1
  256. package/src/topic-clause.ts +109 -0
  257. package/src/uncounted-particles.ts +72 -0
@@ -30,6 +30,7 @@ off_for:
30
30
  academic: "Science writes in the passive, hedges its claims on purpose (may, suggest) and cites numbers as its evidence."
31
31
  literature: "Length, rhythm, repetition and register are the author's choices in literature, and dialogue mixes styles. Only what one work can hold itself to runs: consistent spacing and commas, and numbering without gaps."
32
32
  speech: "A speech opens with greetings and closes with thanks: that is its form, not padding."
33
+ business/email: "A letter or an email is its body before any heading; a heading in one marks a reply or acceptance block at the end, not the start of the body."
33
34
 
34
35
  group: structure
35
36
  summary:
@@ -80,8 +81,8 @@ example:
80
81
  The number of stores has doubled in ten years, but suburban stores have struggled to grow.
81
82
 
82
83
  not_flagged:
83
- ja: 表題より下の見出しが一つもない文書は測りません。法務やブログのジャンルでは見ません。本文の語に付けた注(※1 …)の段落は、前置きではなく注なので数えません。
84
- en: A document with no heading below its title is not measured. Legal and blog genres do not run this rule. A note attached to a word of the text (a paragraph opened by a note mark of the note-mark lexicon, as in Japanese ※1) is a note, not preamble, and is not counted.
84
+ ja: 表題より下の見出しが一つもない文書は測りません。法務やブログのジャンルと、手紙・メールでは見ません。本文の語に付けた注(※1 …)の段落は、前置きではなく注なので数えません。
85
+ en: A document with no heading below its title is not measured. Legal and blog genres, and letters and emails, do not run this rule. A note attached to a word of the text (a paragraph opened by a note mark of the note-mark lexicon, as in Japanese ※1) is a note, not preamble, and is not counted.
85
86
  level_meaning:
86
87
  ja: 最初の見出しまで {limit} 段落まで
87
88
  en: up to {limit} paragraphs before the first heading
@@ -42,8 +42,8 @@ example:
42
42
  Groups of 10–20 meet for 30–40 minutes, at 5–8 dollars each. Children aged 6–12 are welcome.
43
43
 
44
44
  not_flagged:
45
- ja: 前後にさらに数の続く並び(日付の 2026-10-02、電話番号、時刻の 10:00〜12:00、版)と、終わりが始まりより小さいもの(郵便番号の 100-0001、試合の 3-2)は範囲と読みません。「10から20」のような言葉の範囲は比べません。少ないほうが範囲の三分の一を超える文書と、二つの記号が同じ数の文書は、使い分けと読んで言いません。コードの中は読みません。
46
- en: Runs with more numbers around them (the date 2026-10-02, phone numbers, times such as 10:00–12:00, versions) and pairs whose end is smaller than the start (a score of 3-2, a postcode such as 100-0001) are not read as ranges. Words ("10 to 20") are not compared. When the less common mark is over a third of the ranges, or the two are equally common, the document is read as using both on purpose. Code is not read.
45
+ ja: 前後にさらに数の続く並び(日付の 2026-10-02、電話番号、時刻の 10:00〜12:00、版)と、終わりが始まりより小さいもの(郵便番号の 100-0001、試合の 3-2)、漢字の語に詰めて書いた文書の番号(直資4-5ほか)は範囲と読みません。「10から20」のような言葉の範囲は比べません。少ないほうが範囲の三分の一を超える文書と、二つの記号が同じ数の文書は、使い分けと読んで言いません。コードの中は読みません。
46
+ en: Runs with more numbers around them (the date 2026-10-02, phone numbers, times such as 10:00–12:00, versions) and pairs whose end is smaller than the start (a score of 3-2, a postcode such as 100-0001), and Japanese document numbers written against a kanji word (直資4-5ほか) are not read as ranges. Words ("10 to 20") are not compared. When the less common mark is over a third of the ranges, or the two are equally common, the document is read as using both on purpose. Code is not read.
47
47
  level_meaning:
48
48
  ja: 少ないほうの記号が、範囲の {limit}% 以下のとき指摘
49
49
  en: reported when the less common mark is {limit}% of the ranges or less
@@ -74,5 +74,5 @@ example:
74
74
  config: { genre: technical/spec }
75
75
 
76
76
  not_flagged:
77
- ja: 要求の文でない文(説明や背景)、法令、ブログやビジネス文書は見ません。語彙表にない言い方は指しません。文書が定めた名前の「等」(以下「委託先等」という、銀行等(…をいう。)、定義の号の頭の語)と、監査等委員会のように後ろへ語が続く「等」は、列挙を閉じているので指しません。すぐ後ろで種類を名指す「等」(印刷等、二次的に利用される場合/印刷等の二次的利用)も、一覧がその種類の例なので指しません。条件(訂正等を行った際に)、与える権利(消去等できるものとします)、禁止や免責(転載等をしてはならない、責任を負わないものとします)の中の「等」も、わざと広く取った一覧なので指しません。条件の語のすぐ後ろの括弧の中の「等」(不要な場合(一つの表だけを読むとき等)も付けること)は、いつ当てはまるかの例なので指しません。英語の as soon as possible は、同じ文に日数の期限(no later than two days after)があれば、期限が決まっているので指しません。
78
- en: Sentences that state no requirement (background, explanation), statutes, and blogs and business documents are not checked. Phrases not on chaff's lists are not reported. An open-ended word inside a name the document defines (a quoted term, or one defined in the brackets after it) is not reported. "As soon as possible" followed in the same sentence by a limit in numbers ("but in any event no later than two days after") is bounded, and not reported. In Japanese, 等 right before the words that name the kind it gives examples of (印刷等、二次的に利用, "secondary use, such as printing") is not reported either. Nor is a Japanese 等 in a condition (訂正等を行った際に), a right the writer grants (できるものとする) or a prohibition or disclaimer (てはならない, ないものとする), because such a list is kept wide on purpose, or an open list in brackets right after a condition word (場合(…等), "in cases (…, etc.)"), which gives examples of when the requirement applies.
77
+ ja: 要求の文でない文(説明や背景)、法令、ブログやビジネス文書は見ません。語彙表にない言い方は指しません。文書が定めた名前の「等」(以下「委託先等」という、銀行等(…をいう。)、定義の号の頭の語)と、監査等委員会のように後ろへ語が続く「等」は、列挙を閉じているので指しません。すぐ後ろで種類を名指す「等」(印刷等、二次的に利用される場合/印刷等の二次的利用)も、一覧がその種類の例なので指しません。条件(訂正等を行った際に)、与える権利(消去等できるものとします)、禁止や免責(転載等をしてはならない、責任を負わないものとします)の中の「等」も、わざと広く取った一覧なので指しません。条件の語のすぐ後ろの括弧の中の「等」(不要な場合(一つの表だけを読むとき等)も付けること)は、いつ当てはまるかの例なので指しません。英語の as soon as possible は、同じ文に日数の期限(no later than two days after)があれば、期限が決まっているので指しません。英語の but not limited to(including, but not limited to)は、契約書では例の一覧で範囲を狭めないための決まった言い方なので、契約書では指しません。
78
+ en: Sentences that state no requirement (background, explanation), statutes, and blogs and business documents are not checked. Phrases not on chaff's lists are not reported. An open-ended word inside a name the document defines (a quoted term, or one defined in the brackets after it) is not reported. "As soon as possible" followed in the same sentence by a limit in numbers ("but in any event no later than two days after") is bounded, and not reported. In a contract, "including, but not limited to" is a term of art that keeps the listed examples from narrowing the class, and is not reported. In Japanese, 等 right before the words that name the kind it gives examples of (印刷等、二次的に利用, "secondary use, such as printing") is not reported either. Nor is a Japanese 等 in a condition (訂正等を行った際に), a right the writer grants (できるものとする) or a prohibition or disclaimer (てはならない, ないものとする), because such a list is kept wide on purpose, or an open list in brackets right after a condition word (場合(…等), "in cases (…, etc.)"), which gives examples of when the requirement applies.
@@ -79,8 +79,8 @@ example:
79
79
  What every store does during the move in the 2025 financial year.
80
80
 
81
81
  not_flagged:
82
- ja: 見出しの頭の番号(「第2章」「1.」)は数えません。英字の語は一語を二文字と数えます。報告書・プレスリリース・議事録・仕様書・論文は上限が高く、法務・文学・話し言葉のジャンルと、見出しが質問の文になる FAQ では見ません。ほかのジャンルでも、?で終わる質問の見出しは、長さが読み手の問いそのものなので見ません。
83
- en: A number label at the head of a heading (Chapter 2, 1.) is not counted. Reports, press releases, minutes, specifications and papers have a higher limit; legal, literary and speech genres, and FAQs (whose headings are questions), do not run this rule. In any genre, a heading that ends with a question mark is not counted, since its length is the reader's question.
82
+ ja: "見出しの頭の番号(「第2章」「1.」)は数えません。末尾の括弧の中の番号付きの通し名(「(490: Employee travel)」)も数えず、ほかの文書の題をかぎ括弧で引いた所(「「…の取扱いについて」等の一部改正」)は一つの名前と数えます。英字の語は一語を二文字と数えます。報告書・プレスリリース・議事録・仕様書・論文は上限が高く、法務・文学・話し言葉のジャンルと、見出しが質問の文になる FAQ では見ません。ほかのジャンルでも、?で終わる質問の見出しは、長さが読み手の問いそのものなので見ません。"
83
+ en: "A number label at the head of a heading (Chapter 2, 1.) is not counted. Nor is a numbered series label in brackets at its end (“(490: Employee travel)”), and another document's title quoted in 「」 counts as one name. Reports, press releases, minutes, specifications and papers have a higher limit; legal, literary and speech genres, and FAQs (whose headings are questions), do not run this rule. In any genre, a heading that ends with a question mark is not counted, since its length is the reader's question."
84
84
  level_meaning:
85
85
  ja: 題や見出しが {limit} 文字を超えたら指摘
86
86
  en: reported when a title or heading runs past {limit} words
@@ -42,8 +42,8 @@ example:
42
42
  来週は、新しい担当者の紹介、先月から続く予算の見直し、春の本社移転の準備、秋の採用計画の確認、年末の懇親会の段取りを話し合います。そのため定例会議は二時間に延長します。
43
43
 
44
44
  not_flagged:
45
- ja: 主題(名詞+は)の無い文、鉤括弧で引いたものの中、上限までの距離は見ません。英語の文書には使いません。
46
- en: Sentences with no topic (noun + は), quotations in 「」, and distances up to the limit are not counted. Not used on English documents.
45
+ ja: 主題(名詞+は)の無い文、鉤括弧で引いたものの中、上限までの距離は見ません。主題の述語が先の節で閉じる文(「この計画は簡単ではありませんが、…」「〜であって、…」、後ろの節に別の主語がある「〜ではなく、」「〜であるため、」「〜であるならば、」「〜にあるところ、」「〜ており、」、受け身で伝える引用「〜だと言われているが」)は、その節の終わりまでを数えます。英語の文書には使いません。
46
+ en: Sentences with no topic (noun + は), quotations in 「」, and distances up to the limit are not counted. When the topic gets its own predicate in an earlier clause (〜ではありませんが, 〜であって; 〜ではなく, 〜であるため, 〜であるならば, 〜にあるところ or 〜ており followed by a clause with its own subject; a passive report 〜だと言われているが), the count stops at that clause's end. Not used on English documents.
47
47
  level_meaning:
48
48
  ja: 主題から述語まで {limit} 字まで
49
49
  en: up to {limit} characters between the topic and the predicate
@@ -30,6 +30,7 @@ extra_word_lists:
30
30
  tax-code,
31
31
  capital-phrase,
32
32
  name-determiner,
33
+ abbreviation-mention,
33
34
  ]
34
35
  uses: [pos]
35
36
  severity: info
@@ -78,8 +79,8 @@ example:
78
79
  after: Our key goal indicator (KGI) this term is to reach product-market fit (PMF).
79
80
 
80
81
  not_flagged:
81
- ja: 誰でも知っている略語(URL、PDF など)と、前後の括弧で説明した略語、会社の名前の中の英字(㈱ADEKA、CO.,LTD.)は指しませんひな形の名前の置き場所(OOOO、XX 月)と、外のページへのリンクを並べた一覧の題名の中の略語も指しません(題名の略語は数には入れます)。
82
- en: Common acronyms (URL, PDF), acronyms spelled out in brackets next to them, and the letters of a company name (㈱ADEKA, CO.,LTD.) are not reported. Neither are a template's placeholders (OOOO, XX 月) or an acronym in the title of a page in a list of links (it still counts towards the limit).
82
+ ja: 誰でも知っている略語(URL、PDF など)と、前後の括弧で説明した略語、会社の名前の中の英字(㈱ADEKA、CO.,LTD.)は指しません。ひな形の名前の置き場所(OOOO、XX 月)と、外のページへのリンクを並べた一覧の題名の中の略語も指しません(題名の略語は数には入れます)。大文字の違うだけの書き方で説明した略語(TCoP と TCOP)、略語を話題にする文で語に書き出した略語(略語の DOB … date of birth)、一覧で語のすぐ下にその語を名指すリンクを入れ子にした語(- GPL の下の GNU General Public License)も指しません。
83
+ en: Common acronyms (URL, PDF), acronyms spelled out in brackets next to them, and the letters of a company name (㈱ADEKA, CO.,LTD.) are not reported. Neither are a template's placeholders (OOOO, XX 月) or an acronym in the title of a page in a list of links (it still counts towards the limit). Nor are an acronym explained under another capitalisation (TCoP, then TCOP), one a sentence about the abbreviation writes out (the abbreviation DOB … date of birth), or a term in a list whose nested link names it (- GPL above GNU General Public License).
83
84
  level_meaning:
84
85
  ja: 説明のない略語が {limit} 種類あれば指摘。文書が自分で略語を {limit} 個展開していれば、1 つの抜けから指摘
85
86
  en: reported once {limit} different acronyms go unexplained, or from the first one when the document itself spells out {limit} others
@@ -69,8 +69,8 @@ example:
69
69
  1. The Supplier shall provide the hosting services (the "Services") to the Customer.
70
70
 
71
71
  not_flagged:
72
- ja: 文書が「本」を付けた語を一つも定義していなければ見ません。定義した語とその言い換え(本件業務、defined-term-form が見ます)、定義した語で始まる語(本サービス上)、この文書やその一部を指す語(本条、本項、本書、本契約、本利用規約、本サイト)、どこかでかぎ括弧に入れた語(定義の書き方を chaff が読めなかったもの)、一語の言葉(本人、本日、本社)は見ません。同じ語は最初の一か所だけ指します。英語は、大文字は法令・組織・方針の名前(the Federal Trade Commission Act、Acceptable Use Policies)にも使うので、狭く読みます。"X" means や (the "X") の形の定義が二つ未満の文書は見ません。文の頭や記号の後ろの語、小文字の名詞の前に付けた語(the Premium plan)、名前の一覧(proper-name-word、地名、曜日と月、会社の形、当事者の役割)の語を含むもの、条の参照(Section 4、Schedule A)、前後の語から名前と分かるもの(後ろに番号が付く Proposition 65、長い名前の頭 Post Holdings, Inc. や Canton, Ohio や the Federal Food, Drug, and Cosmetic Act、直後の括弧で定義するもの Certificate of Analysis (“COA”) や the Closing (as defined in the Merger Agreement)、文書が定義した略語の頭文字になるもの “IRS” means the Internal Revenue Service、当事者の立場 Citibank, N.A., as Administrative Agent)、見出し・リンク・太字・HTML の要素・表・箇条書きの見出し・条の頭の見出し(5.8 No Representation or Warranty.)の中、どこかで引用符か太字に入れた語、定義した語を含むもの(大文字小文字を問いません)、文書のどこかで小文字でも書いた語(your Account と your account。普通の語か画面の名前)は見ません。法令・判決・特許は見ません。契約書・規約のジャンルで見ます。
73
- en: "A document that defines no term with the prefix is not read. Defined terms and their other-prefix forms (本件業務, which defined-term-form reads), terms that start with a defined term (本サービス上), the document's names for itself and its parts (本条, 本項, 本書, 本契約, 本利用規約, 本サイト), terms set in quotes anywhere (a definition chaff could not read), and single words (本人, 本日, 本社) are not reported. Each term is reported once, at its first use. English is read narrowly, since capitals also name laws, organisations and policies (the Federal Trade Commission Act, Acceptable Use Policies): a document with fewer than two definitions written with a defining word (“X” means, (the “X”)) is not read; and a word at the start of a sentence or after a mark, a modifier of a lower-case noun (the Premium plan), a run holding a word of the name lists (proper-name-word, places, weekdays and months, company forms, party roles), a reference to a part (Section 4, Schedule A), a name shown by the words around it (a number after it: Proposition 65; a longer name it starts: Post Holdings, Inc., Canton, Ohio, the Federal Food, Drug, and Cosmetic Act; brackets right after it that define it: Certificate of Analysis (“COA”), the Closing (as defined in the Merger Agreement); initials that spell a term the document defines: “IRS” means the Internal Revenue Service; a party's capacity: Citibank, N.A., as Administrative Agent), anything inside a heading, a link, bold, an HTML element, a table, a list item's label or a clause's run-in caption (5.8 No Representation or Warranty.), a word set in quotes or bold anywhere, a run holding a defined term in any case, and a word the document also writes in lower case (your Account and your account: an ordinary word or a screen's name) are not reported. Statutes, court decisions and patents are not read; the contract genre runs this rule."
72
+ ja: 文書が「本」を付けた語を一つも定義していなければ見ません。定義した語とその言い換え(本件業務、defined-term-form が見ます)、定義した語で始まる語(本サービス上)、この文書やその一部を指す語(本条、本項、本書、本契約、本利用規約、本サイト)、どこかでかぎ括弧に入れた語(定義の書き方を chaff が読めなかったもの)、一語の言葉(本人、本日、本社)は見ません。同じ語は最初の一か所だけ指します。英語は、大文字は法令・組織・方針の名前(the Federal Trade Commission Act、Acceptable Use Policies)にも使うので、狭く読みます。"X" means や (the "X") の形の定義が二つ未満の文書は見ません。文の頭や記号の後ろの語、小文字の名詞の前に付けた語(the Premium plan)、名前の一覧(proper-name-word、地名、曜日と月、会社の形、当事者の役割)の語を含むもの、条の参照(Section 4、Schedule A)、前後の語から名前と分かるもの(後ろに番号が付く Proposition 65、長い名前の頭 Post Holdings, Inc. や Canton, Ohio や the Federal Food, Drug, and Cosmetic Act、直後の括弧で定義するもの Certificate of Analysis (“COA”) や the Closing (as defined in the Merger Agreement)、文書が定義した略語の頭文字になるもの “IRS” means the Internal Revenue Service、当事者の立場 Citibank, N.A., as Administrative Agent、すぐ後ろの attached hereto などで添付した書式の題と分かるもの the Termination Certificate attached hereto)、見出し・リンク・太字・HTML の要素・表・箇条書きの見出し・条の頭の見出し(5.8 No Representation or Warranty.)の中、どこかで引用符か太字に入れた語、定義した語を含むもの(大文字小文字を問いません)、文書のどこかで小文字でも書いた語(your Account と your account。普通の語か画面の名前)は見ません。法令・判決・特許は見ません。契約書・規約のジャンルで見ます。
73
+ en: "A document that defines no term with the prefix is not read. Defined terms and their other-prefix forms (本件業務, which defined-term-form reads), terms that start with a defined term (本サービス上), the document's names for itself and its parts (本条, 本項, 本書, 本契約, 本利用規約, 本サイト), terms set in quotes anywhere (a definition chaff could not read), and single words (本人, 本日, 本社) are not reported. Each term is reported once, at its first use. English is read narrowly, since capitals also name laws, organisations and policies (the Federal Trade Commission Act, Acceptable Use Policies): a document with fewer than two definitions written with a defining word (“X” means, (the “X”)) is not read; and a word at the start of a sentence or after a mark, a modifier of a lower-case noun (the Premium plan), a run holding a word of the name lists (proper-name-word, places, weekdays and months, company forms, party roles), a reference to a part (Section 4, Schedule A), a name shown by the words around it (a number after it: Proposition 65; a longer name it starts: Post Holdings, Inc., Canton, Ohio, the Federal Food, Drug, and Cosmetic Act; brackets right after it that define it: Certificate of Analysis (“COA”), the Closing (as defined in the Merger Agreement); initials that spell a term the document defines: “IRS” means the Internal Revenue Service; a party's capacity: Citibank, N.A., as Administrative Agent; words right after it that make it the title of an attached form: the Termination Certificate attached hereto), anything inside a heading, a link, bold, an HTML element, a table, a list item's label or a clause's run-in caption (5.8 No Representation or Warranty.), a word set in quotes or bold anywhere, a run holding a defined term in any case, and a word the document also writes in lower case (your Account and your account: an ordinary word or a screen's name) are not reported. Statutes, court decisions and patents are not read; the contract genre runs this rule."
74
74
  rewrite:
75
75
  depth: light
76
76
  ja:
@@ -46,7 +46,7 @@ example:
46
46
  after: Our new product processes orders a fifth faster than the previous model.
47
47
 
48
48
  not_flagged:
49
- ja: 「当社比」「〜の中で」「〜のほうが」のように、比べる相手や範囲が同じ文にあれば指しません。「SES との最大の違い」のように、最上級のすぐ前に相手があるときも同じです。「国内最大」「業界最速」のように名詞がじかに付いた最上級と、「日本で最も」のように名前と「で」が前にある最上級は、範囲を言っているので指しません。「バグを検出できる唯一のルール」のように、動詞で終わる節が最上級のすぐ前にあって範囲を決めているときも同じです。「」や引用符で引いた言葉の中の最上級は、書き手の主張ではないので指しません。
49
+ ja: 「当社比」「〜の中で」「〜のほうが」のように、比べる相手や範囲が同じ文にあれば指しません。「SES との最大の違い」のように、最上級のすぐ前に相手があるときも同じです。「国内最大」「業界最速」のように名詞がじかに付いた最上級と、「日本で最も」のように名前と「で」が前にある最上級は、範囲を言っているので指しません。「バグを検出できる唯一のルール」のように、動詞で終わる節が最上級のすぐ前にあって範囲を決めているときも同じです。「」や引用符で引いた言葉の中の最上級は、書き手の主張ではないので指しません。「最大化する」のように接尾辞が付いて別の語になったものも指しません。
50
50
  en: A superlative with its comparison or scope in the same sentence ("in our range", "than last year", "the fastest in the industry") is not reported, nor is one limited by a clause ("the best we have measured"), nor one inside quotation marks, which are someone else's words.
51
51
  level_meaning:
52
52
  ja: 比べる相手のない最上級が {limit} 箇所あれば指摘
@@ -48,8 +48,8 @@ example:
48
48
  "Service" means the booking website we run. Users may use the Service free of charge.
49
49
 
50
50
  not_flagged:
51
- ja: 定義の書き方(「X」とは、以下「X」という、規約やポリシーの以下「X」といいます)で書いた語だけを見ます。複数形と所有の形(Services、Seller's)と、見出しの中の語も使用に数えます。同じ語の二つ目の定義は duplicate-definition が言います。
52
- en: Only terms defined in a recognised form ("X" means, (the "X")) are checked. Plurals and possessives (Services, Seller's) count as uses, and so does the term in a heading. A second definition of the same term is duplicate-definition's to report.
51
+ ja: 定義の書き方(「X」とは、以下「X」という、規約やポリシーの以下「X」といいます)で書いた語だけを見ます。複数形と所有の形(Services、Seller's)と、見出しの中の語も使用に数えます。英語の動名詞と目的語の句(Exercising your options)は、動詞のほかの形で書いた所(exercise your options)も使用に数えます。同じ語の二つ目の定義は duplicate-definition が言います。
52
+ en: Only terms defined in a recognised form ("X" means, (the "X")) are checked. Plurals and possessives (Services, Seller's) count as uses, and so does the term in a heading. A phrase of a gerund and its object (Exercising your options) is also used where its verb is written in another form (exercise your options). A second definition of the same term is duplicate-definition's to report.
53
53
  rewrite:
54
54
  depth: light
55
55
  ja:
@@ -50,8 +50,8 @@ example:
50
50
  This agreement is between Harbour Ltd (the "Seller") and Hill Ltd (the "Buyer"). The Seller ships within five days.
51
51
 
52
52
  not_flagged:
53
- ja: 定義の条(「X」とは…をいう)で定義した語は、前で使っても言いません。定義の条は文書の頭にも終わりにも置く書き方があるためです。規約やポリシーの丁寧な形(以下「X」といいます)も、法令の形と同じに読みます。見出しの行(章・節の行、条の前の「(買主の義務)」のような見出し)の語と、漢字で終わる語にさらに漢字が続く長い語(「監査委員会」の「監査委員」)と、定義の括弧の前の長い名前をそのまま書いた所(「…作成の手引き(以下「手引き」という。)」より前の「…作成の手引き」)は、使った所に数えません。最初の一か所だけを指します。別の語を定義する括弧の前の名前の中(This Services Agreement (this "Agreement") の Services、本サービス利用契約(以下「本契約」という。)の本サービス)も、使った所に数えません。目次・索引の行(目次の見出しの下の項目、一行に詰めた目次、定義した語と条の番地の一覧)と、条の番号の後ろの題(Section 1.1 Closing.)と、語を含むもっと長い名前(定義の括弧の前の名前を前で写した株式会社みなと、もっと長い定義した語、Navistar Financial Corporation)も数えません。文書自身の名前と定義した語に同じ頭を付けた所(この就業規則(以下「規則」)より前の「この規則」)と、すぐ後ろの括弧に定義の長い名前を書き添えた所は、読み手に分かるので言いません。
54
- en: Terms defined in a definitions clause ("X" means ...) are not checked, since a definitions clause may come first or last. A heading line (a chapter or section line, or the caption above an article) does not count as a use, nor does a longer word that runs on in kanji past a term ending in kanji, nor an earlier copy of the long name written just before the definition's bracket. The polite Japanese form (以下「X」といいます) is read like the statute form. Only the first early use is pointed at. A word inside the name written right before another term's defining bracket (Services in This Services Agreement (this "Agreement")) is not a use either. Nor are contents and index lines (the items under a contents heading, a table of contents or a list of defined terms and section numbers packed on one line), a caption after a section number (Section 1.1 Closing.), or a longer name that holds the term, such as an earlier copy of the name before the bracket (Board of Directors before our Board of Directors ("Board")), a longer defined term, a capitalised name ending in it (Navistar Financial Corporation). The document naming itself (this Agreement before (this "Agreement")) and a use followed by the long name in brackets are clear to the reader and not pointed at.
53
+ ja: 定義の条(「X」とは…をいう)で定義した語は、前で使っても言いません。定義の条は文書の頭にも終わりにも置く書き方があるためです。規約やポリシーの丁寧な形(以下「X」といいます)も、法令の形と同じに読みます。見出しの行(章・節の行、条の前の「(買主の義務)」のような見出し)の語と、漢字で終わる語にさらに漢字が続く長い語(「監査委員会」の「監査委員」)と、定義の括弧の前の長い名前をそのまま書いた所(「…作成の手引き(以下「手引き」という。)」より前の「…作成の手引き」)は、使った所に数えません。最初の一か所だけを指します。別の語を定義する括弧の前の名前の中(This Services Agreement (this "Agreement") の Services、本サービス利用契約(以下「本契約」という。)の本サービス)も、使った所に数えません。目次・索引の行(目次の見出しの下の項目、一行に詰めた目次、定義した語と条の番地の一覧)と、条の番号の後ろの題(Section 1.1 Closing.)と、番号と語の終わりに条の番地の括弧を添えただけの札((1)文化芸術推進基本計画(第7条関係))と、語を含むもっと長い名前(定義の括弧の前の名前を前で写した株式会社みなと、もっと長い定義した語、Navistar Financial Corporation)も数えません。文書自身の名前と定義した語に同じ頭を付けた所(この就業規則(以下「規則」)より前の「この規則」)と、すぐ後ろの括弧に定義の長い名前を書き添えた所は、読み手に分かるので言いません。
54
+ en: Terms defined in a definitions clause ("X" means ...) are not checked, since a definitions clause may come first or last. A heading line (a chapter or section line, or the caption above an article) does not count as a use, nor does a longer word that runs on in kanji past a term ending in kanji, nor an earlier copy of the long name written just before the definition's bracket. The polite Japanese form (以下「X」といいます) is read like the statute form. Only the first early use is pointed at. A word inside the name written right before another term's defining bracket (Services in This Services Agreement (this "Agreement")) is not a use either. Nor are contents and index lines (the items under a contents heading, a table of contents or a list of defined terms and section numbers packed on one line), a caption after a section number (Section 1.1 Closing.), a label of a number and the term ending in a bracketed section reference ((a) Seller (Section 2)), or a longer name that holds the term, such as an earlier copy of the name before the bracket (Board of Directors before our Board of Directors ("Board")), a longer defined term, a capitalised name ending in it (Navistar Financial Corporation). The document naming itself (this Agreement before (this "Agreement")) and a use followed by the long name in brackets are clear to the reader and not pointed at.
55
55
  rewrite:
56
56
  depth: structure
57
57
  ja:
@@ -111,7 +111,7 @@ const oneOf = (words: readonly string[]): string => (words.length === 0 ? "(?!)"
111
111
  const definitionPatterns = (words: DefinitionWords): DefinitionPatterns => ({
112
112
  opened: new RegExp(String.raw`[((]\s*(?<markers>(?:(?:${oneOf(words.markers)})${MARKER_GAP})*)${WRAP}(?<![A-Za-z])$`, "iu"),
113
113
  closes: new RegExp(String.raw`^${WRAP}(?<verb>(?:${oneOf(words.verbs)})[。.]?)?${WRAP}[))]`, "iu"),
114
- alternative: new RegExp(String.raw`[,,、]\s*(?:${oneOf(words.alternatives ?? [])})\s*$`, "iu"),
114
+ alternative: new RegExp(String.raw`[,,、]\s*(?:${oneOf(words.alternatives ?? [])})\s*["“'‘]?$`, "iu"),
115
115
  });
116
116
 
117
117
  /**
@@ -191,10 +191,17 @@ const isPrefixedAt = (body: string, acronym: string, at: number): boolean => {
191
191
  return name.slice(-DEFINITION_REACH).includes(words) && spellsOut(name, acronym);
192
192
  };
193
193
 
194
- /** 名前の後ろの読点と言い換えの語に続く略語(Special Interest Groups, or SIGs)。名前の頭文字が略語と揃うときだけ。 */
194
+ /** 引用符で囲んで書き終えた名前(‘abstract syntax tree’)。小文字の語でも、囲みが名前の始まりと終わりを決める。 */
195
+ const QUOTED_NAME_END = /(?:“(?<curly>[^“”\n]{1,60})”|‘(?<single>[^‘’\n]{1,60})’|"(?<straight>[^"\n]{1,60})")$/u;
196
+
197
+ /** 名前の後ろの読点と言い換えの語に続く略語(Special Interest Groups, or SIGs、‘abstract syntax tree’, or ‘AST’)。名前の頭文字が略語と揃うときだけ。 */
195
198
  const isAlternativeAt = (patterns: DefinitionPatterns, body: string, acronym: string, at: number): boolean => {
196
199
  const joined = patterns.alternative.exec(body.slice(Math.max(0, at - DEFINITION_REACH), at));
197
- return joined !== null && spellsOut(body.slice(0, at - joined[0].length), acronym);
200
+ if (joined === null) return false;
201
+ const name = body.slice(0, at - joined[0].length);
202
+ const groups = QUOTED_NAME_END.exec(name)?.groups;
203
+ const quoted = groups?.["curly"] ?? groups?.["single"] ?? groups?.["straight"];
204
+ return spellsOut(name, acronym) || (quoted !== undefined && namesAcronym(quoted, acronym));
198
205
  };
199
206
 
200
207
  const isBracketedAt = (body: string, acronym: string, at: number): boolean => {
@@ -35,3 +35,41 @@ export const linkTitleSpans = (source: string, links: readonly Span[]): Span[] =
35
35
  const text = externalTextOf(source, link);
36
36
  return text !== undefined && standsAlone(source, link) ? [text] : [];
37
37
  });
38
+
39
+ /** 語だけの箇条書きの項目(- GPL)と、その下に入れ子にした、外のページへのリンクだけの項目( - [GNU General Public License …](https://…))。 */
40
+ const TERM_ITEM = /^(?<indent>[ \t]*)(?:[-*+]|\d+[.)])[ \t]+(?<term>[A-Z][A-Za-z\d&-]*[A-Z\d])[ \t]*$/u;
41
+ const LINK_ITEM = /^(?<indent>[ \t]*)(?:[-*+]|\d+[.)])[ \t]+\[(?<title>[^\]\n]+)\]\(<?https?:\/\/[^)\s]+>?\)[ \t]*$/u;
42
+ /** 題名の名前の終わり。サイトの名前を添える区切り( | Open Source Initiative、 — CC BY 4.0)。 */
43
+ const TITLE_PART_END = /\s[|—–-]\s/u;
44
+ const CAPITALIZED = /^[A-Z]/u;
45
+
46
+ const wordsOf = (text: string): string[] => text.split(/[\s-]+/u).filter((word) => word !== "");
47
+
48
+ /** 題名が語そのものを含む(About the WTFPL、… — CC BY 4.0)か、題名の名前の大文字で始まる語の頭文字が語の字で終わる(GNU General Public License)。 */
49
+ const titleNames = (title: string, term: string): boolean => {
50
+ const termWords = wordsOf(term.toUpperCase());
51
+ const titleWords = wordsOf(title.toUpperCase());
52
+ const contains = titleWords.some((_, start) => termWords.every((word, offset) => titleWords[start + offset] === word));
53
+ const initials = wordsOf(title.split(TITLE_PART_END)[0] ?? "")
54
+ .filter((word) => CAPITALIZED.test(word))
55
+ .map((word) => word.charAt(0))
56
+ .join("");
57
+ return contains || initials.endsWith(term.replaceAll(/[&-]/gu, ""));
58
+ };
59
+
60
+ /**
61
+ * 名前を並べた一覧で、語だけの項目のすぐ下に、その語を名指す外のページへのリンクを入れ子にした語(ライセンスの一覧の GPL、WTFPL)。
62
+ * 語はリンク先のページの名前で、読み手はリンクから中身に行ける。題名が語を名指さないリンク(- AWS / - [Console](…))では数えない。
63
+ */
64
+ export const linkedListTerms = (source: string): Set<string> => {
65
+ const lines = source.split(/\r?\n/u);
66
+ return new Set(
67
+ lines.flatMap((line, index) => {
68
+ const term = TERM_ITEM.exec(line)?.groups;
69
+ const link = LINK_ITEM.exec(lines[index + 1] ?? "")?.groups;
70
+ if (term?.["term"] === undefined || link?.["title"] === undefined) return [];
71
+ const nested = (link["indent"] ?? "").length > (term["indent"] ?? "").length;
72
+ return nested && titleNames(link["title"], term["term"]) ? [term["term"]] : [];
73
+ }),
74
+ );
75
+ };
@@ -0,0 +1,44 @@
1
+ // Acronyms a sentence about abbreviations spells out in words (the abbreviation DOB … spell out the field to date of birth),
2
+ // and the other capitalisations of an acronym that a text writes (TCoP beside TCOP). Pure; the mention words come from the
3
+ // abbreviation-mention lexicon.
4
+ import { escapeRegExp } from "../orthography.ts";
5
+
6
+ const ACRONYM = /(?<![\p{L}\p{N}])[A-Z][A-Z&-]*[A-Z](?![\p{L}\p{N}])/gu;
7
+ /** A word of two letters or more: the letters of a dotted form (B.B.C.) spell nothing out. */
8
+ const LATIN_WORD = /[A-Za-z]{2,}/gu;
9
+ const CAPITAL = /\p{Lu}/gu;
10
+ /** A capitalisation of an acronym keeps at least this many capitals (TCoP); fewer is a word (Tcop). */
11
+ const MIN_CAPITALS = 2;
12
+
13
+ const mentionsAbbreviation = (sentence: string, mentions: readonly string[]): boolean => {
14
+ const lowered = sentence.toLowerCase();
15
+ return mentions.some((mention) => lowered.includes(mention.toLowerCase()));
16
+ };
17
+
18
+ /** Whether some run of consecutive words, other than the acronym itself, has the acronym's letters as its initials. */
19
+ const spellsOutInWords = (sentence: string, acronym: string): boolean => {
20
+ const letters = acronym.replaceAll(/[&-]/gu, "").toLowerCase();
21
+ const words = (sentence.match(LATIN_WORD) ?? []).filter((word) => word !== acronym.replaceAll(/[&-]/gu, ""));
22
+ return words.some(
23
+ (_, start) =>
24
+ words
25
+ .slice(start, start + letters.length)
26
+ .map((word) => word.charAt(0).toLowerCase())
27
+ .join("") === letters,
28
+ );
29
+ };
30
+
31
+ /** The acronyms that a sentence naming an abbreviation also writes out in full. */
32
+ export const spelledOutAcronyms = (sentences: readonly string[], mentions: readonly string[]): Set<string> =>
33
+ new Set(
34
+ sentences
35
+ .filter((sentence) => mentionsAbbreviation(sentence, mentions))
36
+ .flatMap((sentence) => (sentence.match(ACRONYM) ?? []).filter((acronym) => spellsOutInWords(sentence, acronym))),
37
+ );
38
+
39
+ /** The other ways the text capitalises an acronym (TCoP for TCOP, TCOP for TCoP), each with two capitals or more. */
40
+ export const caseVariantsIn = (text: string, acronym: string): string[] => {
41
+ const written = new RegExp(String.raw`(?<![\p{L}\p{N}])${escapeRegExp(acronym)}(?![\p{L}\p{N}])`, "giu");
42
+ const variants = [...text.matchAll(written)].map((match) => match[0]);
43
+ return [...new Set(variants)].filter((variant) => variant !== acronym && (variant.match(CAPITAL) ?? []).length >= MIN_CAPITALS);
44
+ };
@@ -46,8 +46,8 @@ export const oneOf = (words: readonly string[]): string =>
46
46
  const spansOf = (text: string, pattern: RegExp): Span[] =>
47
47
  [...text.matchAll(pattern)].map((match) => ({ start: match.index, end: match.index + match[0].length }));
48
48
 
49
- /** 名前の語。大文字で始まり、点・& ・アポストロフィ・ハイフンを含んでよい(BorrowMoney.com、PTE.、O'Neil)。 */
50
- const NAME_WORD = String.raw`\p{Lu}[\p{L}\p{N}&.'’-]*`;
49
+ /** 名前の語。大文字か、数字に続く字で始まり(37signals)、点・& ・アポストロフィ・ハイフンを含んでよい(BorrowMoney.com、PTE.、O'Neil)。 */
50
+ const NAME_WORD = String.raw`(?:\p{Lu}|\p{N}+\p{L})[\p{L}\p{N}&.'’-]*`;
51
51
  /** 法人の形の前に並ぶ名前の語の数の上限(CNH INDUSTRIAL SOUTH DAKOTA, INC.)。文の頭まで遡って別の語を名前に入れないため。 */
52
52
  const NAME_WORDS_MAX = 5;
53
53
 
@@ -0,0 +1,39 @@
1
+ import type { Token } from "../plugin.ts";
2
+
3
+ /**
4
+ * 宛名の並び(各国公私立高等専門学校長、公益財団法人日本博物館協会会長)の、名前の外の語。頭の「各」(語彙表 addressee-prefix)と、
5
+ * 尻の役職(addressee-title: 長、会長、教育長)。endings は組織の名前を閉じる語(organisation-unit)、forms は法人格(organisation-form)。
6
+ */
7
+ export type AddresseeWords = {
8
+ readonly prefixes: readonly string[];
9
+ readonly titles: readonly string[];
10
+ readonly endings: readonly string[];
11
+ readonly forms: readonly string[];
12
+ };
13
+
14
+ /** 組織の語の前に要る字数。1 字なら、普通の語の一部(本部、学校)と見分けられない。 */
15
+ const MIN_BODY_LENGTH = 2;
16
+
17
+ const charLength = (text: string): number => [...text].length;
18
+
19
+ /** 名前が組織の語(2 字以上の名の後ろの協会、学校)か法人格(独立行政法人)で閉じるか。 */
20
+ export const endsInOrganisation = (name: string, words: AddresseeWords): boolean =>
21
+ words.forms.some((form) => name.endsWith(form)) ||
22
+ words.endings.some((unit) => name.endsWith(unit) && charLength(name) - charLength(unit) >= MIN_BODY_LENGTH);
23
+
24
+ /** 尻の役職のうち、残りが組織の名前で閉じる最も長いもの(機構長 > 長)。役職だけの連なりや、組織で閉じないもの(延長、成長)は undefined。 */
25
+ export const trailingTitle = (run: string, words: AddresseeWords): string | undefined =>
26
+ words.titles
27
+ .filter((title) => run.length > title.length && run.endsWith(title) && endsInOrganisation(run.slice(0, run.length - title.length), words))
28
+ .reduce<string | undefined>((longest, title) => (title.length > (longest?.length ?? 0) ? title : longest), undefined);
29
+
30
+ /**
31
+ * 連なり(文書全体の座標で start から始まる)の頭の「各」の語。その語が連なりの頭にあり、残り(name: 役職を外した連なり)が
32
+ * 組織の名前で閉じるときだけ。各国の「各」のような語の一部は、解析が別の語に分けたときしか外さない。
33
+ */
34
+ export const leadingPrefix = (tokens: readonly Token[], start: number, name: string, words: AddresseeWords): Token | undefined => {
35
+ const token = tokens.find((candidate) => candidate.span.start === start);
36
+ if (token === undefined || !words.prefixes.includes(token.surface)) return undefined;
37
+ const rest = name.slice(token.surface.length);
38
+ return name.startsWith(token.surface) && rest !== "" && endsInOrganisation(rest, words) ? token : undefined;
39
+ };
@@ -28,6 +28,10 @@ export type CapitalisedWords = {
28
28
  readonly capacityMarkers: readonly string[];
29
29
  /** Articles, which may stand between a capacity word and the capacity (as the Borrower). */
30
30
  readonly articles: readonly string[];
31
+ /** Words right after a run that say it is attached (Termination Certificate attached hereto). */
32
+ readonly attachedCues: readonly string[];
33
+ /** Last words of a run that name a kind of document (Certificate, Form): with attachedCues after, the run is a title. */
34
+ readonly attachedTitleEnds: readonly string[];
31
35
  };
32
36
 
33
37
  /** A term the document defines; bare is a definition with no defining word (("GPC")), which names more than it defines. */
@@ -216,9 +220,26 @@ const capacityOfAName = (tokens: readonly Token[], start: number, words: Capital
216
220
  return words.capacityMarkers.includes(marker.surface) && (continues(name) || name.surface === ".");
217
221
  };
218
222
 
223
+ /** Words right after a run that say it is attached (the Certificate attached hereto), ending at a word's edge. */
224
+ const attachedAfterPattern = (cues: readonly string[]): RegExp | undefined => {
225
+ const words = cues.filter((cue) => cue !== "").map(escapeRegExp);
226
+ return words.length === 0 ? undefined : new RegExp(String.raw`^[ \t]+(?:${words.join("|")})(?![\p{L}\p{N}_])`, "u");
227
+ };
228
+
229
+ /**
230
+ * The run is the title of a document the agreement attaches: its last word names a kind of document (Certificate,
231
+ * Form) and words right after it say it is attached. Confidential Information attached hereto is no title.
232
+ */
233
+ const titlesAttachment = (words: CapitalisedWords): ((last: Token, after: string) => boolean) => {
234
+ const attached = attachedAfterPattern(words.attachedCues);
235
+ const titleEnds = new Set(words.attachedTitleEnds);
236
+ return (last, after) => attached !== undefined && titleEnds.has(last.surface) && attached.test(after);
237
+ };
238
+
219
239
  /**
220
240
  * Whether a run is a name by what surrounds it: a number after it, a longer name it starts, brackets after it that
221
- * define it, initials that spell a term the document defines (“IRS” means the Internal Revenue Service), or a capacity.
241
+ * define it, initials that spell a term the document defines (“IRS” means the Internal Revenue Service), a capacity, or
242
+ * words that make it the title of an attached form.
222
243
  */
223
244
  type NameCue = (tokens: readonly Token[], start: number, end: number, term: string) => boolean;
224
245
 
@@ -226,14 +247,17 @@ const nameCues = (input: CapitalisedInput, joiners: ReadonlySet<string>): NameCu
226
247
  const words = input.words;
227
248
  const ends = { names: lastWordsOf(words.nameEnds), forms: lastWordsOf(words.formEnds), formEntries: new Set(words.formEnds) };
228
249
  const definitionAfter = definitionAfterPattern(words);
250
+ const attachedTitle = titlesAttachment(words);
229
251
  const abbreviations = new Set([...input.defined.map((definition) => definition.term), ...input.named].filter((term) => ACRONYM.test(term)));
230
252
  return (tokens, start, end, term) => {
231
253
  const last = tokens[end - 1];
232
254
  if (last === undefined) return false;
255
+ const after = input.source.slice(last.span.end, last.span.end + DEFINITION_AFTER_CHARS);
233
256
  return (
234
257
  numberedName(last, tokens[end]) ||
235
258
  startsLongerName(tokens, end, ends, joiners) ||
236
- definitionAfter.test(input.source.slice(last.span.end, last.span.end + DEFINITION_AFTER_CHARS)) ||
259
+ definitionAfter.test(after) ||
260
+ attachedTitle(last, after) ||
237
261
  initialsOf(term, joiners).some((initials) => abbreviations.has(initials)) ||
238
262
  capacityOfAName(tokens, start, words)
239
263
  );
@@ -89,15 +89,49 @@ const spanOf = (paragraph: readonly Line[]): Span[] => {
89
89
  };
90
90
 
91
91
  /**
92
- * The paragraphs in capitals at the head of a document, before its first sentence: a letterhead, an office's name, an
93
- * address block (BOARD OF GOVERNORS / WASHINGTON, D.C. 20551). Every line is in capitals and none ends a sentence, so a
94
- * disclaimer in capitals, which is sentences, is not one. A document with no sentence has no head to tell apart.
92
+ * The paragraphs whose every line is in capitals and none ends a sentence: a letterhead or an address block at the head
93
+ * (BOARD OF GOVERNORS / WASHINGTON, D.C. 20551), and a heading a transcript sets in capitals on lines of its own
94
+ * (OPENING STATEMENT OF HON. JANE DOE, / U.S. SENATOR FROM OHIO). A warning in capitals is sentences, so it is not one. A
95
+ * document with no sentence has no prose to tell such a paragraph apart from.
95
96
  */
96
- export const letterheadSpans = (source: string): Span[] => {
97
+ export const capitalParagraphSpans = (source: string): Span[] => {
97
98
  const paragraphs = paragraphsOf(linesOf(source));
98
- const firstSentence = paragraphs.findIndex((paragraph) => paragraph.some((line) => SENTENCE_END.test(line.text)));
99
- return paragraphs
100
- .slice(0, Math.max(firstSentence, 0))
101
- .filter((paragraph) => paragraph.every(isCapitalLine))
102
- .flatMap(spanOf);
99
+ const endsSentence = (paragraph: readonly Line[]): boolean => paragraph.some((line) => SENTENCE_END.test(line.text));
100
+ if (!paragraphs.some(endsSentence)) return [];
101
+ return paragraphs.filter((paragraph) => paragraph.every(isCapitalLine) && !endsSentence(paragraph)).flatMap(spanOf);
103
102
  };
103
+
104
+ /** The capital paragraphs and the roster names of a source, in order of where they start. */
105
+ export const capitalLineSpans = (source: string): Span[] =>
106
+ [...capitalParagraphSpans(source), ...rosterNameSpans(source)].sort((left, right) => left.start - right.start);
107
+
108
+ /** The index of the last span that starts at or before at, by halving: -1 when none does. */
109
+ const lastStartingBy = (spans: readonly Span[], at: number, low = 0, high = spans.length): number => {
110
+ if (low >= high) return low - 1;
111
+ const middle = Math.floor((low + high) / 2);
112
+ return (spans[middle]?.start ?? 0) <= at ? lastStartingBy(spans, at, middle + 1, high) : lastStartingBy(spans, at, low, middle);
113
+ };
114
+
115
+ /** Whether inner lies wholly in one of the spans, which do not overlap and are sorted by start (capitalLineSpans). */
116
+ export const isWithinSorted = (spans: readonly Span[], inner: Span): boolean => {
117
+ const span = spans[lastStartingBy(spans, inner.start)];
118
+ return span !== undefined && inner.end <= span.end;
119
+ };
120
+
121
+ /** Columns on a line: two spaces or more, or a tab, stand between them. */
122
+ const COLUMN_CELL = /[^\t ]+(?: [^\t ]+)*/gu;
123
+ /** A name in capitals, a comma, and a place or office in ordinary case: JANE Q. DOE, Ohio / JOHN DOE, JR., New Mexico. */
124
+ const ROSTER_CELL = /^([A-Z][A-Z.'’-]*(?: [A-Z][A-Z.'’-]*)*(?:, [JS]R\.)?), \p{Lu}\p{Ll}+(?: \p{Lu}\p{Ll}+)*$/u;
125
+
126
+ /**
127
+ * The names of a roster: a column that holds a name in capitals and the member's place in ordinary case (a committee's
128
+ * members, as a hearing sets them). A run in capitals followed by a comma and prose is no roster.
129
+ */
130
+ export const rosterNameSpans = (source: string): Span[] =>
131
+ linesOf(source).flatMap((line) =>
132
+ [...line.text.matchAll(COLUMN_CELL)].flatMap((cell) => {
133
+ const name = ROSTER_CELL.exec(cell[0])?.[1];
134
+ const start = line.start + cell.index;
135
+ return name === undefined ? [] : [{ start, end: start + name.length }];
136
+ }),
137
+ );
@@ -1,7 +1,8 @@
1
1
  import type { Detector, DocumentProfile, Finding, ProseDocument, Sentence, Span, Token } from "../plugin.ts";
2
2
  import { compacted } from "./gram-place.ts";
3
3
  import { leadingCounter, trailingPrefix } from "./counter-edge.ts";
4
- import { trailingAddressee } from "./addressee-edge.ts";
4
+ import { trailingAddressee, type AddresseeSuffix } from "./addressee-edge.ts";
5
+ import { leadingPrefix, trailingTitle, type AddresseeWords } from "./addressee-title-edge.ts";
5
6
  import { maskAddresses } from "../address-chain.ts";
6
7
  import { isAddressRun } from "./place-run.ts";
7
8
  import { isOneName } from "./name-run.ts";
@@ -22,8 +23,15 @@ const KANJI_RUN = /[一-鿿]+/gu;
22
23
  /**
23
24
  * 漢字の連なりと、その文書全体の座標での範囲。quoted は括弧の中身がまるごとこの連なりであること。
24
25
  * placed は、題の名前の置き方(括弧を閉じる、補いの括弧が続く、文がこれだけ)をしていること。
26
+ * after は、連なりが文の頭にあるときの、その後ろの文字(宛名の行の、空白と「殿」)。頭になければ undefined。
25
27
  */
26
- type KanjiRun = { readonly text: string; readonly span: Span | undefined; readonly quoted: boolean; readonly placed: boolean };
28
+ type KanjiRun = {
29
+ readonly text: string;
30
+ readonly span: Span | undefined;
31
+ readonly quoted: boolean;
32
+ readonly placed: boolean;
33
+ readonly after: string | undefined;
34
+ };
27
35
 
28
36
  const SPACE = /\s/gu;
29
37
 
@@ -48,9 +56,10 @@ const runsOf = (sentence: Sentence, profile: DocumentProfile | undefined, names:
48
56
  return [...maskedProse(prose, profile, names).matchAll(KANJI_RUN)].map((match) => {
49
57
  const before = prose.slice(0, match.index).replace(SPACE, "").length;
50
58
  const [start, last] = [printed[before], printed[before + match[0].length - 1]];
51
- if (start === undefined || last === undefined) return { text: match[0], span: undefined, quoted: false, placed: false };
59
+ if (start === undefined || last === undefined) return { text: match[0], span: undefined, quoted: false, placed: false, after: undefined };
52
60
  const span = { start: sentence.span.start + start, end: sentence.span.start + last + 1 };
53
- return { text: match[0], span, quoted: fillsQuote(sentence, start, last + 1), placed: isTitlePosition(sentence.text, start, last + 1) };
61
+ const after = sentence.text.slice(0, start).trim() === "" ? sentence.text.slice(last + 1) : undefined;
62
+ return { text: match[0], span, quoted: fillsQuote(sentence, start, last + 1), placed: isTitlePosition(sentence.text, start, last + 1), after };
54
63
  });
55
64
  };
56
65
 
@@ -63,8 +72,12 @@ type NameWords = {
63
72
  readonly positional: readonly string[];
64
73
  /** 宛先の名前に付く語(宛、御中)。連なりの尻にあれば、名前の後ろに付いた語。 */
65
74
  readonly addressee: readonly string[];
75
+ /** 宛名の並びの、組織の名前の前の「各」と後ろの役職(各…学校長、…協会会長)。 */
76
+ readonly addressees: AddresseeWords;
66
77
  };
67
78
 
79
+ type Tail = AddresseeSuffix;
80
+
68
81
  /** 1 つの文書で連なりを読むのに要るもの。titled は、文書の中で題として置かれた名前(titledNames)。 */
69
82
  type RunContext = {
70
83
  readonly profile: DocumentProfile | undefined;
@@ -93,21 +106,44 @@ const isTitle = (tokens: readonly Token[], span: Span, text: string, placed: boo
93
106
  ) &&
94
107
  (placed || isTitledName(text, context.titled));
95
108
 
109
+ /** 宛名の行(各国公私立高等専門学校長、…協会会長 殿): 連なりが行の頭にあり、後ろは空白と宛先に付く語だけ。 */
110
+ const isAddresseeLine = (run: KanjiRun, words: NameWords): boolean => {
111
+ const after = run.after?.trim();
112
+ return after !== undefined && (after === "" || words.addressee.includes(after));
113
+ };
114
+
115
+ /** 尻の外す語: 数に付く接頭の語か宛先に付く語と、宛名の行ならその前の役職(日本博物館協会会長殿の「会長殿」)。 */
116
+ const tailOf = (tokens: readonly Token[], run: KanjiRun, span: Span, words: NameWords): Tail | undefined => {
117
+ const prefix = trailingPrefix(tokens, span.end, run.text);
118
+ const suffix = prefix === undefined ? trailingAddressee(tokens, span.end, run.text, words.addressee) : { surface: prefix.surface, start: prefix.span.start };
119
+ const title = isAddresseeLine(run, words) ? trailingTitle(run.text.slice(0, run.text.length - (suffix?.surface.length ?? 0)), words.addressees) : undefined;
120
+ if (title === undefined) return suffix;
121
+ return { surface: title + (suffix?.surface ?? ""), start: (suffix?.start ?? span.end) - title.length };
122
+ };
123
+
124
+ /** 外す語を外した残り。頭の「各」は、宛名の行でなければ、残りが名前と決まったときだけ外す(呼び出し側が戻す)。 */
125
+ type Measured = { readonly rest: Span; readonly text: string; readonly keptHead: string };
126
+
127
+ const measuredOf = (tokens: readonly Token[], run: KanjiRun, span: Span, words: NameWords): Measured => {
128
+ const tail = tailOf(tokens, run, span, words);
129
+ const name = run.text.slice(0, run.text.length - (tail?.surface.length ?? 0));
130
+ const counter = leadingCounter(tokens, span.start, run.text, words.positional);
131
+ const prefix = counter === undefined ? leadingPrefix(tokens, span.start, name, words.addressees) : undefined;
132
+ const head = counter ?? prefix;
133
+ const keptHead = prefix !== undefined && !isAddresseeLine(run, words) ? prefix.surface : "";
134
+ return { rest: { start: head?.span.end ?? span.start, end: tail?.start ?? span.end }, text: name.slice(head?.surface.length ?? 0), keptHead };
135
+ };
136
+
96
137
  /**
97
138
  * 数えない連なりは空。頭の、数に付いた助数詞(2日日本弁護士連合会の「日」)と、尻の、数に付く接頭の語(施行令第172条の「第」)と
98
- * 宛先に付く語(市政情報課宛ての「宛」)は数えない。
139
+ * 宛先に付く語(市政情報課宛ての「宛」)は数えない。宛名の「各」と、宛名の行の役職(各国公私立高等専門学校長の「各」と「長」)も数えない。
99
140
  * 住所か名前かは、それを外した残りで決める(1日日本銀行の「日本銀行」)。品詞が無ければ判定しない。
100
141
  */
101
142
  const measuredRun = (tokens: readonly Token[] | undefined, run: KanjiRun, context: RunContext): string => {
102
143
  if (run.quoted) return "";
103
144
  if (tokens === undefined || run.span === undefined) return run.text;
104
- const counter = leadingCounter(tokens, run.span.start, run.text, context.words.positional);
105
- const prefix = trailingPrefix(tokens, run.span.end, run.text);
106
- const tail =
107
- prefix === undefined ? trailingAddressee(tokens, run.span.end, run.text, context.words.addressee) : { surface: prefix.surface, start: prefix.span.start };
108
- const rest = { start: counter?.span.end ?? run.span.start, end: tail?.start ?? run.span.end };
109
- const text = run.text.slice(counter?.surface.length ?? 0, run.text.length - (tail?.surface.length ?? 0));
110
- return isPlaceName(tokens, rest, context.words) || isTitle(tokens, rest, text, run.placed, context) ? "" : text;
145
+ const { rest, text, keptHead } = measuredOf(tokens, run, run.span, context.words);
146
+ return isPlaceName(tokens, rest, context.words) || isTitle(tokens, rest, text, run.placed, context) ? "" : keptHead + text;
111
147
  };
112
148
 
113
149
  const longestKanji = (sentence: Sentence, context: RunContext): string =>
@@ -130,16 +166,18 @@ const titledNames = (doc: ProseDocument, profile: DocumentProfile | undefined, n
130
166
  */
131
167
  export const kanjiRun: Detector = (doc, options): Finding[] => {
132
168
  const patterns = (id: string): string[] => (doc.lexicons[id] ?? []).map((entry) => entry.pattern);
169
+ const organisation: OrganisationWords = {
170
+ forms: patterns("organisation-form"),
171
+ closingForms: (doc.lexicons["organisation-form"] ?? []).filter((entry) => entry.position !== "before").map((entry) => entry.pattern),
172
+ units: patterns("organisation-unit"),
173
+ };
133
174
  const words: NameWords = {
134
175
  topUnits: new Set(patterns("prefecture-unit")),
135
- organisation: {
136
- forms: patterns("organisation-form"),
137
- closingForms: (doc.lexicons["organisation-form"] ?? []).filter((entry) => entry.position !== "before").map((entry) => entry.pattern),
138
- units: patterns("organisation-unit"),
139
- },
176
+ organisation,
140
177
  title: { endings: patterns("title-ending") },
141
178
  positional: patterns("calendar-unit"),
142
179
  addressee: patterns("addressee-suffix"),
180
+ addressees: { prefixes: patterns("addressee-prefix"), titles: patterns("addressee-title"), endings: organisation.units, forms: organisation.closingForms },
143
181
  };
144
182
  const names = doc.names ?? [];
145
183
  const context: RunContext = { profile: doc.profile, names, words, titled: titledNames(doc, doc.profile, names) };