@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,491 @@
1
+ /**
2
+ * Gwoyeu Romatzyh (國語羅馬字), in both directions.
3
+ *
4
+ * GR is the odd one of the five. Every other system here writes a syllable and
5
+ * then marks its tone — a diacritic, a raised digit, a tone letter — so the
6
+ * spelling is one mapping over the inventory and the tone rides on top of it.
7
+ * GR spells the tone *into* the syllable: 山 shān is `shan`, 陝 shǎn is `shaan`,
8
+ * 善 shàn is `shann`. So it is four mappings rather than one, related by the
9
+ * tonal rules below, and reading it back is an index keyed per tone.
10
+ *
11
+ * The rules come from Wikipedia's *Spelling in Gwoyeu Romatzyh*, which states
12
+ * them as a rule of thumb per tone plus a handful of clauses. They are
13
+ * implemented here as stated, with one amendment recorded in
14
+ * {@link zeroInitial}, and the whole of it is scored against a different page's
15
+ * four GR columns in [syllabary.test.ts](syllabary.test.ts).
16
+ *
17
+ * Three things follow from the tone being in the spelling:
18
+ *
19
+ * - **There is no toneless form.** The basic form *is* the first tone, so a
20
+ * syllable whose tone was never written cannot be told from one written in
21
+ * the first tone. That is bopomofo's shortfall exactly, arrived at from the
22
+ * other direction — see `docs/romanization/`.
23
+ * - **The neutral tone is a dot in front**, and GR keeps the etymological tone
24
+ * spelling of the syllable behind it (`perng.yeou` for 朋友). A neutral
25
+ * pinyin syllable does not record what that tone was, so this module writes
26
+ * the basic form: `.iou` where a GR writer has `.yeou`. Reading is not so
27
+ * limited — a dot in front of any tonal spelling reads as the neutral tone.
28
+ * - **儿化 is the `-l` suffix**, and it is written here as a plain suffix
29
+ * rather than as the fusion GR actually performs. See {@link ERHUA_SUFFIX}.
30
+ */
31
+ import { DICTIONARY_SYLLABLES } from "../syllable/inventory.js";
32
+ import { readSyllable } from "../syllable/syllable.js";
33
+ import { NEUTRAL_TONE } from "../tone/tone.js";
34
+ /**
35
+ * The four tones, for building the index.
36
+ */
37
+ const CONTOUR_TONES = [1, 2, 3, 4];
38
+ /**
39
+ * How each initial is spelled.
40
+ *
41
+ * GR uses the voiced letters for the unaspirated series, as pinyin does, so
42
+ * this half of the table is nearly pinyin's own. The three that differ are the
43
+ * alveolar affricates — `tz` and `ts` for pinyin z and c — and the palatals,
44
+ * which GR does not distinguish from the retroflexes at all: `j`, `ch` and `sh`
45
+ * stand for both series, and it is the following `i` that says which. That is
46
+ * why pinyin zhu, ju and jiu come out as `ju`, `jiu` and `jiou`.
47
+ */
48
+ const INITIAL_SPELLINGS = new Map([
49
+ ["b", "b"],
50
+ ["p", "p"],
51
+ ["m", "m"],
52
+ ["f", "f"],
53
+ ["d", "d"],
54
+ ["t", "t"],
55
+ ["n", "n"],
56
+ ["l", "l"],
57
+ ["g", "g"],
58
+ ["k", "k"],
59
+ ["h", "h"],
60
+ ["j", "j"],
61
+ ["q", "ch"],
62
+ ["x", "sh"],
63
+ ["zh", "j"],
64
+ ["ch", "ch"],
65
+ ["sh", "sh"],
66
+ ["r", "r"],
67
+ ["z", "tz"],
68
+ ["c", "ts"],
69
+ ["s", "s"],
70
+ ]);
71
+ /**
72
+ * How each final is spelled in the basic form.
73
+ *
74
+ * Written out in full, as the finals table of *Spelling in Gwoyeu Romatzyh*
75
+ * has them: `iou` and `uei` rather than pinyin's contracted iu and ui, `iu` for
76
+ * ü, `au` for ao, and `el` for er — the `-r` being spoken for by the second
77
+ * tone.
78
+ */
79
+ const FINAL_SPELLINGS = {
80
+ a: "a",
81
+ o: "o",
82
+ e: "e",
83
+ ê: "è",
84
+ er: "el",
85
+ ai: "ai",
86
+ ei: "ei",
87
+ ao: "au",
88
+ ou: "ou",
89
+ an: "an",
90
+ en: "en",
91
+ ang: "ang",
92
+ eng: "eng",
93
+ ong: "ong",
94
+ i: "i",
95
+ ia: "ia",
96
+ io: "io",
97
+ ie: "ie",
98
+ iao: "iau",
99
+ iou: "iou",
100
+ ian: "ian",
101
+ in: "in",
102
+ iang: "iang",
103
+ ing: "ing",
104
+ iong: "iong",
105
+ u: "u",
106
+ ua: "ua",
107
+ uo: "uo",
108
+ uai: "uai",
109
+ uei: "uei",
110
+ uan: "uan",
111
+ uen: "uen",
112
+ uang: "uang",
113
+ ueng: "ueng",
114
+ ü: "iu",
115
+ üe: "iue",
116
+ üan: "iuan",
117
+ ün: "iun",
118
+ m: "m",
119
+ n: "n",
120
+ ng: "ng",
121
+ };
122
+ /**
123
+ * The initials after which the empty rhyme is written `-y`.
124
+ *
125
+ * 詩 shī is `shy` and 斯 sī is `sy`, which is the same observation Yale makes
126
+ * with its `r` and `z`: the i of shi is a continuation of the initial rather
127
+ * than a vowel. GR spends a letter of its own on it, and that letter behaves
128
+ * as a vowel for every tonal rule below — `shy`, `shyr`, `shyy`, `shyh`.
129
+ */
130
+ const EMPTY_RHYME_INITIALS = new Set([
131
+ "zh",
132
+ "ch",
133
+ "sh",
134
+ "r",
135
+ "z",
136
+ "c",
137
+ "s",
138
+ ]);
139
+ /**
140
+ * The sonorant initials, which swap what the first two tones do.
141
+ *
142
+ * A syllable beginning l-, m-, n- or r- takes an `-h-` as its second letter in
143
+ * the first tone and is left alone in the second: 媽 mā is `mha` and 麻 má is
144
+ * `ma`. The reason is frequency rather than phonology — these initials carry
145
+ * far more second tones than first ones — and it is the one place where the
146
+ * basic form is not the first tone.
147
+ */
148
+ const SONORANT_INITIALS = new Set(["l", "m", "n", "r"]);
149
+ /**
150
+ * The letters that count as a vowel for the tonal rules.
151
+ *
152
+ * `y` is the empty rhyme and `è` is pinyin's ê; both are the whole of the rime
153
+ * they appear in, so both take the doubling and the `-r` exactly as a vowel
154
+ * does.
155
+ */
156
+ const VOWELS = "aeiouyè";
157
+ /**
158
+ * The vowels a tone mark can sit on in pinyin, in the order the rule tries
159
+ * them, which is what "the main vowel" means for the third tone's doubling.
160
+ */
161
+ const MAIN_VOWELS = ["a", "o", "e"];
162
+ /**
163
+ * The 儿化 suffix.
164
+ *
165
+ * GR marks rhotacisation with `-l`, and this module writes it as a plain
166
+ * suffix: 花儿 huār is `hual`. **What GR itself does is a fusion rather than a
167
+ * suffix** — 一點兒 yìdiǎnr is `ideal`, where `dean` has lost its `-n` — and
168
+ * the fusion collapses syllables that were distinct, which is why *Spelling in
169
+ * Gwoyeu Romatzyh* notes that `jiel` is either 今兒 or 雞兒. Modelling it needs
170
+ * a rime-by-rime table of rhotacised forms, which is a phonological claim
171
+ * rather than a spelling convention, and is not made here — the same line
172
+ * [ipa.ts](ipa.ts) draws around its suffixed [ɚ].
173
+ *
174
+ * The visible consequence is that this module's 儿化 always reads back and
175
+ * GR's own does not.
176
+ */
177
+ const ERHUA_SUFFIX = "l";
178
+ /**
179
+ * The dot written in front of a neutral-tone syllable.
180
+ *
181
+ * A full stop, which is how GR writes it in print. Chao's own practice is to
182
+ * use it on a word's first appearance rather than throughout; this module
183
+ * always writes it, because it is the only thing in GR that can say "neutral"
184
+ * rather than leaving it to be inferred.
185
+ */
186
+ const NEUTRAL_MARK = ".";
187
+ /**
188
+ * Whether a letter is one the tonal rules treat as a vowel.
189
+ *
190
+ * Spelled out rather than written `VOWELS.includes(letter)` because
191
+ * `"aeiouyè".includes("")` is true, and the letter before the first one of a
192
+ * spelling is exactly that: the empty string. A rule asking "is the letter
193
+ * before this one a vowel" would silently say yes at the start of every
194
+ * syllable.
195
+ */
196
+ function isVowel(letter) {
197
+ return letter !== undefined && letter !== "" && VOWELS.includes(letter);
198
+ }
199
+ /**
200
+ * How a final is spelled after a given initial.
201
+ */
202
+ function rimeSpelling(initial, final) {
203
+ return final === "i" && EMPTY_RHYME_INITIALS.has(initial)
204
+ ? "y"
205
+ : FINAL_SPELLINGS[final];
206
+ }
207
+ /**
208
+ * The basic form of a syllable: the spelling every tonal rule starts from.
209
+ */
210
+ function basicForm(syllable) {
211
+ const { initial, final } = syllable;
212
+ const spelt = INITIAL_SPELLINGS.get(initial) ?? "";
213
+ return `${spelt}${rimeSpelling(initial, final)}`;
214
+ }
215
+ /**
216
+ * Where the rime's vowels start and end.
217
+ *
218
+ * The five syllables with no vowel letter at all — the syllabic nasals 呣, 唔,
219
+ * 嗯 and their aspirated pair — fall back to the last letter, so that the rules
220
+ * asking for "the vowel" have something to work on. GR has no attested
221
+ * spelling for any of them; see `docs/romanization/`.
222
+ */
223
+ function vowelSpan(form) {
224
+ let start = 0;
225
+ while (start < form.length && !isVowel(form[start])) {
226
+ start += 1;
227
+ }
228
+ if (start === form.length) {
229
+ return [form.length - 1, form.length - 1];
230
+ }
231
+ let end = start;
232
+ while (isVowel(form[end + 1])) {
233
+ end += 1;
234
+ }
235
+ return [start, end];
236
+ }
237
+ /**
238
+ * Tone 1: the basic form, with `-h-` inserted after a sonorant initial.
239
+ */
240
+ function firstTone(form, initial) {
241
+ return SONORANT_INITIALS.has(initial)
242
+ ? `${form.slice(0, 1)}h${form.slice(1)}`
243
+ : form;
244
+ }
245
+ /**
246
+ * The i or u a glide can be written on, turned into y or w.
247
+ *
248
+ * The rule is `NiV → NyV` and `NuV → NwV`, where N is a non-vowel or nothing at
249
+ * all: the i or u has to be the medial rather than part of a diphthong, which
250
+ * is what the "not preceded by a vowel" test says. The `+ -i if final` clause
251
+ * covers the rimes that are nothing but the medial — `i` becomes `yi` and not
252
+ * a bare `y`.
253
+ */
254
+ function glided(form) {
255
+ for (const [letter, glide] of [
256
+ ["i", "y"],
257
+ ["u", "w"],
258
+ ]) {
259
+ const at = form.indexOf(letter);
260
+ if (at !== -1 && !isVowel(form[at - 1])) {
261
+ const rest = form.slice(at + 1);
262
+ return `${form.slice(0, at)}${glide}${rest === "" ? letter : rest}`;
263
+ }
264
+ }
265
+ return undefined;
266
+ }
267
+ /**
268
+ * Tone 2: i/u to y/w, or an `-r` after the vowels.
269
+ */
270
+ function secondTone(form, initial) {
271
+ if (SONORANT_INITIALS.has(initial)) {
272
+ return form;
273
+ }
274
+ const glide = glided(form);
275
+ if (glide !== undefined) {
276
+ return glide;
277
+ }
278
+ const [, end] = vowelSpan(form);
279
+ return `${form.slice(0, end + 1)}r${form.slice(end + 1)}`;
280
+ }
281
+ /**
282
+ * The i or u a third tone can be swapped for e or o.
283
+ *
284
+ * `Vi`/`iV` becomes `Ve`/`eV` and `Vu`/`uV` becomes `Vo`/`oV`, so the letter
285
+ * has to sit beside a vowel; where both an i and a u are available it is
286
+ * whichever comes *first* that changes, which is why 交 jiǎo is `jeau` and not
287
+ * `jiao`. The swap is abandoned where it would produce `ee` or `oo`, both of
288
+ * which are the doubling below and would collide with it.
289
+ */
290
+ function swapped(form) {
291
+ const found = [
292
+ ["i", "e"],
293
+ ["u", "o"],
294
+ ]
295
+ .map(([letter, swap]) => [form.indexOf(letter), swap])
296
+ .filter(([at]) => at !== -1)
297
+ .toSorted((one, other) => one[0] - other[0])[0];
298
+ if (found === undefined) {
299
+ return undefined;
300
+ }
301
+ const [at, swap] = found;
302
+ const before = form[at - 1];
303
+ const after = form[at + 1];
304
+ const isBeside = isVowel(before) || isVowel(after);
305
+ const isDoubles = before === swap || after === swap;
306
+ return isBeside && !isDoubles
307
+ ? `${form.slice(0, at)}${swap}${form.slice(at + 1)}`
308
+ : undefined;
309
+ }
310
+ /**
311
+ * Tone 3, where the swap does not apply: double the main vowel.
312
+ *
313
+ * The main vowel is the one a pinyin tone mark would sit on — a, then o or e,
314
+ * then the last vowel there is — so 請 qǐng is `chiing` and 給 gěi is `geei`.
315
+ */
316
+ function doubled(form) {
317
+ const [start, end] = vowelSpan(form);
318
+ const rime = form.slice(start, end + 1);
319
+ const main = MAIN_VOWELS.map((vowel) => rime.indexOf(vowel)).find((at) => at !== -1);
320
+ const at = start + (main ?? rime.length - 1);
321
+ return `${form.slice(0, at + 1)}${form.slice(at, at + 1)}${form.slice(at + 1)}`;
322
+ }
323
+ /**
324
+ * Tone 3: i/u to e/o beside a vowel, or the main vowel doubled.
325
+ */
326
+ function thirdTone(form) {
327
+ return swapped(form) ?? doubled(form);
328
+ }
329
+ /**
330
+ * Tone 4: change or double the last letter, or add `-h`.
331
+ *
332
+ * The `⇏iw` clause is why 去 qù is `chiuh` and not `chiw`: the u of `iu` is the
333
+ * ü rather than the second half of a diphthong, and turning it into a w would
334
+ * say the wrong thing about the vowel.
335
+ */
336
+ function fourthTone(form) {
337
+ const last = form.slice(-1);
338
+ const before = form.slice(-2, -1);
339
+ if (last === "i" && isVowel(before)) {
340
+ return `${form.slice(0, -1)}y`;
341
+ }
342
+ if (last === "u" && isVowel(before) && before !== "i") {
343
+ return `${form.slice(0, -1)}w`;
344
+ }
345
+ if (form.endsWith("ng")) {
346
+ return `${form.slice(0, -2)}nq`;
347
+ }
348
+ if (last === "n" || last === "l") {
349
+ return `${form}${last}`;
350
+ }
351
+ return `${form}h`;
352
+ }
353
+ /**
354
+ * Put the y- or w- on a syllable that has no initial, in tones 3 and 4.
355
+ *
356
+ * **This is the one place where the published rules needed amending, and the
357
+ * amendment is a correction of one word.** They say that a basic form starting
358
+ * i- or u- has that letter *replaced* by y- or w- (tone 4), or changed to e- or
359
+ * o- with a y- or w- added (tone 3). Applied literally that deletes a vowel the
360
+ * attested spellings keep: 一 yī's basic form is `i`, and tone 4 comes out `yh`
361
+ * where the source has `yih`. Nine cells of the syllabary fail that way, all of
362
+ * them a rime that is bare or closed by a consonant — `yii`, `yih`, `wuu`,
363
+ * `wuh`, `yiin`, `yinn`, `yiing`, `yinq`, `yuh`.
364
+ *
365
+ * The rime table of *Spelling in Gwoyeu Romatzyh* says what actually happens,
366
+ * and it is one rule rather than two: **the letter replaced is a medial**, so
367
+ * it goes only when a different vowel follows it. `iuh` has a u after the i and
368
+ * becomes `yuh`; `ih` has an h and becomes `yih`; `ii` has the same vowel
369
+ * doubled, which is not a following vowel at all, and becomes `yii`. Tone 3
370
+ * needs no separate clause once that is said, because its own swap has already
371
+ * eaten the medial wherever there was one.
372
+ */
373
+ function zeroInitial(form, basic) {
374
+ const onset = basic.slice(0, 1);
375
+ if (onset !== "i" && onset !== "u") {
376
+ return form;
377
+ }
378
+ const glide = onset === "i" ? "y" : "w";
379
+ const next = form[1];
380
+ const isMedial = form.slice(0, 1) === onset && isVowel(next) && next !== onset;
381
+ return `${glide}${isMedial ? form.slice(1) : form}`;
382
+ }
383
+ /**
384
+ * Spell a syllable in a given tone, which is the whole of GR's tonal rules.
385
+ */
386
+ function toneForm(syllable, tone) {
387
+ const { initial } = syllable;
388
+ const basic = basicForm(syllable);
389
+ if (tone === 1) {
390
+ return firstTone(basic, initial);
391
+ }
392
+ if (tone === 2) {
393
+ return secondTone(basic, initial);
394
+ }
395
+ const changed = tone === 3 ? thirdTone(basic) : fourthTone(basic);
396
+ return initial === "" ? zeroInitial(changed, basic) : changed;
397
+ }
398
+ /**
399
+ * Write a syllable in Gwoyeu Romatzyh: 就 jiù becomes `jiow`.
400
+ *
401
+ * An unwritten tone is written as the basic form, because GR has no other
402
+ * form to write — which means it reads back as a first tone, exactly as
403
+ * bopomofo's unmarked syllable does. The neutral tone takes the dot in front,
404
+ * and the syllable behind it is the basic form rather than the etymological
405
+ * spelling GR would use, for the reason the module comment gives.
406
+ */
407
+ export function writeGwoyeu(syllable) {
408
+ const { tone } = syllable;
409
+ const spelt = toneForm(syllable, tone === undefined || tone === NEUTRAL_TONE ? 1 : tone);
410
+ const suffix = syllable.erhua === true ? ERHUA_SUFFIX : "";
411
+ const dot = tone === NEUTRAL_TONE ? NEUTRAL_MARK : "";
412
+ return `${dot}${spelt}${suffix}`;
413
+ }
414
+ /**
415
+ * Write a word in Gwoyeu Romatzyh, one syllable after another.
416
+ *
417
+ * Solid, as GR writes a word: 北京 is `Beeijing`, which is the convention
418
+ * pinyin inherited. GR uses an apostrophe where the join is ambiguous — its own
419
+ * name for pinyin, `Pin'in`, is the standard example — and that is not written
420
+ * here, for the same reason `readGwoyeu` takes one syllable at a time: knowing
421
+ * where the boundary is ambiguous means being able to split the word, and
422
+ * nothing here splits a GR word.
423
+ */
424
+ export function writeGwoyeuWord(syllables) {
425
+ return syllables.map((syllable) => writeGwoyeu(syllable)).join("");
426
+ }
427
+ /**
428
+ * Every syllable each spelling stands for, keyed per tone.
429
+ *
430
+ * The other readers here index the inventory once and read the tone off the
431
+ * marks; GR has no marks, so the index is built four times over and each entry
432
+ * carries the tone its spelling means. That is the whole practical difference
433
+ * between reading GR and reading the rest.
434
+ */
435
+ function indexTonalForms() {
436
+ const index = new Map();
437
+ for (const pinyin of DICTIONARY_SYLLABLES) {
438
+ const syllable = readSyllable(pinyin);
439
+ /* c8 ignore next 3 -- inventory.test.ts holds the parser to the inventory */
440
+ if (syllable === undefined) {
441
+ continue;
442
+ }
443
+ for (const tone of CONTOUR_TONES) {
444
+ const toned = { ...syllable, tone };
445
+ const key = toneForm(syllable, tone);
446
+ const found = index.get(key);
447
+ if (found === undefined) {
448
+ index.set(key, [toned]);
449
+ }
450
+ else {
451
+ found.push(toned);
452
+ }
453
+ }
454
+ }
455
+ return index;
456
+ }
457
+ const INDEX = indexTonalForms();
458
+ /**
459
+ * Look a spelling up, allowing for the 儿化 suffix on the end of it.
460
+ *
461
+ * Tried second and separately, as it is for Yale: `ell` is 二 èr, and it is also
462
+ * the same rime in the first tone with the suffix on the end. Both readings are
463
+ * real, and it is the caller's to narrow.
464
+ */
465
+ function lookUp(spelling) {
466
+ const base = spelling.endsWith(ERHUA_SUFFIX) ? spelling.slice(0, -1) : "";
467
+ const suffixed = base === ""
468
+ ? []
469
+ : (INDEX.get(base) ?? []).map((syllable) => ({
470
+ ...syllable,
471
+ erhua: true,
472
+ }));
473
+ return [...(INDEX.get(spelling) ?? []), ...suffixed];
474
+ }
475
+ /**
476
+ * Read a Gwoyeu Romatzyh syllable: `jiow` becomes 就 jiù.
477
+ *
478
+ * A leading dot is the neutral tone, and it wins over whatever tone the
479
+ * spelling behind it carries — `.yeou` is a neutral 友, which is what GR means
480
+ * by it. Returns every syllable the spelling stands for, and nothing at all for
481
+ * a spelling no syllable of the inventory writes.
482
+ */
483
+ export function readGwoyeu(text) {
484
+ const written = text.trim().normalize("NFC").toLowerCase();
485
+ const isNeutral = written.startsWith(NEUTRAL_MARK);
486
+ const found = lookUp(isNeutral ? written.slice(NEUTRAL_MARK.length) : written);
487
+ return isNeutral
488
+ ? found.map((syllable) => ({ ...syllable, tone: NEUTRAL_TONE }))
489
+ : found;
490
+ }
491
+ //# sourceMappingURL=gwoyeu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gwoyeu.js","sourceRoot":"","sources":["../../src/transcription/gwoyeu.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { Syllable } from "../syllable/syllable.js";
2
+ /**
3
+ * How an IPA syllable writes its tone.
4
+ *
5
+ * Chao's tone letters are the standard notation and the default; `numbers`
6
+ * writes his pitch numerals instead (55, 35, 214, 51), which is what a plain
7
+ * ASCII field wants.
8
+ */
9
+ export interface IpaOptions {
10
+ readonly tones?: "letters" | "numbers" | "none";
11
+ }
12
+ /**
13
+ * Transcribe a syllable without its tone: 就 jiù becomes `tɕiou`.
14
+ */
15
+ export declare function writeIpaSymbols(syllable: Syllable): string;
16
+ /**
17
+ * Transcribe a syllable in IPA: 就 jiù becomes `tɕiou˥˩`.
18
+ *
19
+ * Without brackets, since whether it is phonemic or phonetic is the caller's
20
+ * claim to make and not this table's.
21
+ */
22
+ export declare function writeIpa(syllable: Syllable, options?: IpaOptions): string;
23
+ /**
24
+ * Transcribe a word, one syllable after another.
25
+ *
26
+ * Run together, as a transcription of connected speech is written. The tone
27
+ * letters mark where each syllable ends, so nothing is lost by it — except for
28
+ * the neutral tone, which writes nothing and so leaves a boundary unmarked.
29
+ */
30
+ export declare function writeIpaWord(syllables: readonly Syllable[], options?: IpaOptions): string;
31
+ /**
32
+ * Read an IPA transcription back: `tɕiou˥˩` becomes 就 jiù.
33
+ *
34
+ * An index over the inventory, as everything here is, so a transcription of
35
+ * something Mandarin does not have reads as nothing rather than as a syllable
36
+ * nobody says. Returns an array for the same reason the other readers do,
37
+ * though IPA has less to be ambiguous about than any of them.
38
+ */
39
+ export declare function readIpa(text: string): readonly Syllable[];
40
+ //# sourceMappingURL=ipa.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipa.d.ts","sourceRoot":"","sources":["../../src/transcription/ipa.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAqIxD;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;CACjD;AA0BD;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAI1D;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,GAAE,UAAe,GAAG,MAAM,CAW7E;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,UAAe,GACvB,MAAM,CAER;AAkCD;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,QAAQ,EAAE,CAGzD"}