@kensio/pinyinjs 0.1.0-beta.0 → 1.0.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 (145) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/NOTICE +7 -0
  3. package/README.md +144 -19
  4. package/data/full.entries +13400 -13400
  5. package/data/manifest.json +4 -4
  6. package/data/standard.entries +2492 -2492
  7. package/dist/cli/arguments.d.ts +67 -0
  8. package/dist/cli/arguments.d.ts.map +1 -1
  9. package/dist/cli/arguments.js +79 -0
  10. package/dist/cli/arguments.js.map +1 -1
  11. package/dist/cli/colour.d.ts +77 -0
  12. package/dist/cli/colour.d.ts.map +1 -0
  13. package/dist/cli/colour.js +117 -0
  14. package/dist/cli/colour.js.map +1 -0
  15. package/dist/cli/commands.d.ts +49 -1
  16. package/dist/cli/commands.d.ts.map +1 -1
  17. package/dist/cli/commands.js +404 -42
  18. package/dist/cli/commands.js.map +1 -1
  19. package/dist/cli/environment.d.ts.map +1 -1
  20. package/dist/cli/environment.js +20 -0
  21. package/dist/cli/environment.js.map +1 -1
  22. package/dist/cli/help.d.ts.map +1 -1
  23. package/dist/cli/help.js +43 -16
  24. package/dist/cli/help.js.map +1 -1
  25. package/dist/cli/run.d.ts +11 -0
  26. package/dist/cli/run.d.ts.map +1 -1
  27. package/dist/cli/run.js +4 -2
  28. package/dist/cli/run.js.map +1 -1
  29. package/dist/decode/convert.d.ts +20 -0
  30. package/dist/decode/convert.d.ts.map +1 -1
  31. package/dist/decode/convert.js +170 -12
  32. package/dist/decode/convert.js.map +1 -1
  33. package/dist/decode/decode.d.ts +3 -2
  34. package/dist/decode/decode.d.ts.map +1 -1
  35. package/dist/decode/decode.js +17 -4
  36. package/dist/decode/decode.js.map +1 -1
  37. package/dist/decode/reading-rules.d.ts +56 -0
  38. package/dist/decode/reading-rules.d.ts.map +1 -0
  39. package/dist/decode/reading-rules.js +107 -0
  40. package/dist/decode/reading-rules.js.map +1 -0
  41. package/dist/decode/rules.d.ts +66 -0
  42. package/dist/decode/rules.d.ts.map +1 -0
  43. package/dist/decode/rules.js +138 -0
  44. package/dist/decode/rules.js.map +1 -0
  45. package/dist/decode/sandhi.d.ts +1 -1
  46. package/dist/decode/sandhi.d.ts.map +1 -1
  47. package/dist/decode/sandhi.js +60 -1
  48. package/dist/decode/sandhi.js.map +1 -1
  49. package/dist/decode/word.d.ts +18 -0
  50. package/dist/decode/word.d.ts.map +1 -1
  51. package/dist/dictionary/artifact.d.ts.map +1 -1
  52. package/dist/dictionary/artifact.js +28 -1
  53. package/dist/dictionary/artifact.js.map +1 -1
  54. package/dist/dictionary/dictionary.d.ts +9 -0
  55. package/dist/dictionary/dictionary.d.ts.map +1 -1
  56. package/dist/dictionary/dictionary.js +7 -0
  57. package/dist/dictionary/dictionary.js.map +1 -1
  58. package/dist/dictionary/entry.d.ts +12 -0
  59. package/dist/dictionary/entry.d.ts.map +1 -1
  60. package/dist/dictionary/entry.js.map +1 -1
  61. package/dist/dictionary/merge.d.ts +3 -1
  62. package/dist/dictionary/merge.d.ts.map +1 -1
  63. package/dist/dictionary/merge.js +19 -1
  64. package/dist/dictionary/merge.js.map +1 -1
  65. package/dist/format/transcription.d.ts +52 -0
  66. package/dist/format/transcription.d.ts.map +1 -0
  67. package/dist/format/transcription.js +88 -0
  68. package/dist/format/transcription.js.map +1 -0
  69. package/dist/index.d.ts +24 -3
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +15 -1
  72. package/dist/index.js.map +1 -1
  73. package/dist/numerals/cardinal.d.ts +39 -0
  74. package/dist/numerals/cardinal.d.ts.map +1 -0
  75. package/dist/numerals/cardinal.js +111 -0
  76. package/dist/numerals/cardinal.js.map +1 -0
  77. package/dist/numerals/characters.d.ts +31 -0
  78. package/dist/numerals/characters.d.ts.map +1 -0
  79. package/dist/numerals/characters.js +80 -0
  80. package/dist/numerals/characters.js.map +1 -0
  81. package/dist/numerals/numerals.d.ts +76 -0
  82. package/dist/numerals/numerals.d.ts.map +1 -0
  83. package/dist/numerals/numerals.js +119 -0
  84. package/dist/numerals/numerals.js.map +1 -0
  85. package/dist/numerals/text.d.ts +52 -0
  86. package/dist/numerals/text.d.ts.map +1 -0
  87. package/dist/numerals/text.js +248 -0
  88. package/dist/numerals/text.js.map +1 -0
  89. package/dist/orthography/grouping.d.ts +56 -11
  90. package/dist/orthography/grouping.d.ts.map +1 -1
  91. package/dist/orthography/grouping.js +109 -35
  92. package/dist/orthography/grouping.js.map +1 -1
  93. package/dist/orthography/idiom-list.d.ts +56 -0
  94. package/dist/orthography/idiom-list.d.ts.map +1 -0
  95. package/dist/orthography/idiom-list.js +296 -0
  96. package/dist/orthography/idiom-list.js.map +1 -0
  97. package/dist/orthography/idioms.d.ts +18 -0
  98. package/dist/orthography/idioms.d.ts.map +1 -0
  99. package/dist/orthography/idioms.js +22 -0
  100. package/dist/orthography/idioms.js.map +1 -0
  101. package/dist/orthography/reduplication.d.ts +52 -0
  102. package/dist/orthography/reduplication.d.ts.map +1 -0
  103. package/dist/orthography/reduplication.js +99 -0
  104. package/dist/orthography/reduplication.js.map +1 -0
  105. package/dist/orthography/rule.d.ts +50 -0
  106. package/dist/orthography/rule.d.ts.map +1 -0
  107. package/dist/orthography/rule.js +101 -0
  108. package/dist/orthography/rule.js.map +1 -0
  109. package/dist/sources/cedict.d.ts +23 -0
  110. package/dist/sources/cedict.d.ts.map +1 -1
  111. package/dist/sources/cedict.js +28 -0
  112. package/dist/sources/cedict.js.map +1 -1
  113. package/dist/syllable/syllable.d.ts +1 -1
  114. package/dist/syllable/syllable.d.ts.map +1 -1
  115. package/dist/syllable/syllable.js +2 -37
  116. package/dist/syllable/syllable.js.map +1 -1
  117. package/dist/tone/tone-mark.d.ts +23 -0
  118. package/dist/tone/tone-mark.d.ts.map +1 -1
  119. package/dist/tone/tone-mark.js +49 -0
  120. package/dist/tone/tone-mark.js.map +1 -1
  121. package/dist/transcription/bopomofo.d.ts +46 -0
  122. package/dist/transcription/bopomofo.d.ts.map +1 -0
  123. package/dist/transcription/bopomofo.js +371 -0
  124. package/dist/transcription/bopomofo.js.map +1 -0
  125. package/dist/transcription/gwoyeu.d.ts +32 -0
  126. package/dist/transcription/gwoyeu.d.ts.map +1 -0
  127. package/dist/transcription/gwoyeu.js +491 -0
  128. package/dist/transcription/gwoyeu.js.map +1 -0
  129. package/dist/transcription/ipa.d.ts +40 -0
  130. package/dist/transcription/ipa.d.ts.map +1 -0
  131. package/dist/transcription/ipa.js +221 -0
  132. package/dist/transcription/ipa.js.map +1 -0
  133. package/dist/transcription/spelling-index.d.ts +23 -0
  134. package/dist/transcription/spelling-index.d.ts.map +1 -0
  135. package/dist/transcription/spelling-index.js +61 -0
  136. package/dist/transcription/spelling-index.js.map +1 -0
  137. package/dist/transcription/wade-giles.d.ts +108 -0
  138. package/dist/transcription/wade-giles.d.ts.map +1 -0
  139. package/dist/transcription/wade-giles.js +656 -0
  140. package/dist/transcription/wade-giles.js.map +1 -0
  141. package/dist/transcription/yale.d.ts +43 -0
  142. package/dist/transcription/yale.d.ts.map +1 -0
  143. package/dist/transcription/yale.js +242 -0
  144. package/dist/transcription/yale.js.map +1 -0
  145. package/package.json +9 -5
