forked from nk2028/tshet-uinh-examples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmsoeg_v8.js
209 lines (194 loc) · 8.67 KB
/
msoeg_v8.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/* msoeg 中古擬音 V8
*
* https://zhuanlan.zhihu.com/p/145409852
*
* @author unt
*/
const is = (...x) => 音韻地位.屬於(...x);
const when = (...x) => 音韻地位.判斷(...x);
let isIPA = 選項.音標體系 !== 'MPA';
let 音標體系changed = 選項._last音標體系 && 選項._last音標體系 !== 選項.音標體系;
let mpaRevoked = false; // 用於在用戶選擇放棄使用 MPA 時刷新選項列表的內容
if (音標體系changed && 選項.音標體系 === 'MPA') {
const message = '請注意 MPA 不是國際音標,其中諸多符號不可按國際音標理解,容易引起誤會。在公共場合使用 MPA 後果自負!\n\n確認使用 MPA?';
if (!confirm(message)) {
isIPA = true;
音標體系changed = false;
mpaRevoked = true;
}
}
if (!音韻地位) return [
['_last音標體系', [1, !mpaRevoked && 選項.音標體系 || '國際音標'], { hidden: true }],
['音標體系', [1, '國際音標', 'MPA'], {
reset: mpaRevoked,
description: [
'擬音中用到的 MPA(即 msoeg 音標)與國際音標對照',
// 詳見 https://zhuanlan.zhihu.com/p/710982203
'(1)\u2002腭噝音(章組)[tɕ] MPA 作 ⟨tç⟩',
'(2)\u2002r 化元音記號 [◌˞\u2006] MPA 作 ⟨◌̣⟩',
'(3)\u2002[i ɯ̯ ɯ u ɛ ɔ a] 對應的 r 化元音 MPA 作 ⟨ị ɨ̣ ɯ̣ ụ ɜ̣ ɞ̣ ạ⟩',
'(4)\u2002[o̯](實即 [ȗ̙])MPA 作 ⟨ᵒ⟩',
'此外,擬音中的 MPA ⟨ɯ/u、ɤ/o、ʌ/ɔ、ɑ⟩ 是非前元音,⟨ɛ、ɜ̣/ɞ̣、ʌ/ɔ⟩ 是中元音,這裏保留 MPA 原貌',
].join('\n'),
}],
['r化元音記號|r 化元音記號\n知乎文章用下加點\n韻鑒用 r 音鉤\n「r 化」原文稱「捲舌」', [
isIPA ? 1 : 3,
{ text: 'r 音鉤(帶空隙)◌˞', value: '\u02DE\u2006' },
{ text: 'r 音鉤(無空隙)◌˞', value: '\u02DE' },
{ text: '下加點 ◌̣', value: '\u0323' },
].slice(0, isIPA ? 3 : 4), { reset: 音標體系changed }],
['通江宕攝韻尾|\n知乎文章用 ŋʷ/kʷ\n韻鑒用 ɴ/q', [3, 'ŋ/k', 'ŋʷ/kʷ', 'ɴ/q']],
['聲調記號|\n上標的 ʔ Unicode 未收,這裏以 ˀ 代替',
[1, '上ʔ 去h', '上ˀ 去ʰ'].filter((_, i) => isIPA || i !== 1),
{ reset: 音標體系changed },
],
['顯示高級選項', false],
選項.顯示高級選項 ? '高級選項' : '',
['保留非三等ʶ記號|保留非三等 ʶ 記號\nʶ 不是標準國際音標',
false, { hidden: !選項.顯示高級選項 }],
['章組|\n知乎文章和韻鑒用腭噝音',
[2, '齦後噝音 tʃ', isIPA ? '腭噝音 tɕ' : '腭噝音 tç'], { hidden: !選項.顯示高級選項 }],
['莊三韻母起始|\n知乎文章用 r 化元音\n韻鑒用 ɻ\n這裏默認用普通的三等起始(B 類或 C 類)',
[1, '普通', 'r 化元音', 'ɻ'], { hidden: !選項.顯示高級選項 }],
['覺韻|\n知乎文章和韻鑒用低元音\n這裏默認用中元音,與江韻一致',
[1, '中元音', '低元音'], { hidden: !選項.顯示高級選項 }], //
['庚三清|\n知乎文章和韻鑒用低元音',
[2, '中元音', '低元音'], { hidden: !選項.顯示高級選項 }],
['宕攝入聲附加|\n知乎文章和韻鑒用 ⁽ʷ⁾\n這裏默認省略',
[1, '無', '⁽ʷ⁾', 'ʷ'], { hidden: !選項.顯示高級選項 || 選項.通江宕攝韻尾?.includes('ʷ') }],
];
function get聲母_默認拼寫() {
// 五十一聲類 + 俟母
const 普通聲母字典 = {
幫: 'p', 滂: 'pʰ', 並: 'b', 明: 'm',
知: 'ʈ', 徹: 'ʈʰ', 澄: 'ɖ', 孃: 'ɳ', 來: 'ɭ',
見: 'k', 溪: 'kʰ', 羣: 'ɡ', 疑: 'ŋ', 云: 'w',
影: 'ʔ', 曉: 'x',
精: 'ts', 清: 'tsʰ', 從: 'dz', 心: 's', 邪: 'z',
莊: 'tʂ', 初: 'tʂʰ', 崇: 'dʐ', 生: 'ʂ', 俟: 'ʐ',
章: 'tɕ', 昌: 'tɕʰ', 常: 'dʑ', 書: 'ɕ', 船: 'ʑ', 日: 'ɲ', 以: 'j',
};
const 小舌化聲母字典 = {
幫: 'pʶ', 滂: 'pʶʰ', 並: 'bʶ', 明: 'mʶ',
端: 'tʶ', 透: 'tʶʰ', 定: 'dʶ', 泥: 'nʶ', 來: 'lʶ',
見: 'q', 溪: 'qʰ', 疑: 'ɴ',
影: 'ʔʶ', 曉: 'χ', 匣: 'ʁ',
精: 'tsʶ', 清: 'tsʶʰ', 從: 'dzʶ', 心: 'sʶ',
};
if (is('云母 開口 非 侵鹽韻')) return 'ɰ';
if (is('以母 合口 或 以母 東鍾虞韻')) return 'ɥ';
if (is('三等 或 來母 二等 非 庚韻')) return 普通聲母字典[音韻地位.母] || 小舌化聲母字典[音韻地位.母];
return 小舌化聲母字典[音韻地位.母] || 普通聲母字典[音韻地位.母];
}
function get聲母() {
let 聲母 = get聲母_默認拼寫();
if (選項.章組.includes('ʃ')) {
聲母 = 聲母.replace('ɕ', 'ʃ').replace('ʑ', 'ʒ');
} else if (選項.章組.includes('ç')) {
聲母 = 聲母.replace('ɕ', 'ç').replace('ʑ', 'ʝ');
}
if (!選項.保留非三等ʶ記號) {
聲母 = 聲母.replace('ʶ', '');
}
return 聲母;
}
function get韻母() {
// 爲了方便推導,對韻類稍作調整
const 韻 = when([
['臻韻 或 莊組 殷韻', '真'],
['文韻', '殷'],
['灰韻', '咍'],
['魂韻', '痕'],
['凡韻', '嚴'],
['東韻 三等', '終'],
['麻韻 三四等', '遮'],
['歌韻 三等', '迦'],
['庚清韻 三等', 選項.庚三清 === '中元音' ? '淸' : '清'],
['庚韻 二等 (端組 或 來母)', '打'],
['江韻 入聲', 選項.覺韻 === '中元音' ? '江' : '覺'],
['', 音韻地位.韻],
]);
const 推導韻到元音 = [
// ŋ u ŋʷi n m
[' 幽 脂真侵', 'i'],
['之蒸 微殷 ', 'ɯ'],
[' 尤終 ', 'u'],
[' 侯東 ', 'ᵒu'],
['支 ', 'ie'], // 知乎原文支韻在 -i 列
['魚 ', 'ɯɤ'],
['虞 鍾 ', 'uo'], // 知乎原文虞韻在 -u 列
['模 ', 'o'], // 知乎原文模韻在 -u 列
[' 淸宵 祭仙鹽', 'iɛ'], // 用“淸”表示庚三清的中元音變體
[' 青蕭 齊先添', 'ɛ'],
['佳耕 皆山咸', 'ɜ̣'],
[' 江 ', 'ɞ̣'],
[' 宵 廢元嚴', 'ɯʌ'],
[' 登 咍痕 ', 'ʌ'],
[' 冬 覃', 'ɔ'],
['遮清 ', 'ia'],
[' 打 ', 'a'],
['麻庚肴 夬刪銜', 'ạ'],
[' 覺 ', 'ɑ̣'],
['迦 陽 ', 'ɯɑ'],
['歌 豪唐泰寒談', 'ɑ'],
];
const 韻尾列表 = ['', 'ŋ/k', 'u', 選項.通江宕攝韻尾, 'i', 'n/t', 'm/p'];
let 匹配行 = 推導韻到元音.find(e => e[0].includes(韻));
let 元音 = 匹配行[1];
let 韻尾 = 韻尾列表[匹配行[0].indexOf(韻)].split('/')[+is('入聲')];
if (韻 === '覺' && !韻尾.includes('ʷ')) {
韻尾 += 'ʷ';
}
if (is('宕攝 入聲') && !韻尾.includes('ʷ')) {
韻尾 += 選項.宕攝入聲附加.replace('無', '');
}
// 處理三等介音
if (元音 === 'ɯ' && is('銳音 或 蒸韻 B類')) {
// 銳音之蒸韻歸 A 類,唇音性蒸韻下面歸 B 類
元音 = 'i' + 元音;
}
if (元音.includes('i') && is('B類 非 幽韻')) {
// 幽韻知乎原文和韻鑒僅 A 類
// 其中,支宵侵的重紐三等開口歸 C 類
元音 = 元音.replace('i', is('支宵韻 或 侵韻 見影組 非 云母') ? 'ɯ' : 'ị');
}
if (選項.莊三韻母起始 === '普通' && is('莊組 三等')) {
// AB 類歸 B 類
元音 = 元音[0].replace('i', 'ị') + 元音.slice(1);
} else if (is('莊組 三等')) {
元音 = 元音[0] + '̣' + 元音.slice(1);
if (!is('支魚韻')) {
// 介音變爲等同於二等的 ɨ̣(支魚的第一部分不是介音)
元音 = 元音[0].replace('ɯ', 'ɨ').replace('i', 'ɨ') + 元音.slice(1);
}
if (選項.莊三韻母起始 !== 'r 化元音') {
// 介音是 ɨ̣ 則改寫爲 ɻ,否則前加 ɻ。支韻的 ị 也改寫爲 ɻ
元音 = 選項.莊三韻母起始 + 元音.replace('ị', '').replace('ɨ̣', '').replace('̣', '');
}
}
// 處理開合介音
if (元音[0] === 'ɯ') {
if (is('合口') || is('幫組 非 支宵侵韻')) {
元音 = 元音.replace('ɯ', 'u');
}
} else if (is('合口 非 云以母 非 虞韻')) {
元音 = 'ʷ' + 元音;
元音 = 元音.replace('ʷɻ', 'ɻʷ');
}
if (isIPA) {
[
['ɨ', 'ɯ'],
['ɜ', 'ɛ'],
['ɞ', 'ɔ'],
].forEach(([a, b]) => { 元音 = 元音.replace(a + '̣', b + '̣'); });
元音 = 元音.replace('ᵒ', 'o̯');
}
元音 = 元音.replace('̣', 選項.r化元音記號);
韻尾 = 韻尾.replace(元音.slice(-1), ''); // 刪去重複字母 ii、uu
return 元音 + 韻尾;
}
function get聲調() {
if (is`平入聲`) return '';
return 選項.聲調記號.split(' ')[+is`去聲`].slice(1);
}
return get聲母() + get韻母() + get聲調();