@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,221 @@
1
+ import { NEUTRAL_TONE, toneFromNotation } from "../tone/tone.js";
2
+ import { indexInventory, readIndexed } from "./spelling-index.js";
3
+ /**
4
+ * The symbol each initial is transcribed with.
5
+ *
6
+ * The aspiration series is where pinyin's letters mislead most: pinyin b is
7
+ * [p], and pinyin p is [pʰ]. Nothing in Mandarin is voiced here, which is what
8
+ * both Wade-Giles and Yale are working around in their own directions.
9
+ */
10
+ const INITIAL_SYMBOLS = new Map([
11
+ ["b", "p"],
12
+ ["p", "pʰ"],
13
+ ["m", "m"],
14
+ ["f", "f"],
15
+ ["d", "t"],
16
+ ["t", "tʰ"],
17
+ ["n", "n"],
18
+ ["l", "l"],
19
+ ["g", "k"],
20
+ ["k", "kʰ"],
21
+ ["h", "x"],
22
+ ["j", "tɕ"],
23
+ ["q", "tɕʰ"],
24
+ ["x", "ɕ"],
25
+ ["zh", "ʈʂ"],
26
+ ["ch", "ʈʂʰ"],
27
+ ["sh", "ʂ"],
28
+ ["r", "ʐ"],
29
+ ["z", "ts"],
30
+ ["c", "tsʰ"],
31
+ ["s", "s"],
32
+ ]);
33
+ /**
34
+ * The symbols each final is transcribed with.
35
+ *
36
+ * The vowels are where the transcription and the spelling part company: pinyin
37
+ * writes one e for [ɤ], [ə] and [ɛ], and one i for [i] and [ɨ], because a
38
+ * spelling can let the context do the work and a transcription cannot. 天 tiān
39
+ * is [tʰiɛn] and 恩 ēn is [ən].
40
+ */
41
+ const FINAL_SYMBOLS = {
42
+ a: "a",
43
+ o: "ɔ",
44
+ e: "ɤ",
45
+ ê: "ɛ",
46
+ er: "aɚ",
47
+ ai: "ai",
48
+ ei: "ei",
49
+ ao: "au",
50
+ ou: "ou",
51
+ an: "an",
52
+ en: "ən",
53
+ ang: "aŋ",
54
+ eng: "əŋ",
55
+ ong: "ʊŋ",
56
+ i: "i",
57
+ ia: "ia",
58
+ io: "iɔ",
59
+ ie: "ie",
60
+ iao: "iau",
61
+ iou: "iou",
62
+ ian: "iɛn",
63
+ in: "in",
64
+ iang: "iaŋ",
65
+ ing: "iŋ",
66
+ iong: "iʊŋ",
67
+ u: "u",
68
+ ua: "ua",
69
+ uo: "uo",
70
+ uai: "uai",
71
+ uei: "uei",
72
+ uan: "uan",
73
+ uen: "uən",
74
+ uang: "uaŋ",
75
+ ueng: "uəŋ",
76
+ ü: "y",
77
+ üe: "ye",
78
+ üan: "yɛn",
79
+ ün: "yn",
80
+ m: "m̩",
81
+ n: "n̩",
82
+ ng: "ŋ̍",
83
+ };
84
+ /**
85
+ * The initials after which the i final is the empty rhyme [ɨ].
86
+ */
87
+ const EMPTY_RHYME_INITIALS = new Set([
88
+ "zh",
89
+ "ch",
90
+ "sh",
91
+ "r",
92
+ "z",
93
+ "c",
94
+ "s",
95
+ ]);
96
+ /**
97
+ * The initials after which pinyin's -o final is [uo] rather than [ɔ].
98
+ */
99
+ const LABIAL_INITIALS = new Set(["b", "p", "m", "f"]);
100
+ /**
101
+ * What 儿化 adds.
102
+ *
103
+ * A suffix, and that is an approximation this module states rather than hides:
104
+ * a rhotacised syllable is not the plain one with [ɚ] after it. 玩儿 wánr is
105
+ * [wɑɚ̯] with the nasal gone, and 味儿 wèir keeps its glide while 事儿 shìr
106
+ * loses its empty rhyme entirely. Modelling that needs a rhyme-by-rhyme table
107
+ * of fused forms, which is a phonological claim rather than a transcription
108
+ * convention, and it is not made here.
109
+ */
110
+ const ERHUA_SUFFIX = "ɚ";
111
+ /**
112
+ * The Chao tone letter each tone is written with.
113
+ *
114
+ * The neutral tone has none: it has no contour of its own, taking its pitch
115
+ * from the syllable before it, and the transcription leaves it unwritten. That
116
+ * is bopomofo's problem in reverse — bopomofo cannot say "no tone", and this
117
+ * cannot say "neutral".
118
+ */
119
+ const TONE_LETTERS = new Map([
120
+ [1, "˥"],
121
+ [2, "˧˥"],
122
+ [3, "˨˩˦"],
123
+ [4, "˥˩"],
124
+ [NEUTRAL_TONE, ""],
125
+ ]);
126
+ /**
127
+ * The pitch value each tone is numbered with, in Chao's own notation.
128
+ */
129
+ const TONE_NUMBERS = new Map([
130
+ [1, "55"],
131
+ [2, "35"],
132
+ [3, "214"],
133
+ [4, "51"],
134
+ [NEUTRAL_TONE, ""],
135
+ ]);
136
+ /**
137
+ * How a final is transcribed after a given initial.
138
+ */
139
+ function finalSymbols(initial, final) {
140
+ if (final === "i" && EMPTY_RHYME_INITIALS.has(initial)) {
141
+ return "ɨ";
142
+ }
143
+ if (final === "o" && LABIAL_INITIALS.has(initial)) {
144
+ return "uo";
145
+ }
146
+ return FINAL_SYMBOLS[final];
147
+ }
148
+ /**
149
+ * Transcribe a syllable without its tone: 就 jiù becomes `tɕiou`.
150
+ */
151
+ export function writeIpaSymbols(syllable) {
152
+ const { initial, final } = syllable;
153
+ const suffix = syllable.erhua === true ? ERHUA_SUFFIX : "";
154
+ return `${INITIAL_SYMBOLS.get(initial) ?? ""}${finalSymbols(initial, final)}${suffix}`;
155
+ }
156
+ /**
157
+ * Transcribe a syllable in IPA: 就 jiù becomes `tɕiou˥˩`.
158
+ *
159
+ * Without brackets, since whether it is phonemic or phonetic is the caller's
160
+ * claim to make and not this table's.
161
+ */
162
+ export function writeIpa(syllable, options = {}) {
163
+ const symbols = writeIpaSymbols(syllable);
164
+ const { tone } = syllable;
165
+ const { tones = "letters" } = options;
166
+ if (tone === undefined || tones === "none") {
167
+ return symbols;
168
+ }
169
+ const written = tones === "numbers" ? TONE_NUMBERS.get(tone) : TONE_LETTERS.get(tone);
170
+ /* c8 ignore next -- every tone is in both tables */
171
+ return `${symbols}${written ?? ""}`;
172
+ }
173
+ /**
174
+ * Transcribe a word, one syllable after another.
175
+ *
176
+ * Run together, as a transcription of connected speech is written. The tone
177
+ * letters mark where each syllable ends, so nothing is lost by it — except for
178
+ * the neutral tone, which writes nothing and so leaves a boundary unmarked.
179
+ */
180
+ export function writeIpaWord(syllables, options = {}) {
181
+ return syllables.map((syllable) => writeIpa(syllable, options)).join("");
182
+ }
183
+ /**
184
+ * Every syllable each transcription stands for.
185
+ */
186
+ const INDEX = indexInventory((syllable) => writeIpaSymbols(syllable));
187
+ /**
188
+ * The tone each written form stands for, letters and pitch numerals alike.
189
+ */
190
+ const TONES_BY_WRITTEN = new Map([...TONE_LETTERS, ...TONE_NUMBERS]
191
+ .filter(([, written]) => written !== "")
192
+ .map(([tone, written]) => [written, tone]));
193
+ /**
194
+ * Take the tone off the end, however it was written.
195
+ *
196
+ * Tone letters, a pitch numeral, or the plain 1-to-5 digit this package uses
197
+ * everywhere else — the last is not IPA at all, but it costs nothing to accept
198
+ * and it is what somebody typing at a terminal will reach for.
199
+ */
200
+ function splitTone(text) {
201
+ const found = /^(.*?)([˥˦˧˨˩]+|214|55|35|51|[0-5])$/u.exec(text);
202
+ const written = found?.[2];
203
+ if (written === undefined) {
204
+ return [text, undefined];
205
+ }
206
+ const tone = TONES_BY_WRITTEN.get(written) ?? toneFromNotation(Number(written));
207
+ return tone === undefined ? [text, undefined] : [found?.[1] ?? "", tone];
208
+ }
209
+ /**
210
+ * Read an IPA transcription back: `tɕiou˥˩` becomes 就 jiù.
211
+ *
212
+ * An index over the inventory, as everything here is, so a transcription of
213
+ * something Mandarin does not have reads as nothing rather than as a syllable
214
+ * nobody says. Returns an array for the same reason the other readers do,
215
+ * though IPA has less to be ambiguous about than any of them.
216
+ */
217
+ export function readIpa(text) {
218
+ const [symbols, tone] = splitTone(text.trim().normalize("NFC"));
219
+ return readIndexed(INDEX, symbols, tone, ERHUA_SUFFIX);
220
+ }
221
+ //# sourceMappingURL=ipa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipa.js","sourceRoot":"","sources":["../../src/transcription/ipa.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { type Syllable } from "../syllable/syllable.js";
2
+ import type { Tone } from "../tone/tone.js";
3
+ /**
4
+ * Every syllable a spelling stands for, which is one for all but a handful.
5
+ */
6
+ export type SpellingIndex = ReadonlyMap<string, readonly Syllable[]>;
7
+ /**
8
+ * Index the whole inventory by how `spell` writes each of its syllables.
9
+ *
10
+ * The syllables carry no tone: a spelling stands for a syllable, and the tone
11
+ * is read off the marks separately.
12
+ */
13
+ export declare function indexInventory(spell: (syllable: Syllable) => string): SpellingIndex;
14
+ /**
15
+ * Read a spelling out of an index, allowing for a 儿化 suffix on the end of it.
16
+ *
17
+ * The suffix is tried second and separately, because both readings can be real:
18
+ * Yale's `er` is 兒 ér, and it is also 餓儿 with the suffix on it. Returning
19
+ * both is the same answer Wade-Giles gives to an ambiguous spelling, and it is
20
+ * the caller's to narrow.
21
+ */
22
+ export declare function readIndexed(index: SpellingIndex, spelling: string, tone: Tone | undefined, suffix: string): readonly Syllable[];
23
+ //# sourceMappingURL=spelling-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spelling-index.d.ts","sourceRoot":"","sources":["../../src/transcription/spelling-index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,MAAM,GACpC,aAAa,CAiBf;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,IAAI,GAAG,SAAS,EACtB,MAAM,EAAE,MAAM,GACb,SAAS,QAAQ,EAAE,CAarB"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * An index from a written spelling to the syllables it can stand for.
3
+ *
4
+ * Every reader in this directory is an index over the attested inventory rather
5
+ * than a reverse rule, for the reason [wade-giles.ts](wade-giles.ts) records: a
6
+ * rule would happily read `shung` or `ki`, which are regular spellings of
7
+ * syllables Mandarin does not have. Building the index by *writing* every
8
+ * syllable also means a reader cannot drift from its writer — there is one
9
+ * table, and reading is what falls out of it backwards.
10
+ *
11
+ * Wade-Giles keeps its own version of this, because it needs two indexes and a
12
+ * per-mark repair for the apostrophes and diacritics real text drops. Yale and
13
+ * IPA drop nothing, so they share the plain one.
14
+ */
15
+ import { DICTIONARY_SYLLABLES } from "../syllable/inventory.js";
16
+ import { readSyllable } from "../syllable/syllable.js";
17
+ /**
18
+ * Index the whole inventory by how `spell` writes each of its syllables.
19
+ *
20
+ * The syllables carry no tone: a spelling stands for a syllable, and the tone
21
+ * is read off the marks separately.
22
+ */
23
+ export function indexInventory(spell) {
24
+ const index = new Map();
25
+ for (const pinyin of DICTIONARY_SYLLABLES) {
26
+ const syllable = readSyllable(pinyin);
27
+ /* c8 ignore next 3 -- inventory.test.ts holds the parser to the inventory */
28
+ if (syllable === undefined) {
29
+ continue;
30
+ }
31
+ const key = spell(syllable);
32
+ const found = index.get(key);
33
+ if (found === undefined) {
34
+ index.set(key, [syllable]);
35
+ }
36
+ else {
37
+ found.push(syllable);
38
+ }
39
+ }
40
+ return index;
41
+ }
42
+ /**
43
+ * Read a spelling out of an index, allowing for a 儿化 suffix on the end of it.
44
+ *
45
+ * The suffix is tried second and separately, because both readings can be real:
46
+ * Yale's `er` is 兒 ér, and it is also 餓儿 with the suffix on it. Returning
47
+ * both is the same answer Wade-Giles gives to an ambiguous spelling, and it is
48
+ * the caller's to narrow.
49
+ */
50
+ export function readIndexed(index, spelling, tone, suffix) {
51
+ const found = (written, hasErhua) => (index.get(written) ?? []).map((syllable) => ({
52
+ ...syllable,
53
+ tone,
54
+ ...(hasErhua && { erhua: true }),
55
+ }));
56
+ const base = spelling !== suffix && spelling.endsWith(suffix)
57
+ ? spelling.slice(0, -suffix.length)
58
+ : "";
59
+ return [...found(spelling, false), ...(base === "" ? [] : found(base, true))];
60
+ }
61
+ //# sourceMappingURL=spelling-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spelling-index.js","sourceRoot":"","sources":["../../src/transcription/spelling-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAiB,MAAM,yBAAyB,CAAC;AAQtE;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAqC;IAErC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC5C,KAAK,MAAM,MAAM,IAAI,oBAAoB,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;QACtC,6EAA6E;QAC7E,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,KAAoB,EACpB,QAAgB,EAChB,IAAsB,EACtB,MAAc;IAEd,MAAM,KAAK,GAAG,CAAC,OAAe,EAAE,QAAiB,EAAuB,EAAE,CACxE,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC5C,GAAG,QAAQ;QACX,IAAI;QACJ,GAAG,CAAC,QAAQ,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;KACjC,CAAC,CAAC,CAAC;IAEN,MAAM,IAAI,GACR,QAAQ,KAAK,MAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC9C,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC;QACnC,CAAC,CAAC,EAAE,CAAC;IACT,OAAO,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AAChF,CAAC"}
@@ -0,0 +1,108 @@
1
+ import { type Syllable } from "../syllable/syllable.js";
2
+ /**
3
+ * How a Wade-Giles syllable writes its tone.
4
+ *
5
+ * Wade-Giles writes it as a raised digit after the syllable, which is why
6
+ * `superscript` is the default; `numbers` writes the same digit on the line,
7
+ * which is what a plain-text field or a filename wants.
8
+ */
9
+ export interface WadeGilesOptions {
10
+ readonly tones?: "superscript" | "numbers" | "none";
11
+ }
12
+ /**
13
+ * Spell a syllable in Wade-Giles without its tone.
14
+ */
15
+ export declare function writeWadeGilesSpelling(syllable: Syllable): string;
16
+ /**
17
+ * Write a syllable in Wade-Giles: 就 jiù becomes `chiu⁴`.
18
+ *
19
+ * Total over well-formed syllables, as the bopomofo writer is. An unwritten
20
+ * tone stays unwritten rather than being invented as a first tone, which is
21
+ * what lets the round trip come back exactly.
22
+ */
23
+ export declare function writeWadeGiles(syllable: Syllable, options?: WadeGilesOptions): string;
24
+ /**
25
+ * Write a word in Wade-Giles, hyphenating between its syllables.
26
+ *
27
+ * The hyphen is Wade's own convention and it is not decoration: `Tse-tung`
28
+ * written solid could be read as two syllables or as three, and the system has
29
+ * no 隔音符号 to fall back on. Pinyin writes the word solid and reaches for an
30
+ * apostrophe only where the boundary is genuinely ambiguous, which is the
31
+ * opposite default.
32
+ */
33
+ export declare function writeWadeGilesWord(syllables: readonly Syllable[], options?: WadeGilesOptions): string;
34
+ /**
35
+ * Read a Wade-Giles syllable exactly as it is written: `chiu⁴` becomes 就 jiù.
36
+ *
37
+ * Returns every syllable the spelling stands for, which is one for all but a
38
+ * handful of them — `lo` is both 羅 luó and 咯 lo, and `o` is both 俄 é and
39
+ * 哦 ó. Empty for anything that is not Wade-Giles at all.
40
+ */
41
+ export declare function readWadeGiles(text: string): readonly Syllable[];
42
+ /**
43
+ * The same, allowing for the marks real-world Wade-Giles drops.
44
+ *
45
+ * `chi` read exactly is 機 jī; read loosely it is jī or 七 qī, because a text
46
+ * that meant `chʻi` may simply never have typed the apostrophe. The exact
47
+ * readings come first, so a caller that wants to believe what was written can
48
+ * take the head of the list and one that wants every possibility can take the
49
+ * lot.
50
+ */
51
+ export declare function readWadeGilesLoosely(text: string): readonly Syllable[];
52
+ /**
53
+ * Split written Wade-Giles into syllables: `maotsetung` becomes three.
54
+ *
55
+ * **The hyphen is Wade's own boundary and is honoured where it is there.** What
56
+ * this is for is the text that dropped it, which is most of the Wade-Giles
57
+ * anybody meets — and there the system has nothing to fall back on, since its
58
+ * apostrophe marks aspiration rather than separation. Pinyin's 隔音符号 has no
59
+ * counterpart here.
60
+ *
61
+ * Longest-first, as `splitSyllables` is for pinyin, and measured on the same
62
+ * vocabulary the 52.07% ambiguity figure comes from — 411,956 multi-syllable
63
+ * words of the phrase corpus, written in Wade-Giles and run together:
64
+ *
65
+ * | | marks kept | marks dropped |
66
+ * | --- | ---: | ---: |
67
+ * | the boundary is found | 99.19% | 99.04% |
68
+ * | the word comes back | **99.45%** | **56.04%** |
69
+ *
70
+ * **Finding the boundary is not the hard part; saying which syllable it was
71
+ * is.** The boundary is found either way; what collapses is the reading, and
72
+ * only when the marks are gone, because 52.07% of written syllables then no
73
+ * longer say which syllable they were. See {@link readWadeGilesLoosely}.
74
+ *
75
+ * The true split is among the candidates 100.00% of the time and is the only
76
+ * candidate 17.08% of the time, at a mean of 5.23 candidates per word — so
77
+ * longest-first is a choice among real rivals rather than the only reading
78
+ * available. It comes back whole slightly *more* often than it finds the
79
+ * boundary, because two of the variant spellings read the same either way.
80
+ *
81
+ * The 0.81% of boundaries that are missed are one mechanism: Wade-Giles ends
82
+ * syllables in -n and -ng and begins them with vowels and n-, so `i-ti-hu-na`
83
+ * runs together as `itihuna` and comes back `i-ti-hun-a`. Of 3,317 misses,
84
+ * 53.39% swallow a syllable beginning with n- and 36.72% one beginning with a
85
+ * vowel. Pinyin is spared most of this by spelling a zero-initial i- as `yi-`;
86
+ * Wade-Giles writes 一 as `i`, and 960 of the misses — 28.94% — are a
87
+ * swallowed 一.
88
+ *
89
+ * Returns undefined for a run that does not split into Wade-Giles at all —
90
+ * which includes `Chungking` and `Tsingtao`, because those are Postal
91
+ * Romanisation rather than Wade-Giles and `king`, `tsing` and `pe` are not
92
+ * Wade-Giles syllables.
93
+ */
94
+ export declare function splitWadeGiles(text: string): readonly string[] | undefined;
95
+ /**
96
+ * Read a whole Wade-Giles word, splitting it first: `maotsetung` is 毛澤東.
97
+ *
98
+ * Takes the first candidate for each syllable rather than every combination,
99
+ * which is the same choice {@link readWadeGilesLoosely}'s ordering offers and
100
+ * for the same reason: a caller looking at a word cannot be handed the 5.23
101
+ * splits and the candidates under each of them and be said to have an answer.
102
+ * Measured over the phrase corpus, that recovers 99.45% of words written with
103
+ * their marks and 56.04% of words written without them.
104
+ *
105
+ * Undefined where the run does not split at all.
106
+ */
107
+ export declare function readWadeGilesWord(text: string): readonly Syllable[] | undefined;
108
+ //# sourceMappingURL=wade-giles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wade-giles.d.ts","sourceRoot":"","sources":["../../src/transcription/wade-giles.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AA6OtE;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,GAAG,MAAM,CAAC;CACrD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAIjE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,gBAAqB,GAC7B,MAAM,CAWR;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,OAAO,GAAE,gBAAqB,GAC7B,MAAM,CAIR;AA+MD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,QAAQ,EAAE,CAG/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,QAAQ,EAAE,CActE;AAsFD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CA+B1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,GACX,SAAS,QAAQ,EAAE,GAAG,SAAS,CAYjC"}