@@ -0,0 +1,248 @@
1
+ import { applySandhi } from "../decode/sandhi.js";
2
+ import { toCharacters } from "../script/characters.js";
3
+ import { numeralHanzi, percentHanzi, readNumeralHanzi, } from "./numerals.js";
4
+ /**
5
+ * A number as it appears in running text: digits, with the separators a writer
6
+ * uses and an optional decimal part.
7
+ */
8
+ const NUMBER = /\d[\d,]*(?:\.\d+)?/gu;
9
+ /**
10
+ * The percent signs a number can carry, half-width and full-width.
11
+ */
12
+ const PERCENT = new Set(["%", "%"]);
13
+ /**
14
+ * Marks that make the digits beside them an identifier rather than a quantity.
15
+ *
16
+ * 6:30 is a time, 3202-5625 is a phone number and COVID-19 is a name; none of
17
+ * them is counted, and the mark between the parts has no reading at all. So a
18
+ * digit run touching one is left exactly as it was written — of 7,846 digit
19
+ * runs measured over Tatoeba and zh.wikipedia about 55 are this shape, and
20
+ * reading them produced `liù:sānshí` for 6:30, which is worse than leaving it.
21
+ */
22
+ const IDENTIFIER = new Set([":", ":", "-", "-", "—", "–", "/", "/"]);
23
+ /**
24
+ * A time as a writer punctuates it: 6:30, 07:05, 2:30.
25
+ *
26
+ * Sticky, because it is tried at the start of a digit run the number scanner
27
+ * has already found rather than searched for on its own.
28
+ *
29
+ * Two digits after the colon and no more, which is what separates a time from
30
+ * a ratio: 16:9 and 2:1 are proportions and scores and are read `shíliù bǐ
31
+ * jiǔ`, which this does not attempt. Measured over Tatoeba and zh.wikipedia,
32
+ * the shape catches 104 runs and every one of them is a time — none has an
33
+ * hour above 23 or a minute above 59, and the four colon runs that are not
34
+ * times all have a single digit after the colon.
35
+ */
36
+ const TIME = /(\d{1,2})[::](\d{2})(?!\d)/duy;
37
+ /**
38
+ * The bounds a time is written within.
39
+ */
40
+ const HOURS_IN_A_DAY = 24;
41
+ const MINUTES_IN_AN_HOUR = 60;
42
+ /**
43
+ * The minute below which the zero is said: 6:05 is 六点零五分.
44
+ */
45
+ const SPOKEN_ZERO_BELOW = 10;
46
+ /**
47
+ * The hour that is 两 rather than 二.
48
+ *
49
+ * Two o'clock is 两点 and never 二点, which is the same 两 that counts things —
50
+ * an hour is a quantity of hours. It is the only hour this applies to, since
51
+ * 12:00 is 十二点 with the 二 inside a larger number.
52
+ */
53
+ const LIANG_HOUR = 2;
54
+ /**
55
+ * How many digits a year is written with, when it is a year and not a count.
56
+ *
57
+ * 1997年 is a year and is spelled out; 30年 is thirty years and is counted.
58
+ * Four is the length that separates them, and a two-digit year — 98年 for 1998
59
+ * — is left counted, because 20年 meaning twenty years is far commoner than
60
+ * 20年 meaning 2020 and nothing in the text tells them apart.
61
+ */
62
+ const YEAR_DIGITS = 4;
63
+ /**
64
+ * Whether a number is read out digit by digit rather than counted.
65
+ *
66
+ * The one context where this is not a guess: a four-digit number in front of 年
67
+ * is a year, and a year is spelled out. Everything else is counted, which is
68
+ * what the overwhelming majority of digits in running text are — of 7,846 digit
69
+ * runs measured over Tatoeba and zh.wikipedia, the commonest things after them
70
+ * are 年, 月, 日, 人, 号, 点, 个 and 岁, and only the years want spelling out.
71
+ */
72
+ function styleFor(digits, following) {
73
+ return following === "年" && digits.length === YEAR_DIGITS
74
+ ? "digits"
75
+ : "cardinal";
76
+ }
77
+ /**
78
+ * Read one number, with the sign that may follow it.
79
+ */
80
+ function readNumber(digits, isPercent, style, options) {
81
+ // A writer's thousands separators are not part of the number; nothing is
82
+ // said for them.
83
+ const value = digits.replaceAll(",", "");
84
+ const settings = { ...options, style };
85
+ const hanzi = isPercent
86
+ ? percentHanzi(value, settings)
87
+ : numeralHanzi(value, settings);
88
+ const reading = hanzi === undefined ? undefined : readNumeralHanzi(hanzi, settings);
89
+ if (hanzi === undefined || reading === undefined) {
90
+ return undefined;
91
+ }
92
+ return { text: digits, reading, hanzi, style };
93
+ }
94
+ /**
95
+ * A stretch that stays exactly as it was written.
96
+ */
97
+ function unread(text) {
98
+ return { text, reading: undefined, hanzi: undefined, style: undefined };
99
+ }
100
+ /**
101
+ * A time written out in 汉字: 6:30 is 六点三十分.
102
+ *
103
+ * The 分 is written even though a speaker often drops it, because without it
104
+ * 六点三十 is the decimal 6.30 said aloud — the same 点 does both jobs, and the
105
+ * 分 is the only thing that separates them. On the hour it is left off
106
+ * instead, since 六点零零分 is not something anybody says.
107
+ */
108
+ function timeHanzi(hours, minutes, options) {
109
+ const counted = { ...options, style: "cardinal" };
110
+ const hour = hours === LIANG_HOUR ? "两" : numeralHanzi(hours, counted);
111
+ const minute = minutes === 0 ? "" : numeralHanzi(minutes, counted);
112
+ /* c8 ignore next 3 -- an hour is 0 to 23 and a minute 0 to 59, and the
113
+ cardinal reader counts every one of them */
114
+ if (hour === undefined || minute === undefined) {
115
+ return undefined;
116
+ }
117
+ if (minute === "") {
118
+ return `${hour}点`;
119
+ }
120
+ const zero = minutes < SPOKEN_ZERO_BELOW ? "零" : "";
121
+ return `${hour}点${zero}${minute}分`;
122
+ }
123
+ /**
124
+ * Where a written-out time breaks into words.
125
+ *
126
+ * The hour, the 点, the minutes and the 分, which is how the same time reads
127
+ * when it is written 6点30分 in the first place.
128
+ */
129
+ function timeWords(hanzi) {
130
+ const [hour = "", rest = ""] = hanzi.split("点");
131
+ const minutes = rest.replace("分", "");
132
+ return minutes === ""
133
+ ? [hour.length, 1]
134
+ : [hour.length, 1, minutes.length, 1];
135
+ }
136
+ /**
137
+ * Read a digit run as a time, where that is what it is.
138
+ *
139
+ * Tried before the identifier rule, which would otherwise leave the whole
140
+ * thing alone: a colon between digits is exactly the mark that makes 6:30 a
141
+ * time rather than two numbers.
142
+ */
143
+ function readTime(text, from, options) {
144
+ TIME.lastIndex = from;
145
+ const found = TIME.exec(text);
146
+ const written = found?.[1];
147
+ const minutes = found?.[2];
148
+ if (found === null || written === undefined || minutes === undefined) {
149
+ return undefined;
150
+ }
151
+ const hourValue = Number(written);
152
+ const minuteValue = Number(minutes);
153
+ if (hourValue >= HOURS_IN_A_DAY || minuteValue >= MINUTES_IN_AN_HOUR) {
154
+ return undefined;
155
+ }
156
+ const hanzi = timeHanzi(hourValue, minuteValue, options);
157
+ const reading = hanzi === undefined ? undefined : readNumeralHanzi(hanzi, options);
158
+ /* c8 ignore next 3 -- the hanzi is built out of the same table that reads it
159
+ back, so it reads whenever it was built at all */
160
+ if (hanzi === undefined || reading === undefined) {
161
+ return undefined;
162
+ }
163
+ return {
164
+ text: found[0],
165
+ reading,
166
+ hanzi,
167
+ style: "cardinal",
168
+ words: timeWords(hanzi),
169
+ };
170
+ }
171
+ /**
172
+ * Split a stretch of non-Han text into what is read and what is not.
173
+ *
174
+ * `following` is the first character of the Han that comes after this stretch,
175
+ * which is the only context a number has: 年 makes 1997 a year and 个 makes 3 a
176
+ * count. Where there is nothing after it, or nothing that decides, the number
177
+ * is counted.
178
+ *
179
+ * Text that is not a number is returned unread rather than dropped, so the
180
+ * segments always concatenate back to the input.
181
+ */
182
+ export function readNumbersIn(text, following, options = {}) {
183
+ const segments = [];
184
+ let at = 0;
185
+ for (const match of text.matchAll(NUMBER)) {
186
+ const digits = match[0];
187
+ const from = match.index;
188
+ // The minutes of a time the hour already took.
189
+ if (from < at) {
190
+ continue;
191
+ }
192
+ const time = readTime(text, from, options);
193
+ if (time !== undefined) {
194
+ if (from > at) {
195
+ segments.push(unread(text.slice(at, from)));
196
+ }
197
+ segments.push(time);
198
+ at = from + time.text.length;
199
+ continue;
200
+ }
201
+ const after = text[from + digits.length] ?? "";
202
+ if (IDENTIFIER.has(after) || IDENTIFIER.has(text[from - 1] ?? "")) {
203
+ continue;
204
+ }
205
+ const isPercent = PERCENT.has(after);
206
+ // What decides the style is whatever comes next: the sign if there is one,
207
+ // otherwise the first character of the following Han.
208
+ const style = styleFor(digits, isPercent ? "" : following);
209
+ const read = readNumber(digits, isPercent, style, options);
210
+ if (read === undefined) {
211
+ continue;
212
+ }
213
+ if (from > at) {
214
+ segments.push(unread(text.slice(at, from)));
215
+ }
216
+ segments.push(isPercent ? { ...read, text: digits + after } : read);
217
+ at = from + digits.length + (isPercent ? after.length : 0);
218
+ }
219
+ if (at < text.length) {
220
+ segments.push(unread(text.slice(at)));
221
+ }
222
+ return segments;
223
+ }
224
+ /**
225
+ * A number as it is said, with the sandhi a counted quantity takes.
226
+ *
227
+ * Two things decide how much of it assimilates. A number read out digit by
228
+ * digit does not at all — 110 is `yāo yāo líng` and never `yì yì líng` —
229
+ * and in a counted one the sandhi stops at the 点, since everything after the
230
+ * decimal point is read digit by digit whatever the style: 3.14 is
231
+ * `sān diǎn yī sì`.
232
+ *
233
+ * `following` is the syllable after the number, which is what a 一 at the end
234
+ * of it assimilates to: the 一 of 一个 is `yí` because 个 is a fourth tone, and
235
+ * that tone is in the next run rather than in the number.
236
+ */
237
+ export function saidNumeral(segment, following, sandhi) {
238
+ const reading = segment.reading ?? [];
239
+ if (segment.style === "digits") {
240
+ return reading;
241
+ }
242
+ const point = toCharacters(segment.hanzi ?? "").indexOf("点");
243
+ const quantity = point === -1 ? reading.length : point;
244
+ const context = following === undefined ? [] : [following];
245
+ const said = applySandhi([...reading.slice(0, quantity), ...context], sandhi);
246
+ return [...said.slice(0, quantity), ...reading.slice(quantity)];
247
+ }
248
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/numerals/text.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1,6 @@
1
1
  import type { Dictionary } from "../dictionary/dictionary.js";
