chaffjs 0.26.0 → 0.27.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.
- package/dist/ai-score/human-shares.js +59 -59
- package/dist/ai-score/human-shares.js.map +1 -1
- package/dist/capitals-with-small.d.ts +8 -0
- package/dist/capitals-with-small.d.ts.map +1 -0
- package/dist/capitals-with-small.js +20 -0
- package/dist/capitals-with-small.js.map +1 -0
- package/dist/derived/relative-weekdays.d.ts +16 -0
- package/dist/derived/relative-weekdays.d.ts.map +1 -0
- package/dist/derived/relative-weekdays.js +52 -0
- package/dist/derived/relative-weekdays.js.map +1 -0
- package/dist/detectors/change-rate.d.ts +3 -0
- package/dist/detectors/change-rate.d.ts.map +1 -0
- package/dist/detectors/change-rate.js +102 -0
- package/dist/detectors/change-rate.js.map +1 -0
- package/dist/detectors/derived-numbers.d.ts.map +1 -1
- package/dist/detectors/derived-numbers.js +3 -7
- package/dist/detectors/derived-numbers.js.map +1 -1
- package/dist/detectors/document-date.d.ts +4 -0
- package/dist/detectors/document-date.d.ts.map +1 -0
- package/dist/detectors/document-date.js +12 -0
- package/dist/detectors/document-date.js.map +1 -0
- package/dist/detectors/orthographic-variant.d.ts.map +1 -1
- package/dist/detectors/orthographic-variant.js +16 -11
- package/dist/detectors/orthographic-variant.js.map +1 -1
- package/dist/detectors/registry/change-rate.d.ts +3 -0
- package/dist/detectors/registry/change-rate.d.ts.map +1 -0
- package/dist/detectors/registry/change-rate.js +3 -0
- package/dist/detectors/registry/change-rate.js.map +1 -0
- package/dist/detectors/registry/version-mismatch.d.ts +3 -0
- package/dist/detectors/registry/version-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/version-mismatch.js +3 -0
- package/dist/detectors/registry/version-mismatch.js.map +1 -0
- package/dist/detectors/relative-dates.d.ts.map +1 -1
- package/dist/detectors/relative-dates.js +3 -1
- package/dist/detectors/relative-dates.js.map +1 -1
- package/dist/detectors/relative-day-words.d.ts +4 -0
- package/dist/detectors/relative-day-words.d.ts.map +1 -0
- package/dist/detectors/relative-day-words.js +105 -0
- package/dist/detectors/relative-day-words.js.map +1 -0
- package/dist/detectors/structure-tree.d.ts +4 -1
- package/dist/detectors/structure-tree.d.ts.map +1 -1
- package/dist/detectors/structure-tree.js +65 -3
- package/dist/detectors/structure-tree.js.map +1 -1
- package/dist/detectors/version-mismatch.d.ts +3 -0
- package/dist/detectors/version-mismatch.d.ts.map +1 -0
- package/dist/detectors/version-mismatch.js +19 -0
- package/dist/detectors/version-mismatch.js.map +1 -0
- package/dist/structure/change-rate.d.ts +33 -0
- package/dist/structure/change-rate.d.ts.map +1 -0
- package/dist/structure/change-rate.js +71 -0
- package/dist/structure/change-rate.js.map +1 -0
- package/dist/structure/list-numbering.d.ts +17 -0
- package/dist/structure/list-numbering.d.ts.map +1 -0
- package/dist/structure/list-numbering.js +27 -0
- package/dist/structure/list-numbering.js.map +1 -0
- package/dist/structure/percent-sum-prose.d.ts +7 -0
- package/dist/structure/percent-sum-prose.d.ts.map +1 -0
- package/dist/structure/percent-sum-prose.js +44 -0
- package/dist/structure/percent-sum-prose.js.map +1 -0
- package/dist/structure/version-mismatch.d.ts +23 -0
- package/dist/structure/version-mismatch.d.ts.map +1 -0
- package/dist/structure/version-mismatch.js +74 -0
- package/dist/structure/version-mismatch.js.map +1 -0
- package/dist/structure/weekday.d.ts +21 -2
- package/dist/structure/weekday.d.ts.map +1 -1
- package/dist/structure/weekday.js +70 -8
- package/dist/structure/weekday.js.map +1 -1
- package/package.json +3 -3
- package/rules/agreement-slip.yaml +2 -2
- package/rules/change-rate-mismatch.yaml +44 -0
- package/rules/date-weekday-mismatch.yaml +4 -4
- package/rules/max-sentence-length.yaml +1 -0
- package/rules/numbering-gap.yaml +4 -4
- package/rules/one-sentence-paragraph-run.yaml +1 -0
- package/rules/orthographic-variant.yaml +3 -3
- package/rules/percent-sum-mismatch.yaml +4 -4
- package/rules/relative-date-mismatch.yaml +4 -4
- package/rules/undefined-acronym.yaml +1 -0
- package/rules/version-mismatch.yaml +77 -0
- package/src/ai-score/human-shares.ts +59 -59
- package/src/capitals-with-small.ts +23 -0
- package/src/derived/relative-weekdays.ts +65 -0
- package/src/detectors/change-rate.ts +122 -0
- package/src/detectors/derived-numbers.ts +3 -11
- package/src/detectors/document-date.ts +18 -0
- package/src/detectors/orthographic-variant.ts +8 -4
- package/src/detectors/registry/change-rate.ts +4 -0
- package/src/detectors/registry/version-mismatch.ts +4 -0
- package/src/detectors/relative-dates.ts +3 -1
- package/src/detectors/relative-day-words.ts +126 -0
- package/src/detectors/structure-tree.ts +76 -4
- package/src/detectors/version-mismatch.ts +23 -0
- package/src/structure/change-rate.ts +103 -0
- package/src/structure/list-numbering.ts +34 -0
- package/src/structure/percent-sum-prose.ts +54 -0
- package/src/structure/version-mismatch.ts +103 -0
- package/src/structure/weekday.ts +83 -6
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
id: change-rate-mismatch
|
|
2
|
+
layer: L3
|
|
3
|
+
status: stable
|
|
4
|
+
how_to_find: change-rate
|
|
5
|
+
requires: [structure]
|
|
6
|
+
severity: warning
|
|
7
|
+
|
|
8
|
+
name:
|
|
9
|
+
ja: 増減率が二つの値と合わない
|
|
10
|
+
en: A rate of change the two values do not give
|
|
11
|
+
|
|
12
|
+
why:
|
|
13
|
+
ja: 「前年同月の1,000社から20%増えて1,200社」のように、もとの値・今の値・増減率を一つの文に書くと、読み手は率を確かめずに信じます。数字を一つだけ更新したとき、率だけを去年の文書から写したときに食い違います。一つの文に、もとの値(「から」「に比べて」の付いた値)、同じ単位の今の値、向きの語(増加、減少)の付いた百分率が一つずつあるとき、二つの値から率を計算して比べます。値は書いた桁までの丸めを認めます(12億円は11.5億円から12.5億円のどれか)。
|
|
14
|
+
en: When one sentence gives the earlier value, the current one and the rate of change ("1,200 companies, up 20% from 1,000"), readers trust the rate without checking it. It goes wrong when one figure is updated and the others are not, or when the rate is copied from last year's text. When a sentence has one earlier value (marked by "from" or "compared with"), one current value in the same unit, and one percentage with a word of direction (up, decline), chaff computes the rate from the two values and compares. Each value may be rounded to its last written digit ($12 million is anything from $11.5 million to $12.5 million).
|
|
15
|
+
|
|
16
|
+
message:
|
|
17
|
+
ja: 「{rate}%」と書いていますが、二つの値からは {computed}% です
|
|
18
|
+
en: '"{rate}%" is written, but the two values give {computed}%'
|
|
19
|
+
|
|
20
|
+
how_to_fix:
|
|
21
|
+
ja: 二つの値と率のどれが正しいかを確かめて、ほかを直してください。
|
|
22
|
+
en: Check which of the two values and the rate is right, and correct the others.
|
|
23
|
+
|
|
24
|
+
levels: { strict: error, normal: warning, relaxed: info }
|
|
25
|
+
|
|
26
|
+
use_for: [business, technical, blog, legal, docs, academic, literature, speech]
|
|
27
|
+
off_for:
|
|
28
|
+
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."
|
|
29
|
+
|
|
30
|
+
group: facts
|
|
31
|
+
summary:
|
|
32
|
+
ja: 「1,000社から20%増えて1,200社」のように、一つの文の二つの値と増減率が合わない所
|
|
33
|
+
en: A rate of change that the two values in the same sentence do not give
|
|
34
|
+
example:
|
|
35
|
+
ja:
|
|
36
|
+
before: 利用企業は1,300社となり、前年の1,000社から20%増えました。
|
|
37
|
+
after: 利用企業は1,300社となり、前年の1,000社から30%増えました。
|
|
38
|
+
en:
|
|
39
|
+
before: 1,300 companies used the app, up 20% from 1,000 companies a year earlier.
|
|
40
|
+
after: 1,300 companies used the app, up 30% from 1,000 companies a year earlier.
|
|
41
|
+
|
|
42
|
+
not_flagged:
|
|
43
|
+
ja: 率が二つある文、もとの値が「から」「に比べて」で名指されていない文、同じ単位の今の値が一つでない文、向きの語が率のそばに無い文は比べません。「前年比20%増」のように値が一つしか無いもの、ポイントの差、倍は見ません。丸めで届く範囲の率は言いません。
|
|
44
|
+
en: 'A sentence with two rates, with no value marked as the earlier one, with no single current value in the same unit, or with no word of direction beside the rate is not compared. A change given with one value ("up 20% year on year"), percentage points and "times" are not read. A rate the rounding of the values can reach is not reported.'
|
|
@@ -10,8 +10,8 @@ name:
|
|
|
10
10
|
en: Date and weekday disagree
|
|
11
11
|
|
|
12
12
|
why:
|
|
13
|
-
ja: 「2026年10月1
|
|
14
|
-
en: When a date and its weekday disagree ("Friday, 1 October 2026"), the reader has to find out which one is right. It happens when a schedule is copied, or reused for another year.
|
|
13
|
+
ja: 「2026年10月1日(金)」のように、日付と曜日が食い違っていると、読み手はどちらが正しいのか確かめに行くことになります。日程を書き写すときや、年をまたいで予定を使い回すときに起きます。年を書いていない日付(メールの「10月9日(金)」)は、年を書いたすぐ前の日付(前に無ければ文書で最初のもの)から5か月以内に収まる年の日付として、暦と比べます。
|
|
14
|
+
en: When a date and its weekday disagree ("Friday, 1 October 2026"), the reader has to find out which one is right. It happens when a schedule is copied, or reused for another year. A date written without its year ("Friday, October 9" in an email) is checked as the date in the year that puts it within five months of the dated date just before it (or, with none before, the first one in the document).
|
|
15
15
|
|
|
16
16
|
message:
|
|
17
17
|
ja: 「{date}」は{actual}です({written}と書いてあります)
|
|
@@ -40,5 +40,5 @@ example:
|
|
|
40
40
|
after: The next meeting is on Thursday, 1 October 2026.
|
|
41
41
|
|
|
42
42
|
not_flagged:
|
|
43
|
-
ja:
|
|
44
|
-
en:
|
|
43
|
+
ja: 年を書いていない日付は、文書に年を書いた日付が無いときと、年を書いた日付から5か月より離れていてどちらの年とも読めるときは、暦と照らしません。
|
|
44
|
+
en: A date without its year is not checked when the document gives no date with a year, or when it is more than five months from the dated date and could be in either year.
|
|
@@ -79,6 +79,7 @@ use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
|
|
79
79
|
off_for:
|
|
80
80
|
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."
|
|
81
81
|
speech/transcript: "A transcript records what was said: its sentences, repeats and register are the speaker's, not the writer's to fix."
|
|
82
|
+
academic: measured by yarn rules:measure
|
|
82
83
|
legal: measured by yarn rules:measure
|
|
83
84
|
sources: [shibasaki-tamaoka-2010, yasashii-nihongo-2020, koyobun-2022, gov-uk-clear-language]
|
|
84
85
|
|
package/rules/numbering-gap.yaml
CHANGED
|
@@ -10,8 +10,8 @@ name:
|
|
|
10
10
|
en: Skipped or repeated number
|
|
11
11
|
|
|
12
12
|
why:
|
|
13
|
-
ja: 第3条の次が第5条、(a) の次が (c)、同じ第2項が二つ。番号が飛ぶか重なると、読み手は何かが消えたのか、参照がどちらを指すのかを判断できません。同じ親の中で並ぶ番号だけを比べます。枝番号(第3条の2)は並びの外として扱います。
|
|
14
|
-
en: Article 5 right after Article 3, (c) right after (a), two paragraphs numbered 2. A reader cannot tell whether something was removed, or which one a reference means. Only numbers side by side under the same parent are compared.
|
|
13
|
+
ja: 第3条の次が第5条、(a) の次が (c)、同じ第2項が二つ。番号が飛ぶか重なると、読み手は何かが消えたのか、参照がどちらを指すのかを判断できません。同じ親の中で並ぶ番号だけを比べます。枝番号(第3条の2)は並びの外として扱います。Markdown の番号付きの箇条書き(手順の「1.」「2.」「4.」)も、書いた番号を比べます。表示は最初の番号から数え直しますが、原文と「手順4を参照」は書いた番号を見ます。
|
|
14
|
+
en: Article 5 right after Article 3, (c) right after (a), two paragraphs numbered 2. A reader cannot tell whether something was removed, or which one a reference means. Only numbers side by side under the same parent are compared. The numbers written on a Markdown ordered list (steps "1.", "2.", "4.") are compared too. The page renders them counted from the first, but the source and a "see step 4" use the numbers as written.
|
|
15
15
|
|
|
16
16
|
message:
|
|
17
17
|
ja: 「{previous}」の次が「{label}」です({expected} 番目のはず)
|
|
@@ -74,5 +74,5 @@ example:
|
|
|
74
74
|
Working hours are from nine to six.
|
|
75
75
|
|
|
76
76
|
not_flagged:
|
|
77
|
-
ja:
|
|
78
|
-
en: Only numbers side by side under the same parent are compared.
|
|
77
|
+
ja: 同じ親の下で隣り合う番号だけを比べます。別の章の番号どうしは比べません。どの項目にも同じ番号(「1.」)を書いた箇条書きと、引用の中の箇条書きは見ません。
|
|
78
|
+
en: Only numbers side by side under the same parent are compared. A list that writes the same number on every item ("1." on each) and a list inside a quotation are not checked.
|
|
@@ -26,6 +26,7 @@ use_for: [business, technical, blog, legal, docs, academic, literature, speech]
|
|
|
26
26
|
off_for:
|
|
27
27
|
legal: "A clause, a definition or a claim is a paragraph of one sentence by form."
|
|
28
28
|
literature: "Dialogue and verse set each line apart."
|
|
29
|
+
academic: measured by yarn rules:measure
|
|
29
30
|
|
|
30
31
|
group: readability
|
|
31
32
|
summary:
|
|
@@ -14,7 +14,7 @@ name:
|
|
|
14
14
|
|
|
15
15
|
why:
|
|
16
16
|
ja: 「出来る」と「できる」、「引っ越し」と「引越し」、「ウィンドウ」と「ウインドウ」、「e-mail」と「email」のように、同じ語を一つの文書の中で二通りに書くと、検索で別の語になり、読み手は別のものを指しているのかと迷います。どちらが正しいかは決めません。chaff は語の一覧を持たず、文書の中の語を読み(日本語)やならした綴り(カタカナ語と英字)で束ね、文書の中で少ないほうを指摘します。補助動詞と形式名詞(見て下さい、その事)は、本来の使い方(資料を下さい)と別に比べるので、「補助動詞はかなで書く」と決めた文書はそろっていると見ます。漢字の違う同音の語(書く・描く、機会・機械)は別の語と見ます。少ないほうが三分の一を超える文書は、使い分けていると見て言いません。
|
|
17
|
-
en: Writing one word two ways in one document (e-mail and email, GitHub and Github) makes search treat them as different words and leaves the reader wondering whether two things are meant. chaff does not decide which way is right and keeps no list of words; it groups the document's own words, by reading in Japanese and by spelling without hyphens and case in English, and points at whichever way the document uses less. The case of the first letter is left out, since a sentence or a title gives it (Email, email), and so are words in capitals only. It says nothing when the minority is more than a third, since the document then uses both on purpose.
|
|
17
|
+
en: Writing one word two ways in one document (e-mail and email, GitHub and Github) makes search treat them as different words and leaves the reader wondering whether two things are meant. chaff does not decide which way is right and keeps no list of words; it groups the document's own words, by reading in Japanese and by spelling without hyphens and case in English, and points at whichever way the document uses less. The case of the first letter is left out, since a sentence or a title gives it (Email, email), and so are words in capitals only, a name in capitals with an ending (SENDs) and the letters of an acronym picked out (CLImatology next to CLIPER). It says nothing when the minority is more than a third, since the document then uses both on purpose.
|
|
18
18
|
|
|
19
19
|
message:
|
|
20
20
|
ja: 「{written}」と書いています(この文書はふつう「{other}」と書く語です。{of} 箇所のうち {count} 箇所が違う)
|
|
@@ -56,8 +56,8 @@ example:
|
|
|
56
56
|
Send the form by email. We answer every email within a day. Attach the receipt to the email.
|
|
57
57
|
|
|
58
58
|
not_flagged:
|
|
59
|
-
ja: 読みが同じでも漢字の違う語(書く・描く)、補助動詞・形式名詞と本来の使い方の書き分け(見て下さい・資料を下さい)、書き分けが意味の違いになる語(成る・なる、言う・いう)、語末の「ー」だけの違い(katakana-long-vowel が見ます)、鉤括弧や引用符の中、語の直後のふりがなの括弧の中(HTTP(えいちてぃーてぃーぴー))、chaff.yaml の names に書いた名前は見ません。名詞に続けて書いたかなの名詞(全員ぶん)と慣用句の動詞(気をつける)は、同じ読みの語(文、名札を付ける)と別に比べます。英字は、頭の字の大文字小文字(Email と email
|
|
60
|
-
en: The case of a word's first letter (Email, email; Long-Term, long-term) is not a spelling, nor is a word in capitals only (emphasis or an acronym). Hyphenated words that are another word (re-sign, resign), part of an address (github.com), anything in quotation marks, a Japanese reading in brackets right after a word (HTTP(えいちてぃーてぃーぴー)) and the team's names are not compared. Nothing is reported when the minority is more than a third. The literature preset turns it off, since old spelling belongs to its time.
|
|
59
|
+
ja: 読みが同じでも漢字の違う語(書く・描く)、補助動詞・形式名詞と本来の使い方の書き分け(見て下さい・資料を下さい)、書き分けが意味の違いになる語(成る・なる、言う・いう)、語末の「ー」だけの違い(katakana-long-vowel が見ます)、鉤括弧や引用符の中、語の直後のふりがなの括弧の中(HTTP(えいちてぃーてぃーぴー))、chaff.yaml の names に書いた名前は見ません。名詞に続けて書いたかなの名詞(全員ぶん)と慣用句の動詞(気をつける)は、同じ読みの語(文、名札を付ける)と別に比べます。英字は、頭の字の大文字小文字(Email と email)と、大文字だけの語(強調や略語)、大文字の名前に語尾が付いた語(関数名の SENDs)、同じ文の略語の字を示した語(CLIPER と並ぶ CLImatology)を書き方の違いにしません(HBase のような名前の大文字は比べます)。少ないほうが三分の一を超えるときも言いません。文学のジャンルでは既定で止めています(その時代の書き方のため)。
|
|
60
|
+
en: The case of a word's first letter (Email, email; Long-Term, long-term) is not a spelling, nor is a word in capitals only (emphasis or an acronym), nor a name in capitals with an ending (SENDs, OPENed), nor the letters of an acronym picked out beside it (CLImatology next to CLIPER). A name spelled with capitals (HBase) is still compared. Hyphenated words that are another word (re-sign, resign), part of an address (github.com), anything in quotation marks, a Japanese reading in brackets right after a word (HTTP(えいちてぃーてぃーぴー)) and the team's names are not compared. Nothing is reported when the minority is more than a third. The literature preset turns it off, since old spelling belongs to its time.
|
|
61
61
|
level_meaning:
|
|
62
62
|
ja: 少ないほうが語の {limit}% 以下のとき指摘
|
|
63
63
|
en: reported when the minority spelling is {limit}% of the word's uses or less
|
|
@@ -10,8 +10,8 @@ name:
|
|
|
10
10
|
en: Shares of a whole that do not add up to 100%
|
|
11
11
|
|
|
12
12
|
why:
|
|
13
|
-
ja: 構成比や内訳の百分率を足して 100%
|
|
14
|
-
en: When the shares of a whole do not add up to 100%, the reader has to work out which figure is wrong or which item is missing. It happens when an item is added or removed, or when one figure is changed. The percentages are added only when the column's header, or the sentence or heading right above the list or the table, names the parts of a whole (breakdown, market share, composition). Rounding is allowed, up to half the last digit per item (at least one point).
|
|
13
|
+
ja: 構成比や内訳の百分率を足して 100% にならないと、読み手はどの数字が違うのか、項目が抜けたのかを確かめることになります。項目を足したり消したり、数字を一つだけ直したりしたときに起きます。表のその列の見出しか、箇条書きや表のすぐ上の文・見出しに、全体を分けた割合の語(構成比、内訳、シェア)があるときだけ足します。一つの文に並べた内訳(「構成比は、Aが50%、Bが30%、Cが25%」)も、その文に割合の語があり百分率が三つ以上あれば足します。丸めの誤差は、項目ごとに最後の桁の半分まで(少なくとも 1 ポイント)認めます。
|
|
14
|
+
en: When the shares of a whole do not add up to 100%, the reader has to work out which figure is wrong or which item is missing. It happens when an item is added or removed, or when one figure is changed. The percentages are added only when the column's header, or the sentence or heading right above the list or the table, names the parts of a whole (breakdown, market share, composition). A breakdown written in one sentence ("software made up 50% of sales, maintenance 30% and training 25%") is added too, when the sentence has such a word and three or more percentages. Rounding is allowed, up to half the last digit per item (at least one point).
|
|
15
15
|
|
|
16
16
|
message:
|
|
17
17
|
ja: 内訳の割合の和が {sum} で、100% になりません
|
|
@@ -56,5 +56,5 @@ example:
|
|
|
56
56
|
- Europe: 40%
|
|
57
57
|
|
|
58
58
|
not_flagged:
|
|
59
|
-
ja: 割合の語の無い百分率(利用率、伸び率)、複数回答の集計、符号の付いた増減(+3%)、合計の行がある表(合計が内訳の和と合わない
|
|
60
|
-
en: 'Percentages not named as shares (rates, growth), multiple-answer counts, signed changes (+3%), a table with a total row (left to "A total that is not the sum of its items"), and a single item are not added.'
|
|
59
|
+
ja: 割合の語の無い百分率(利用率、伸び率)、複数回答の集計、符号の付いた増減(+3%)、合計の行がある表(合計が内訳の和と合わない の対象)、項目が一つだけのものは足しません。一つの文の内訳は、和が 100% から 10 ポイントより離れていれば言いません(大きい項目だけを挙げた文や、別の全体・前の年の割合を並べた文)。
|
|
60
|
+
en: 'Percentages not named as shares (rates, growth), multiple-answer counts, signed changes (+3%), a table with a total row (left to "A total that is not the sum of its items"), and a single item are not added. A breakdown in one sentence whose sum is more than 10 points from 100% is not reported: it names only the larger parts, or shares of different wholes or of two periods.'
|
|
@@ -10,8 +10,8 @@ name:
|
|
|
10
10
|
en: A relative date that does not match its base date
|
|
11
11
|
|
|
12
12
|
why:
|
|
13
|
-
ja: 「10月2日に申し込み、3日後の10月6日に通知」のように、相対の期間と日付を一緒に書くと、読み手はどちらが正しいのか分かりません。日程をずらしたときに、日付か期間の片方だけを直すと起きます。「3日後の」「1か月前の」のすぐ後ろの日付を、同じ文で前にある日付(文が「2
|
|
14
|
-
en: 'When a relative length and a date are written together ("Applications open on May 1, 2026. Two weeks later, on May 16, 2026, …"), the reader cannot tell which is right. It happens when a schedule moves and only the date or only the length is changed. The date right after "N days later" or "N weeks earlier" is compared with the nearest earlier date in the same sentence (or in the previous sentence when the sentence opens with the relative length), plus or minus the length.'
|
|
13
|
+
ja: 「10月2日に申し込み、3日後の10月6日に通知」のように、相対の期間と日付を一緒に書くと、読み手はどちらが正しいのか分かりません。日程をずらしたときに、日付か期間の片方だけを直すと起きます。「3日後の」「1か月前の」のすぐ後ろの日付を、同じ文で前にある日付(文が「2週間後、」で始まるなら前の文の日付)に期間を足した(前なら引いた)日と比べます。メールや議事録の「来週月曜(10月12日)」「明日(10月7日)」も、メールや議事録の頭の日付の欄(「日付:」「日時:」)の日付から数えた日と比べます。週は月曜からとも日曜からとも、「来週月曜」はすぐ次の月曜とも読み、どの読み方にも合わない日付だけを指します。
|
|
14
|
+
en: 'When a relative length and a date are written together ("Applications open on May 1, 2026. Two weeks later, on May 16, 2026, …"), the reader cannot tell which is right. It happens when a schedule moves and only the date or only the length is changed. The date right after "N days later" or "N weeks earlier" is compared with the nearest earlier date in the same sentence (or in the previous sentence when the sentence opens with the relative length), plus or minus the length. A day word in an email or minutes ("next Monday (October 12)", "tomorrow (October 7)") is compared with the day it counts to from the date field at the head of the email or the minutes ("Date:"). A week may start on Monday or on Sunday, and "next Monday" may be the coming Monday; only a date that fits none of these readings is reported.'
|
|
15
15
|
|
|
16
16
|
message:
|
|
17
17
|
ja: "{base}の{relative}なら{expected}のはずですが、{target}と書かれています"
|
|
@@ -42,8 +42,8 @@ example:
|
|
|
42
42
|
Applications close on May 1, 2026. Two weeks later, on May 15, 2026, results are sent.
|
|
43
43
|
|
|
44
44
|
not_flagged:
|
|
45
|
-
ja: 期間のすぐ後ろ(「の」や読点だけを挟む)に日付があるときだけ見ます。基準の日付が同じ文(文の頭の「2週間後、」なら前の文)に無いもの、「3
|
|
46
|
-
en: Only a date right after the relative length (with "on" or punctuation between) is checked. A relative length with no date before it in the same sentence (or the previous one, when the sentence opens with it), and units other than days, weeks, months and years ("3 business days later"), are left alone.
|
|
45
|
+
ja: 期間のすぐ後ろ(「の」や読点だけを挟む)に日付があるときだけ見ます。基準の日付が同じ文(文の頭の「2週間後、」なら前の文)に無いもの、「3営業日後」のように単位が日・週・月・年でないものは見ません。「来週月曜」「明日」は、頭に日付の欄が無い文書、すぐ後ろに日付の無い語、引用した返信の中の語、日付を名指す見出しの節(「## 10月1日の会」)の語は見ません。
|
|
46
|
+
en: Only a date right after the relative length (with "on" or punctuation between) is checked. A relative length with no date before it in the same sentence (or the previous one, when the sentence opens with it), and units other than days, weeks, months and years ("3 business days later"), are left alone. A day word ("next Monday", "tomorrow") is left alone when the document has no date field at its head, when no date follows the word, in a quoted reply, and in a section whose heading names a date ("## October 1 meeting").
|
|
47
47
|
rewrite:
|
|
48
48
|
depth: light
|
|
49
49
|
ja:
|
|
@@ -49,6 +49,7 @@ use_for: [business, technical, legal, docs, academic, literature, speech]
|
|
|
49
49
|
off_for:
|
|
50
50
|
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."
|
|
51
51
|
speech/transcript: "A transcript records what was said: its sentences, repeats and register are the speaker's, not the writer's to fix."
|
|
52
|
+
academic: measured by yarn rules:measure
|
|
52
53
|
sources: [plain-2011, jis-z8301-2019]
|
|
53
54
|
|
|
54
55
|
group: readability
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
id: version-mismatch
|
|
2
|
+
layer: L1
|
|
3
|
+
status: stable
|
|
4
|
+
how_to_find: version-mismatch
|
|
5
|
+
requires: [markdown]
|
|
6
|
+
severity: warning
|
|
7
|
+
|
|
8
|
+
name:
|
|
9
|
+
ja: 説明の版とコードの版が違う
|
|
10
|
+
en: The version in the text is not the one in the code
|
|
11
|
+
|
|
12
|
+
why:
|
|
13
|
+
ja: 「2.3.0 を入れます」のすぐ下のコマンドが「tidyq@2.4.0」だと、読み手はどちらを入れればよいか分かりません。新しい版を出したときに、コード例か説明の片方だけを直すと起きます。コードブロックのすぐ上の段落に書いた版(2.3.0 のように点で三つに区切った番号)を、そのコードブロックにある版と比べます。コードに版がちょうど一つあり、段落の版がそれと違うとき指します。
|
|
14
|
+
en: 'When "Install version 2.3.0" sits right above a command that installs tidyq@2.4.0, the reader cannot tell which to install. It happens when a release updates the example or the text but not both. chaff compares a version written in the paragraph right above a code block (a number in three dotted parts, such as 2.3.0) with the version the block shows. It reports when the block shows exactly one version and the paragraph names another.'
|
|
15
|
+
|
|
16
|
+
message:
|
|
17
|
+
ja: 「{written}」と書いていますが、すぐ下のコードは {code} です
|
|
18
|
+
en: '"{written}" is written, but the code below has {code}'
|
|
19
|
+
|
|
20
|
+
how_to_fix:
|
|
21
|
+
ja: 説明とコードのどちらが今の版かを確かめて、もう一方を直してください。
|
|
22
|
+
en: Check which of the text and the code has the current version, and correct the other.
|
|
23
|
+
|
|
24
|
+
levels: { strict: error, normal: warning, relaxed: info }
|
|
25
|
+
|
|
26
|
+
use_for: [technical, docs, blog, legal, academic, literature, speech]
|
|
27
|
+
off_for:
|
|
28
|
+
legal: "Code blocks, shell prompts, command headings and numbered steps belong to technical writing."
|
|
29
|
+
academic: "Code blocks, shell prompts, command headings and numbered steps belong to technical writing."
|
|
30
|
+
literature: "Code blocks, shell prompts, command headings and numbered steps belong to technical writing."
|
|
31
|
+
speech: "Code blocks, shell prompts, command headings and numbered steps belong to technical writing."
|
|
32
|
+
|
|
33
|
+
group: facts
|
|
34
|
+
summary:
|
|
35
|
+
ja: コードブロックのすぐ上の説明が名指す版と、コードの版が違う所
|
|
36
|
+
en: A version named right above a code block that the block does not use
|
|
37
|
+
example:
|
|
38
|
+
ja:
|
|
39
|
+
config: { genre: technical/readme }
|
|
40
|
+
before: |
|
|
41
|
+
# インストール
|
|
42
|
+
|
|
43
|
+
npm から 2.3.0 を入れます。
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install --global tidyq@2.4.0
|
|
47
|
+
```
|
|
48
|
+
after: |
|
|
49
|
+
# インストール
|
|
50
|
+
|
|
51
|
+
npm から 2.4.0 を入れます。
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install --global tidyq@2.4.0
|
|
55
|
+
```
|
|
56
|
+
en:
|
|
57
|
+
config: { genre: technical/readme }
|
|
58
|
+
before: |
|
|
59
|
+
# Installation
|
|
60
|
+
|
|
61
|
+
Install version 2.3.0 from npm:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install --global tidyq@2.4.0
|
|
65
|
+
```
|
|
66
|
+
after: |
|
|
67
|
+
# Installation
|
|
68
|
+
|
|
69
|
+
Install version 2.4.0 from npm:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm install --global tidyq@2.4.0
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
not_flagged:
|
|
76
|
+
ja: 版の無いコードブロック、版が二つ以上あるコードブロック(「2.3.0 から 2.4.0 へ」の更新)、範囲の端や起点として書いた版(「2.3.0 から」「20.0.0 以上」)、別のプログラムの名前のすぐ後ろの版(「Node.js 18.17.0 が必要」)、見出しや箇条書きや表の版、版だけの行、二つに区切った番号(22.04、3.1)と先頭が四桁の番号(2024.01.15)は比べません。
|
|
77
|
+
en: 'A code block with no version or with two or more (an upgrade from one to the other), a version written as a bound or a starting point ("from 2.3.0", "20.0.0 or later"), a version right after the name of another program ("Requires Node.js 18.17.0"), a version in a heading, a list item, a table or a line of its own, and numbers in two parts (22.04, 3.1) or with a four-digit first part (2024.01.15) are not compared.'
|
|
@@ -4,47 +4,47 @@ import type { HumanShares } from "./score.ts";
|
|
|
4
4
|
/** On how many human documents of each genre group each AI-shape rule reported, at the genre's level. */
|
|
5
5
|
export const HUMAN_SHARES: HumanShares = {
|
|
6
6
|
"ai-tell": {
|
|
7
|
-
academic: { documents:
|
|
7
|
+
academic: { documents: 10, fired: 0 },
|
|
8
8
|
blog: { documents: 23, fired: 0 },
|
|
9
|
-
business: { documents:
|
|
10
|
-
docs: { documents:
|
|
9
|
+
business: { documents: 80, fired: 0 },
|
|
10
|
+
docs: { documents: 21, fired: 0 },
|
|
11
11
|
legal: { documents: 35, fired: 0 },
|
|
12
12
|
literature: { documents: 13, fired: 0 },
|
|
13
13
|
speech: { documents: 7, fired: 0 },
|
|
14
14
|
technical: { documents: 26, fired: 0 },
|
|
15
15
|
},
|
|
16
16
|
"announcing-opener": {
|
|
17
|
-
academic: { documents:
|
|
17
|
+
academic: { documents: 10, fired: 0 },
|
|
18
18
|
blog: { documents: 23, fired: 0 },
|
|
19
|
-
business: { documents:
|
|
20
|
-
docs: { documents:
|
|
19
|
+
business: { documents: 80, fired: 0 },
|
|
20
|
+
docs: { documents: 21, fired: 0 },
|
|
21
21
|
legal: { documents: 35, fired: 0 },
|
|
22
22
|
literature: { documents: 13, fired: 0 },
|
|
23
23
|
speech: { documents: 7, fired: 0 },
|
|
24
24
|
technical: { documents: 26, fired: 0 },
|
|
25
25
|
},
|
|
26
26
|
"assistant-residue": {
|
|
27
|
-
academic: { documents:
|
|
27
|
+
academic: { documents: 10, fired: 0 },
|
|
28
28
|
blog: { documents: 23, fired: 0 },
|
|
29
|
-
business: { documents:
|
|
30
|
-
docs: { documents:
|
|
29
|
+
business: { documents: 80, fired: 0 },
|
|
30
|
+
docs: { documents: 21, fired: 0 },
|
|
31
31
|
legal: { documents: 35, fired: 0 },
|
|
32
32
|
literature: { documents: 13, fired: 0 },
|
|
33
33
|
speech: { documents: 7, fired: 0 },
|
|
34
|
-
technical: { documents:
|
|
34
|
+
technical: { documents: 49, fired: 0 },
|
|
35
35
|
},
|
|
36
36
|
"bold-density": {
|
|
37
|
-
academic: { documents:
|
|
37
|
+
academic: { documents: 10, fired: 0 },
|
|
38
38
|
blog: { documents: 23, fired: 0 },
|
|
39
|
-
business: { documents:
|
|
40
|
-
docs: { documents:
|
|
39
|
+
business: { documents: 80, fired: 1 },
|
|
40
|
+
docs: { documents: 21, fired: 0 },
|
|
41
41
|
legal: { documents: 35, fired: 0 },
|
|
42
42
|
literature: { documents: 13, fired: 0 },
|
|
43
43
|
speech: { documents: 7, fired: 0 },
|
|
44
|
-
technical: { documents:
|
|
44
|
+
technical: { documents: 49, fired: 2 },
|
|
45
45
|
},
|
|
46
46
|
"bold-label-list": {
|
|
47
|
-
academic: { documents:
|
|
47
|
+
academic: { documents: 5, fired: 0 },
|
|
48
48
|
blog: { documents: 14, fired: 0 },
|
|
49
49
|
business: { documents: 39, fired: 0 },
|
|
50
50
|
docs: { documents: 10, fired: 0 },
|
|
@@ -54,25 +54,25 @@ export const HUMAN_SHARES: HumanShares = {
|
|
|
54
54
|
technical: { documents: 13, fired: 1 },
|
|
55
55
|
},
|
|
56
56
|
"chat-citation-residue": {
|
|
57
|
-
academic: { documents:
|
|
57
|
+
academic: { documents: 10, fired: 0 },
|
|
58
58
|
blog: { documents: 23, fired: 0 },
|
|
59
|
-
business: { documents:
|
|
60
|
-
docs: { documents:
|
|
59
|
+
business: { documents: 80, fired: 0 },
|
|
60
|
+
docs: { documents: 21, fired: 0 },
|
|
61
61
|
legal: { documents: 35, fired: 0 },
|
|
62
62
|
literature: { documents: 13, fired: 0 },
|
|
63
63
|
speech: { documents: 7, fired: 0 },
|
|
64
|
-
technical: { documents:
|
|
64
|
+
technical: { documents: 49, fired: 0 },
|
|
65
65
|
},
|
|
66
66
|
"closing-cliche": {
|
|
67
|
-
academic: { documents:
|
|
67
|
+
academic: { documents: 10, fired: 0 },
|
|
68
68
|
blog: { documents: 23, fired: 0 },
|
|
69
|
-
docs: { documents:
|
|
69
|
+
docs: { documents: 21, fired: 0 },
|
|
70
70
|
legal: { documents: 35, fired: 1 },
|
|
71
71
|
literature: { documents: 13, fired: 0 },
|
|
72
72
|
speech: { documents: 7, fired: 0 },
|
|
73
73
|
},
|
|
74
74
|
"colon-lead-in": {
|
|
75
|
-
academic: { documents:
|
|
75
|
+
academic: { documents: 5, fired: 0 },
|
|
76
76
|
blog: { documents: 14, fired: 0 },
|
|
77
77
|
business: { documents: 39, fired: 0 },
|
|
78
78
|
docs: { documents: 10, fired: 0 },
|
|
@@ -82,110 +82,110 @@ export const HUMAN_SHARES: HumanShares = {
|
|
|
82
82
|
technical: { documents: 13, fired: 1 },
|
|
83
83
|
},
|
|
84
84
|
"contrast-framing": {
|
|
85
|
-
academic: { documents:
|
|
85
|
+
academic: { documents: 10, fired: 0 },
|
|
86
86
|
blog: { documents: 23, fired: 0 },
|
|
87
|
-
business: { documents:
|
|
88
|
-
docs: { documents:
|
|
87
|
+
business: { documents: 80, fired: 0 },
|
|
88
|
+
docs: { documents: 21, fired: 0 },
|
|
89
89
|
legal: { documents: 35, fired: 0 },
|
|
90
90
|
literature: { documents: 13, fired: 0 },
|
|
91
91
|
speech: { documents: 7, fired: 0 },
|
|
92
92
|
technical: { documents: 26, fired: 0 },
|
|
93
93
|
},
|
|
94
94
|
"emoji-density": {
|
|
95
|
-
academic: { documents:
|
|
95
|
+
academic: { documents: 10, fired: 0 },
|
|
96
96
|
blog: { documents: 23, fired: 0 },
|
|
97
|
-
business: { documents:
|
|
98
|
-
docs: { documents:
|
|
97
|
+
business: { documents: 80, fired: 0 },
|
|
98
|
+
docs: { documents: 21, fired: 0 },
|
|
99
99
|
legal: { documents: 35, fired: 0 },
|
|
100
100
|
literature: { documents: 13, fired: 0 },
|
|
101
101
|
speech: { documents: 7, fired: 0 },
|
|
102
|
-
technical: { documents:
|
|
102
|
+
technical: { documents: 49, fired: 0 },
|
|
103
103
|
},
|
|
104
104
|
"emoji-heading": {
|
|
105
|
-
academic: { documents:
|
|
105
|
+
academic: { documents: 10, fired: 0 },
|
|
106
106
|
blog: { documents: 23, fired: 0 },
|
|
107
|
-
business: { documents:
|
|
108
|
-
docs: { documents:
|
|
107
|
+
business: { documents: 78, fired: 1 },
|
|
108
|
+
docs: { documents: 21, fired: 0 },
|
|
109
109
|
legal: { documents: 28, fired: 0 },
|
|
110
110
|
literature: { documents: 4, fired: 0 },
|
|
111
111
|
speech: { documents: 7, fired: 0 },
|
|
112
112
|
},
|
|
113
113
|
"figurative-density": {
|
|
114
|
-
academic: { documents:
|
|
114
|
+
academic: { documents: 10, fired: 0 },
|
|
115
115
|
blog: { documents: 23, fired: 0 },
|
|
116
|
-
business: { documents:
|
|
117
|
-
docs: { documents:
|
|
116
|
+
business: { documents: 80, fired: 0 },
|
|
117
|
+
docs: { documents: 21, fired: 0 },
|
|
118
118
|
legal: { documents: 35, fired: 0 },
|
|
119
119
|
literature: { documents: 13, fired: 0 },
|
|
120
120
|
speech: { documents: 7, fired: 0 },
|
|
121
|
-
technical: { documents:
|
|
121
|
+
technical: { documents: 49, fired: 0 },
|
|
122
122
|
},
|
|
123
123
|
"no-em-dash": {
|
|
124
|
-
academic: { documents:
|
|
124
|
+
academic: { documents: 10, fired: 0 },
|
|
125
125
|
blog: { documents: 23, fired: 0 },
|
|
126
|
-
business: { documents:
|
|
127
|
-
docs: { documents:
|
|
126
|
+
business: { documents: 80, fired: 2 },
|
|
127
|
+
docs: { documents: 21, fired: 0 },
|
|
128
128
|
legal: { documents: 35, fired: 2 },
|
|
129
129
|
literature: { documents: 13, fired: 2 },
|
|
130
130
|
speech: { documents: 7, fired: 0 },
|
|
131
|
-
technical: { documents:
|
|
131
|
+
technical: { documents: 49, fired: 0 },
|
|
132
132
|
},
|
|
133
133
|
"padded-intro": {
|
|
134
|
-
academic: { documents:
|
|
134
|
+
academic: { documents: 10, fired: 0 },
|
|
135
135
|
blog: { documents: 23, fired: 0 },
|
|
136
|
-
business: { documents:
|
|
137
|
-
docs: { documents:
|
|
136
|
+
business: { documents: 47, fired: 1 },
|
|
137
|
+
docs: { documents: 21, fired: 0 },
|
|
138
138
|
legal: { documents: 35, fired: 0 },
|
|
139
139
|
literature: { documents: 13, fired: 0 },
|
|
140
140
|
speech: { documents: 7, fired: 0 },
|
|
141
141
|
},
|
|
142
142
|
"paragraph-length-variance": {
|
|
143
|
-
academic: { documents:
|
|
143
|
+
academic: { documents: 10, fired: 0 },
|
|
144
144
|
blog: { documents: 23, fired: 0 },
|
|
145
|
-
docs: { documents:
|
|
145
|
+
docs: { documents: 21, fired: 0 },
|
|
146
146
|
legal: { documents: 35, fired: 0 },
|
|
147
147
|
literature: { documents: 13, fired: 1 },
|
|
148
148
|
speech: { documents: 7, fired: 0 },
|
|
149
149
|
},
|
|
150
150
|
"rule-of-three": {
|
|
151
|
-
academic: { documents:
|
|
151
|
+
academic: { documents: 10, fired: 0 },
|
|
152
152
|
blog: { documents: 23, fired: 0 },
|
|
153
|
-
docs: { documents:
|
|
153
|
+
docs: { documents: 21, fired: 0 },
|
|
154
154
|
legal: { documents: 35, fired: 0 },
|
|
155
155
|
literature: { documents: 13, fired: 0 },
|
|
156
156
|
speech: { documents: 7, fired: 0 },
|
|
157
157
|
technical: { documents: 26, fired: 0 },
|
|
158
158
|
},
|
|
159
159
|
"section-length-uniformity": {
|
|
160
|
-
academic: { documents:
|
|
160
|
+
academic: { documents: 10, fired: 0 },
|
|
161
161
|
blog: { documents: 23, fired: 0 },
|
|
162
|
-
docs: { documents:
|
|
162
|
+
docs: { documents: 21, fired: 0 },
|
|
163
163
|
legal: { documents: 35, fired: 0 },
|
|
164
164
|
literature: { documents: 13, fired: 1 },
|
|
165
165
|
speech: { documents: 7, fired: 0 },
|
|
166
166
|
technical: { documents: 26, fired: 0 },
|
|
167
167
|
},
|
|
168
168
|
"sentence-rhythm": {
|
|
169
|
-
academic: { documents:
|
|
169
|
+
academic: { documents: 10, fired: 0 },
|
|
170
170
|
blog: { documents: 23, fired: 0 },
|
|
171
|
-
docs: { documents:
|
|
171
|
+
docs: { documents: 21, fired: 0 },
|
|
172
172
|
legal: { documents: 35, fired: 0 },
|
|
173
173
|
literature: { documents: 13, fired: 1 },
|
|
174
174
|
speech: { documents: 7, fired: 0 },
|
|
175
175
|
technical: { documents: 26, fired: 1 },
|
|
176
176
|
},
|
|
177
177
|
"stock-transition": {
|
|
178
|
-
academic: { documents:
|
|
178
|
+
academic: { documents: 10, fired: 0 },
|
|
179
179
|
blog: { documents: 23, fired: 0 },
|
|
180
|
-
business: { documents:
|
|
181
|
-
docs: { documents:
|
|
180
|
+
business: { documents: 80, fired: 0 },
|
|
181
|
+
docs: { documents: 21, fired: 0 },
|
|
182
182
|
legal: { documents: 35, fired: 0 },
|
|
183
183
|
literature: { documents: 13, fired: 0 },
|
|
184
184
|
speech: { documents: 7, fired: 0 },
|
|
185
185
|
technical: { documents: 26, fired: 0 },
|
|
186
186
|
},
|
|
187
187
|
"translationese-density": {
|
|
188
|
-
academic: { documents:
|
|
188
|
+
academic: { documents: 5, fired: 0 },
|
|
189
189
|
blog: { documents: 14, fired: 0 },
|
|
190
190
|
business: { documents: 39, fired: 0 },
|
|
191
191
|
docs: { documents: 10, fired: 0 },
|
|
@@ -195,13 +195,13 @@ export const HUMAN_SHARES: HumanShares = {
|
|
|
195
195
|
technical: { documents: 22, fired: 0 },
|
|
196
196
|
},
|
|
197
197
|
"unfilled-placeholder": {
|
|
198
|
-
academic: { documents:
|
|
198
|
+
academic: { documents: 10, fired: 0 },
|
|
199
199
|
blog: { documents: 23, fired: 0 },
|
|
200
|
-
business: { documents:
|
|
201
|
-
docs: { documents:
|
|
200
|
+
business: { documents: 80, fired: 1 },
|
|
201
|
+
docs: { documents: 21, fired: 0 },
|
|
202
202
|
legal: { documents: 35, fired: 0 },
|
|
203
203
|
literature: { documents: 13, fired: 0 },
|
|
204
204
|
speech: { documents: 7, fired: 0 },
|
|
205
|
-
technical: { documents:
|
|
205
|
+
technical: { documents: 49, fired: 0 },
|
|
206
206
|
},
|
|
207
207
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Words that start with two capitals or more and go on in small letters, where the capitals are not a spelling of the
|
|
2
|
+
// plain word: a name in capitals with an English ending (SENDs, OPENed), or the letters of an acronym picked out in its
|
|
3
|
+
// expansion (CLImatology and PERsistence for CLIPER: part of a longer acronym). A name written that way (HBase, RSpec,
|
|
4
|
+
// SCIMple beside SCIM) is a spelling, and stays one.
|
|
5
|
+
|
|
6
|
+
const CAPITALS_THEN_SMALL = /^(\p{Lu}{2,})\p{Ll}+$/u;
|
|
7
|
+
const CAPITALS_WITH_ENDING = /^\p{Lu}{2,}(?:s|es|d|ed|ing)$/u;
|
|
8
|
+
const CAPITALS_ONLY = /^\p{Lu}[\p{Lu}\d]+$/u;
|
|
9
|
+
const LATIN_RUN = /[\p{L}\d]+/gu;
|
|
10
|
+
|
|
11
|
+
/** The words of a stretch of text written in capitals only (CLIPER, CAMEX), which a picked-out expansion spells. */
|
|
12
|
+
export const acronymsIn = (text: string): string[] => [...text.matchAll(LATIN_RUN)].map((match) => match[0]).filter((word) => CAPITALS_ONLY.test(word));
|
|
13
|
+
|
|
14
|
+
const isPickedOut = (word: string, acronyms: readonly string[]): boolean => {
|
|
15
|
+
const capitals = CAPITALS_THEN_SMALL.exec(word)?.[1];
|
|
16
|
+
return capitals !== undefined && acronyms.some((acronym) => acronym.length > capitals.length && acronym.includes(capitals));
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Whether a word is capitals with an ending, or the letters of an acronym among acronyms. A hyphenated word is compared
|
|
21
|
+
* as written: leaving it out for one part would hide a variant in another (GitHub-SENDs, Github-SENDs).
|
|
22
|
+
*/
|
|
23
|
+
export const isCapitalsNotSpelling = (word: string, acronyms: readonly string[]): boolean => CAPITALS_WITH_ENDING.test(word) || isPickedOut(word, acronyms);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// A date named by a weekday or a day relative to the document's date (「来週月曜(10月12日)」"next Monday (October 12)",
|
|
2
|
+
// 「明日(10月7日)」) that the absolute date beside it contradicts. Pure; the detector finds the words and the dates.
|
|
3
|
+
import type { Span } from "../plugin.ts";
|
|
4
|
+
|
|
5
|
+
/** 「来週月曜」"next Monday": weeks from the base's week (来週 is 1), and the weekday (Sunday is 0). */
|
|
6
|
+
export type WeekdayRelative = Span & { readonly kind: "weekday"; readonly weeks: number; readonly weekday: number };
|
|
7
|
+
/** 「明日」"tomorrow": days from the base. */
|
|
8
|
+
export type DayRelative = Span & { readonly kind: "day"; readonly days: number };
|
|
9
|
+
export type DayWord = WeekdayRelative | DayRelative;
|
|
10
|
+
|
|
11
|
+
const DAY_MS = 86_400_000;
|
|
12
|
+
const WEEK = 7;
|
|
13
|
+
const MONDAY = 1;
|
|
14
|
+
const FULL = /^(\d{4})-(\d{2})-(\d{2})$/u;
|
|
15
|
+
const MONTH_DAY = /^(\d{2})-(\d{2})$/u;
|
|
16
|
+
|
|
17
|
+
const timeOf = (value: string): number | undefined => {
|
|
18
|
+
const found = FULL.exec(value);
|
|
19
|
+
if (found === null) return undefined;
|
|
20
|
+
const [year, month, day] = [Number(found[1]), Number(found[2]), Number(found[3])];
|
|
21
|
+
const time = Date.UTC(year, month - 1, day);
|
|
22
|
+
const date = new Date(time);
|
|
23
|
+
return date.getUTCMonth() === month - 1 && date.getUTCDate() === day ? time : undefined;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const valueOf = (time: number): string => new Date(time).toISOString().slice(0, "2026-10-06".length);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The days a weekday word can mean. Weeks are counted from Monday or from Sunday, and "next Monday" can also mean the
|
|
30
|
+
* coming Monday: every reading is allowed, so only a date that fits none of them is wrong.
|
|
31
|
+
*/
|
|
32
|
+
const weekdayCandidates = (base: number, relative: WeekdayRelative): number[] => {
|
|
33
|
+
const baseDay = new Date(base).getUTCDay();
|
|
34
|
+
const fromMonday = base - ((baseDay - MONDAY + WEEK) % WEEK) * DAY_MS;
|
|
35
|
+
const fromSunday = base - baseDay * DAY_MS;
|
|
36
|
+
const inWeek = (weekStart: number, firstDay: number): number => weekStart + (relative.weeks * WEEK + ((relative.weekday - firstDay + WEEK) % WEEK)) * DAY_MS;
|
|
37
|
+
// "next Monday" and "this Monday" can mean the coming one; "last Monday" the one just past.
|
|
38
|
+
const coming = base + (((relative.weekday - baseDay + WEEK - 1) % WEEK) + 1) * DAY_MS;
|
|
39
|
+
const past = base - (((baseDay - relative.weekday + WEEK - 1) % WEEK) + 1) * DAY_MS;
|
|
40
|
+
const nearest = new Map([
|
|
41
|
+
[0, [coming]],
|
|
42
|
+
[1, [coming]],
|
|
43
|
+
[-1, [past]],
|
|
44
|
+
]).get(relative.weeks);
|
|
45
|
+
return [inWeek(fromMonday, MONDAY), inWeek(fromSunday, 0), ...(nearest ?? [])];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const candidatesOf = (base: number, relative: DayWord): number[] =>
|
|
49
|
+
relative.kind === "day" ? [base + relative.days * DAY_MS] : weekdayCandidates(base, relative);
|
|
50
|
+
|
|
51
|
+
/** Whether the written date (2026-10-12, or 10-12 without its year) is one of the days the word can mean. */
|
|
52
|
+
const fits = (written: string, candidates: readonly number[]): boolean => {
|
|
53
|
+
const monthDay = MONTH_DAY.exec(written);
|
|
54
|
+
return candidates.some((time) => (monthDay === null ? valueOf(time) === written : valueOf(time).slice("2026-".length) === written));
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** The day the word means, as the message shows it: the first reading, written as the date was (with or without the year). */
|
|
58
|
+
export const relativeDayMismatch = (base: string, relative: DayWord, written: string): string | undefined => {
|
|
59
|
+
const baseTime = timeOf(base);
|
|
60
|
+
if (baseTime === undefined || (!FULL.test(written) && !MONTH_DAY.test(written))) return undefined;
|
|
61
|
+
const candidates = candidatesOf(baseTime, relative);
|
|
62
|
+
const [first] = candidates;
|
|
63
|
+
if (first === undefined || fits(written, candidates)) return undefined;
|
|
64
|
+
return MONTH_DAY.test(written) ? valueOf(first).slice("2026-".length) : valueOf(first);
|
|
65
|
+
};
|