2
2
  import type { DecodedWord } from "../decode/word.js";
3
- /**
4
- * One 分词连写 rule, rewriting the words a decode produced.
5
- *
6
- * Typed and individually testable, which is the point ALGORITHM.md makes about
7
- * rules: the old project patched the output string with regexes, so its rules
8
- * were order-dependent and could not be tested apart from each other.
9
- */
10
- export interface GroupingRule {
11
- readonly name: string;
12
- readonly apply: (words: readonly DecodedWord[], dictionary: Dictionary) => readonly DecodedWord[];
13
- }
3
+ import { type GroupingRule } from "./rule.js";
14
4
  /**
15
5
  * 了, 着 and 过 attach to the verb in front of them.
16
6
  *
@@ -54,8 +44,63 @@ export declare const PLACE_GENERICS: GroupingRule;
54
44
  * not about the middle of one.
55
45
  */
56
46
  export declare const SPACED_WORD_LIST: GroupingRule;
47
+ /**
48
+ * 老王 is `Lǎo Wáng`: the prefix takes a capital of its own.
49
+ *
50
+ * The words are already written apart — nothing here moves a boundary — and
51
+ * what was missing is only the capital, so this marks the prefix a proper noun
52
+ * and lets the writer do what it already does with one. That is why it is a
53
+ * grouping rule despite grouping nothing: `isProperNoun` is the flag the
54
+ * capital hangs off, and there is one place it is set.
55
+ *
56
+ * The surname is the evidence. A one-character word the dictionary marks a
57
+ * proper noun is what 老 and 小 attach to, and CC-CEDICT's capitalisation has
58
+ * already vetoed the tags that would otherwise let 小 in front of anything —
59
+ * see "jieba's 专名 tags need a second opinion" in ROADMAP.md.
60
+ */
61
+ export declare const ADDRESS_PREFIX: GroupingRule;
62
+ /**
63
+ * The parts of a proper name are written apart: 毛泽东 is `Máo Zédōng`.
64
+ *
65
+ * GB/T 16159 5.1 writes 姓 apart from 名 and a proper noun apart from its
66
+ * generic, each part capitalised. Both halves of that clause are this rule,
67
+ * because the evidence for them is the same evidence.
68
+ *
69
+ * 毛泽东 and 北京大学 are dictionary entries, so the decoder produces **one**
70
+ * word each — and reading them as one word is what makes them read correctly at
71
+ * all. That makes this a *split*, and splitting contradicts the dictionary's own
72
+ * claim that the characters belong together. It needs a condition strong enough
73
+ * to survive the whole dictionary, and the obvious ones are not: a surname list
74
+ * takes 马克思, 高尔基, 巴赫 and 牛顿 apart, none of which is a Chinese name,
75
+ * and a list of generics cannot say where 上海浦东发展银行 divides.
76
+ *
77
+ * **The condition is CC-CEDICT's own capitalisation**, which states the
78
+ * boundaries outright: 毛泽东 is `[Mao2 Ze2 dong1]`, 司马迁 `[Si1 ma3 Qian1]`,
79
+ * 上海交通大学 `[Shang4 hai3 Jiao1 tong1 Da4 xue2]`, and 马克思 `[Ma3 ke4 si1]`
80
+ * with no second capital at all. So a compound surname is recognised without a
81
+ * list of compound surnames, a generic without a list of generics, and a
82
+ * transliteration is excluded without a list of transliterations. It is the same
83
+ * source and the same signal the proper-noun veto already trusts — see "jieba's
84
+ * 专名 tags need a second opinion" in ROADMAP.md — extended from *whether* a
85
+ * word is a proper noun to *where* its parts divide.
86
+ *
87
+ * **Every stated boundary is cut, not only the first**, which is what separates
88
+ * an organisation from a person: 48% of `nt` entries carrying a boundary carry
89
+ * more than one, against 1.6% of `nr`. One cut would leave 上海交通大学 as
90
+ * `Shànghǎi Jiāotōngdàxué`.
91
+ *
92
+ * A tag is still required, because the mark is not confined to what 5.1 covers:
93
+ * 5,341 `ns` entries carry one, where `PLACE_GENERICS` already applies a
94
+ * measured condition, and `nz` carries 346 of which 第二次世界大战
95
+ * `[Di4 er4 Ci4 Shi4 jie4 Da4 zhan4]` divides in the wrong place. Both are
96
+ * measured out in `docs/orthography/`.
97
+ */
98
+ export declare const NAME_PARTS: GroupingRule;
57
99
  /**
58
100
  * The rules applied by default, in order.
101
+ *
102
+ * The hyphens come last, because every rule before them moves word boundaries
103
+ * and a hyphen is a statement about where a boundary ended up.
59
104
  */
60
105
  export declare const GROUPING_RULES: readonly GroupingRule[];
61
106
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"grouping.d.ts","sourceRoot":"","sources":["../../src/orthography/grouping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,CACd,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,UAAU,EAAE,UAAU,KACnB,SAAS,WAAW,EAAE,CAAC;CAC7B;AAgFD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAO9B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,EAAE,YAItB,CAAC;AA0BF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,EAAE,YAsB5B,CAAC;AAgFF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAmB9B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAKjD,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,UAAU,EAAE,UAAU,EACtB,KAAK,GAAE,SAAS,YAAY,EAAmB,GAC9C,SAAS,WAAW,EAAE,CAMxB"}
1
+ {"version":3,"file":"grouping.d.ts","sourceRoot":"","sources":["../../src/orthography/grouping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,OAAO,EAAY,KAAK,YAAY,EAAiB,MAAM,WAAW,CAAC;AAiEvE;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAO9B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,EAAE,YAItB,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,EAAE,YAsB5B,CAAC;AAgFF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAmB9B,CAAC;AAcF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,EAAE,YAgB5B,CAAC;AAUF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,UAAU,EAAE,YAgBxB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAUjD,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,UAAU,EAAE,UAAU,EACtB,KAAK,GAAE,SAAS,YAAY,EAAmB,GAC9C,SAAS,WAAW,EAAE,CAMxB"}
@@ -1,4 +1,7 @@
1
1
  import { characterCount, toCharacters } from "../script/characters.js";
2
+ import { IDIOM_HYPHENS } from "./idioms.js";
3
+ import { AABB_REDUPLICATION, ABAB_REDUPLICATION } from "./reduplication.js";
4
+ import { divideAt, join, splitAt } from "./rule.js";
2
5
  import { LONGEST_SPACED_WORD, SPACED_WORD_FORMS } from "./word-list.js";
3
6
  /**
4
7
  * jieba's tags for the aspect particles 了, 着 and 过.
@@ -39,21 +42,6 @@ const ADMINISTRATIVE_GENERICS = new Set([
39
42
  "村",
40
43
  "州",
41
44
  ]);
42
- /**
43
- * Join two decoded words into one.
44
- *
45
- * The first word's flags carry, because it is the head: 看了 is the verb 看
46
- * with an aspect particle stuck to it, not a new word of its own.
47
- */
48
- function join(head, tail) {
49
- return {
50
- text: head.text + tail.text,
51
- reading: [...head.reading, ...tail.reading],
52
- isProperNoun: head.isProperNoun,
53
- partOfSpeech: head.partOfSpeech,
54
- isKnown: head.isKnown && tail.isKnown,
55
- };
56
- }
57
45
  /**
58
46
  * Fold every word a predicate accepts into the word before it.
59
47
  */
@@ -92,26 +80,6 @@ export const SUFFIXES = {
92
80
  name: "suffixes",
93
81
  apply: (words) => joinBackwards(words, (word) => word.partOfSpeech === SUFFIX_TAG),
94
82
  };
95
- /**
96
- * Split a decoded word into two at a character offset.
97
- *
98
- * Only possible where the reading has one syllable per character, which 儿化
99
- * does not: there is no way to cut `wánr` in half.
100
- */
101
- function splitAt(word, at) {
102
- const characters = toCharacters(word.text);
103
- if (word.reading.length !== characters.length) {
104
- return undefined;
105
- }
106
- const part = (from, to) => ({
107
- text: characters.slice(from, to).join(""),
108
- reading: word.reading.slice(from, to),
109
- isProperNoun: word.isProperNoun,
110
- partOfSpeech: word.partOfSpeech,
111
- isKnown: word.isKnown,
112
- });
113
- return [part(0, at), part(at, characters.length)];
114
- }
115
83
  /**
116
84
  * A place name's generic half is written separately, and capitalised.
117
85
  *
@@ -238,14 +206,120 @@ export const SPACED_WORD_LIST = {
238
206
  return grouped;
239
207
  },
240
208
  };
209
+ /**
210
+ * The 称呼语 GB/T 16159 writes in front of a surname.
211
+ *
212
+ * 老 and 小 only. 大 is written the same way in 大李, but it is also an ordinary
213
+ * adjective in front of anything at all, and it is the one that goes wrong:
214
+ * over 88,866 lines the three of them fire 49 times together and both clear
215
+ * mistakes are 大 — 泡大池 is a big pool and 那头大熊 is a big bear. Dropping it
216
+ * leaves 38 firings and every one visible in the sample is a real form of
217
+ * address.
218
+ */
219
+ const ADDRESS_PREFIXES = new Set(["老", "小"]);
220
+ /**
221
+ * 老王 is `Lǎo Wáng`: the prefix takes a capital of its own.
222
+ *
223
+ * The words are already written apart — nothing here moves a boundary — and
224
+ * what was missing is only the capital, so this marks the prefix a proper noun
225
+ * and lets the writer do what it already does with one. That is why it is a
226
+ * grouping rule despite grouping nothing: `isProperNoun` is the flag the
227
+ * capital hangs off, and there is one place it is set.
228
+ *
229
+ * The surname is the evidence. A one-character word the dictionary marks a
230
+ * proper noun is what 老 and 小 attach to, and CC-CEDICT's capitalisation has
231
+ * already vetoed the tags that would otherwise let 小 in front of anything —
232
+ * see "jieba's 专名 tags need a second opinion" in ROADMAP.md.
233
+ */
234
+ export const ADDRESS_PREFIX = {
235
+ name: "address-prefix",
236
+ apply: (words) => words.map((word, at) => {
237
+ const next = words[at + 1];
238
+ if (!ADDRESS_PREFIXES.has(word.text) ||
239
+ next === undefined ||
240
+ !next.isProperNoun ||
241
+ characterCount(next.text) !== 1 ||
242
+ next.separator !== undefined) {
243
+ return word;
244
+ }
245
+ return { ...word, isProperNoun: true };
246
+ }),
247
+ };
248
+ /**
249
+ * jieba's tags for the two kinds of proper noun 5.1 divides.
250
+ *
251
+ * A person and an organisation. Places are `PLACE_GENERICS`' business, and `nz`
252
+ * is left out — both are measured out in `docs/orthography/`.
253
+ */
254
+ const DIVIDED_TAGS = new Set(["nr", "nt"]);
255
+ /**
256
+ * The parts of a proper name are written apart: 毛泽东 is `Máo Zédōng`.
257
+ *
258
+ * GB/T 16159 5.1 writes 姓 apart from 名 and a proper noun apart from its
259
+ * generic, each part capitalised. Both halves of that clause are this rule,
260
+ * because the evidence for them is the same evidence.
261
+ *
262
+ * 毛泽东 and 北京大学 are dictionary entries, so the decoder produces **one**
263
+ * word each — and reading them as one word is what makes them read correctly at
264
+ * all. That makes this a *split*, and splitting contradicts the dictionary's own
265
+ * claim that the characters belong together. It needs a condition strong enough
266
+ * to survive the whole dictionary, and the obvious ones are not: a surname list
267
+ * takes 马克思, 高尔基, 巴赫 and 牛顿 apart, none of which is a Chinese name,
268
+ * and a list of generics cannot say where 上海浦东发展银行 divides.
269
+ *
270
+ * **The condition is CC-CEDICT's own capitalisation**, which states the
271
+ * boundaries outright: 毛泽东 is `[Mao2 Ze2 dong1]`, 司马迁 `[Si1 ma3 Qian1]`,
272
+ * 上海交通大学 `[Shang4 hai3 Jiao1 tong1 Da4 xue2]`, and 马克思 `[Ma3 ke4 si1]`
273
+ * with no second capital at all. So a compound surname is recognised without a
274
+ * list of compound surnames, a generic without a list of generics, and a
275
+ * transliteration is excluded without a list of transliterations. It is the same
276
+ * source and the same signal the proper-noun veto already trusts — see "jieba's
277
+ * 专名 tags need a second opinion" in ROADMAP.md — extended from *whether* a
278
+ * word is a proper noun to *where* its parts divide.
279
+ *
280
+ * **Every stated boundary is cut, not only the first**, which is what separates
281
+ * an organisation from a person: 48% of `nt` entries carrying a boundary carry
282
+ * more than one, against 1.6% of `nr`. One cut would leave 上海交通大学 as
283
+ * `Shànghǎi Jiāotōngdàxué`.
284
+ *
285
+ * A tag is still required, because the mark is not confined to what 5.1 covers:
286
+ * 5,341 `ns` entries carry one, where `PLACE_GENERICS` already applies a
287
+ * measured condition, and `nz` carries 346 of which 第二次世界大战
288
+ * `[Di4 er4 Ci4 Shi4 jie4 Da4 zhan4]` divides in the wrong place. Both are
289
+ * measured out in `docs/orthography/`.
290
+ */
291
+ export const NAME_PARTS = {
292
+ name: "name-parts",
293
+ apply: (words, dictionary) => words.flatMap((word) => {
294
+ const boundaries = dictionary.lookup(word.text)?.nameBoundaries ?? [];
295
+ if (boundaries.length === 0 || !word.isProperNoun) {
296
+ return [word];
297
+ }
298
+ if (!DIVIDED_TAGS.has(word.partOfSpeech)) {
299
+ return [word];
300
+ }
301
+ const parts = divideAt(word, boundaries);
302
+ // Every part is a proper noun in its own right, so each takes a capital:
303
+ // `Máo Zédōng`, `Běijīng Dàxué`.
304
+ return parts.map((part) => ({ ...part, isProperNoun: true }));
305
+ }),
306
+ };
241
307
  /**
242
308
  * The rules applied by default, in order.
309
+ *
310
+ * The hyphens come last, because every rule before them moves word boundaries
311
+ * and a hyphen is a statement about where a boundary ended up.
243
312
  */
244
313
  export const GROUPING_RULES = [
245
314
  ASPECT_PARTICLES,
246
315
  SUFFIXES,
247
316
  PLACE_GENERICS,
317
+ NAME_PARTS,
248
318
  SPACED_WORD_LIST,
319
+ ADDRESS_PREFIX,
320
+ AABB_REDUPLICATION,
321
+ ABAB_REDUPLICATION,
322
+ IDIOM_HYPHENS,
249
323
  ];
250
324
  /**
251
325
  * Run the 分词连写 rules over a decoded run.
@@ -1 +1 @@
1
- {"version":3,"file":"grouping.js","sourceRoot":"","sources":["../../src/orthography/grouping.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"grouping.js","sourceRoot":"","sources":["../../src/orthography/grouping.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The 成语 GB/T 16159 writes with a hyphen down the middle.
3
+ *
4
+ * 6.3.2: a four-syllable 成语 *that can be read as two disyllables* takes a
5
+ * connecting hyphen — `fēngpíng-làngjìng` — and one that cannot is written
6
+ * solid: `bùyìlèhū`, `chūqíbùyì`. The condition is prosodic and structural, and
7
+ * **nothing in the data tracks it.** Conditioning on both halves being
8
+ * dictionary words fires on 10,202 of the 22,192 four-character words jieba tags
9
+ * `i` and is uncorrelated with the standard: it fires on 层出不穷 and not on
10
+ * 风平浪静, which are the standard's own two examples of the same rule, and it
11
+ * fires on 精神文明 and 凯旋归来, which want a space rather than a hyphen. No
12
+ * source carries hyphenated pinyin either, so unlike every other claim this
13
+ * package makes, a 成语 rule could not be scored even if one were written.
14
+ *
15
+ * So: a list, on the same argument as [the spaced word list](./word-list.ts).
16
+ *
17
+ * **What is in it, and what is not.** Only 成语 whose two halves are each a
18
+ * self-contained disyllable — parallel halves (风平|浪静, 千军|万马), a
19
+ * disyllable and its predicate (声势|浩大), or a disyllabic stem with a
20
+ * reduplicated tail (小心|翼翼). Those are the ones where 2+2 is beyond doubt.
21
+ * Anything with a monosyllabic word breaking the rhythm is left out and stays
22
+ * solid, which is what the standard does with it anyway: 目不转睛 is 目 |
23
+ * 不转睛, 迫不及待 is 迫 | 不及待, 情不自禁 and 显而易见 and 名副其实 the same.
24
+ * Left out too is anything this file is not sure of, because a missing entry
25
+ * keeps today's output and a wrong one breaks it.
26
+ *
27
+ * It is not a complete 成语 list and is not meant to become one — 22,192 of them
28
+ * exist. Measured over 88,866 lines of Tatoeba and zh.wikipedia, the 117 here
29
+ * cover 264 of the 1,737 four-character `i`-tagged words that turn up, or
30
+ * 15.2%. Most of what is left is either genuinely not 2+2 (据我所知, 一无所知,
31
+ * 心不在焉, 难以置信) or not a 成语 at all (非常感谢, 重新启动, 可口可乐), which
32
+ * is the shape a curated list should have: the cases it declines are mostly
33
+ * cases it should decline.
34
+ */
35
+ /**
36
+ * One 成语, spelled both ways.
37
+ *
38
+ * The two spellings are often the same string — 山清水秀 and 三心二意 are
39
+ * written identically in both scripts — and that is why the pair is kept rather
40
+ * than a flat list of forms: it is what makes "every 简体 entry has the 繁體 one
41
+ * that reads the same beside it" a checkable claim.
42
+ */
43
+ export interface IdiomSpelling {
44
+ readonly hans: string;
45
+ /** Empty where the list is missing its counterpart, which a test rejects. */
46
+ readonly hant: string;
47
+ }
48
+ /**
49
+ * Every 成语 written with the 2+2 hyphen, paired across the two scripts.
50
+ */
51
+ export declare const HYPHENATED_IDIOMS: readonly IdiomSpelling[];
52
+ /**
53
+ * Every spelling that takes the hyphen, which is how the rule asks.
54
+ */
55
+ export declare const HYPHENATED_IDIOM_FORMS: ReadonlySet<string>;
56
+ //# sourceMappingURL=idiom-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idiom-list.d.ts","sourceRoot":"","sources":["../../src/orthography/idiom-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAmQH;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,aAAa,EAErD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAEtD,CAAC"}