├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── .github ├── actions │ └── publish-to-cos │ │ └── action.yml └── workflows │ └── build.yml ├── rules └── tagged-is.js ├── package.json ├── yec_en_hua.js ├── README.md ├── test └── main.js ├── tupa.js ├── baxter.js ├── panwuyun.js ├── karlgren.js ├── wangli.js ├── high_tang.js ├── gwongzau.js ├── position.js ├── mid_tang.js ├── eslint.config.js ├── msoeg_v8.js ├── LICENSE ├── putonghua.js ├── onp.js ├── unt.js ├── n_song.js ├── ayaka_v8.js ├── zhongyuan.js ├── zaonhe.js ├── unt_legacy.js └── mongol.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["ms-python.python"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[javascript]": { 3 | "editor.formatOnSave": false 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.github/actions/publish-to-cos/action.yml: -------------------------------------------------------------------------------- 1 | name: Publish to Tencent Cloud COS 2 | description: Publish static files to Tencent Cloud Object Storage 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Setup Python 8 | uses: actions/setup-python@v5 9 | with: 10 | python-version: '3.11' 11 | 12 | - name: Install coscmd 13 | run: pip install coscmd 14 | shell: bash 15 | 16 | - name: Configure coscmd 17 | run: coscmd config -a $SECRET_ID -s $SECRET_KEY -b nk2028-1305783649 -r ap-guangzhou 18 | shell: bash 19 | 20 | - name: Publish static files to COS 21 | run: coscmd upload -rs --delete -f . /tshet-uinh-examples --ignore '*/.*,*/node_modules/*,./test/*,./build/*,./dist/*,./package.json,./package-lock.json,./eslint.config.js' 22 | shell: bash 23 | -------------------------------------------------------------------------------- /rules/tagged-is.js: -------------------------------------------------------------------------------- 1 | export default { 2 | meta: { 3 | type: 'suggestion', 4 | docs: { 5 | description: 'Enforce using tagged template with the `is` function', 6 | }, 7 | fixable: 'code', 8 | schema: [], 9 | }, 10 | create(context) { 11 | return { 12 | CallExpression(node) { 13 | if (node.callee.name === 'is') { 14 | const arg = node.arguments[0]; 15 | if (arg && (arg.type === 'TemplateLiteral' || (arg.type === 'Literal' && typeof arg.value === 'string'))) { 16 | context.report({ 17 | node, 18 | message: 'Use tagged template with the `is` function.', 19 | fix(fixer) { 20 | if (arg.type === 'TemplateLiteral') { 21 | return fixer.replaceText(node, `is${context.getSourceCode().getText(arg)}`); 22 | } 23 | if (arg.type === 'Literal' && typeof arg.value === 'string') { 24 | return fixer.replaceText(node, `is\`${ 25 | arg.raw.slice(1, -1).replace(/\\(('|")|.)|`|\$\{/g, (match, escaped, quote) => quote || `\\${escaped || match}`) 26 | }\``); 27 | } 28 | return null; 29 | }, 30 | }); 31 | } 32 | } 33 | }, 34 | }; 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tshet-uinh-examples", 3 | "version": "20241008.0.0", 4 | "description": "JavaScript code examples to generate the derivatives of the Qieyun phonological system using qieyun-js", 5 | "type": "module", 6 | "main": "./dist/index.js", 7 | "types": "./dist/index.d.ts", 8 | "exports": { 9 | ".": { 10 | "default": "./dist/index.js", 11 | "types": "./dist/index.d.ts" 12 | } 13 | }, 14 | "files": ["dist"], 15 | "scripts": { 16 | "lint": "eslint . --ignore-pattern dist/**/*", 17 | "lint:fix": "eslint --fix . --ignore-pattern dist/**/*", 18 | "build": "node build/main.js", 19 | "test": "node test/main.js" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/nk2028/tshet-uinh-examples.git" 24 | }, 25 | "keywords": [ 26 | "historical-linguistics", 27 | "middle-chinese", 28 | "qieyun" 29 | ], 30 | "author": "nk2028", 31 | "license": "CC0-1.0", 32 | "bugs": { 33 | "url": "https://github.com/nk2028/tshet-uinh-examples/issues" 34 | }, 35 | "homepage": "https://github.com/nk2028/tshet-uinh-examples#readme", 36 | "dependencies": { 37 | "tshet-uinh": "^0.15.1", 38 | "tshet-uinh-deriver-tools": "^0.2.0" 39 | }, 40 | "devDependencies": { 41 | "@stylistic/eslint-plugin-js": "^2.11.0", 42 | "eslint": "^9.15.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [main, dev, dev-*] 7 | pull_request: 8 | branches: [main] 9 | release: 10 | types: [created] 11 | 12 | jobs: 13 | build: 14 | name: ${{ github.event_name == 'release' && 'Publish to NPM' || (github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.repository == 'nk2028/tshet-uinh-examples' && github.ref == 'refs/heads/main')) && 'Publish to Tencent Cloud COS' || 'Test' }} 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout latest code 18 | uses: actions/checkout@v4 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: '22' 24 | registry-url: https://registry.npmjs.org/ 25 | 26 | - name: Install Node.js dependencies 27 | run: npm ci 28 | 29 | - name: Lint schemata 30 | run: npm run lint 31 | 32 | - name: Build project 33 | run: npm run build 34 | 35 | - name: Run tests 36 | run: npm test 37 | 38 | - if: github.event_name == 'release' 39 | name: Publish to NPM 40 | run: npm publish 41 | env: 42 | NODE_AUTH_TOKEN: ${{ secrets.npm_token }} 43 | 44 | - if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.repository == 'nk2028/tshet-uinh-examples' && github.ref == 'refs/heads/main') 45 | name: Publish to Tencent Cloud COS 46 | uses: ./.github/actions/publish-to-cos 47 | env: 48 | SECRET_ID: ${{ secrets.SecretId }} 49 | SECRET_KEY: ${{ secrets.SecretKey }} 50 | -------------------------------------------------------------------------------- /yec_en_hua.js: -------------------------------------------------------------------------------- 1 | /* 不通話 2 | * 3 | * https://www.zhihu.com/question/381250756/answer/2709792926 4 | * https://zhuanlan.zhihu.com/p/572453269 5 | * 6 | * @author 笑也 (JavaScript: unt) 7 | */ 8 | 9 | /** @type { 音韻地位['屬於'] } */ 10 | const is = (...x) => 音韻地位.屬於(...x); 11 | /** @type { 音韻地位['判斷'] } */ 12 | const when = (...x) => 音韻地位.判斷(...x); 13 | 14 | if (!音韻地位) return [ 15 | ['顯示', [1, '正字法', '國際音標']], 16 | ]; 17 | 18 | function get聲母() { 19 | return when([ 20 | // 單豎線左側爲正字法,右側爲國際音標,下同;雙豎線左清右濁 21 | ['云船母', '‖V|ʋ'], 22 | ['精組 三四等 或 知組', 'T|t‖D|n'], // 不含來母 23 | ['莊組', 'S|k‖R|ŋ'], 24 | ['', '|h‖ʻ|'], // 幫端章見影組、精組一等 25 | ]).split('‖')[+is`全濁 或 次濁`]; 26 | } 27 | 28 | function get介音() { 29 | return when([ 30 | ['一四等', '|'], 31 | ['二等', 'U|u'], 32 | // 以下爲三等 33 | ['精組 或 來以母', 'I|i'], 34 | ['銳音 或 云母', '|'], 35 | ['A類 非 麻幽韻', 'I|i'], 36 | ['', 'Y|y'], 37 | ]); 38 | // FIXME 麻幽韻此前是按C類推導,亦不分幽A/B, 39 | // 由於尚未檢驗修改它會造成的影響,故暫未修正。 40 | // (此外亦有:蒸C=B、蒸合=東三) 41 | } 42 | 43 | function get韻核() { 44 | const 韻核列表 = { 45 | 脂〇〇真幽臻: 'O|ʉ', 之蒸微殷〇侵: 'E|ə', 臻: 'O|ʉ', 尤侯東文: 'E|ə', 46 | 〇青齊先蕭添: 'A|a', 〇登咍痕〇覃: 'E|ə', 灰魂: 'E|ə', 模冬江: 'O|ʉ', 47 | 支〇祭仙宵鹽: 'A|a', 魚〇廢元〇嚴: 'E|ə', 凡: 'E|ə', 虞鍾: 'O|ʉ', 48 | 佳耕皆山〇咸: 'E|ə', 49 | 麻庚夬刪肴銜: 'A|a', 歌唐泰寒豪談: 'A|a', 清陽: 'A|a', 50 | }; 51 | return Object.entries(韻核列表).find(e => e[0].includes(音韻地位.韻))[1]; 52 | } 53 | 54 | function get韻尾() { 55 | return when([ 56 | ['齊微皆祭韻', 'J|j'], 57 | ['通江宕曾梗攝', 'N|̃'], 58 | ['深咸攝', 'V|ː'], 59 | ['', '|'], 60 | ]); 61 | } 62 | 63 | function get聲調() { 64 | return when([ 65 | ['全濁 上聲', 'L|˨˩'], 66 | ['入聲', 'C|˧˥'], 67 | ['', '|˥'], 68 | ]); 69 | } 70 | 71 | return [ 72 | get聲母(), get介音(), get韻核(), get韻尾(), get聲調(), 73 | ].map(e => e.split('|')[+(選項.顯示 === '國際音標')]).join(''); 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tshet-uinh-examples 2 | 3 | JavaScript code examples to generate the derivatives of the Qieyun system using TshetUinh.js 4 | 5 | ## Usage 6 | 7 | ``` 8 | https://nk2028-1305783649.file.myqcloud.com/tshet-uinh-examples/ 9 | ``` 10 | 11 | ## List of included examples 12 | 13 | **音韻地位 phonological position:** `position.js` 14 | 15 | **切韻音系拼音或轉寫 romanization/transcription of the Qieyun system** 16 | 17 | - 切韻拼音 (Tshet-uinh Phonetic Alphabet): `tupa.js` 18 | - 白一平轉寫 (Baxter’s Transcription): `baxter.js` 19 | 20 | **切韻音系擬音 reconstruction of the Qieyun system** 21 | 22 | - 高本漢擬音 (Bernhard Karlgren’s Reconstruction): `karlgren.js` 23 | - 王力擬音 (Wang Li’s Reconstruction): `wangli.js` 24 | - 潘悟雲擬音 (Pan Wuyun’s Reconstruction): `panwuyun.js` 25 | - unt 擬音 (unt’s Reconstruction): `unt.js` 26 | - unt 過往擬音 (unt’s Legacy Reconstructions): `unt_legacy.js` 27 | - msoeg 擬音 V8 (msoeg’s Reconstruction V8): `msoeg_v8.js` 28 | 29 | **推導後世音系 extrapolated phonological system of later periods** 30 | 31 | - 推導盛唐(平水韻)擬音 (Extrapolated Reconstruction of High Tang Chinese (*Pingshui Yun*)): `high_tang.js` 32 | - 推導中唐(韻圖)擬音 (Extrapolated Reconstruction of Middle Tang Chinese (*Yuntu*)): `mid_tang.js` 33 | - 推導北宋(聲音唱和圖)擬音 (Extrapolated Reconstruction of Northern Song Chinese (*Shengyin Changhe Tu*)): `n_song.js` 34 | - 推導《蒙古字韻》 (Extrapolated _Menggu Ziyun_): `mongol.js` 35 | - 推導《中原音韻》擬音 (Extrapolated Reconstruction of _Zhongyuan Yinyun_): `zhongyuan.js` 36 | 37 | **近代方言推導音 extrapolated pronunciations of early modern dialects** 38 | 39 | - 推導老國音 (Extrapolated Old National Pronunciation): `onp.js` 40 | 41 | **現代方言推導音 extrapolated pronunciations of modern dialects** 42 | 43 | - 推導普通話 (Extrapolated Putonghua): `putonghua.js` 44 | - 推導廣州話 (Extrapolated Cantonese): `gwongzau.js` 45 | - 推導上海話 (Extrapolated Shanghainese): `zaonhe.js` 46 | 47 | **人造音系 artificial phonological system** 48 | 49 | - 綾香思考音系 (Ayaka’s Phonological System for Thinking): `ayaka_v8.js` 50 | - 不通話 (Yec-en-ʻua): `yec_en_hua.js` 51 | -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | import { readdirSync } from 'node:fs'; 2 | import util from 'node:util'; 3 | 4 | import { 資料, 音韻地位 } from 'tshet-uinh'; 5 | import * as TshetUinhExamples from '../dist/index.js'; 6 | 7 | const 地位 = 音韻地位.from描述('書開三宵上'); 8 | 9 | const testCases = [ 10 | ['position', '書開三宵上'], 11 | ['tupa', 'sjiewq'], 12 | ['baxter', 'syewX'], 13 | ['karlgren', 'ɕi̯ɛu꞉'], 14 | ['wangli', '꜂ɕĭɛu'], 15 | ['panwuyun', 'ɕiᴇu˧˥'], 16 | ['unt', 'ɕéw'], 17 | ['unt_legacy', 'ɕéw'], 18 | ['msoeg_v8', 'ɕiɛuʔ'], 19 | ['high_tang', 'ɕéw'], 20 | ['mid_tang', 'ɕɛ́w'], 21 | ['n_song', 'ɕjɛ́w'], 22 | ['mongol', 'ꡮꡠꡓ'], 23 | ['zhongyuan', 'ʂjɛw³'], 24 | ['onp', 'ㄕㄠˇ'], 25 | // ['fanwan', 'shiu2'], 26 | ['putonghua', 'shǎo'], 27 | ['gwongzau', 'siu2'], 28 | ['zaonhe', 'sɔ̄'], 29 | ['ayaka_v8', 'seu'], 30 | ['yec_en_hua', 'A'], 31 | ]; 32 | 33 | const directorySchemata = new Set(readdirSync('.').flatMap(file => file !== (file = file.replace(/\.js$/, '')) && !file.endsWith('.config') ? [file] : [])); 34 | const nonExistentSchemata = new Set(); 35 | 36 | let passed = 0; 37 | let total = 0; 38 | for (const [schema, expected] of testCases) { 39 | if (!(schema in TshetUinhExamples)) { 40 | nonExistentSchemata.add(schema); 41 | continue; 42 | } 43 | 44 | console.log(`Testing: ${schema}`); 45 | total++; 46 | try { 47 | const deriver = TshetUinhExamples[schema](); 48 | Array.from(資料.iter音韻地位(), deriver); // Ensure no error is thrown from all 音韻地位 49 | const result = deriver(地位); 50 | if (result === expected) { 51 | passed++; 52 | } else { 53 | console.log( 54 | ` Expected ${util.inspect(expected)}, got ${util.inspect(result)}` 55 | ); 56 | } 57 | } catch (e) { 58 | console.log(` Error thrown: ${util.inspect(e)}`); 59 | } 60 | 61 | directorySchemata.delete(schema); 62 | } 63 | 64 | console.log(`${passed}/${total} tests passed.`); 65 | if (directorySchemata.size || nonExistentSchemata.size || passed < total) { 66 | if (directorySchemata.size) console.log('The following schemata are untested:', directorySchemata); 67 | if (nonExistentSchemata.size) console.log('There are test cases for the following schemata but they are missing:', nonExistentSchemata); 68 | process.exit(1); 69 | } 70 | -------------------------------------------------------------------------------- /tupa.js: -------------------------------------------------------------------------------- 1 | /* 切韻拼音 2 | * 3 | * https://phesoca.com/tupa/ 或 4 | * https://www.bilibili.com/read/cv19972367 或 5 | * https://zhuanlan.zhihu.com/p/478751152 6 | * 7 | * @author unt 8 | */ 9 | 10 | /** @type { 音韻地位['屬於'] } */ 11 | const is = (...x) => 音韻地位.屬於(...x); 12 | /** @type { 音韻地位['判斷'] } */ 13 | const when = (...x) => 音韻地位.判斷(...x); 14 | 15 | if (!音韻地位) return []; 16 | 17 | function get聲母() { 18 | return { 19 | 幫: 'p', 滂: 'ph', 並: 'b', 明: 'm', 20 | 端: 't', 透: 'th', 定: 'd', 泥: 'n', 來: 'l', 21 | 知: 'tr', 徹: 'trh', 澄: 'dr', 孃: 'nr', 22 | 見: 'k', 溪: 'kh', 羣: 'g', 疑: 'ng', 云: '', 23 | 影: 'q', 曉: 'h', 匣: 'gh', 24 | 精: 'ts', 清: 'tsh', 從: 'dz', 心: 's', 邪: 'z', 25 | 莊: 'tsr', 初: 'tsrh', 崇: 'dzr', 生: 'sr', 俟: 'zr', 26 | 章: 'tj', 昌: 'tjh', 常: 'dj', 書: 'sj', 船: 'zj', 日: 'nj', 以: 'j', 27 | }[音韻地位.母]; 28 | } 29 | 30 | function get韻母() { 31 | let 韻母 = when([ 32 | ['脂韻', 'i'], ['之韻', 'y'], ['尤侯韻', 'u'], 33 | ['支韻', 'e'], ['佳韻', 'ee'], ['魚韻', 'eo'], ['虞模韻', 'o'], 34 | ['麻韻', 'ae'], ['歌韻', 'a'], 35 | 36 | ['蒸韻 AB類', 'ing'], ['蒸韻', 'yng'], ['東韻', 'ung'], 37 | ['青韻', 'eng'], ['耕韻', 'eeng'], ['登韻', 'eong'], ['冬鍾韻', 'ong'], ['江韻', 'oeung'], 38 | ['庚清韻', 'aeng'], ['陽唐韻', 'ang'], 39 | 40 | ['微韻', 'uj'], 41 | ['齊祭韻', 'ej'], ['皆韻', 'eej'], ['灰咍廢韻', 'oj'], 42 | ['夬韻', 'aej'], ['泰韻', 'aj'], 43 | 44 | ['真臻韻', 'in'], ['殷文韻', 'un'], 45 | ['先仙韻', 'en'], ['山韻', 'een'], ['元魂痕韻', 'on'], 46 | ['刪韻', 'aen'], ['寒韻', 'an'], 47 | 48 | ['幽韻', 'iw'], 49 | ['蕭宵韻', 'ew'], 50 | ['肴韻', 'aew'], ['豪韻', 'aw'], 51 | 52 | ['侵韻', 'im'], 53 | ['鹽添韻', 'em'], ['咸韻', 'eem'], ['覃嚴凡韻', 'om'], 54 | ['銜韻', 'aem'], ['談韻', 'am'], 55 | ]); 56 | // 不圓脣元音 57 | if (is`開口` && !韻母.endsWith('m')) 韻母 = 韻母.replace(/^u/, 'y').replace(/^o/, 'eo'); 58 | // 等類標記 59 | if (is`三等` || is`四等` && 韻母.startsWith('ae')) { 60 | if (is`A類` || is`銳音 非 莊組` && /^i|^e(?!o)|^ae/.test(韻母)) { 61 | // A 類以 i- 標記 62 | if (!韻母.startsWith('i')) 韻母 = 'i' + 韻母; 63 | } else { 64 | // B、C 類以 y-/u- 標記 65 | if (/^[uo]|^a(?!e)/.test(韻母) ? is`開口` : is`非 合口`) { 66 | if (!韻母.startsWith('y')) 韻母 = 'y' + 韻母; 67 | 韻母 = 韻母.replace('yeo', 'yo'); 68 | } else { 69 | if (!韻母.startsWith('u')) 韻母 = 'u' + 韻母; 70 | } 71 | } 72 | } else { 73 | // 高元音非三等以 o- 標記 74 | if (/^[yu]/.test(韻母)) 韻母 = 'o' + 韻母; 75 | } 76 | if (is`合口` && !/^[uo]/.test(韻母)) 韻母 = 'w' + 韻母; 77 | if (is`入聲`) 韻母 = 韻母 78 | .replace('ng', 'k') 79 | .replace('n', 't') 80 | .replace('m', 'p'); 81 | return 韻母; 82 | } 83 | 84 | function get聲調() { 85 | return { 上: 'q', 去: 'h' }[音韻地位.聲] || ''; 86 | } 87 | 88 | return get聲母() + get韻母() + get聲調(); 89 | -------------------------------------------------------------------------------- /baxter.js: -------------------------------------------------------------------------------- 1 | /* 白一平轉寫 2 | * 3 | * - Baxter, W. H. (1992). A Handbook of Old Chinese Phonology. De Gruyter Mouton. 4 | * - Baxter, W. H., & Sagart, L. (2014). Old Chinese: A New Reconstruction. Oxford University Press. 5 | * 6 | * @author Ayaka 7 | */ 8 | 9 | /** @type { 音韻地位['屬於'] } */ 10 | const is = (...x) => 音韻地位.屬於(...x); 11 | /** @type { 音韻地位['判斷'] } */ 12 | const when = (...x) => 音韻地位.判斷(...x); 13 | 14 | if (!音韻地位) return [ 15 | // 版本可選 '1992' 或 '2014',預設值為 '2014' 16 | ['版本', [2, '1992', '2014']], 17 | ]; 18 | 19 | let 聲母 = { 20 | 幫: 'p', 滂: 'ph', 並: 'b', 明: 'm', 21 | 端: 't', 透: 'th', 定: 'd', 泥: 'n', 來: 'l', 22 | 知: 'tr', 徹: 'trh', 澄: 'dr', 孃: 'nr', 23 | 精: 'ts', 清: 'tsh', 從: 'dz', 心: 's', 邪: 'z', 24 | 莊: 'tsr', 初: 'tsrh', 崇: 'dzr', 生: 'sr', 俟: 'zr', 25 | 章: 'tsy', 昌: 'tsyh', 常: 'dzy', 日: 'ny', 書: 'sy', 船: 'zy', 以: 'y', 26 | 見: 'k', 溪: 'kh', 羣: 'g', 疑: 'ng', 27 | 影: "'", 曉: 'x', 匣: 'h', 云: 'h', 28 | }[音韻地位.母]; 29 | 30 | if (選項.版本 === '1992' && 聲母 === "'") { 31 | 聲母 = 'ʔ'; 32 | } 33 | 34 | let 韻母 = { 35 | // 一等韻 36 | 東: 'uwng', 37 | 冬: 'owng', 38 | 模: 'u', 39 | 泰: 'aj', 40 | 灰: 'oj', 41 | 咍: 'oj', 42 | 魂: 'on', 43 | 痕: 'on', 44 | 寒: 'an', 45 | 豪: 'aw', 46 | 歌: 'a', 47 | 唐: 'ang', 48 | 登: 'ong', 49 | 侯: 'uw', 50 | 覃: 'om', 51 | 談: 'am', 52 | 53 | // 二等韻 54 | 江: 'aewng', 55 | 佳: 'ea', 56 | 皆: 'eaj', 57 | 夬: 'aej', 58 | 刪: 'aen', 59 | 山: 'ean', 60 | 肴: 'aew', 61 | 麻: 'ae', 62 | 庚: 'aeng', 63 | 耕: 'eang', 64 | 咸: 'eam', 65 | 銜: 'aem', 66 | 67 | // 四等韻 68 | 齊: 'ej', 69 | 先: 'en', 70 | 蕭: 'ew', 71 | 青: 'eng', 72 | 添: 'em', 73 | 74 | // 三等陰聲韻 75 | 支: 'je', 76 | 脂: 'ij', 77 | 之: 'i', 78 | 微: 'j+j', 79 | 魚: 'jo', 80 | 虞: 'ju', 81 | 祭: 'jej', 82 | 廢: 'joj', 83 | 宵: 'jew', 84 | // 歌: 'ja', 85 | // 麻: 'jae', 86 | 尤: 'juw', 87 | 幽: 'jiw', 88 | 89 | // 三等陽聲韻 90 | // 東: 'juwng', 91 | 鍾: 'jowng', 92 | 真: 'in', 93 | 臻: 'in', 94 | 文: 'jun', 95 | 殷: 'j+n', 96 | 元: 'jon', 97 | 仙: 'jen', 98 | 陽: 'jang', 99 | // 庚: 'jaeng', 100 | 清: 'jeng', 101 | 蒸: 'ing', 102 | 侵: 'im', 103 | 鹽: 'jem', 104 | 嚴: 'jaem', 105 | 凡: 'jom', 106 | }[音韻地位.韻]; 107 | 108 | // 東歌麻庚韻同時含三等與非三等,上文僅處理非三等,此處處理三等 109 | // 「四等」是考慮端組在內 110 | if (is`東歌麻庚韻 三四等`) { 111 | 韻母 = 'j' + 韻母; 112 | } 113 | 114 | if (選項.版本 === '1992') { 115 | if (韻母 === 'ea') 韻母 = 'ɛɨ'; 116 | 韻母 = 韻母.replace('+', 'ɨ').replace('ae', 'æ').replace('ea', 'ɛ'); 117 | } 118 | 119 | // 章組或日以母只與三等韻相拼,省去韻母起始的 j 120 | if (is`章組 或 日以母` && 韻母.startsWith('j')) { 121 | 韻母 = 韻母.slice(1); 122 | } 123 | 124 | // 重紐 A 類添加 j 或 i 125 | if (is`A類 非 麻幽陽韻`) { 126 | if (韻母.startsWith('j')) 韻母 = 'ji' + 韻母.slice(1); 127 | else 韻母 = 'j' + 韻母; 128 | } 129 | 130 | // 合口字添加 w 131 | if (is`(合口 或 灰魂韻) 非 虞文凡韻`) { 132 | if (韻母.startsWith('j')) 韻母 = 'jw' + 韻母.slice(1); 133 | else 韻母 = 'w' + 韻母; 134 | } 135 | 136 | if (is`入聲`) { 137 | if (韻母.endsWith('m')) 韻母 = 韻母.slice(0, -1) + 'p'; 138 | else if (韻母.endsWith('n')) 韻母 = 韻母.slice(0, -1) + 't'; 139 | else if (韻母.endsWith('ng')) 韻母 = 韻母.slice(0, -2) + 'k'; 140 | } 141 | 142 | const 聲調 = { 143 | 上: 'X', 144 | 去: 'H', 145 | }[音韻地位.聲] || ''; 146 | 147 | return 聲母 + 韻母 + 聲調; 148 | -------------------------------------------------------------------------------- /panwuyun.js: -------------------------------------------------------------------------------- 1 | /* 潘悟雲擬音 2 | * 3 | * 3 個版本: 4 | * 5 | * - 潘悟雲. 2000. 漢語歷史音韻學. 上海: 上海教育出版社. 6 | * - 潘悟雲 & 張洪明. 2013. 漢語中古音. 語言研究 33(2), 1–7. 7 | * - 潘悟雲. 2023. 漢語古音手冊. 上海: 中西書局. 8 | * 9 | * @author unt 10 | */ 11 | 12 | /** @type { 音韻地位['屬於'] } */ 13 | const is = (...x) => 音韻地位.屬於(...x); 14 | /** @type { 音韻地位['判斷'] } */ 15 | const when = (...x) => 音韻地位.判斷(...x); 16 | 17 | const is2000 = Boolean(選項.版本?.includes('2000')); 18 | const is2013 = Boolean(選項.版本?.includes('2013')); 19 | const is2023 = 選項.版本?.includes('2023') ?? true; 20 | const 三C介音 = 選項.非前三等介音 ? 選項.非前三等介音.split('(')[0] : 'i'; 21 | 22 | if (!音韻地位) return [ 23 | ['版本', [3, 24 | '2000:漢語歷史音韻學', 25 | '2013:漢語中古音', 26 | '2023:漢語古音手冊', 27 | ], { 28 | description: 29 | is2000 && '《漢語歷史音韻學》勘誤\n生母作 ʃ,係誤植,應爲 ʂ。在推導方案中已更正' || 30 | is2013 && '《漢語中古音》勘誤\n(1)\u2002從母聲母表作 ʣ,係排版錯誤,此處依正文作 dz\n(2)\u2002俟母聲母表未列,此處補上,爲 ʐ' || 31 | is2023 && '《漢語古音手冊》(第一版)勘誤\n(1)\u2002莊組拼重紐三等韻未加 ɨ 介音,再版將加上\n(2)\u2002燭韻作 i̯ʊk,附加符號多餘,再版將去除\n(3)\u2002微韻作 ɤi,遺漏 i 介音,再版將加上\n以上在推導方案中已更正', 32 | }], 33 | ['非前三等介音', [1, 'i(原書簡寫)', 'ɨ(實際音值)']], 34 | ['聲調記號', [2, '隱藏', '五度符號', '調值數字'], { 35 | description: 36 | is2000 && '《漢語歷史音韻學》未給具體調值,此處依《漢語中古音》(2013)調值' || 37 | is2023 && '《漢語古音手冊》未給出調值,此處依《漢語中古音》(2013)調值' || '', 38 | }], 39 | ['送氣記號', [1, 'ʰ(通用)', 'h(原書)'], { hidden: !is2000 }], 40 | ['支韻', [1, 'iɛ(簡寫)', 'iᵉ(實際音值)'], { hidden: !is2000 }], 41 | ['虞韻', [2, 'io(簡寫)', 'iʊ(實際音值)'], { hidden: !is2000 }], 42 | ]; 43 | /* 韻典網與本方案 2000 擬音不同之處: 44 | * 45 | * - 歌一合誤作 uɑ,應爲 ʷɑ 46 | * - 部分祭合、薛合誤作 iei、iet,應爲 iɛi、iɛt 47 | * - 幫組陽韻歸合口。原書雖未指明,但暗示爲開口;本方案歸開口 48 | * - 送氣記號改作 ʰ,原書作 h;本方案可自選 49 | * - 支韻、虞韻作 iɛ、io,原書韻母擬音比較表作 iᵉ、iʊ;本方案可自選 50 | */ 51 | 52 | function get聲母() { 53 | let 聲母 = { 54 | 幫: 'p', 滂: 'pʰ', 並: 'b', 明: 'm', 55 | 端: 't', 透: 'tʰ', 定: 'd', 泥: 'n', 來: 'l', 56 | 知: 'ʈ', 徹: 'ʈʰ', 澄: 'ɖ', 孃: 'ɳ', 57 | 見: 'k', 溪: 'kʰ', 羣: 'ɡ', 疑: 'ŋ', 58 | 影: 'ʔ', 曉: 'h', 匣: 'ɦ', 云: is2023 ? 'ɦᶤ' : 'ɦ', 59 | 精: 'ts', 清: 'tsʰ', 從: 'dz', 心: 's', 邪: 'z', 60 | 莊: 'tʂ', 初: 'tʂʰ', 崇: 'dʐ', 生: 'ʂ', 俟: 'ʐ', 61 | 章: 'tɕ', 昌: 'tɕʰ', 常: 'dʑ', 書: 'ɕ', 船: 'ʑ', 日: 'ȵ', 以: 'j', 62 | }[音韻地位.母]; 63 | if (is2000) 聲母 = 聲母.replace('ʰ', 選項.送氣記號[0]); 64 | return 聲母; 65 | } 66 | 67 | function get韻母() { 68 | const 韻 = 音韻地位.韻.replace('凡', '嚴'); 69 | const 元音表 = { 70 | ɪ: '   臻  ', 71 | i: '脂 侵真 幽', ɨ: '之蒸 殷微尤', u: '侯東 文  ', 72 | e: ' 青添先齊蕭', ə: ' 登覃痕咍 ', o: '模冬 魂灰 ', 73 | ᴇ: '支清鹽仙祭宵', ɤ: '魚  元  ', ʊ: '虞鍾    ', 74 | ɛ: '佳耕咸山皆 ', a: ' 陽嚴 廢 ', ɔ: ' 江    ', 75 | æ: '麻庚銜刪夬肴', ɑ: '歌唐談寒泰豪', 76 | }; 77 | const 韻尾列表 = [''].concat(is`舒聲` ? [...'ŋmniu'] : [...'kpt']); 78 | let 韻核 = Object.keys(元音表).find(e => 元音表[e].includes(韻)); 79 | let 韻尾 = 韻尾列表[元音表[韻核].indexOf(韻)]; 80 | 81 | let 介音 = ''; 82 | if (is`合口` && ![...'mpu'].includes(韻尾) && ![...'uoʊɔ'].includes(韻核)) 83 | 介音 += 'ʷ'; 84 | if (is`幫組` && ![...'ŋkmpu'].includes(韻尾) && [...'ɨəɤaɑ'].includes(韻核) && !is`泰韻` || !is2023 && is`凡韻`) 85 | 介音 += is2013 ? 'u̯' : 'ʷ'; 86 | // 云母 B 類不寫介音 87 | // 《漢語歷史音韻學》韻母擬音比較表庚三直接寫作 B 類,但此處拼銳音和云母時不加介音 88 | // 《漢語中古音》《漢語古音手冊》庚三作無重紐三等,此處暫歸 C 類 89 | if (!is2000 && is`庚韻 三等`) { 90 | 介音 += 三C介音; 91 | } else { 92 | if (is`二等 或 B類 非 蒸幽韻 非 云母` || is2023 && [...'ɪiᴇæ'].includes(韻核) && is`莊組 三等 非 庚韻`) 93 | 介音 += is2000 ? 'ɯ' : is2013 ? 'ɣ' : is`二等` ? 'ᵚ' : 'ɨ'; 94 | if (is`三等` && ![...'ɪiɨ'].includes(韻核) && !介音.includes('ɨ') || is`端組 麻庚清韻 四等`) 95 | 介音 += [...'ᴇæ'].includes(韻核) ? 'i' : 三C介音; 96 | } 97 | if ([...'oʊ'].includes(韻核)) 98 | 介音 += is2013 ? 'u̯' : 99 | 介音 ? '' : is2000 ? 'u' : 'u̯'; 100 | if (is2023 && is`凡韻`) 101 | 介音 += 'u̯'; 102 | 103 | // 韻核相關調整 104 | if (is2000) { 105 | const 韻核鏈移列表 = [...'ᴇɛæaɐ']; // “鏈移”只是比喻 106 | if (韻核鏈移列表.includes(韻核)) 韻核 = 韻核鏈移列表[韻核鏈移列表.indexOf(韻核) + 1]; 107 | 108 | 韻核 = { 109 | 尤: 'i', 幽: 'ɨ', 侯: 'əu', 110 | 支: 選項.支韻[1], 魚: 'ɔ', 虞: 選項.虞韻[1], 111 | 元: 'ɐ', 鍾: 'o', 112 | }[音韻地位.韻] ?? 韻核; 113 | } else if (is2013) { 114 | 韻核 = 韻核.replace('ʊ', 'o̝'); 115 | } else { // is2023 116 | 韻核 = { 117 | 尤: 'i', 幽: 'ɨ', 微: 'ɤ', 118 | 支: 'e', 119 | }[音韻地位.韻] ?? 韻核; 120 | if (is`微韻`) 介音 += 三C介音; 121 | } 122 | return 介音 + 韻核 + 韻尾; 123 | } 124 | 125 | function get聲調() { 126 | return 選項.聲調記號 === '隱藏' ? '' : { 127 | '五度符號': ['˧', '˧˥', '˥˩', '꜊'], 128 | '調值數字': ['³³', '³⁵', '⁵¹', '³'], 129 | }[選項.聲調記號]['平上去入'.indexOf(音韻地位.聲)]; 130 | } 131 | 132 | return get聲母() + get韻母() + get聲調(); 133 | -------------------------------------------------------------------------------- /karlgren.js: -------------------------------------------------------------------------------- 1 | /* 高本漢擬音 2 | * 3 | * 擬音來自高本漢後期著作: 4 | * 5 | * - Grammata Serica. BMFEA, 1940, 12: 1–471. 6 | * - Compendium of Phonetics in Ancient and Archaic Chinese. BMFEA, 1954, 26: 211–367. 7 | * - Grammata Serica Recensa. BMFEA, 1957, 29: 1–332. 8 | * - 中國聲韻學大綱. 張洪年, 譯. 香港: 香港中文大學研究院中國語言文學會, 1968. (臺北: 中華叢書編審委員會, 1972) 9 | * - 中上古漢語音韻綱要. 聶鴻音, 譯. 濟南: 齊魯書社, 1987. 10 | * - 漢文典(修訂本). 潘悟雲, 楊劍橋, 陳重業, 張洪明, 編譯. 上海: 上海辭書出版社, 1997. 11 | * 12 | * 以及後來學者的整理: 13 | * 14 | * - Samuel E. Martin. The Phonemes of Ancient Chinese. JAOS, 1953, 73 (2): Supplement. 15 | * - 李榮. 高本漢構擬的切韵音. 切韵音系. 北京: 科學出版社, 1956: 104–106. (黃笑山, 校訂. 北京: 商務印書館, 2020) 16 | * - 李方桂. 中古音系. 上古音研究. 北京: 商務印書館, 1980: 5–9. 17 | * - 潘悟雲. 諸家《切韻》聲類擬音比較表, 諸家《切韻》韻母擬音比較表. 漢語歷史音韻學. 上海: 上海教育出版社, 2000: 59–61, 83–88. 18 | * 19 | * 這些後期著作與高本漢早期的 Études sur la phonologie chinoise(《中國音韻學研究》)相比, 20 | * 不僅個別聲韻母的擬音作了改動,所採用的音標字母、介音的拼寫風格也完全不同。 21 | * 再考慮到今天引用高本漢擬音一般是引用其後期擬音,因此本方案暫不收錄其早期擬音。 22 | * 23 | * 音標提供 3 種風格: 24 | * 25 | * - 原書音標:高本漢後期著作採用的拉丁字母音標 26 | * - 國際音標(原貌):《中國音韻學研究》中譯本風格(但原書 ɡ 作 g,不採用) 27 | * - 國際音標(通用):現在的中國通用音標符號(即比標準國際音標多 ȶ、ȡ、ȵ) 28 | * 29 | * 聲調提供 3 種風格: 30 | * 31 | * - 不標 32 | * - 平ˉ 上ˊ 去ˋ:Grammata Serica 和 Compendium 的標法 33 | * - 上꞉ 去˗:Grammata Serica Recensa 的標法 34 | * 35 | * @author unt 36 | */ 37 | 38 | /** @type { 音韻地位['屬於'] } */ 39 | const is = (...x) => 音韻地位.屬於(...x); 40 | /** @type { 音韻地位['判斷'] } */ 41 | const when = (...x) => 音韻地位.判斷(...x); 42 | 43 | const 音標字典 = { 44 | '原書音標': { 45 | ʰ: 'ʼ', ʱ: 'ʼ', 46 | ʔ: 'ꞏ', ɡ: 'g', ŋ: 'ng', 47 | ȶ: 't̑', ȡ: 'd̑', ȵ: 'ń', // 上加弧線是瑞典方言字母表腭化的一種方式,不是揚抑符 48 | ɕ: 'ś', ʑ: 'ź', 49 | ʂ: 'ṣ', ʐ: 'ẓ', 50 | x: 'χ', ɣ: 'γ', 51 | 52 | ă: 'ă', ɑ̆: 'ậ', ĕ: 'ĕ', 53 | ɛ: 'ä', ɔ: 'å', 54 | // 央次低元音原書作“ɒ”形,實際上是 ɐ 的斜體,不是很多人引用成的 ɒ。這個符號來自瑞典方言字母 55 | æ: 'ɛ', ɐ: 選項.央次低元音?.slice(0, 1) || 'ɐ', 56 | ɑ: 'â', 57 | }, 58 | '國際音標(原貌)': { 59 | ʰ: 'ʻ', ʱ: 'ʻ', ʔ: 'ˀ', // ɡ: 'g', 60 | tʂ: 'ʈʂ', dʐ: 'ɖʐ', 61 | tɕ: 'ȶɕ', dʑ: 'ȡʑ', 62 | }, 63 | '國際音標(通用)': { 64 | ʱ: 選項.濁送氣 || 'ʰ', 65 | }, 66 | }; 67 | 68 | if (!音韻地位) return [ 69 | ['音標體系', [3].concat(Object.keys(音標字典))], 70 | ['聲調記號', [3, '不標', '平ˉ 上ˊ 去ˋ', '上꞉ 去˗']], 71 | ['央次低元音', 選項.音標體系?.includes('原書') ? [1, 'ɐ(準確)', 'ɒ(流行但不準確)'] : null], 72 | ['濁送氣', !選項.音標體系 || 選項.音標體系.includes('通用') ? [1, 'ʰ', 'ʱ'] : null], 73 | ]; 74 | 75 | function get聲母() { 76 | let 聲母 = { 77 | 幫: 'p', 滂: 'pʰ', 並: 'bʱ', 明: 'm', 78 | 端: 't', 透: 'tʰ', 定: 'dʱ', 泥: 'n', 來: 'l', 79 | 知: 'ȶ', 徹: 'ȶʰ', 澄: 'ȡʱ', 孃: 'ȵ', 80 | 見: 'k', 溪: 'kʰ', 羣: 'ɡʱ', 疑: 'ŋ', 81 | 影: 'ʔ', 曉: 'x', 匣: 'ɣ', 以: '', 82 | 精: 'ts', 清: 'tsʰ', 從: 'dzʱ', 心: 's', 邪: 'z', 83 | 莊: 'tʂ', 初: 'tʂʰ', 崇: 'dʐʱ', 生: 'ʂ', 俟: 'dʐʱ', 84 | 章: 'tɕ', 昌: 'tɕʰ', 船: 'dʑʱ', 書: 'ɕ', 常: 'ʑ', 日: 'ȵʑ', 云: 'j', 85 | // 注意云以、常船是顛倒的,俟同崇 86 | }[音韻地位.母]; 87 | return 聲母; 88 | } 89 | 90 | function get韻母() { 91 | const 韻 = { 92 | 文: '殷', 魂: '痕', 灰: '咍', 凡: '嚴', 93 | 之: '脂', 夬: '佳', // 這兩對高本漢無法找到區分方法 94 | }[音韻地位.韻] ?? 音韻地位.韻; 95 | const 元音表 = { 96 | // 三等的 ə、o 暫加短音符以便與一等區分,之後移除 97 | i: '脂     ', ï: '      ', u: '虞東', 98 | ĕ: '   真幽 ', ə̆: ' 蒸 殷 侵', ŏ: '魚鍾', e̯i: '微', ə̯̆u: '尤', 99 | e: ' 青齊先蕭添', ə: ' 登 痕  ', o: '模冬', ie̯: '支', ə̯u: '侯', 100 | ɛ: ' 清祭仙宵鹽', ɐ: ' 庚廢元 嚴', ɔ: ' 江', 101 | æ: ' 耕 臻  ', 102 | ă: '  皆山 咸', ɑ̆: '  咍  覃', 103 | a: '麻陽佳刪肴銜', ɑ: '歌唐泰寒豪談', 104 | }; 105 | const 韻尾列表 = is`舒聲` ? ['', ...'ŋinum'] : [...' k t p']; 106 | 107 | let 韻核 = Object.keys(元音表).find(e => 元音表[e].includes(韻)); 108 | let 韻尾 = 韻尾列表[元音表[韻核].indexOf(韻)]; 109 | 韻核 = 韻核.replace('ə̆', 'ə').replace('ŏ', 'o'); 110 | let 介音 = ''; 111 | if (is`止攝 (鈍音 非 云母 或 來母)`) 介音 += 'j'; // 云母已經是 j,無需加 112 | if (is`三等 非 止攝`) 介音 += 'i̯'; 113 | if (is`四等`) 介音 += 'i'; 114 | 介音 += when([ 115 | ['模冬灰文魂韻', 'u'], 116 | ['歌寒韻 非 開口', 'u'], // 戈桓 117 | ['真韻 合口 (A類 或 銳音 非 莊組)', 'u'], // 諄 118 | ['合口 非 虞韻 或 魚鍾凡韻', 'w'], // 虞韻元音已是 u 119 | ['幫組', [ 120 | ['微韻', 'w'], 121 | ['廢元韻 或 庚韻 三等', 'w'], // ɐ 122 | ['耕韻 明母', 'w'], // æ 123 | ['陽夬刪韻', 'w'], // a, 但麻佳韻原書無 w(儘管擬音不分佳夬) 124 | ['皆韻 或 山韻 入聲', 'w'], // ă,但山韻舒聲原書無 w 125 | ['泰韻 或 唐韻 舒聲', 'w'], // ɑ,同歌寒,但唐韻入聲原書無 w 126 | // 個別字合口的情況不計入,如《漢文典》中: 127 | // “邊”歸合口,但同小韻的“編”歸開口 128 | // “憫”歸合口,但同小韻的“緡”歸開口 129 | ]], 130 | ['', ''], 131 | ], '', true); 132 | 133 | return 介音 + 韻核 + 韻尾; 134 | } 135 | 136 | function get聲調() { 137 | if (選項.聲調記號 === '四角標圈') return { 138 | 139 | }; 140 | const 聲調記號字典 = Object.fromEntries(選項.聲調記號.split(' ').map(e => [...e])); 141 | return 聲調記號字典[音韻地位.聲] ?? ''; 142 | } 143 | 144 | let 音節 = get聲母() + get韻母() + get聲調(); 145 | Object.entries(音標字典[選項.音標體系]).forEach(([k, v]) => { 音節 = 音節.replace(k, v); }); 146 | return 音節; 147 | -------------------------------------------------------------------------------- /wangli.js: -------------------------------------------------------------------------------- 1 | /* 王力擬音 2 | * 3 | * 擬音來自王力著作: 4 | * 5 | * - 漢語史稿 [M]. 北京: 中華書局, 1980: 50-54. 6 | * - 漢語語音史 [M]. 北京: 中國社會科學出版社, 1985: 110-227. 7 | * 8 | * 提供 3 種擬音版本: 9 | * 10 | * - 《漢語史稿》 第二章 第十節 中古的語音系統(默認) 11 | * - 《漢語語音史》 卷上 第三章 魏晉南北朝音系 12 | * - 《漢語語音史》 卷上 第四章 隋——中唐音系 13 | * 14 | * 提供 2 種音標風格: 15 | * 16 | * - 國際音標(原貌):王力著作採用的音標符號 17 | * - 國際音標(通用):現在的中國通用音標符號 18 | * 19 | * 參考:高本漢擬音 (https://github.com/nk2028/tshet-uinh-examples/blob/qieyun-0.13/karlgren.js) 20 | * 21 | * @author Mishiro 22 | */ 23 | 24 | /** @type { 音韻地位['屬於'] } */ 25 | const is = (...x) => 音韻地位.屬於(...x); 26 | /** @type { 音韻地位['判斷'] } */ 27 | const when = (...x) => 音韻地位.判斷(...x); 28 | 29 | const is史稿 = 選項.擬音版本?.includes('稿') ?? true; 30 | const is隋唐 = 選項.擬音版本?.includes('隋') ?? false; 31 | 32 | const 音標字典 = { 33 | '國際音標(原貌)': { 34 | ʰ: 'ʻ', ʔ: 'ˀ', 35 | ʱ: is史稿 ? 'ʻ' : '', 36 | ɡ: is史稿 ? 'g' : 'ɡ', 37 | ʒ: is史稿 ? 'ʒ' : 'ᶎ', 38 | }, 39 | '國際音標(通用)': { 40 | ʱ: is史稿 ? 'ʱ' : '', 41 | }, 42 | }; 43 | 44 | if (!音韻地位) return [ 45 | ['擬音版本', [1, '漢語史稿', '漢語語音史(魏晉南北朝音系)', '漢語語音史(隋——中唐音系)']], 46 | ['音標體系', [2].concat(Object.keys(音標字典))], 47 | ['知組記法', is隋唐 ? [1, 'ȶ', 't'] : null], 48 | ['聲調記法', [2, '不標', '四角標圈', '數字上標']], 49 | ]; 50 | 51 | function get聲母() { 52 | let 聲母 = { 53 | 幫: 'p', 滂: 'pʰ', 並: 'bʱ', 明: 'm', 54 | 端: 't', 透: 'tʰ', 定: 'dʱ', 泥: 'n',     來: 'l', 55 | 知: 't', 徹: 'tʰ', 澄: 'dʱ', 孃: 'n', 56 | 精: 'ts', 清: 'tsʰ', 從: 'dzʱ',   心: 's', 邪: 'z', 57 | 莊: 'tʃ', 初: 'tʃʰ', 崇: 'dʒʱ',   生: 'ʃ', 俟: 'ʒ', 58 | 章: 'tɕ', 昌: 'tɕʰ', 船: 'dʑʱ', 日: 'ȵ', 書: 'ɕ', 常: 'ʑ', 以: 'j', // 常船顛倒 59 | 見: 'k', 溪: 'kʰ', 羣: 'ɡʱ', 疑: 'ŋ', 曉: 'x', 匣: 'ɣ', 云: 'ɣ', 60 | 影: is史稿 ? '' : 'ʔ', 61 | }[音韻地位.母]; 62 | if (選項.知組記法 === 'ȶ' || is史稿) 聲母 = { 63 | 知: 'ȶ', 徹: 'ȶʰ', 澄: 'ȡʱ', 64 | }[音韻地位.母] || 聲母; 65 | if (is史稿) 聲母 = { 66 | 俟: 'dʒʱ', 日: 'nʑ', // 俟同崇 67 | }[音韻地位.母] || 聲母; 68 | return 聲母; 69 | } 70 | 71 | function get韻母() { 72 | // 漢語語音史 73 | let 韻 = { 74 | 鍾: '冬', 虞: '模', 齊: '祭', 臻: '真', 魂: '元', 痕: '元', 75 | 先: '仙', 蕭: '宵', 陽: '唐', 庚: '耕', 清: '耕', 尤: '侯', 幽: '侯', 添: '鹽', 凡: '嚴', 76 | }[音韻地位.韻] ?? 音韻地位.韻; 77 | if (is隋唐) 韻 = { 78 | 支: '脂', 之: '脂', 灰: '泰', 咍: '泰', 佳: '夬', 皆: '夬', 殷: '真', 山: '刪', 79 | 登: '蒸', 覃: '談', 咸: '銜', 80 | }[音韻地位.韻] || 韻; 81 | else 韻 = { 82 | 江: '冬', 佳: '泰', 皆: '廢', 夬: '祭', 灰: '廢', 咍: '廢', 殷: '文', 83 | 刪: is`入聲` ? '仙' : '寒', 山: is`入聲` ? '寒' : '仙', // 黠鎋顛倒 84 | 肴: '宵', 豪: '宵', 麻: '歌', 青: '耕', 談: '鹽', 咸: '覃', 銜: '鹽', 85 | }[音韻地位.韻] || 韻; 86 | const 元音表1 = { // 魏晉南北朝音系 87 |               u: '侯冬', 88 | e: '支耕脂真  ', ə: '之蒸微文 侵', o: '模東', ou: '宵', 89 | æ: '  祭仙 鹽', ɐ: ' 登廢元 嚴', ɔ: '魚 ', 90 |        ɑ: '歌唐泰寒 覃', 91 | }; 92 | const 元音表2 = { // 隋——中唐音系 93 | i: '脂青 真 侵',        u: '模冬    ', 94 |        ə: ' 蒸微文  ', o: '魚東  侯 ', 95 | æ: '  祭仙宵鹽', ɐ: ' 耕廢元 嚴', ɔ: ' 江    ', 96 | a: '麻 夬刪肴銜',        ɑ: '歌唐泰寒豪談', 97 | }; 98 | 99 | // 漢語史稿 100 | if (is史稿) 韻 = { 101 | 鍾: '冬', 虞: '模', 皆: '廢', 灰: '咍', 臻: '先', 殷: '文', 魂: '文', 痕: '文', 102 | 登: '蒸', 侯: '尤', 幽: '尤', 咸: '嚴', 凡: '嚴', 103 | }[音韻地位.韻] ?? 音韻地位.韻; 104 | const 元音表0 = { 105 | i: '脂     ',        u: '模東    ', 106 | ĕ: '   真 侵', 107 | e: '支青齊先蕭添', ə: '之蒸微文尤 ', o: '魚冬    ', 108 | ɛ: ' 清祭仙宵鹽',        ɔ: ' 江    ', 109 | æ: ' 耕夬山  ', ɐ: ' 庚廢元 嚴', ɒ: '  咍  覃', 110 | a: '麻陽佳刪肴銜',        ɑ: '歌唐泰寒豪談', 111 | }; 112 | 113 | const 韻尾列表 = is`舒聲` ? ['', ...'ŋinum'] : [...' k t p']; 114 | 115 | let 元音表 = is史稿 ? 元音表0 : is隋唐 ? 元音表2 : 元音表1; 116 | let 韻核 = Object.keys(元音表).find(e => 元音表[e].includes(韻)); 117 | let 韻尾 = 韻尾列表[元音表[韻核].indexOf(韻)]; 118 | let 介音 = ''; 119 | if (is史稿) { 120 | if (is`${TshetUinh.表達式.四等韻} 或 幽韻`) 介音 += 'i'; 121 | else if (is`三四等 非 脂幽韻`) 介音 += 'ĭ'; 122 | if (is`冬灰文魂韻 或 泰寒歌韻 非 開口 或 唐登韻 合口 或 真韻 合口 (A類 或 銳音 非 莊組)`) 介音 += 'u'; 123 | else if (is`合口 非 虞韻 或 鍾凡韻 或 幫組 微廢元陽韻`) 介音 += 'w'; 124 | } else { 125 | if (is`${TshetUinh.表達式.四等韻} 或 幽韻`) 介音 += !is`合口` && 韻核 === 'i' ? '' : 'i'; 126 | else if (is`三四等 非 臻幽韻`) 介音 += !is`合口` && 韻核 === 'i' ? '' : 'i̯'; 127 | if (is`二等 或 臻韻` && !is隋唐) 介音 += is`合口` ? 'o' : 韻核 === 'e' ? '' : 'e'; 128 | else if (is`合口 非 虞韻 或 幫組 微泰灰廢文元魂寒歌陽凡韻`) 介音 += 'u'; 129 | } 130 | 131 | return 介音 + 韻核 + 韻尾; 132 | } 133 | 134 | function get聲調() { 135 | return 選項.聲調記法 === '不標' ? '' : { 136 | 四角標圈: { 平: '꜀', 上: '꜂', 去: '꜄', 入: '꜆' }, // 四聲未分陰陽 137 | 數字上標: { 平: '¹', 上: '²', 去: '³', 入: '⁴' }, 138 | }[選項.聲調記法][音韻地位.聲]; 139 | } 140 | 141 | let 音節 = get聲母() + get韻母(); 142 | Object.entries(音標字典[選項.音標體系]).forEach(([k, v]) => { 音節 = 音節.replace(k, v); }); 143 | return (選項.聲調記法 === '四角標圈' && is`平上聲`) ? get聲調() + 音節 : 音節 + get聲調(); 144 | -------------------------------------------------------------------------------- /high_tang.js: -------------------------------------------------------------------------------- 1 | /* 推導盛唐(平水韻)擬音 2 | * 3 | * 盛唐通用語沒有直接描述音系的材料,但有詩韻(「平水韻」)和日語漢音作爲緊密相關的材料 4 | * 5 | * 來源:「平水韻」擬音 6 | * https://phesoca.com/aws/351/ 或 7 | * https://www.bilibili.com/read/cv37390491/ 或 8 | * https://zhuanlan.zhihu.com/p/681190661 9 | * 10 | * @author unt 11 | */ 12 | 13 | /** @type { 音韻地位['屬於'] } */ 14 | const is = (...x) => 音韻地位.屬於(...x); 15 | /** @type { 音韻地位['判斷'] } */ 16 | const when = (...x) => 音韻地位.判斷(...x); 17 | 18 | const 非組字典 = { 19 | 'f': { 幫: 'f', 滂: 'fʰ', 並: 'v', 明: 'ɱ' }, 20 | 'pf': { 幫: 'pf', 滂: 'pfʰ', 並: 'bv', 明: 'ɱ' }, 21 | }; 22 | 23 | function get選單(音類列表, 音標列表s, 音類音標連接符 = ' ', 音類連接符 = ' | ') { 24 | return 音標列表s.map(音標列表 => ({ 25 | value: 音標列表.join(','), 26 | text: 音標列表.map((音標, i) => 音類列表[i] + 音類音標連接符 + 音標).join(音類連接符), 27 | })); 28 | } 29 | 30 | if (!音韻地位) return [ 31 | '聲', 32 | ['非組', [1, ...Object.keys(非組字典)]], 33 | [`等類記法 34 | j 代表聲母腭化、ɣ 代表聲母軟腭化,不是介音; 35 | 非組和莊章組後總是不寫等類記號`, [1, ...get選單( 36 | ['三A', '三BC', '一二四'], 37 | [['j', 'ɣ', ''], ['ʲ', 'ˠ', '']], 38 | ' C', 39 | )]], 40 | ['見組非三等簡寫作軟腭音', false], 41 | 42 | '韻', 43 | ['低元音', [2, ...get選單(['前', '後'], [['æ', 'a'], ['æ', 'ɑ'], ['a', 'ɑ']])]], 44 | ['後高元音\n微韻開口總爲 ɨj,不受本選項影響', [1, ...get選單(['閉音節', '魚韻'], [['ɨ', 'ɨ'], ['ə', 'ɨ'], ['ə', 'ɯ']])]], 45 | ['支脂合併', false], 46 | ['咍泰合併', false], 47 | ['覃談合併', true], 48 | ['東一冬合併', false], 49 | ['輕脣東鍾合併', false], 50 | ['部分蟹攝二等入假攝', false], 51 | ['部分流攝脣音入遇攝', false], 52 | 53 | '調', 54 | ['聲調', [2, '五度符號', '附加符號', '調類數字']], 55 | ['全濁上歸去', false], 56 | ]; 57 | 58 | function 調整音韻地位() { 59 | function 調整(表達式, 調整屬性, 字頭串 = null) { 60 | if (typeof (字頭串) === 'string' && !字頭串.includes(字頭)) return; 61 | if (is(表達式)) 音韻地位 = 音韻地位.調整(調整屬性); 62 | } 63 | 64 | // 輕唇化例外 65 | 調整('明母 尤韻', { 等: '一', 類: null, 韻: '侯' }); 66 | 調整('明母 東韻', { 等: '一', 類: null }); 67 | 68 | // [慧琳反切體現的, 唐代用韻體現的, 據今音推測的] 69 | const 蟹攝二等入假攝字 = ['崖咼(呙)扠涯搋派差絓畫(画)罣罷(罢)', '佳鼃娃解釵(钗)卦柴', '哇洼蛙灑蝸話(话)掛挂查叉杈衩'].join(''); 70 | const 流攝脣音入遇攝字 = ['浮戊母罦罘蜉矛茂覆懋拇某負(负)阜', '謀(谋)部畝(亩)畮婦(妇)不否桴富牟缶', '復複(复)副牡'].join(''); 71 | if (選項.部分蟹攝二等入假攝) 調整('蟹攝 二等', { 韻: '麻' }, 蟹攝二等入假攝字); 72 | if (選項.部分流攝脣音入遇攝) 調整('幫組 尤侯韻', { 韻: is`尤韻` ? '虞' : '模' }, 流攝脣音入遇攝字); 73 | } 74 | 75 | 調整音韻地位(); 76 | 77 | function get聲母() { 78 | let 等類記法 = 選項.等類記法.split(','); 79 | let 聲母 = when([ 80 | ['幫組 C類', 非組字典[選項.非組][音韻地位.母]], 81 | [!選項.見組非三等簡寫作軟腭音 && '見溪疑曉匣母 非 三等', { 82 | 見: 'q', 溪: 'qʰ', 疑: 'ɴ', 曉: 'χ', 匣: 'ʁ', 83 | }[音韻地位.母]], 84 | ['', { 85 | 幫: 'p', 滂: 'pʰ', 並: 'b', 明: 'm', 86 | 端: 't', 透: 'tʰ', 定: 'd', 泥: 'n', 來: 'l', 87 | 知: 'ʈ', 徹: 'ʈʰ', 澄: 'ɖ', 孃: 'ɳ', 88 | 精: 'ts', 清: 'tsʰ', 從: 'dz', 心: 's', 邪: 'z', 89 | 莊: 'tʂ', 初: 'tʂʰ', 崇: 'dʐ', 生: 'ʂ', 俟: 'ʐ', 90 | 章: 'tɕ', 昌: 'tɕʰ', 常: 'dʑ', 書: 'ɕ', 船: 'ʑ', 日: 'ɲ', 以: 'j', 91 | 見: 'k', 溪: 'kʰ', 羣: 'ɡ', 疑: 'ŋ', 92 | 影: 'ʔ', 曉: 'x', 匣: 'ɣ', 云: '', 93 | }[音韻地位.母]], 94 | ]) + when([ 95 | ['幫組 C類', ''], 96 | ['莊章組 或 日以母', ''], 97 | ['三等 (A類 或 精組)', 等類記法[0]], 98 | ['三等', 等類記法[1]], 99 | ['', 等類記法[2]], 100 | ]); 101 | 聲母 = 聲母.replace(/ʰ([ʲˠ])/, '$1ʰ'); 102 | 聲母 = 聲母.replace(/^ˠ/, 'ɰ'); 103 | return 聲母; 104 | } 105 | 106 | function get介音() { 107 | return is`合口` ? 'w' : ''; 108 | } 109 | 110 | function get韻基() { 111 | let 韻基 = when([ 112 | [選項.支脂合併 === true && '脂之韻', 'i'], 113 | [選項.咍泰合併 === true && '灰咍韻', 'ɑj'], // 不含廢韻 114 | ['脂之韻', 'ij'], ['微韻', [['開口', 'ɨj'], ['', 'uj']]], 115 | ['齊祭韻', 'ej'], ['灰咍廢韻', 'əj'], 116 | ['佳皆夬韻', 'æj'], ['泰韻', 'ɑj'], 117 | 118 | ['支佳韻', 'i'], ['魚韻', 'ɨ'], ['虞模韻', 'u'], 119 | ['麻韻', 'æ'], ['歌韻', 'ɑ'], 120 | 121 | ['尤侯幽韻', 'ɨw'], 122 | ['蕭宵韻', 'ew'], 123 | ['肴韻', 'æw'], ['豪韻', 'ɑw'], 124 | 125 | ['真臻韻', 'in'], ['殷韻', 'ɨn'], ['文韻', 'un'], 126 | ['先仙韻', 'en'], ['元魂痕韻', 'ən'], 127 | ['刪山韻', 'æn'], ['寒韻', 'ɑn'], 128 | 129 | ['侵韻', 'im'], 130 | ['鹽添韻', 'em'], ['嚴凡韻', 'əm'], [選項.覃談合併 === false && '覃韻', 'əm'], 131 | ['咸銜韻', 'æm'], ['覃談韻', 'ɑm'], 132 | 133 | [選項.東一冬合併 === true && '冬韻', 'ɨwŋ'], 134 | [選項.輕脣東鍾合併 === true && '通攝 幫組 C類', 'uŋ'], 135 | ['蒸登韻', 'ɨŋ'], ['東韻', 'ɨwŋ'], ['冬鍾韻', 'uŋ'], 136 | ['青韻', 'eŋ'], 137 | ['庚耕清韻', 'æŋ'], ['陽唐韻', 'ɑŋ'], ['江韻', 'æwŋ'], 138 | ]); 139 | 韻基 = 韻基.replace(/ɨ(?=$|[^j])/, 選項.後高元音.split(',')[+is`魚韻`]); 140 | 韻基 = 韻基.replace('æ', 選項.低元音.split(',')[0]); 141 | 韻基 = 韻基.replace('ɑ', 選項.低元音.split(',')[1]); 142 | if (is`入聲`) [...'mnŋɴ'].forEach((v, i) => { 韻基 = 韻基.replace(v, 'ptkq'[i]); }); 143 | return 韻基; 144 | } 145 | 146 | function get聲調() { 147 | const is陰 = is`全清 或 次清 或 次濁 上入聲`; 148 | return { 149 | 五度符號: is陰 ? 150 | { 平: '˦˨', 上: '˦˥', 去: '˧˨˦', 入: '˦' } : 151 | { 平: '˨˩', 上: '˨˦', 去: '˨˨˦', 入: '˨' }, 152 | 附加符號: { 平: '̀', 上: '́', 去: '̌', 入: '' }, 153 | 調類數字: { 平: '¹', 上: '²', 去: '³', 入: '⁴' }, 154 | }[選項.聲調][選項.全濁上歸去 && is`全濁 上聲` ? '去' : 音韻地位.聲]; 155 | } 156 | 157 | function get音節() { 158 | const 音節 = { 159 | 聲母: get聲母(), 160 | 介音: get介音(), 161 | 韻基: get韻基(), 162 | 聲調: get聲調(), 163 | }; 164 | if (音節.韻基[0] === 'i') 音節.聲母 = 音節.聲母.replace(/(? 音韻地位.屬於(...x); 10 | /** @type { 音韻地位['判斷'] } */ 11 | const when = (...x) => 音韻地位.判斷(...x); 12 | 13 | if (!音韻地位) return []; 14 | 15 | if (is`云母 通攝 舒聲`) 音韻地位 = 音韻地位.調整('匣母', ['匣母三等']); 16 | 17 | function 聲母規則() { 18 | return when([ 19 | ['幫滂並母 C類', 'f'], 20 | ['幫母 或 並母 仄聲', 'b'], 21 | ['滂母 或 並母 平聲', 'p'], 22 | ['明母', 'm'], 23 | 24 | ['端母 或 定母 仄聲', 'd'], 25 | ['透母 或 定母 平聲', 't'], 26 | ['泥孃母', 'n'], 27 | ['來母', 'l'], 28 | 29 | ['精莊知章母 或 從崇俟邪澄母 仄聲', 'z'], // 精莊組濁音塞擦音多於擦音(下同) 30 | ['清初徹昌母 或 從崇俟邪澄母 平聲', 'c'], 31 | ['心生常書船母', 's'], // 章組濁音擦音多於塞擦音 32 | 33 | ['見母 或 羣母 仄聲', 'g'], 34 | ['羣母 平聲', 'k'], 35 | ['疑母', 'ng'], // 細音為 j,詳後 36 | 37 | ['溪曉母', 'h'], // 溪母多數擦化;三四等拼 a 元音部分韻母時為 j/w,詳後 38 | ['匣母', [ 39 | ['合口 或 (遇攝 一等)', 'j'], // 拼展脣或後元音時為 w,詳後 40 | ['', 'h'], 41 | ]], 42 | ['影云以日母', [ 43 | ['三四等', 'j'], // 拼展脣或後元音時為 w,詳後 44 | ['', ''], 45 | ]], 46 | ], '無聲母規則'); 47 | } 48 | 49 | function 韻母規則() { 50 | return when([ 51 | ['通攝', 'ung'], 52 | 53 | ['止攝', [ 54 | ['脣音', 'ei'], 55 | ['開口 (端組 或 孃來母 或 見溪羣曉母)', 'ei'], 56 | ['開口', 'i'], 57 | ['合口 舌齒音', 'eoi'], 58 | ['合口 牙喉音', 'ai'], 59 | ]], 60 | 61 | ['遇攝', [ 62 | ['三四等', [ 63 | ['幫滂並母', 'u'], 64 | ['明母', 'ou'], 65 | ['莊組', 'o'], 66 | ['端精組 或 孃來母 或 見溪羣曉母', 'eoi'], 67 | ['', 'yu'], 68 | ]], 69 | ['一等', [ 70 | ['脣音 或 舌齒音', 'ou'], 71 | ['疑母', ''], 72 | ['牙喉音', 'u'], 73 | ]], 74 | ]], 75 | 76 | ['蟹攝', [ 77 | ['廢韻 平上聲 章組', 'oi'], // 參照「茝」coi2 78 | ['合口 銳音', 'eoi'], // 含以母 79 | ['三四等', 'ai'], 80 | ['二等 或 泰韻 開口 (端組 或 來母)', 'aai'], 81 | ['一等', [ 82 | ['開口 或 疑母', 'oi'], 83 | ['', 'ui'], 84 | ]], 85 | ]], 86 | 87 | ['臻攝', [ 88 | ['三四等', [ 89 | ['合口 舌齒音', 'eon'], 90 | ['', 'an'], 91 | ]], 92 | ['一等', [ 93 | ['脣音', 'un'], 94 | ['合口 (端組 或 來母)', 'eon'], 95 | ['合口 精組', 'yun'], 96 | ['', 'an'], 97 | ]], 98 | ]], 99 | 100 | ['山攝', [ 101 | ['二等 或 脣音 C類', 'aan'], 102 | ['三四等', [ 103 | ['開口 或 脣音', 'in'], 104 | ['合口', 'yun'], 105 | ]], 106 | ['一等', [ 107 | ['開口 舌齒音', 'aan'], 108 | ['開口 牙喉音', 'on'], 109 | ['合口 舌齒音', 'yun'], 110 | ['合口 牙喉音 或 脣音', 'un'], 111 | ]], 112 | ]], 113 | 114 | ['效攝', [ 115 | ['三四等', 'iu'], 116 | ['二等', 'aau'], 117 | ['一等', 'ou'], 118 | ]], 119 | 120 | ['果假攝', [ 121 | ['三四等', [ 122 | ['脣音 C類', 'o'], 123 | ['開口 或 脣音', 'e'], 124 | ['合口', 'oe'], 125 | ]], 126 | ['二等', 'aa'], 127 | ['一等', 'o'], 128 | ]], 129 | 130 | ['宕江攝', [ 131 | ['三四等', [ 132 | ['脣音 C類 或 開口 莊組 或 合口', 'ong'], 133 | ['', 'oeng'], 134 | ]], 135 | ['二等 舌齒音', 'oeng'], 136 | ['一二等', 'ong'], 137 | ]], 138 | 139 | ['梗曾攝', [ 140 | ['一二等 或 梗攝 莊組', [ // 文 ang、白 aang,兩者勢均,推導音依文讀 141 | ['庚韻 二等 (來母 或 端組)', 'aang'], // 唯來母「冷」向無 lang 音例,故例外,端組亦從之 142 | ['', 'ang'], 143 | ]], 144 | ['三四等', 'ing'], 145 | ]], 146 | 147 | ['流攝', [ 148 | ['四等 端組 或 幽韻 幫滂並母', 'iu'], // 「丟」「彪」「淲」 149 | ['', 'au'], 150 | ]], 151 | 152 | ['深攝', 'am'], // -m 拼脣音時為 -n,詳後,下同 153 | 154 | ['咸攝', [ 155 | ['二等 或 脣音 C類', 'aam'], 156 | ['三四等', 'im'], 157 | ['一等 脣舌齒音', 'aam'], // 脣音僅僻字,如「姏」maan4 158 | ['一等 牙喉音', 'om'], // -om 併入 -am,但影響陰入分化,詳後 159 | ]], 160 | ], '無韻母規則'); 161 | } 162 | 163 | function 聲調規則() { 164 | return when([ 165 | ['清音', [ 166 | ['平聲', '1'], 167 | ['上聲', '2'], 168 | ['去入聲', '3'], // 中入於短元音為 1(陰入),詳後 169 | ]], 170 | ['濁音', [ 171 | ['平聲', '4'], 172 | ['上聲 次濁', '5'], 173 | ['上聲 全濁 或 去入聲', '6'], 174 | ]] 175 | ], '無聲調規則'); 176 | } 177 | 178 | function is短元音(韻母) { 179 | if (['am', 'an', 'ang', 'eon', 'ing', 'ung'].includes(韻母)) return true; 180 | if (['aam', 'aan', 'im', 'in', 'om', 'on', 'ong', 'oeng', 'un', 'yun'].includes(韻母)) return false; 181 | throw new Error('無長短元音規則:' + 韻母); 182 | } 183 | 184 | let 聲母 = 聲母規則(); 185 | let 韻母 = 韻母規則(); 186 | let 聲調 = 聲調規則(); 187 | 188 | // ng 拼細音時為 j 189 | const is細音 = ['eo', 'i', 'oe', 'u', 'yu'].some(x => 韻母.startsWith(x)); 190 | if (聲母 === 'ng' && is細音) 聲母 = 'j'; 191 | 192 | // 三四等清調 h 拼 a 元音部分韻母時為 j/w 193 | if (聲母 === 'h' && ['au', 'an', 'am'].includes(韻母) && is`清音 三四等 非 (臻攝 開口 入聲) 非 (臻攝 合口 舒聲)`) { 194 | 聲母 = 'j'; 195 | } 196 | 197 | // 陰入分化 198 | if (is`入聲` && 聲調 === '3' && is短元音(韻母)) 聲調 = '1'; 199 | 200 | // 合口 201 | if (is`合口 或 模韻` && !['eo', 'oe', 'yu'].some(x => 韻母.startsWith(x))) { 202 | if ((聲母 === 'g' || 聲母 === 'k') && !韻母.startsWith('u')) 聲母 += 'w'; 203 | else if (聲母 === 'h' && !韻母.startsWith('i')) 聲母 = 'f'; 204 | else if (聲母 === 'j' || 聲母 === '') 聲母 = 'w'; 205 | } 206 | 207 | // -om 併入 -am 208 | if (韻母 === 'om') 韻母 = 'am'; 209 | 210 | // m 韻尾在聲母為脣音時為 n 211 | if (is`脣音` && 韻母.endsWith('m')) 韻母 = 韻母.slice(0, -1) + 'n'; 212 | 213 | if (is`入聲`) { 214 | if (韻母.endsWith('m')) 韻母 = 韻母.slice(0, -1) + 'p'; 215 | else if (韻母.endsWith('n')) 韻母 = 韻母.slice(0, -1) + 't'; 216 | else if (韻母.endsWith('ng')) 韻母 = 韻母.slice(0, -2) + 'k'; 217 | } 218 | 219 | return 聲母 + 韻母 + 聲調; 220 | -------------------------------------------------------------------------------- /position.js: -------------------------------------------------------------------------------- 1 | /* 音韻地位 2 | * 3 | * @author unt 4 | */ 5 | 6 | const is = (...x) => 音韻地位.屬於(...x); 7 | const when = (...x) => 音韻地位.判斷(...x); 8 | 9 | const sextupleKeys = ['母', '呼', '等', '類', '韻系', '聲']; 10 | if (選項._六元組全選) sextupleKeys.forEach(k => 選項[k] = true); 11 | if (選項._六元組全不選) sextupleKeys.forEach(k => 選項[k] = false); 12 | const keys = Object.keys(選項).filter(k => !k.startsWith('_')); 13 | const checkedKeys = keys.filter(k => 選項[k]); 14 | const checkedSextupleKeys = checkedKeys.filter(k => sextupleKeys.includes(k)); 15 | 16 | if (!音韻地位) return [ 17 | '整體', 18 | [`描述|音韻地位描述 19 | 聲母、呼、等、類、韻系、聲調 20 | 韻系用字依《切韻》,舉平以賅上去入`, true], 21 | ['簡略描述|音韻地位簡略描述\n不推薦', false], 22 | '單項', 23 | ['母|聲母', false], 24 | ['呼', false], 25 | ['等\n切韻等\n端組拼三等韻(爹小韻、地小韻)歸四等', false], 26 | ['類', false], 27 | ['韻系', false], 28 | ['聲|聲調', false], 29 | ['_六元組全選|六項全選', false, { hidden: checkedSextupleKeys.length === 6, reset: true }], 30 | ['_六元組全不選|六項全不選', false, { hidden: checkedSextupleKeys.length === 0, reset: true }], 31 | '更多', 32 | ['聲類|聲類\n五十一聲類 + 俟母\n來母二等歸盧類;也有人將其歸力類', false], 33 | ['組|聲母組', false], 34 | ['音|五音', false], 35 | ['清濁\n曉母歸全清;也有人將其歸次清', false], 36 | ['韻目|韻目\n爲理論韻目,非韻書中的韻目原貌', false], 37 | ['韻別|韻母陰陽入', false], 38 | ['表達式|音韻地位表達式', false], 39 | '對應韻圖音系相關屬性', 40 | ['字母\n三十六字母', false], 41 | ['韻圖等', false], 42 | ['攝', false], 43 | [`字彙描述|音韻地位描述(《漢語方音字彙》格式) 44 | 攝、呼、等、聲調、韻目、聲母 45 | 重紐第二類(即三等 A 類)在左上角加黑點表示 46 | 參照《字彙》1962 第一版、1989 第二版、2003 第二版重排本。呼依《字彙》。韻目用字依《廣韻》(個別字形與《字彙》不同;另《字彙》欣韻作殷韻,不取)。幫組 C 類、孃母、常母依《字彙》作非組、泥母、禅母,俟母依《方言調查字表》(第 18 頁)併入崇母`, false], 47 | '', 48 | [`_韻風格|韻系風格 49 | 舉平以賅上去入適合切韻音系、韻圖音系 50 | 舉平以賅上去適合宋代及之後音系`, [1, '舉平以賅上去入', '舉平以賅上去'], { hidden: !選項.韻系 }], 51 | [`_韻用字| 52 | 《切韻》韻目 → 《廣韻》韻目 53 | • 真軫震質 → 真軫震質(開口、脣音、B 類及莊組)、諄準稕術(A 類合口及莊組以外舌齒音合口) 54 | • 殷 → 欣 55 | • 佷 → 很[痕韻上聲] 56 | • 寒旱翰末 → 寒旱翰曷(開口)、桓緩換末(其他) 57 | • 敬 → 映[庚韻去聲] 58 | • 歌哿箇 → 歌哿箇(一等開口)、戈果過(其他) 59 | • [嚴韻上去聲] → 儼釅 60 | (存在少許例外)`, [1, '依《切韻》', '依《廣韻》'], { text: ['韻系', '韻目'].filter(e => 選項[e]).join('、') + '用字', hidden: !選項.韻系 && !選項.韻目 }], 61 | ['_分隔符|多個屬性間的連接符', ',', { hidden: checkedKeys.length < 2 }], 62 | ]; 63 | 64 | const 聲母to聲類列表 = { 65 | 幫: '方博', 滂: '芳普', 並: '符蒲', 明: '武莫', 66 | 端: ' 都', 透: ' 他', 定: ' 徒', 泥: ' 奴', 67 | 來: '力盧', 68 | 見: '居古', 溪: '去苦', 羣: '渠 ', 疑: '魚五', 69 | 影: '於烏', 曉: '許呼', 匣: ' 胡', 云: '于 ', 70 | 71 | 精: '子作', 清: '七倉', 從: '疾昨', 心: '息蘇', 邪: '徐 ', 72 | 73 | 知: '陟', 徹: '丑', 澄: '直', 孃: '女', 74 | 莊: '側', 初: '初', 崇: '士', 生: '所', 俟: '俟', 75 | 章: '之', 昌: '昌', 常: '食', 書: '式', 船: '時', 日: '而', 以: '以', 76 | }; 77 | 78 | const 韻to切韻韻目列表 = Object.fromEntries([ 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 | ].map(切韻韻目列表 => [切韻韻目列表.trim(' ')[0], 切韻韻目列表])); 138 | 139 | function get聲類() { 140 | let 聲類列表 = 聲母to聲類列表[音韻地位.母].trim(' '); 141 | return is`三等` ? 聲類列表[0] : 聲類列表.slice(-1); 142 | } 143 | 144 | function get韻系(is廣韻 = 選項._韻用字 === '依《廣韻》', 入聲獨立 = !選項._韻風格.includes('入')) { 145 | return when([ 146 | [入聲獨立 && '入聲', [ 147 | [is廣韻, [ 148 | ['術', '真韻 合口 非 B類 非 莊組'], 149 | ['曷', '寒韻 開口 入聲'], 150 | ].map(([a, b]) => [b, a])], 151 | ['', 韻to切韻韻目列表[音韻地位.韻][3]], 152 | ]], 153 | ['', [ 154 | [is廣韻, [ 155 | ['諄', '真韻 合口 非 B類 非 莊組'], 156 | ['欣', '殷韻'], 157 | ['桓', '寒韻 非 開口'], 158 | ['戈', '歌韻 非 (一等 開口)'], 159 | ].map(([a, b]) => [b, a])], 160 | ['', 音韻地位.韻], 161 | ]] 162 | ], '', true); 163 | } 164 | 165 | function get韻目(is廣韻 = 選項._韻用字 === '《廣韻》') { 166 | return when([ 167 | [is廣韻, [ 168 | ['諄準稕術', '真韻 合口 非 B類 非 莊組'], 169 | ['欣   ', '殷韻 平聲'], 170 | [' 很  ', '痕韻 上聲'], 171 | ['   曷', '寒韻 開口 入聲'], 172 | ['桓緩換 ', '寒韻 非 開口 舒聲'], 173 | ['  映 ', '庚韻 去聲'], 174 | ['戈果過 ', '歌韻 非 (一等 開口)'], 175 | [' 儼釅 ', '嚴韻 上去聲'], 176 | ].map(([a, b]) => [b, a])], 177 | ['', 韻to切韻韻目列表[when([ 178 | ['祭韻 非 去聲', '齊'], 179 | ['廢韻 非 去聲', [ 180 | ['開口', '咍'], 181 | ['', '灰'], 182 | ]], 183 | ['', 音韻地位.韻], 184 | ])]], 185 | ], '', true)['平上去入'.indexOf(音韻地位.聲)]; 186 | } 187 | 188 | function get字彙描述() { 189 | let { 攝, 呼, 等, 聲, 母 } = 音韻地位; 190 | let 韻 = get韻目(true); 191 | let 重紐第二類標記 = is`A類` ? '˙' : ''; 192 | 呼 = when([ 193 | ['通遇攝', '合'], 194 | ['江流攝', '開'], 195 | ['幫組', [ 196 | // 脣音咍韻在《字彙》《方言調查字表》中實已改作灰韻,與 nk2028 資料一致 197 | ['C類 或 一等 非 登唐咍泰豪覃談韻', '合'], 198 | ['', '開'], 199 | ]], 200 | ['', 呼], 201 | ]); 202 | 母 = when([ 203 | ['幫組 C類', 音韻地位.字母], 204 | ['孃母', '泥'], 205 | ['俟母', '崇'], 206 | ['常母', '禅'], 207 | ['', 母], 208 | ]); 209 | return [重紐第二類標記, 攝, 呼, 等, 聲, 韻, 母].join(''); 210 | } 211 | 212 | const 補充屬性 = { 213 | 聲類: get聲類(), 214 | 韻系: get韻系(), 215 | 韻目: get韻目(), 216 | 字彙描述: get字彙描述(), 217 | }; 218 | 219 | return checkedKeys.map(k => 音韻地位[k] ?? 補充屬性[k]).join(選項._分隔符); 220 | -------------------------------------------------------------------------------- /mid_tang.js: -------------------------------------------------------------------------------- 1 | /* 推導中唐(韻圖)擬音 2 | * 3 | * 中唐通用語以慧琳《一切經音義》反切和韻圖《韻鏡》《七音略》爲代表 4 | * 5 | * 來源:中唐音系韻基和聲類 6 | * https://phesoca.com/linguistics/mid-tang.png 或 7 | * https://t.bilibili.com/1005592182086696967 或 8 | * https://www.zhihu.com/pin/1725177872707268608 9 | * 10 | * @author unt 11 | */ 12 | 13 | /** @type { 音韻地位['屬於'] } */ 14 | const is = (...x) => 音韻地位.屬於(...x); 15 | /** @type { 音韻地位['判斷'] } */ 16 | const when = (...x) => 音韻地位.判斷(...x); 17 | 18 | const 非組字典 = { 19 | 'f': { 幫: 'f', 滂: 'fʰ', 並: 'v', 明: 'ɱ' }, 20 | 'pf': { 幫: 'pf', 滂: 'pfʰ', 並: 'bv', 明: 'ɱ' }, 21 | }; 22 | 23 | function get選單(音類列表, 音標列表s, 音類音標連接符 = ' ', 音類連接符 = ' | ') { 24 | return 音標列表s.map(音標列表 => ({ 25 | value: 音標列表.join(','), 26 | text: 音標列表.map((音標, i) => 音類列表[i] + 音類音標連接符 + 音標).join(音類連接符), 27 | })); 28 | } 29 | 30 | if (!音韻地位) return [ 31 | '聲', 32 | ['非組', [1, ...Object.keys(非組字典)]], 33 | ['常船合併崇俟合併|常船合併、崇俟合併\n常 dʑ 船 ʑ 合併作擦音 ʑ\n崇 dʐ 俟 ʐ 合併作塞擦音 dʐ', true], 34 | [`等類記法 35 | j 代表聲母腭化、ɣ 代表聲母軟腭化,不是介音; 36 | 選擇「三 Cɣ」時,匣母三四等(軟腭音)也寫作 ʁ,以免與 ɣ 衝突; 37 | 非組和莊章組後總是不寫等類記號`, [1, ...get選單( 38 | ['四', '三', '一二'], 39 | [['j', 'ɣ', ''], ['ʲ', 'ˠ', '']], 40 | ' C', 41 | )]], 42 | ['見組一二等簡寫作軟腭音', false, { description: 選項.等類記法?.includes('ˠ') ? null : '匣母總是寫作 ʁ,實際上匣四 ʁj 是腭化軟腭音、匣三 ʁɣ 是軟腭音' }], 43 | 44 | '韻', 45 | ['低元音\n此外,半低元音總爲 ɛ,其前後未定,不一定是前元音', [2, ...get選單(['前', '後'], [['æ', 'a'], ['æ', 'ɑ'], ['a', 'ɑ']])]], 46 | ['後高元音', [1, ...get選單(['閉音節', '魚韻'], [['ɨ', 'ɨ'], ['ə', 'ɨ'], ['ə', 'ɯ']])]], 47 | ['部分蟹攝二等入假攝', true], 48 | ['部分流攝脣音入遇攝', true], 49 | ['幽韻一律歸四等\n韻圖如此,但不符合實際', false], 50 | ['完全莊三化二\n韻圖如此,但不符合實際', false], 51 | 52 | '調', 53 | ['聲調', [2, '五度符號', '附加符號', '調類數字']], 54 | ['全濁上歸去', false], 55 | ]; 56 | 57 | function 調整音韻地位() { 58 | function 調整(表達式, 調整屬性, 字頭串 = null) { 59 | if (typeof (字頭串) === 'string' && !字頭串.includes(字頭)) return; 60 | if (is(表達式)) 音韻地位 = 音韻地位.調整(調整屬性); 61 | } 62 | 63 | // 輕唇化例外 64 | 調整('明母 尤韻', { 等: '一', 類: null, 韻: '侯' }); 65 | 調整('明母 東韻', { 等: '一', 類: null }); 66 | 67 | if (is`云母 通攝 舒聲`) 音韻地位 = 音韻地位.調整('匣母', ['匣母三等']); // 雄熊 68 | 69 | // [慧琳反切體現的, 唐代用韻體現的, 據今音推測的] 70 | const 蟹攝二等入假攝字 = ['崖咼(呙)扠涯搋派差絓畫(画)罣罷(罢)', '佳鼃娃解釵(钗)卦柴', '哇洼蛙灑蝸話(话)掛挂查叉杈衩'].join(''); 71 | const 流攝脣音入遇攝字 = ['浮戊母罦罘蜉矛茂覆懋拇某負(负)阜', '謀(谋)部畝(亩)畮婦(妇)不否桴富牟缶', '復複(复)副牡'].join(''); 72 | if (選項.部分蟹攝二等入假攝 !== false) 調整('蟹攝 二等', { 韻: '麻' }, 蟹攝二等入假攝字); 73 | if (選項.部分流攝脣音入遇攝 !== false) 調整('幫組 尤侯韻', { 韻: is`尤韻` ? '虞' : '模' }, 流攝脣音入遇攝字); 74 | } 75 | 76 | 調整音韻地位(); 77 | 78 | const 韻圖等 = when([ 79 | [!選項.幽韻一律歸四等 && '幽韻 B類', '三'], 80 | [!選項.完全莊三化二 && '莊組 三等', '三'], 81 | ['精組 止攝 開口', '一'], // 實際上無用,因爲 i 前的 j 本身就會被省略 82 | ['', 音韻地位.韻圖等], 83 | ]); 84 | 85 | function get聲母() { 86 | let 等類記法 = 選項.等類記法.split(','); 87 | let 聲母 = when([ 88 | ['幫組 C類', 非組字典[選項.非組][音韻地位.母]], 89 | [選項.常船合併崇俟合併 !== false && '俟母', 'dʐ'], 90 | [選項.常船合併崇俟合併 !== false && '常母', 'ʑ'], 91 | [!選項.見組一二等簡寫作軟腭音 && '見溪疑曉匣母 一二等', { 92 | 見: 'q', 溪: 'qʰ', 疑: 'ɴ', 曉: 'χ', 匣: 'ʁ', 93 | }[音韻地位.母]], 94 | ['', { 95 | 幫: 'p', 滂: 'pʰ', 並: 'b', 明: 'm', 96 | 端: 't', 透: 'tʰ', 定: 'd', 泥: 'n', 來: 'l', 97 | 知: 'ʈ', 徹: 'ʈʰ', 澄: 'ɖ', 孃: 'ɳ', 98 | 精: 'ts', 清: 'tsʰ', 從: 'dz', 心: 's', 邪: 'z', 99 | 莊: 'tʂ', 初: 'tʂʰ', 崇: 'dʐ', 生: 'ʂ', 俟: 'ʐ', 100 | 章: 'tɕ', 昌: 'tɕʰ', 常: 'dʑ', 書: 'ɕ', 船: 'ʑ', 日: 'ɲ', 以: 'j', 101 | 見: 'k', 溪: 'kʰ', 羣: 'ɡ', 疑: 'ŋ', 102 | 影: 'ʔ', 曉: 'x', 匣: 'ʁ', 云: '', 103 | }[音韻地位.母]], 104 | ]) + when([ 105 | ['幫組 C類', ''], 106 | ['莊章組 或 日以母', ''], 107 | [韻圖等 === '四', 等類記法[0]], 108 | [韻圖等 === '三', 等類記法[1]], 109 | ['', 等類記法[2]], 110 | ]); 111 | 聲母 = 聲母.replace(/ʰ([ʲˠ])/, '$1ʰ'); 112 | 聲母 = 聲母.replace(/ʁʲ/, 'ɣʲ'); 113 | 聲母 = 聲母.replace(/ʁˠ/, 'ɣ'); 114 | 聲母 = 聲母.replace(/^ˠ/, 'ɰ'); 115 | return 聲母; 116 | } 117 | 118 | function get介音() { 119 | return is`合口` ? 'w' : ''; 120 | } 121 | 122 | function get韻基() { 123 | let 韻基 = when([ 124 | ['遇攝 非 開口', 'u'], 125 | ['遇攝', 'ɨ'], 126 | ['假攝', 'æ'], ['果攝', 'ɑ'], 127 | 128 | ['止攝', 'i'], ['流攝', 'ɨw'], 129 | ['蟹攝', 'ɛj'], ['效攝', 'ɛw'], 130 | 131 | ['臻攝 (B類 合口 或 C類 非 開口)', 'un'], 132 | ['臻攝', 'in'], ['深攝', 'im'], 133 | ['山攝', 'ɛn'], ['咸攝', 'ɛm'], 134 | 135 | ['通攝 幫組 C類 或 鍾韻', 'uŋ'], // 鍾韻底層形式爲 /wɨwŋ/ 136 | ['曾攝', 'ɨŋ'], ['通攝', 'ɨwŋ'], 137 | ['梗攝', 'ɛŋ'], ['宕攝', 'ɑŋ'], ['江攝', 'æwŋ'], 138 | ]); 139 | if (韻圖等 === '一') 韻基 = 韻基.replace(/i(?=[^ŋ])/, 'ɨ'); 140 | 韻基 = 韻基.replace('ɨ', 選項.後高元音.split(',')[+is`魚韻`]); 141 | if (韻圖等 === '二') 韻基 = 韻基.replace(/ɛ(?=[^ŋ])/, 'æ'); 142 | if (韻圖等 === '一') 韻基 = 韻基.replace(/ɛ(?=[^ŋ])/, 'ɑ'); 143 | 韻基 = 韻基.replace('æ', 選項.低元音.split(',')[0]); 144 | 韻基 = 韻基.replace('ɑ', 選項.低元音.split(',')[1]); 145 | if (is`入聲`) [...'mnŋɴ'].forEach((v, i) => { 韻基 = 韻基.replace(v, 'ptkq'[i]); }); 146 | return 韻基; 147 | } 148 | 149 | function get聲調() { 150 | const is陰 = is`全清 或 次清 或 次濁 上入聲`; 151 | return { 152 | 五度符號: is陰 ? 153 | { 平: '˦˨', 上: '˦˥', 去: '˧˨˦', 入: '˦' } : 154 | { 平: '˨˩', 上: '˨˦', 去: '˨˨˦', 入: '˨' }, 155 | 附加符號: { 平: '̀', 上: '́', 去: '̌', 入: '' }, 156 | 調類數字: { 平: '¹', 上: '²', 去: '³', 入: '⁴' }, 157 | }[選項.聲調][選項.全濁上歸去 && is`全濁 上聲` ? '去' : 音韻地位.聲]; 158 | } 159 | 160 | function get音節() { 161 | const 音節 = { 162 | 聲母: get聲母(), 163 | 介音: get介音(), 164 | 韻基: get韻基(), 165 | 聲調: get聲調(), 166 | }; 167 | if (音節.韻基[0] === 'i') 音節.聲母 = 音節.聲母.replace(/(? 音韻地位.屬於(...x); 10 | /** @type { 音韻地位['判斷'] } */ 11 | const when = (...x) => 音韻地位.判斷(...x); 12 | 13 | let isIPA = 選項.音標體系 !== 'MPA'; 14 | let 音標體系changed = 選項._last音標體系 && 選項._last音標體系 !== 選項.音標體系; 15 | let mpaRevoked = false; // 用於在用戶選擇放棄使用 MPA 時刷新選項列表的內容 16 | if (音標體系changed && 選項.音標體系 === 'MPA') { 17 | const message = '請注意 MPA 不是國際音標,其中諸多符號不可按國際音標理解,容易引起誤會。在公共場合使用 MPA 後果自負!\n\n確認使用 MPA?'; 18 | if (!confirm(message)) { // eslint-disable-line no-undef 19 | isIPA = true; 20 | 音標體系changed = false; 21 | mpaRevoked = true; 22 | } 23 | } 24 | 25 | if (!音韻地位) return [ 26 | ['_last音標體系', [1, !mpaRevoked && 選項.音標體系 || '國際音標'], { hidden: true }], 27 | ['音標體系', [1, '國際音標', 'MPA'], { 28 | reset: mpaRevoked, 29 | description: [ 30 | '擬音中用到的 MPA(即 msoeg 音標)與國際音標對照', 31 | // 詳見 https://zhuanlan.zhihu.com/p/710982203 32 | '(1)\u2002腭噝音(章組)[tɕ] MPA 作 ⟨tç⟩', 33 | '(2)\u2002r 化元音記號 [◌˞\u2006] MPA 作 ⟨◌̣⟩', 34 | '(3)\u2002[i ɯ̯ ɯ u ɛ ɔ a] 對應的 r 化元音 MPA 作 ⟨ị ɨ̣ ɯ̣ ụ ɜ̣ ɞ̣ ạ⟩', 35 | '(4)\u2002[o̯](實即 [ȗ̙])MPA 作 ⟨ᵒ⟩', 36 | '此外,擬音中的 MPA ⟨ɯ/u、ɤ/o、ʌ/ɔ、ɑ⟩ 是非前元音,⟨ɛ、ɜ̣/ɞ̣、ʌ/ɔ⟩ 是中元音,這裏保留 MPA 原貌', 37 | ].join('\n'), 38 | }], 39 | ['r化元音記號|r 化元音記號\n知乎文章用下加點\n韻鑒用 r 音鉤\n「r 化」原文稱「捲舌」', [ 40 | isIPA ? 1 : 3, 41 | { text: 'r 音鉤(帶空隙)◌˞', value: '\u02DE\u2006' }, 42 | { text: 'r 音鉤(無空隙)◌˞', value: '\u02DE' }, 43 | { text: '下加點 ◌̣', value: '\u0323' }, 44 | ].slice(0, isIPA ? 3 : 4), { reset: 音標體系changed }], 45 | ['通江宕攝韻尾|\n知乎文章用 ŋʷ/kʷ\n韻鑒用 ɴ/q', [3, 'ŋ/k', 'ŋʷ/kʷ', 'ɴ/q']], 46 | ['聲調記號|\n上標的 ʔ Unicode 未收,這裏以 ˀ 代替', 47 | [1, '上ʔ 去h', '上ˀ 去ʰ'].filter((_, i) => isIPA || i !== 1), 48 | { reset: 音標體系changed }, 49 | ], 50 | ['顯示高級選項', false], 51 | 52 | 選項.顯示高級選項 ? '高級選項' : '', 53 | ['保留非三等ʶ記號|保留非三等 ʶ 記號\nʶ 不是標準國際音標,代表舌根偏後', 54 | false, { hidden: !選項.顯示高級選項 }], 55 | ['章組|\n知乎文章和韻鑒用腭噝音', 56 | [2, '齦後噝音 tʃ', isIPA ? '腭噝音 tɕ' : '腭噝音 tç'], { hidden: !選項.顯示高級選項 }], 57 | ['莊三韻母起始|\n知乎文章用 r 化元音\n韻鑒用 ɻ\n這裏默認用普通的三等起始(B 類或 C 類)', 58 | [1, '普通', 'r 化元音', 'ɻ'], { hidden: !選項.顯示高級選項 }], 59 | ['覺韻|\n知乎文章和韻鑒用低元音\n這裏默認用中元音,與江韻一致', 60 | [1, '中元音', '低元音'], { hidden: !選項.顯示高級選項 }], 61 | ['庚三清|\n知乎文章和韻鑒用低元音', 62 | [2, '中元音', '低元音'], { hidden: !選項.顯示高級選項 }], 63 | ['宕攝入聲附加|\n知乎文章和韻鑒用 ⁽ʷ⁾\n這裏默認省略', 64 | [1, '無', '⁽ʷ⁾', 'ʷ'], { hidden: !選項.顯示高級選項 || 選項.通江宕攝韻尾?.includes('ʷ') }], 65 | ]; 66 | 67 | function get聲母_默認拼寫() { 68 | // 五十一聲類 + 俟母 69 | const 普通聲母字典 = { 70 | 幫: 'p', 滂: 'pʰ', 並: 'b', 明: 'm', 71 | 知: 'ʈ', 徹: 'ʈʰ', 澄: 'ɖ', 孃: 'ɳ', 來: 'ɭ', 72 | 見: 'k', 溪: 'kʰ', 羣: 'ɡ', 疑: 'ŋ', 云: 'w', 73 | 影: 'ʔ', 曉: 'x', 74 | 精: 'ts', 清: 'tsʰ', 從: 'dz', 心: 's', 邪: 'z', 75 | 莊: 'tʂ', 初: 'tʂʰ', 崇: 'dʐ', 生: 'ʂ', 俟: 'ʐ', 76 | 章: 'tɕ', 昌: 'tɕʰ', 常: 'dʑ', 書: 'ɕ', 船: 'ʑ', 日: 'ɲ', 以: 'j', 77 | }; 78 | const 非三等聲母字典 = { 79 | 幫: 'pʶ', 滂: 'pʶʰ', 並: 'bʶ', 明: 'mʶ', 80 | 端: 'tʶ', 透: 'tʶʰ', 定: 'dʶ', 泥: 'nʶ', 來: 'lʶ', 81 | 見: 'q', 溪: 'qʰ', 疑: 'ɴ', 82 | 影: 'ʔʶ', 曉: 'χ', 匣: 'ʁ', 83 | 精: 'tsʶ', 清: 'tsʶʰ', 從: 'dzʶ', 心: 'sʶ', 84 | }; 85 | if (is`云母 開口 非 侵鹽韻`) return 'ɰ'; 86 | if (is`以母 合口 或 以母 東鍾虞韻`) return 'ɥ'; 87 | if (is`三等 或 來母 二等 非 庚韻`) return 普通聲母字典[音韻地位.母] || 非三等聲母字典[音韻地位.母]; 88 | return 非三等聲母字典[音韻地位.母] || 普通聲母字典[音韻地位.母]; 89 | } 90 | 91 | function get聲母() { 92 | let 聲母 = get聲母_默認拼寫(); 93 | if (選項.章組.includes('ʃ')) { 94 | 聲母 = 聲母.replace('ɕ', 'ʃ').replace('ʑ', 'ʒ'); 95 | } else if (選項.章組.includes('ç')) { 96 | 聲母 = 聲母.replace('ɕ', 'ç').replace('ʑ', 'ʝ'); 97 | } 98 | if (!選項.保留非三等ʶ記號) { 99 | 聲母 = 聲母.replace('ʶ', ''); 100 | } 101 | return 聲母; 102 | } 103 | 104 | function get韻母() { 105 | let 韻母 = when([ 106 | ['之韻', 'ɯ'], ['支韻', 'e'], ['魚韻', 'ɤ'], ['虞模韻', 'o'], 107 | ['佳韻', 'ɜ̣'], 108 | ['麻韻 三四等', 'a'], ['麻韻', 'ạ'], ['歌韻', 'ɑ'], 109 | 110 | [選項.庚三清 === '中元音' && '庚清韻 三等', 'ɛŋ'], 111 | [選項.覺韻 === '低元音' && '江韻 入聲', 'ɑ̣ɴ'], 112 | ['蒸韻', 'ɯŋ'], ['東韻', 'uɴ'], ['鍾韻', 'oɴ'], 113 | ['青韻', 'ɛŋ'], ['耕韻', 'ɜ̣ŋ'], ['登韻', 'ʌŋ'], ['冬韻', 'ɔɴ'], ['江韻', 'ɞ̣ɴ'], 114 | ['庚清韻 (三四等 或 端組 或 來母)', 'aŋ'], ['庚清韻', 'ạŋ'], ['陽唐韻', 'ɑɴ'], 115 | 116 | ['脂韻', 'ii'], ['微韻', 'ɯi'], 117 | ['齊祭韻', 'ɛi'], ['皆韻', 'ɜ̣i'], ['灰咍廢韻', 'ʌi'], 118 | ['夬韻', 'ại'], ['泰韻', 'ɑi'], 119 | 120 | ['真臻韻', 'in'], ['殷韻', 'ɯn'], ['文韻', 'un'], 121 | ['先仙韻', 'ɛn'], ['山韻', 'ɜ̣n'], ['元魂痕韻', 'ʌn'], 122 | ['刪韻', 'ạn'], ['寒韻', 'ɑn'], 123 | 124 | ['幽韻', 'iu'], ['尤侯韻', 'uu'], 125 | ['蕭宵韻', 'ɛu'], 126 | ['肴韻', 'ạu'], ['豪韻', 'ɑu'], 127 | 128 | ['侵韻', 'im'], 129 | ['鹽添韻', 'ɛm'], ['咸韻', 'ɜ̣m'], ['嚴凡韻', 'ʌm'], ['覃韻', 'ɔm'], 130 | ['銜韻', 'ạm'], ['談韻', 'ɑm'], 131 | ]); 132 | 133 | // 等類記號 134 | if (is`三等` || is`四等` && 韻母.startsWith('a')) { 135 | if (韻母.startsWith('ɯ') && is`銳音 或 蒸韻 AB類`) 韻母 = 'i' + 韻母; 136 | else if (/^[eɛa]/.test(韻母)) 韻母 = 'i' + 韻母; 137 | else if (/^[ɤʌɑ]/.test(韻母)) 韻母 = 'ɯ' + 韻母; 138 | else if (韻母.startsWith('o')) 韻母 = 'u' + 韻母; 139 | 140 | if (韻母.startsWith('i') && is`B類 非 幽韻`) { 141 | // 幽韻知乎原文和韻鑒僅 A 類 142 | // 其中,支宵侵的重紐三等開口歸 C 類 143 | 韻母 = 韻母.replace('i', is`支宵韻 或 侵韻 見影組 非 云母` ? 'ɯ' : 'ị'); 144 | } 145 | 146 | if (is`莊組`) { 147 | 韻母 = 韻母.replace(/^i/, 'ị'); // 莊組 AB 類歸 B 類 148 | if (選項.莊三韻母起始 === 'r 化元音') { 149 | 韻母 = 韻母.replace(/^(.)̣?(.*)/, '$1̣$2'); 150 | 韻母 = 韻母.replace(/ị(?!e)|ɯ̣(?!ɤ)/, 'ɨ̣'); // ABC 類的起始都等同於二等的 ɨ̣,支魚韻除外 151 | } else if (選項.莊三韻母起始 === 'ɻ') { 152 | 韻母 = 韻母.replace(/(ị|^ɯ(?!ɤ))?/, 'ɻ'); // 以其他元音起始的則直接前加 ɻ,魚韻除外 153 | } 154 | } 155 | } else { 156 | if (韻母.startsWith('u')) 韻母 = 'ᵒ' + 韻母; 157 | } 158 | 159 | // 開合記號 160 | if (韻母.startsWith('ɯ')) { 161 | if (is`合口 或 幫組 非 支宵侵韻`) 韻母 = 韻母.replace('ɯ', 'u'); 162 | } else { 163 | if (is`合口 非 云以母`) 韻母 = 韻母.replace(/(ɻ?)(.*)/, '$1ʷ$2').replace('ʷu', 'u'); 164 | } 165 | 166 | if (isIPA) 韻母 = 韻母 167 | .replace('ɨ̣', 'ɯ̣') 168 | .replace('ɜ̣', 'ɛ̣').replace('ɞ̣', 'ɔ̣') 169 | .replace('ᵒ', 'o̯'); 170 | 韻母 = 韻母 171 | .replace('ii', 'i').replace('uu', 'u') 172 | .replace('̣', 選項.r化元音記號) 173 | .replace('ɴ', 選項.通江宕攝韻尾.split('/')[0]); 174 | if (is`入聲`) 韻母 = 韻母 175 | .replace('m', 'p') 176 | .replace('n', 't') 177 | .replace('ŋ', 'k') 178 | .replace('ɴ', 'q'); 179 | if (is`江攝 入聲` && !韻母.endsWith('ʷ') && 選項.覺韻 === '低元音') 韻母 += 'ʷ'; 180 | if (is`宕攝 入聲` && !韻母.endsWith('ʷ')) 韻母 += 選項.宕攝入聲附加.replace('無', ''); 181 | return 韻母; 182 | } 183 | 184 | function get聲調() { 185 | if (is`平入聲`) return ''; 186 | return 選項.聲調記號.split(' ')[+is`去聲`].slice(1); 187 | } 188 | 189 | return get聲母() + get韻母() + get聲調(); 190 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /putonghua.js: -------------------------------------------------------------------------------- 1 | /* 推導普通話 2 | * 3 | * @author graphemecluster 4 | * @author JwietPuj-Drin 5 | * @author SyiMyuZya 6 | * 7 | * 選項「清聲母入聲調分派層次」詳見平山久雄《中古汉语的清入声在北京话里的对应规律》 8 | * http://ccj.pku.edu.cn/Article/DownLoad?id=271015083&&type=ArticleFile 9 | * 默認選擇為「皆派入陰平」,参考刘海阳在對「北京话的入声为什么会派入三个不同的声调?」的回答中披露的材料—— 10 | * 「古代清音入声字在北京話的声調,凡是沒有异讀的,就采用北京已經通行的讀法。凡是有异讀的,假若其中有一个是陰平調,原則上就采用陰平,例如:“息”ㄒㄧ(xī)“击”ㄐㄧ(jī)。否則逐字考慮,采用比較通行……」 11 | * https://www.zhihu.com/question/30370012/answer/533234460 12 | * 13 | * 選項「常母平聲陰聲韻聲母和船母平聲聲母」詳見 unt 對「为何中古的dʑ ʑ和普通话读音的对应似乎是反的(即dʑ > ʂ、ʑ > ʈʂ)?」的回答 14 | * https://www.zhihu.com/question/526195183/answer/2425807330 15 | */ 16 | 17 | /** @type { 音韻地位['屬於'] } */ 18 | const is = (...x) => 音韻地位.屬於(...x); 19 | /** @type { 音韻地位['判斷'] } */ 20 | const when = (...x) => 音韻地位.判斷(...x); 21 | 22 | const is更多選項 = 選項.更多選項 ?? false; 23 | 24 | if (!音韻地位) return [ 25 | ['標調方式', [2, '數字', '附標']], 26 | 27 | ['更多選項', is更多選項], 28 | ...(is更多選項 ? [ 29 | '更多選項', 30 | ['清聲母入聲調分派層次', 31 | [2, 32 | '皆派入上聲', 33 | '皆派入陰平', 34 | '次清、擦音和零聲母字派入去聲,其餘派入陽平', 35 | '次清和零聲母字派入去聲,其餘派入陽平', 36 | '皆不標調', 37 | '連同濁聲母,所有入聲字皆派入去聲', 38 | ] 39 | ], 40 | ['常母平聲陰聲韻聲母和船母平聲聲母', [2, 'ch', 'sh']], 41 | ] : []), 42 | ]; 43 | 44 | // 預調整(中古中期通語已產生的不同) 45 | if (is`明母 尤東韻`) 音韻地位 = 音韻地位.調整(`${音韻地位.韻 === '尤' ? '侯' : 音韻地位.韻}韻 一等 不分類`); 46 | if (is`云母 通攝 舒聲`) 音韻地位 = 音韻地位.調整('匣母', ['匣母三等']); 47 | // TODO 蟹攝入假攝、流攝入遇攝等 48 | 49 | // j、q、x 不列於聲母規則,之後會由「拼細音」條件得出 50 | const 聲母規則 = () => when([ 51 | ['幫滂並母 C類', 'f'], 52 | ['幫母', 'b'], 53 | ['滂母', 'p'], 54 | ['並母', [['平聲', 'p'], ['', 'b']]], 55 | ['明母', [['C類', 'w'], ['', 'm']]], 56 | 57 | ['端母', 'd'], 58 | ['透母', 't'], 59 | ['定母', [['平聲', 't'], ['', 'd']]], 60 | ['泥孃母', 'n'], 61 | ['來母', 'l'], 62 | 63 | ['精母', 'z'], 64 | ['清母', 'c'], 65 | ['從母', [['平聲', 'c'], ['', 'z']]], 66 | ['心邪母', 's'], 67 | 68 | ['知莊章母', 'zh'], 69 | ['徹初昌母', 'ch'], 70 | ['澄崇母', [['平聲', 'ch'], ['', 'zh']]], 71 | ['常母', [['平聲 陽聲韻', 'ch'], ['', 'sh']]], 72 | ['生書母', 'sh'], 73 | ['俟船母', 'sh'], 74 | ['日母', 'r'], 75 | 76 | ['見母', 'g'], 77 | ['溪母', 'k'], 78 | ['羣母', [['平聲', 'k'], ['', 'g']]], 79 | ['曉匣母', 'h'], 80 | ['以母 蟹攝 三四等 合口', 'r'], // 「銳」 81 | ['疑影云以母', ''], 82 | ], '無聲母規則'); 83 | 84 | // 韻母均按零聲母寫法,唯 y、w、yu 作 i、u、ü(例如用 uen、ueng 不用 un、ong),這是為了方便後面的拼寫處理。 85 | // 韻母規則不額外列出 zh、ch、sh、r、f、w 及部分 n、l 系統性地拼為洪音的情形,後面會處理。 86 | const 舒聲韻母規則 = () => when([ 87 | ['通攝', [['三四等 牙喉音', 'iong'], ['', 'ueng']]], 88 | 89 | ['止攝', [ 90 | ['合口', [['莊組', 'uai'], ['', 'uei']]], 91 | ['', 'er'], // 'er' 指示其拼日母時為 er,以及拼 z、c、s 時聲母不作 j、q、x,其餘情形均同 i 92 | ]], 93 | 94 | ['遇攝', [['三四等', 'ü'], ['', 'u']]], 95 | 96 | ['蟹攝', [ 97 | ['廢韻 平上聲 章組', [['合口', 'uai'], ['', 'ai']]], // 「茝」 98 | ['三四等', [['合口 莊組', 'uai'], ['合口', 'uei'], ['', 'i']]], 99 | // FIXME 蟹攝二等有古已轉入假攝者,依更早韻書推導時需判斷具體的字。目前暫僅列出個別可能存在系統性者 100 | ['二等 開口 溪影母', 'ai'], // 「矮隘楷揩」等 101 | ['佳韻 合口 牙喉音', 'ua'], // 「畫掛」等(FIXME 未覆蓋如「佳」「話」等,且過度覆蓋「拐」等,當依具體字而非音) 102 | ['佳韻 開口 疑母', 'ia'], // 「崖睚」等(FIXME 當依具體字而非音) 103 | ['二等', [ 104 | ['開口 牙喉音', 'ie'], 105 | ['合口', 'uai'], 106 | ['', 'ai']], 107 | ], 108 | ['一等', [['開口', 'ai'], ['', 'uei']]], 109 | ]], 110 | 111 | ['臻深攝', [ 112 | ['三四等', [['合口', 'ün'], ['', 'in']]], 113 | ['', [ 114 | ['合口 或 舌齒音', 'uen'], // 覆蓋「吞」 115 | ['', 'en'], 116 | ]], 117 | ]], 118 | 119 | ['山咸攝', [ 120 | ['三四等', [['合口', 'üan'], ['', 'ian']]], 121 | ['二等 牙喉音 開口', 'ian'], 122 | ['', [['合口', 'uan'], ['', 'an']]], 123 | ]], 124 | 125 | ['效攝', [ 126 | ['三四等 或 二等 牙喉音', 'iao'], 127 | ['', 'ao'], 128 | ]], 129 | 130 | ['果假攝', [ 131 | ['三四等', [['合口', 'üe'], ['', 'ie']]], 132 | ['二等', [['合口', 'ua'], ['牙喉音', 'ia'], ['', 'a']]], 133 | ['一等', [['開口 牙喉音', 'e'], ['', 'uo']]], 134 | ]], 135 | 136 | ['宕江攝', [ 137 | ['合口 或 莊組 或 二等 知組', 'uang'], 138 | ['三四等 或 二等 牙喉音', 'iang'], 139 | ['', 'ang'], 140 | ]], 141 | 142 | ['梗曾攝', [ 143 | ['三四等', [['合口', 'iong'], ['', 'ing']]], 144 | ['', [['合口', 'ueng'], ['', 'eng']]], 145 | ]], 146 | 147 | ['流攝', [ 148 | // FIXME 流攝脣音有古已轉入遇攝者,依更早韻書推導時需判斷具體的字。目前暫僅列出個別可能存在系統性者 149 | ['幽韻 幫滂並母', 'iao'], // 「彪髟淲」等 150 | ['尤韻 脣音 非 (幫母 上聲)', 'u'], // 排除「否缶」(FIXME 當依具體字而非音) 151 | ['三四等', 'iou'], 152 | ['', 'ou'], // FIXME 未覆蓋如「牡」「部」「矛」「茂」等,當依具體字而非音 153 | ]], 154 | ], '無韻母規則'); 155 | 156 | const 入聲韻母規則 = () => when([ 157 | ['通攝', [['三四等 牙喉音', 'ü'], ['', 'u']]], 158 | 159 | ['臻深攝', [ 160 | ['莊組', [['合口', 'uai'], ['', 'e']]], // TODO 「櫛」? 161 | ['三四等', [['合口 或 脣音 C類', 'ü'], ['', 'i']]], 162 | ['', [['脣音', 'o'], ['合口', 'u'], ['', 'e']]], 163 | ]], 164 | 165 | ['山咸攝', [ 166 | // TODO 「茁」 167 | ['二等 或 莊組 或 脣音 C類', [['合口', 'ua'], ['牙喉音', 'ia'], ['', 'a']]], 168 | ['三四等', [['合口', 'üe'], ['', 'ie']]], 169 | ['', [ 170 | ['開口', [['牙喉音', 'e'], ['', 'a']]], 171 | ['', 'uo'], 172 | ]], 173 | ]], 174 | 175 | ['宕江攝', [ 176 | // TODO 白讀 177 | ['三四等 或 二等 牙喉音', 'üe'], 178 | ['一等 開口 牙喉音', 'e'], 179 | ['', 'uo'], 180 | ]], 181 | 182 | ['梗曾攝', [ 183 | // TODO 白讀 184 | ['三四等 非 莊組', [['合口', 'ü'], ['', 'i']]], 185 | ['', [['開口', 'e'], ['', 'uo']]], 186 | ]], 187 | ], '無韻母規則'); 188 | 189 | const 聲調規則 = () => when([ 190 | ['清音', [ 191 | ['平聲', '1'], 192 | ['上聲', '3'], 193 | ['去聲', '4'], 194 | ['入聲', ''], 195 | ]], 196 | ['濁音', [ 197 | ['平聲', '2'], 198 | ['上聲', [['全濁', '4'], ['次濁', '3']]], 199 | ['去聲', '4'], 200 | ['入聲', [['全濁', '2'], ['次濁', '4']]], 201 | ]], 202 | ], '無聲調規則'); 203 | 204 | let 聲母 = 聲母規則(); 205 | let 韻母 = is`舒聲` ? 舒聲韻母規則() : 入聲韻母規則(); 206 | let 聲調 = 聲調規則(); 207 | 208 | if (選項.更多選項) { 209 | // 參考 https://www.zhihu.com/question/526195183/answer/2425807330 210 | if (is`(常母 陰聲韻 或 船母) 平聲`) 聲母 = 選項.常母平聲陰聲韻聲母和船母平聲聲母; 211 | 212 | /* 參考 213 | * http://ccj.pku.edu.cn/Article/DownLoad?id=271015083&&type=ArticleFile (https://web.archive.org/web/20240223084634/http://ccj.pku.edu.cn/Article/DownLoad?id=271015083&&type=ArticleFile) 214 | * https://www.zhihu.com/question/30370012/answer/533234460 215 | * https://www.zhihu.com/question/30370012/answer/535713330 216 | */ 217 | if (is`入聲`) { 218 | switch (選項.清聲母入聲調分派層次) { 219 | case '皆派入上聲': 220 | if (is`清音`) 聲調 = '3'; 221 | break; 222 | case '皆派入陰平': 223 | if (is`清音`) 聲調 = '1'; 224 | break; 225 | case '次清、擦音和零聲母字派入去聲,其餘派入陽平': 226 | if (is`心生書影曉母 或 次清`) 聲調 = '4'; 227 | else if (is`全清`) 聲調 = '2'; 228 | break; 229 | case '次清和零聲母字派入去聲,其餘派入陽平': 230 | if (is`影母 或 次清`) 聲調 = '4'; 231 | else if (is`全清`) 聲調 = '2'; 232 | break; 233 | case '連同濁聲母,所有入聲字皆派入去聲': 234 | 聲調 = '4'; 235 | break; 236 | } 237 | } 238 | } 239 | 240 | // j、q、x 聲母 241 | if (韻母 === 'er' || ['i', 'ü'].includes(韻母[0])) { 242 | 聲母 = { g: 'j', k: 'q', h: 'x' }[聲母] ?? 聲母; 243 | if (韻母 !== 'er') 聲母 = { z: 'j', c: 'q', s: 'x' }[聲母] ?? 聲母; 244 | } 245 | 246 | // er 247 | if (韻母 === 'er') { 248 | if (聲母 === 'r') 聲母 = ''; 249 | else 韻母 = 'i'; 250 | } 251 | 252 | // 以下音節系統性地作開口而非合口 253 | if (['n', 'l'].includes(聲母) && ['ua', 'uai', 'uang', 'uei'].includes(韻母)) 韻母 = 韻母.slice(1); 254 | // 以下音節系統性地作合口而非撮口 255 | if (韻母[0] === 'ü' && ['n', 'l'].includes(聲母) && !['ü', 'üe'].includes(韻母)) { 256 | 韻母 = 'u' + (韻母[1] === 'n' ? 'e' : '') + 韻母.slice(1); 257 | } 258 | 259 | // 以下音節系統性地作洪音而非細音 260 | if (['zh', 'ch', 'sh', 'r', 'f', 'w'].includes(聲母)) { 261 | if (韻母 === 'i') { 262 | if (聲母 === 'f' || 聲母 === 'w') 韻母 = 'ei'; 263 | } else if (韻母[0] === 'i' || 韻母[0] === 'ü') { 264 | 韻母 = (韻母[0] === 'ü' ? 'u' : '') + (韻母[1] === 'n' ? 'e' : '') + 韻母.slice(1); 265 | if (韻母 === 'ue') 韻母 = 'uo'; 266 | else if (韻母 === 'e' && ['f', 'w'].includes(聲母)) 韻母 = 'o'; 267 | } 268 | } 269 | 270 | // 以下音節系統性地作開口而非合口 271 | if (['b', 'p', 'm', 'f', 'w'].includes(聲母) && 韻母[0] === 'u' && 韻母[1]) 韻母 = 韻母.slice(1); 272 | 273 | // 拼音拼寫規則 274 | if (!聲母) { 275 | if (韻母[0] === 'i' || 韻母[0] === 'ü') 聲母 = 'y'; 276 | if (韻母[0] === 'u') 聲母 = 'w'; 277 | if (聲母 && 韻母[0] !== 'ü' && 韻母[1] && 韻母[1] !== 'n') 韻母 = 韻母.slice(1); 278 | } 279 | 韻母 = { iou: 'iu', uei: 'ui', uen: 'un', ueng: 'ong' }[韻母] || 韻母; 280 | if (韻母[0] === 'ü' && !['n', 'l'].includes(聲母)) 韻母 = 'u' + 韻母.slice(1); 281 | 282 | if (選項.標調方式 === '數字') return 聲母 + 韻母 + 聲調; 283 | return 聲母 + (聲調 ? 韻母.replace(/.*a|.*[eo]|.*[iuü]/, '$&' + ' ̄́̌̀'[聲調]) : 韻母); 284 | -------------------------------------------------------------------------------- /onp.js: -------------------------------------------------------------------------------- 1 | /* 推導老國音 2 | * 3 | * 音系:https://zh.wikiversity.org/zh-hant/%E8%80%81%E5%9C%8B%E9%9F%B3%E9%9F%B3%E7%B4%A0 4 | * 音變規則:https://zh.wikiversity.org/zh-hant/%E8%80%81%E5%9C%8B%E9%9F%B3%E8%88%87%E5%BB%A3%E9%9F%BB%E5%B0%8D%E6%AF%94 5 | * 字音來源:https://github.com/baopaau/rime-bepemefeve/blob/main/bepemefeve.dict.yaml 6 | * 注意「僞老國音」:https://www.bilibili.com/read/cv17377530/ 7 | * 8 | * @author 307587 9 | */ 10 | 11 | /** @type { 音韻地位['屬於'] } */ 12 | const is = (...x) => 音韻地位.屬於(...x); 13 | /** @type { 音韻地位['判斷'] } */ 14 | const when = (...x) => 音韻地位.判斷(...x); 15 | 16 | if (!音韻地位) return [ 17 | ['注音符號|標注方式', [1, { text: '注音符號', value: true }, { text: '國際音標', value: false }]], 18 | '標注風格', 19 | ...(選項.注音符號 !== false ? [ 20 | ['注音符號省略陰平調號|省略陰平調號', false], 21 | ] : [ 22 | ['ㄬㄭ', [2, 'ȵ ɿ ʅ', 'ɲ ɹ̩ ɻ̍']], 23 | ['介音', [2, { text: 'j w ɥ', value: '半元音' }, { text: 'i u y', value: '元音' }]], 24 | ['ㄧㄞ', [2, 25 | { text: 選項.介音 === '半元音' ? 'jæi' : 'iæi', value: 'iæi' }, 26 | { text: 選項.介音 === '半元音' ? 'jai' : 'iai', value: 'iai' }, 27 | ]], 28 | ['ㄨㄣ\n趙元任《國音新詩韻》:「ㄨㄣ」也可以一律照樣拼念作「ㄨㄜㄋ」,但是在「ㄉ,ㄊ,ㄋ,ㄌ;ㄗ,ㄘ,ㄙ」七個聲母後頭可以省去「ㄜ」音念「ㄨㄋ」', [2, 'un', 'uən']], 29 | ['喉塞|入聲標示喉塞尾', true], 30 | ]), 31 | ['聲調', [7, '調值數字(趙元任)', '調值數字(王璞)', '調值符號(趙元任)', '調值符號(王璞)', '調類數字', '調類數字(不上標)', '調符']], 32 | '特殊演變', 33 | [`匣上|匣上變去 34 | 匣母上聲有很多字不變去聲`, true], 35 | ]; 36 | 37 | const 聲母規則 = () => when([ 38 | ['幫滂並母 C類', 'ㄈ'], 39 | ['幫母 或 並母 仄聲', 'ㄅ'], 40 | ['滂並母', 'ㄆ'], 41 | ['明母', [['微韻', 'ㄪ'], ['C類 非 流通攝', ''], ['', 'ㄇ']]], 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 | const 舒聲韻母規則 = () => when([ 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 | const 入聲韻母規則 = () => when([ 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 | const 聲調規則 = () => when([ 178 | ['平聲', [['清音', 'ˉ'], ['濁音', 'ˊ']]], 179 | ['去聲 或 上聲 全濁', 'ˋ'], 180 | ['上聲', 'ˇ'], 181 | ['入聲', '˙'], 182 | ], '無聲調規則'); 183 | 184 | let 聲母 = 聲母規則(); 185 | let 韻母 = is`舒聲` ? 舒聲韻母規則() : 入聲韻母規則(); 186 | let 聲調 = 聲調規則(); 187 | 188 | if (選項.匣上 !== true && is`匣母 上聲`) 聲調 = 'ˇ'; 189 | 190 | // 顎化;疑母齊、撮呼不規則脫鼻 191 | if (['ㄧ', 'ㄩ'].includes(韻母[0])) 聲母 = { ㄍ: 'ㄐ', ㄎ: 'ㄑ', ㄏ: 'ㄒ' }[聲母] || 聲母; 192 | if (['ㄧ', 'ㄩ'].includes(韻母[0]) && 聲母 === 'ㄋ' && !is`齊之韻 開口`) 聲母 = 'ㄬ'; 193 | if (韻母[0] === 'ㄧ' && 聲母 === 'ㄫ') 聲母 = is`(齊之尤蒸陽韻 非 合口) 或 (侵仙先元庚韻 入聲)` ? 'ㄬ' : ''; 194 | if (韻母[0] === 'ㄩ' && 聲母 === 'ㄫ') 聲母 = ''; 195 | 196 | // 捲舌音 197 | if (['ㄓ', 'ㄔ', 'ㄕ', 'ㄖ'].includes(聲母)) { 198 | if (韻母 === 'ㄧ') 韻母 = 'ㄭ'; 199 | if (韻母[0] === 'ㄧ' && 韻母[1]) 韻母 = 韻母.slice(1); 200 | if (韻母 === 'ㄩㄝ') 韻母 = 'ㄨㄛ'; 201 | if (韻母[0] === 'ㄩ') 韻母 = 'ㄨ' + 韻母.slice(1); 202 | } 203 | 204 | // 脣音 205 | if (is`脣音` && 韻母[0] === 'ㄩ') 韻母 = 'ㄨ' + 韻母.slice(1); 206 | if (['ㄅ', 'ㄆ', 'ㄇ', 'ㄈ'].includes(聲母) && 韻母[0] === 'ㄨ' && 韻母[1] && !['ㄣ', 'ㄥ'].includes(韻母[1])) 韻母 = 韻母.slice(1); 207 | if (['ㄈ', 'ㄪ'].includes(聲母) && 韻母[0] === 'ㄧ') 韻母 = 韻母.slice(1) || 'ㄟ'; 208 | 209 | if (聲母 === 'ㄫ' && 韻母[0] === 'ㄨ' && !is`一等 山宕通攝 入聲`) 聲母 = ''; // 疑母合口 210 | 211 | // IPA 212 | if (選項.注音符號 === false) { 213 | 聲母 = { 214 | ㄅ: 'p', ㄆ: 'pʰ', ㄇ: 'm', ㄈ: 'f', ㄪ: 'ʋ', 215 | ㄉ: 't', ㄊ: 'tʰ', ㄋ: 'n', ㄌ: 'l', 216 | ㄍ: 'k', ㄎ: 'kʰ', ㄫ: 'ŋ', ㄏ: 'x', 217 | ㄐ: 'tɕ', ㄑ: 'tɕʰ', ㄬ: 'ɲ', ㄒ: 'ɕ', 218 | ㄓ: 'tʂ', ㄔ: 'tʂʰ', ㄕ: 'ʂ', ㄖ: 'ɻ', 219 | ㄗ: 'ts', ㄘ: 'tsʰ', ㄙ: 's', 220 | }[聲母] || 聲母; 221 | 韻母 = { 222 | ㄧ: 'i', ㄨ: 'u', ㄩ: 'y', ㄭ: ['ts', 'tsʰ', 's'].includes(聲母) ? 'ɹ̩' : 'ɻ̍', 223 | ㄚ: 'a', ㄛ: 'o', ㄜ: 'ə', ㄦ: 'ɚ', ㄝ: 'e', 224 | ㄞ: 'ai', ㄟ: 'əi', ㄠ: 'au', ㄡ: 'əu', 225 | ㄢ: 'an', ㄣ: 'ən', ㄤ: 'aŋ', ㄥ: 'əŋ', 226 | ㄧㄚ: 'ia', ㄧㄛ: 'io', ㄧㄝ: 'ie', 227 | ㄧㄞ: 'iai', ㄧㄠ: 'iau', ㄧㄡ: 'iəu', 228 | ㄧㄢ: 'iɛn', ㄧㄣ: 'in', ㄧㄤ: 'iaŋ', ㄧㄥ: 'iŋ', 229 | ㄨㄚ: 'ua', ㄨㄛ: 'uo', ㄨㄞ: 'uai', ㄨㄟ: 'uəi', 230 | ㄨㄢ: 'uan', ㄨㄣ: 'uən', ㄨㄤ: 'uaŋ', ㄨㄥ: 'oŋ', 231 | ㄩㄛ: 'yo', ㄩㄝ: 'ye', ㄩㄢ: 'yɛn', ㄩㄣ: 'yn', ㄩㄥ: 'ioŋ', 232 | }[韻母] || 韻母; 233 | 234 | // 音標風格 235 | if (選項.ㄬㄭ === 'ȵ ɿ ʅ') { // ㄬㄭ 236 | if (聲母 === 'ɲ') 聲母 = 'ȵ'; 237 | if (韻母 === 'ɹ̩') 韻母 = 'ɿ'; 238 | if (韻母 === 'ɻ̍') 韻母 = 'ʅ'; 239 | } 240 | if (韻母 === 'iai') 韻母 = 選項.ㄧㄞ; // ㄧㄞ 241 | if (韻母 === 'uən') 韻母 = 選項.ㄨㄣ; // ㄨㄣ 242 | if (選項.喉塞 === true && is`入聲`) 韻母 += 'ʔ'; // 喉塞音韻尾 243 | if (選項.介音 === '半元音' && 韻母[1] && !['n', 'ŋ', 'ʔ'].includes(韻母[1])) { // 介音 244 | if (韻母[0] === 'i') 韻母 = 'j' + 韻母.slice(1); 245 | if (韻母[0] === 'u') 韻母 = 'w' + 韻母.slice(1); 246 | if (韻母[0] === 'y') 韻母 = 'ɥ' + 韻母.slice(1); 247 | } 248 | } 249 | 250 | if (韻母 === 'ㄭ') 韻母 = ''; 251 | 252 | 聲調 = { 253 | '調值數字(趙元任)': { 'ˉ': '⁵⁵', 'ˊ': '³⁵', 'ˇ': '²¹⁴', 'ˋ': '⁵¹', '˙': '⁵' }, 254 | '調值數字(王璞)': { 'ˉ': '⁴⁴', 'ˊ': '³⁵⁵', 'ˇ': '²¹⁴', 'ˋ': '⁴¹', '˙': '⁴¹' }, 255 | '調值符號(趙元任)': { 'ˉ': '˥', 'ˊ': '˧˥', 'ˇ': '˨˩˦', 'ˋ': '˥˩', '˙': '˥' }, 256 | '調值符號(王璞)': { 'ˉ': '˦', 'ˊ': '˧˥˥', 'ˇ': '˨˩˦', 'ˋ': '˦˩', '˙': '˦˩' }, 257 | '調類數字(不上標)': { 'ˉ': '1', 'ˊ': '2', 'ˇ': '3', 'ˋ': '4', '˙': '5' }, 258 | '調類數字': { 'ˉ': '¹', 'ˊ': '²', 'ˇ': '³', 'ˋ': '⁴', '˙': '⁵' }, 259 | }[選項.聲調]?.[聲調] || 聲調; 260 | if (選項.注音符號省略陰平調號 !== false && 選項.注音符號 !== false && 聲調 === 'ˉ') 聲調 = ''; 261 | 262 | return 聲母 + 韻母 + 聲調; 263 | -------------------------------------------------------------------------------- /unt.js: -------------------------------------------------------------------------------- 1 | /* unt 切韻擬音 2 | * unt’s Qieyun Reconstruction 3 | * 4 | * [出處待公佈 / Source TBA] 5 | * 6 | * 過往 unt 擬音(切韻擬音 L、切韻擬音 J、切韻通俗擬音、切韻朗讀音)已移入「unt 過往擬音」推導方案,不建議使用 7 | * Previous versions of unt’s reconstruction have been moved to the derivation scheme “unt’s Legacy Reconstructions” and are no longer recommended for use 8 | * 9 | * @author unt 10 | */ 11 | 12 | /** @type { 音韻地位['屬於'] } */ 13 | const is = (...x) => 音韻地位.屬於(...x); 14 | /** @type { 音韻地位['判斷'] } */ 15 | const when = (...x) => 音韻地位.判斷(...x); 16 | 17 | if (!音韻地位) { 18 | /* global document */ 19 | let isZH = typeof document === 'undefined' || (document.documentElement?.lang?.startsWith('zh') ?? true); 20 | let prevRTR = 選項._prevRTR ?? true; 21 | let prevATR = 選項._prevATR ?? false; 22 | let prev小舌 = 選項._prev小舌 ?? true; 23 | let currRTR = 選項.RTR ?? true; 24 | let currATR = 選項.ATR ?? false; 25 | let curr小舌 = 選項.小舌 ?? true; 26 | if (curr小舌 !== prev小舌 && !curr小舌) currRTR = false; // RTR => 見組非三等寫小舌音 27 | if (!currRTR && !currATR) { // RTR 和 ATR 中至少選一項 28 | if (currRTR !== prevRTR) currATR = true; 29 | else currRTR = true; 30 | } 31 | if (currRTR !== prevRTR && currRTR) curr小舌 = true; // RTR => 見組非三等寫小舌音 32 | return [ 33 | ['_prevRTR', currRTR, { reset: true, hidden: true }], 34 | ['_prevATR', currATR, { reset: true, hidden: true }], 35 | ['_prev小舌', curr小舌, { reset: true, hidden: true }], 36 | 37 | isZH ? '非三等' : 'Type A: Divisions I, II & IV', 38 | ['RTR', currRTR, { 39 | reset: currRTR !== prevRTR, 40 | text: isZH ? '寫出 RTR(舌根偏後)符號' : 'Use RTR Diacritic', 41 | description: isZH ? 42 | `聲母下加├ 43 | 見組除外,因爲見組非三等已經是小舌音,無需再加 RTR 符號 44 | (寫出 RTR 符號時,見組非三等必寫小舌音)` : 45 | `Add the retracted tongue root (RTR) diacritic [├] to the initial 46 | Except for Type-A dorsals (見 Jiàn group), which are already uvulars and do not require additional indication for RTR 47 | (When applying the RTR diacritic, Type-A dorsals must be transcribed as uvulars)`, 48 | }], 49 | ['pRTR', 1, { 50 | hidden: !currRTR, 51 | text: isZH ? 'RTR 的 p' : 'RTR Diacritic on [p]', 52 | options: [ 53 | { text: isZH ? '符號加在上方 p᫡' : 'Above [p᫡]', value: 'p᫡' }, 54 | { text: isZH ? '符號加在下方 p̙' : 'Below [p̙]', value: 'p̙' }, 55 | ], 56 | }], 57 | 58 | isZH ? '三 等' : 'Type B: Division III', 59 | ['ATR', currATR, { 60 | reset: currATR !== prevATR, 61 | text: isZH ? '寫出 ATR(舌根偏前)介音' : 'Use ATR Medials', 62 | description: isZH ? 63 | `鈍音聲母三等 C 類寫 ɣ 介音(代表軟腭近音 ɣ̞ = ɨ̯~ɯ̯) 64 | 莊組三等寫 ɹ 介音 65 | 其他銳音聲母三等寫 j 介音 66 | (RTR 符號和 ATR 介音至少要寫出一方)` : 67 | `Add advanced tongue root (ATR) medials to Division-III syllables: 68 | After non-coronal (grave) initials, Subdivision C: [ɣ] (representing the velar approximant [ɣ̞] = [ɨ̯~ɯ̯]) 69 | After retroflex sibilants (莊 Zhuāng group): [ɹ] 70 | After other coronal (acute) initials: [j] 71 | (At least one of RTR and ATR must be marked)`, 72 | }], 73 | 74 | isZH ? '小舌音' : 'Uvulars', 75 | ['小舌', curr小舌, { 76 | reset: curr小舌 !== prev小舌, 77 | text: isZH ? '見組非三等寫小舌音' : 'Transcribe Type-A Dorsals as Uvulars', 78 | description: isZH ? null : 'Dorsals = 見 Jiàn group', 79 | }], 80 | ['通江宕攝音節尾寫小舌音', true, { 81 | hidden: !curr小舌, 82 | text: isZH ? null : 'Transcribe Back Dorsal Codas as Uvulars', 83 | description: isZH ? null : 'Back dorsal codas = codas of the 通 Tōng, 江 Jiāng, and 宕 Dàng rhyme groups', 84 | }], 85 | ['後低元音', [1, 'a', 'ɑ'], { 86 | hidden: !curr小舌 || 選項.通江宕攝音節尾寫小舌音 === false, 87 | text: isZH ? null : 'Low Back Vowel', 88 | description: isZH ? 89 | '宕攝音節尾寫小舌音時,前後低元音互補,可合併' : 90 | 'When transcribing the 宕 Dàng rhyme group codas as uvulars, the low front and back vowels are in complementary distribution and can be merged', 91 | }], 92 | 93 | isZH ? '其 他' : 'Misc', 94 | ['支韻', [2, 'ie̯', 'ie', 'e'], { 95 | text: isZH ? null : '支 Zhī Rhyme', 96 | }], 97 | ['微韻合口', [2, 'wɨj', 'uj'], { 98 | text: isZH ? null : '微 Wēi Rhyme, Hékǒu 合口', 99 | }], 100 | ['聲調記號', 0, { 101 | text: isZH ? null : 'Tone Marks', 102 | options: [ 103 | { text: isZH ? '上ˊ 去ˋ' : 'Rising [ˊ], Departing [ˋ]', value: ',\u0301,\u0300,' }, 104 | { text: isZH ? '上ʔ 去h' : 'Rising [ʔ], Departing [h]', value: ',ʔ,h,' }, 105 | { text: isZH ? '五度符號' : 'Chao Tone Letters', value: '˦,˦˥,˦˩,˦,˨,˨˥,˨˩,˨' }, // ...陰調, ...陽調 106 | { text: isZH ? '省略' : 'None', value: ',,,' }, 107 | ], 108 | }], 109 | ['音韻地位正則化', true, { 110 | text: isZH ? '音韻地位正則化' : 'Phonological Position Normalization', 111 | description: isZH ? 112 | `銳音三 C₁ 韻 → 對應的 AB 韻(廢韻除外) 113 | 銳音幽韻 → 尤韻 114 | 明母尤韻 → 侯韻` : 115 | `Division-III Subdivision-C₁ rhymes following coronal (acute) initials → Corresponding Subdivision-AB rhymes (except for the 廢 Fèi rhyme) 116 | 幽 Yōu rhyme following coronal (acute) initials → 尤 Yóu rhyme 117 | 尤 Yóu rhyme following the initial 明 Míng → 侯 Hóu rhyme`, 118 | }], 119 | ['幫組拼ə時添加w介音', false, { 120 | text: isZH ? 121 | 選項.微韻合口 === 'wɨj' ? 122 | '幫組拼 ɨ、ə 時添加 w 介音(蒸登韻除外)' : 123 | '幫組拼 ə 時添加 w 介音(登韻除外)' : 124 | 選項.微韻合口 === 'wɨj' ? 125 | 'Insert [w] Between Labials and [ɨ]/[ə]' : 126 | 'Insert [w] Between Labials and [ə]', 127 | description: isZH ? null : 128 | `${選項.微韻合口 === 'wɨj' ? 129 | 'Except for the 蒸 Zhēng and 登 Dēng rhymes' : 130 | 'Except for the 登 Dēng rhyme'} 131 | Labials = 幫 Bāng group`, 132 | }], 133 | ]; 134 | } 135 | 136 | function 音韻地位正則化() { 137 | function 調整(表達式, 調整屬性) { if (is(表達式)) 音韻地位 = 音韻地位.調整(調整屬性); } 138 | [['微', '脂'], ['殷文', '真'], ['元', '仙'], ['嚴凡', '鹽']] 139 | .forEach(([C1, AB]) => 調整(`銳音 ${C1}韻`, `${AB}韻`)); 140 | 調整('銳音 幽韻', `尤韻 開合中立 ${is`端組` ? '四等' : ''}`); 141 | 調整('明母 尤韻', '侯韻 一等 不分類'); 142 | } 143 | 144 | function get聲母() { 145 | if (is`云母 開口 非 (深咸攝 入聲)`) return ''; // 云母開口爲零聲母,但煜、曄小韻(視爲“合口”)除外 146 | return { 147 | 幫: 'p p̙', 滂: 'pʰ p̙ʰ', 並: 'b b̙', 明: 'm m̙', 148 | 端: ' t̙', 透: ' t̙ʰ', 定: ' d̙', 泥: ' n̙', 來: 'l l̙', 149 | 知: 'ʈ ', 徹: 'ʈʰ ', 澄: 'ɖ ', 孃: 'ɳ ', 150 | 見: 'k q', 溪: 'kʰ qʰ', 羣: 'ɡ ', 疑: 'ŋ ɴ', 曉: 'x χ', 匣: 'ʁ', 云: 'w', 151 | 影: 'ʔ ʔ̙', 152 | 精: 'ts t̙s̙', 清: 'tsʰ t̙s̙ʰ', 從: 'dz d̙z̙', 心: 's s̙', 邪: 'z', 153 | 莊: 'tʂ ', 初: 'tʂʰ ', 崇: 'dʐ ', 生: 'ʂ ', 俟: 'ʐ', 154 | 章: 'tɕ ', 昌: 'tɕʰ ', 常: 'dʑ ', 書: 'ɕ ', 船: 'ʑ', 日: 'ɲ', 以: 'j', 155 | }[音韻地位.母].trim().split(' ').at(is`三等` - 1); 156 | } 157 | 158 | function get介音() { 159 | let 類介音 = { A: 'j', B: 'ɹ' }[音韻地位.類] ?? ''; 160 | let 呼介音 = is`合口 非 云母` ? 'w' : ''; 161 | return 類介音 + 呼介音; 162 | } 163 | 164 | function get音節核尾() { 165 | let [韻列表, 核] = [ 166 | ['脂 │   │幽 │  │  │真臻 │侵  ', 'i'], 167 | ['之 │微  │  │蒸 │  │殷  │   ', 'ɨ'], 168 | ['尤侯│___│__│__│東_│文__│___', 'u'], 169 | ['支 │齊祭 │蕭宵│青 │  │先仙 │鹽添 ', 'e'], 170 | ['佳 │皆  │  │耕 │  │山  │咸  ', 'eˤ'], 171 | ['魚 │灰咍廢│豪 │登 │  │元魂痕│覃嚴凡', 'ə'], 172 | ['虞模│   │  │  │冬鍾│   │   ', 'o'], 173 | ['__│___│__│__│江_│___│___', 'oˤ'], 174 | ['麻 │夬  │肴 │庚清│  │刪  │銜  ', is`二等` ? 'aˤ' : 'a'], 175 | ['歌 │泰  │  │  │陽唐│寒  │談  ', 'ɑ'], 176 | ].find(e => e[0].includes(音韻地位.韻)); 177 | let 尾 = ['', ...'jwŋɴnm'][韻列表.split('│').findIndex(e => e.includes(音韻地位.韻))]; 178 | if (is`入聲`) 尾 = { ŋ: 'k', ɴ: 'q', n: 't', m: 'p' }[尾]; 179 | return { 核, 尾 }; 180 | } 181 | 182 | function get聲調() { 183 | return 選項.聲調記號.split(',').at('平上去入'.indexOf(音韻地位.聲) - is`全濁` * 4); 184 | } 185 | 186 | function get音節() { 187 | const 音節 = { 188 | 聲母: get聲母(), 189 | 介音: get介音(), 190 | ...get音節核尾(), 191 | 聲調: get聲調(), 192 | }; 193 | if (!選項.RTR) 音節.聲母 = 音節.聲母.replace('̙', ''); 194 | if (is`幫滂母`) 音節.聲母 = 音節.聲母.replace('p̙', 選項.pRTR); 195 | if (選項.ATR) 音節.介音 = when([ 196 | ['C類', 'ɣ'], 197 | ['莊組 三等', 'ɹ'], 198 | ['銳音 三等 非 以母', 'j'], 199 | ['', ''], 200 | ]) + 音節.介音; 201 | const 小舌音替換字典 = { q: 'k', qʰ: 'kʰ', ɴ: 'ŋ', χ: 'x', ʁ: 'ɣ' }; 202 | if (!選項.小舌) 音節.聲母 = 小舌音替換字典[音節.聲母] ?? 音節.聲母; 203 | if (!選項.小舌 || !選項.通江宕攝音節尾寫小舌音) 音節.尾 = 小舌音替換字典[音節.尾] ?? 音節.尾; 204 | else if (音節.核 === 'ɑ') 音節.核 = 選項.後低元音; 205 | if (is`支韻`) 音節.核 = 選項.支韻; 206 | if (is`微韻 非 開口`) 音節.核 = 選項.微韻合口.slice(-2, -1); 207 | if (選項.幫組拼ə時添加w介音 && ['ɨ', 'ə'].includes(音節.核) && is`幫組 非 曾攝`) 音節.介音 += 'w'; 208 | 209 | if (is`四等` && 音節.核 !== 'e') 音節.介音 = 'j' + 音節.介音; // 爹小韻 210 | if (音節.核[0] === 'i') 音節.介音 = 音節.介音.replace('j', ''); 211 | if (音節.核[0] === 'ɨ') 音節.介音 = 音節.介音.replace('ɣ', ''); 212 | if (['u', 'o'].includes(音節.核[0])) 音節.介音 = 音節.介音.replace('w', ''); 213 | 音節.首 = 音節.聲母 + 音節.介音; 214 | 音節.韻基 = 音節.核 + 音節.尾; 215 | 音節.帶調韻基 = 選項.聲調記號.includes('\u0301') ? 216 | 音節.核.slice(0, 1) + 音節.聲調 + 音節.核.slice(1) + 音節.尾 : 217 | 音節.核 + 音節.尾 + 音節.聲調; 218 | 音節.韻母 = 音節.介音 + 音節.韻基; 219 | 音節.帶調韻母 = 音節.介音 + 音節.帶調韻基; 220 | return 音節; 221 | } 222 | 223 | if (選項.音韻地位正則化) 音韻地位正則化(); 224 | const 音節 = get音節(); 225 | return 音節.聲母 + 音節.帶調韻母; 226 | -------------------------------------------------------------------------------- /n_song.js: -------------------------------------------------------------------------------- 1 | /* 推導北宋擬音(聲音唱和圖) 2 | * 3 | * 以《聲音唱和圖》和北宋中原押韻爲基礎。《聲音唱和圖》分析並記錄了北宋共通語的音系,對該圖的解析與擬音詳見:https://zhuanlan.zhihu.com/p/498778513 4 | * 5 | * 代碼注釋中的等均指韻圖等(聲音唱和圖等),而不是切韻等 6 | * 7 | * @author unt 8 | */ 9 | 10 | /** @type { 音韻地位['屬於'] } */ 11 | const is = (...x) => 音韻地位.屬於(...x); 12 | /** @type { 音韻地位['判斷'] } */ 13 | const when = (...x) => 音韻地位.判斷(...x); 14 | 15 | const is表層 = 選項.顯示形式 !== '底層'; 16 | 17 | if (!音韻地位) return [ 18 | ['_last顯示形式', [1, 選項.顯示形式 ?? '底層'], { hidden: true }], 19 | ['顯示形式|顯示\n音位分析據《聲音唱和圖》', [2, 20 | { value: '底層', text: '底層形式(音位形式)' }, 21 | { value: '表層', text: '表層形式(語音實現)' }, 22 | ]], 23 | '聲', 24 | ['全濁平送氣|全濁平送氣 ʰ\n《聲音唱和圖》全濁平塞音送氣,但按通常習慣可不標', true], 25 | ['次濁上喉化|次濁上喉化 ˀ\n《聲音唱和圖》次濁上歸陰調,但按通常習慣可不標\n喉化此處寫作上標的 ʔ,此上標字母 Unicode 未收,以 ˀ 代替', true], 26 | ['知照組\ntʂ、tɕ《聲音唱和圖》已合爲同一音位。按通用的國際音標習慣,無對立的 tʂ、tɕ 可一律標爲 tʃ,但爲了照顧漢語習慣,表層形式默認選擇 tʂ、tɕ 分立', 27 | [is表層 ? 3 : 1, 28 | { value: 'ʃʒɹ', text: '知tɹ 莊章tʃ' }, 29 | { value: 'ʂʐɻ', text: '知tɻ 莊章tʂ' }, 30 | { value: 'ʂʐɹ|ɕʑɹ', text: '知tɹ 莊tʂ 章tɕ' }, 31 | { value: 'ʂʐɻ|ɕʑɻ', text: '知tɻ 莊tʂ 章tɕ' }, 32 | ].slice(0, is表層 ? 5 : 3), 33 | { reset: 選項._last顯示形式 === '底層' && 選項.顯示形式 !== '底層' }, 34 | ], 35 | '韻', 36 | is表層 ? ['入聲尾\n深咸 | 臻山 | 曾梗通江宕', [1, 37 | { value: 'p t k', text: 'p | t | k(符合通常習慣)' }, 38 | { value: 'β ɾ ɣ', text: 'β | ɾ | ɣ(接近實際音值)' }, 39 | ]] : ['入聲尾\n陰入相配依《聲音唱和圖》\n深咸 | 臻山 | 曾梗 | 通江宕', [1, 40 | { value: 'p t k wk', text: 'p | t | k | wk(陽入相配)' }, 41 | { value: 'ʋˀ ˀ jˀ wˀ', text: 'ʋˀ | ˀ | jˀ | wˀ(陰入相配)' }, 42 | ]], 43 | ['咸山蟹攝銳音一開歸\n保守:一等\n時興:二等\n(泥母蟹攝除外。《聲音唱和圖》“乃”仍在一等)', [2, '一等', '二等']], 44 | ['咸山攝輕脣歸\n保守:一等\n時興:二等', [2, '一等', '二等']], 45 | { 46 | key: '止蟹三四合韻基', 47 | text: '止蟹三四合', 48 | description: is表層 ? '保守:ɥi\n時興:yj' : '保守:jwɨ\n時興:jwɨj', 49 | value: 'ɨj', 50 | options: [ 51 | { value: 'ɨ', text: is表層 ? 'ɥi' : 'jwɨ' }, 52 | { value: 'ɨj', text: is表層 ? 'yj' : 'jwɨj' }, 53 | ], 54 | }, 55 | ['蟹一合\n保守:wɔj\n時興:uj', [2, 'wɔj', 'uj'], { hidden: !is表層 }], 56 | ['章止同蟹|章止開 = 章蟹開\n《聲音唱和圖》和《蒙古字韻》同音,但北方官話實際不同音。《中原音韻》中,章止開歸支思韻,章蟹開歸齊微韻', false], 57 | ['部分蟹攝二等歸假攝', true], 58 | ['部分流攝脣音歸遇攝', true], 59 | '調', 60 | ['聲調', [1, '附加符號', '調類數字', '省略']], 61 | ['全濁上歸去\n《聲音唱和圖》未體現全濁上字的聲調,按口語則已歸去聲,按《蒙古字韻》風格則仍算上聲', true], 62 | ['影喻上聲合併\n《聲音唱和圖》無影喻上聲對立的空間,可能口語已合併,但同期反切未見相混的情況', false], 63 | ]; 64 | 65 | function 調整音韻地位() { 66 | function 調整(表達式, 調整屬性) { if (is(表達式)) 音韻地位 = 音韻地位.調整(調整屬性); } 67 | // 輕唇化例外 68 | 調整('明母 尤韻', { 等: '一', 類: null, 韻: '侯' }); 69 | 調整('明母 東韻', { 等: '一', 類: null }); 70 | 71 | if (is`云母 通攝 舒聲`) 音韻地位 = 音韻地位.調整('匣母', ['匣母三等']); // 雄熊 72 | 73 | // [慧琳反切體現的, 唐代用韻體現的, 據今音推測的] 74 | const 蟹攝二等歸假攝字 = ['崖咼(呙)扠涯派差絓畫(画)罣罷(罢)', '佳鼃娃解卦', '灑蝸話(话)掛挂查叉杈衩'].join(''); 75 | const 流攝脣音歸遇攝字 = ['浮戊母罦罘蜉覆拇負(负)阜', '部畝(亩)畮婦(妇)不否桴富', '復複(复)副牡'].join(''); 76 | if (選項.部分蟹攝二等歸假攝 !== false && 蟹攝二等歸假攝字.includes(字頭)) 調整('蟹攝 二等', { 韻: '麻' }); 77 | if (選項.部分流攝脣音歸遇攝 !== false && 流攝脣音歸遇攝字.includes(字頭)) 調整('幫組 尤侯韻', { 韻: is`尤韻` ? '虞' : '模' }); 78 | } 79 | 80 | function get聲母() { 81 | return when([ 82 | ['匣母 肴韻 平聲', 'ɰ'], // 匣母“爻”在三音,但一等無字,說明“完”不是零聲母 83 | ['崇母 止攝 仄聲', 'ʒ'], // 崇母“士”在十音。另船母船小韻和繩小韻中“乘”聲符字今讀塞擦音,但圖中未體現,這裏不考慮 84 | 85 | ['幫組 C類', [ 86 | ['幫滂母', 'f'], ['並母', 'v'], ['明母 上聲', 'ˀʋ'], ['明母', 'ʋ'], // 四音 87 | ]], 88 | ['曉母', 'x'], ['匣母', 'ɣ'], ['疑母 上聲', 'ˀŋ'], ['疑母', 'ŋ'], // 二音 89 | ['心母', 's'], ['邪母', 'z'], // 九音。這一組的後兩個聲母(同部位的近音)有音無字 90 | ['生書母', 'ʃ'], ['常母 仄聲 或 俟船母', 'ʒ'], ['日母 上聲', 'ˀɹ'], ['日母', 'ɹ'], // 十音 91 | 92 | ['幫母', 'p'], ['並母 仄聲', 'b'], ['滂母', 'pʰ'], ['並母', 'bʰ'], // 五音 93 | ['端母', 't'], ['定母 仄聲', 'd'], ['透母', 'tʰ'], ['定母', 'dʰ'], // 六音 94 | ['見母', 'k'], ['羣母 仄聲', 'ɡ'], ['溪母', 'kʰ'], ['羣母', 'ɡʰ'], // 一音 95 | ['知母', 'tɹ'], ['澄母 仄聲', 'dɹ'], ['徹母', 'tɹʰ'], ['澄母', 'dɹʰ'], // 十二音 96 | ['精母', 'ts'], ['從母 仄聲', 'dz'], ['清母', 'tsʰ'], ['從母', 'dzʰ'], // 八音 97 | ['莊章母', 'tʃ'], ['崇母 仄聲', 'dʒ'], ['初昌母', 'tʃʰ'], ['崇常母', 'dʒʰ'], // 十一音 98 | 99 | ['泥孃母 上聲', 'ˀn'], ['泥孃母', 'n'], ['來母 上聲', 'ˀl'], ['來母', 'l'], // 七音 100 | ['影母', 'ʔ'], ['云以母 上聲', 選項.影喻上聲合併 ? 'ʔ' : 'ˀɰ'], ['云以母', 'ɰ'], ['明母 上聲', 'ˀm'], ['明母', 'm'], // 三音 101 | ]); 102 | } 103 | 104 | function get等() { 105 | return when([ 106 | ['幫組 C類', [ 107 | ['止蟹攝', '四'], 108 | ['咸山攝', 選項.咸山攝輕脣歸?.[0] ?? '二'], 109 | ['', '一'], 110 | ]], 111 | ['精組 止攝 開口', '一'], // 中唐 /sjɨ/ > /sɨ/ 變一等 112 | ['銳音 蟹山咸攝 一等 開口 非 (泥母 蟹攝)', 選項.咸山蟹攝銳音一開歸?.[0] ?? '二'], 113 | [音韻地位.韻圖等 === '四' && '銳音 非 以母', '三'], // 圖中銳音一律無四等 114 | ['', 音韻地位.韻圖等], 115 | ]); 116 | } 117 | 118 | function get開合() { 119 | return when([ 120 | [選項.咸山攝輕脣歸 === '一等' && '咸山攝 幫組 C類', '合'], 121 | ['流深咸攝', '開'], // 深咸攝舒入聲都視爲開口,不依圖中定義 122 | ['幫組', [ 123 | ['通宕攝', '開'], 124 | ['一等 或 虞文歌韻', '合'], // 其餘一等(包括輕脣變一等的)歸合口 125 | ['', '開'], 126 | // 圖中“八”在合口,但二等不應該在合口。這可能是繼承了《韻鏡》《七音略》脣音刪舒、山入在合口,刪入、山舒在開口。本方案不考慮 127 | ]], 128 | ['鍾虞模韻', '合'], 129 | ['江韻 銳音', '合'], 130 | ['', 音韻地位.呼 ?? '開'], 131 | ]); 132 | } 133 | 134 | function get介音(等, 開合) { 135 | return { 136 | 一: { 開: '', 合: 'w' }, 137 | 二: { 開: 'ʕ', 合: 'ʕw' }, 138 | 三: { 開: 'j', 合: 'jw' }, 139 | 四: { 開: 'ʲj', 合: 'ʲjw' }, 140 | }[等][開合]; 141 | } 142 | 143 | function get韻基() { 144 | return when([ 145 | ['遇攝', 'ɯ'], // 六聲下(僅舒聲) 146 | 147 | ['止攝 (莊組 或 (精章組 或 日母) 開口)', 'ɨ'], // 五聲上(舒) 148 | ['臻攝', is`入聲` ? 'ɨt' : 'ɨn'], // 五聲上(入) & 三聲下 149 | 150 | ['果假攝', 'a'], // 一聲上(舒) 151 | ['山攝', is`入聲` ? 'at' : 'an'], // 一聲上(入) & 三聲上 152 | 153 | ['蟹攝 (一二等 或 莊組)', 'aj'], // 一聲下(僅舒聲) 154 | 155 | ['止蟹攝 合口', 選項.止蟹三四合韻基 ?? 'ɨj'], // 五聲下(舒) 156 | ['止蟹攝', 選項.章止同蟹 ? 'ɨ' : 'ɨj'], 157 | ['曾梗攝', is`入聲` ? 'ɨk' : 'ɨŋ'], // 五聲下(入) & 二聲下 158 | 159 | ['流攝', 'ɨw'], // 四聲下(舒) 160 | ['通攝', is`入聲` ? 'ɨwk' : 'ɨwŋ'], // 四聲下(入) & 六聲上 161 | 162 | ['效攝', 'aw'], // 四聲上(舒) 163 | ['宕江攝', is`入聲` ? 'awk' : 'awŋ'], // 四聲上(入) & 二聲上 164 | 165 | ['深攝', is`入聲` ? 'ɨp' : 'ɨm'], // 七聲上 166 | ['咸攝', is`入聲` ? 'ap' : 'am'], // 七聲下 167 | ]); 168 | } 169 | 170 | function get聲調() { 171 | if (選項.聲調 === '省略') return ''; 172 | let 聲調 = 音韻地位.聲; 173 | if (選項.全濁上歸去 && is`全濁 上聲`) 聲調 = '去'; 174 | return { 175 | 附加符號: ['̀', '́', '̌', ''], // 入聲已由韻尾表明,無需附加符號 176 | 調類數字: ['¹', '²', '³', '⁴'], 177 | }[選項.聲調 ?? '附加符號']['平上去入'.indexOf(聲調)]; 178 | } 179 | 180 | function 底層to表層(音節) { 181 | function 替換韻核(from, tos, condition = true) { 182 | if (!condition) return; 183 | 音節.韻核 = 音節.韻核.replace(from, 音節.介音.includes('w') ? tos.pop() : tos[0]); 184 | } 185 | 186 | const is曾梗攝 = ['ŋ', 'k'].includes(音節.韻尾); 187 | 替換韻核('a', ['ɑ']); // 先把 /a/ 重置成一等的表層形式 [ɑ] 188 | if (音節.介音.includes('ʕ')) { 189 | // 二等 190 | 替換韻核('ɯ', ['ɯ', 'u']); 191 | 替換韻核('ɨ', ['iˤ'], is曾梗攝); 192 | 替換韻核('ɨ', ['ɨ', 'u'], 音節.韻尾); 193 | 替換韻核('ɑ', ['a']); 194 | } else if (音節.介音.includes('j')) { 195 | // 三四等 196 | 替換韻核('ɯ', ['ɯ', 'u']); 197 | 替換韻核('ɨ', ['i', 'y'], 音節.韻尾); 198 | 替換韻核('ɨ', ['i'], 選項.章止同蟹); 199 | 替換韻核('ɨ', ['ɨ', 'i']); 200 | 替換韻核('ɑ', ['æ', 'ɐ'], !音節.韻尾); // 北宋時麻二麻三未有明確分開的跡象,暫擬作 [jæ] 201 | 替換韻核('ɑ', ['ɛ', 'ɔ'], is`鈍音` && !音節.介音.includes('ʲ')); 202 | 替換韻核('ɑ', ['ɛ']); 203 | } else { 204 | // 一等 205 | 替換韻核('ɯ', ['ɯ', 'u']); 206 | 替換韻核('ɨ', ['ɨ', 'u']); 207 | 替換韻核('ɑ', ['ʌ', 'ɔ'], !音節.韻尾); 208 | 替換韻核('ɑ', ['ɑ', 'ɔ']); 209 | 替換韻核('ɔ', [選項.蟹一合?.slice(-2, -1) ?? 'u'], 音節.韻尾 === 'j'); 210 | } 211 | if (is曾梗攝) { 212 | 替換韻核('u', ['ɨ']); // /wɨŋ/ 不寫作 [uŋ],以免與通攝混淆 213 | } else if (['wŋ', 'wk'].includes(音節.韻尾)) { 214 | 替換韻核(/[iɨy]/, ['u']); 215 | 替換韻核(/[ɛɔ]/, ['ɑ']); 216 | 音節.韻尾 = 音節.韻尾.replace('w', ''); 217 | } 218 | 音節.介音 = 音節.介音.replace('jw', 'ɥ'); 219 | if (['ji', 'ɥy', 'wu'].includes(音節.介音.slice(-1) + 音節.韻核)) 音節.介音 = 音節.介音.slice(0, -1); 220 | if (音節.韻核 === 'i' && 音節.韻尾 === 'j') 音節.韻尾 = ''; 221 | } 222 | 223 | function 後處理(音節) { 224 | let 知照組符號 = 選項.知照組 ?? 'ʂʐɹ|ɕʑɹ'; 225 | if (!音節.介音.includes('ʕ')) 知照組符號 = 知照組符號.slice(-3); // 二等取開頭 3 個符號,三等取最後 3 個符號 226 | [...'ʃʒɹ'].forEach((e, i) => { 音節.聲母 = 音節.聲母.replace(e, 知照組符號[i]); }); 227 | if (is表層) 音節.介音 = 音節.介音.replace('ʕ', ''); 228 | 229 | if (音節.介音.includes('ʲ')) { 230 | 音節.聲母 += 'ʲ'; 231 | 音節.聲母 = 音節.聲母.replace('ʰʲ', 'ʲʰ'); 232 | 音節.介音 = 音節.介音.replace('ʲ', ''); 233 | } 234 | if (is表層 && 音節.聲母.includes('ɰ')) { 235 | 音節.聲母 = 音節.聲母.replace('ɰʲ', ''); // 韻圖四等由細音韻母指示 236 | if (音節.介音 === 'ɥ' || !音節.介音 && 音節.韻核 === 'y') 音節.聲母 = 音節.聲母.replace('ɰ', 'w'); 237 | if (音節.介音 === 'w' || !音節.介音 && 音節.韻核 === 'u') 音節.聲母 = 音節.聲母.replace('ɰ', ''); 238 | } 239 | if (選項.次濁上喉化 === false) 音節.聲母 = 音節.聲母.replace('ˀ', ''); 240 | if (選項.全濁平送氣 === false && is`全濁`) 音節.聲母 = 音節.聲母.replace('ʰ', ''); 241 | if (is`入聲`) { 242 | let 韻尾from = ['p', 't', 'k', 'wk']; 243 | let 韻尾to = 選項.入聲尾?.split(' ') ?? 韻尾from; 244 | 音節.韻尾 = 韻尾to[韻尾from.indexOf(音節.韻尾)]; 245 | } 246 | } 247 | 248 | function get音節() { 249 | const 韻基 = get韻基(); 250 | const 音節 = { 251 | 聲母: get聲母(), 252 | 介音: get介音(get等(), get開合()), 253 | 韻核: 韻基[0], 254 | 韻尾: 韻基.substring(1), 255 | 聲調: get聲調(), 256 | }; 257 | if (is表層) 底層to表層(音節); 258 | 後處理(音節); 259 | 音節.韻母 = 音節.介音 + 音節.韻核 + 音節.韻尾; 260 | if (選項.聲調 === '調類數字') 音節.帶調韻母 = 音節.韻母 + 音節.聲調; 261 | else if (音節.韻核.length === 1) 音節.帶調韻母 = 音節.介音 + 音節.韻核 + 音節.聲調 + 音節.韻尾; 262 | else 音節.帶調韻母 = 音節.介音 + 音節.韻核[0] + 音節.聲調 + 音節.韻核.slice(1) + 音節.韻尾; 263 | return 音節; 264 | } 265 | 266 | 調整音韻地位(); 267 | const 音節 = get音節(); 268 | return 音節.聲母 + 音節.帶調韻母; 269 | -------------------------------------------------------------------------------- /ayaka_v8.js: -------------------------------------------------------------------------------- 1 | /* 綾香思考音系 2 | * 3 | * https://ayaka.shn.hk/v8/ 4 | * 5 | * @author Ayaka 6 | */ 7 | 8 | /** @type { 音韻地位['屬於'] } */ 9 | const is = (...x) => 音韻地位.屬於(...x); 10 | /** @type { 音韻地位['判斷'] } */ 11 | const when = (...x) => 音韻地位.判斷(...x); 12 | 13 | // 1. 選項 14 | 15 | if (!音韻地位) return [ 16 | ['書寫系統', [3, '平假名', '片假名', '日本式羅馬字', '平文式羅馬字'], { 17 | description: [ 18 | "'平假名': 地 ち", 19 | "'片假名': 地 チ", 20 | "'日本式羅馬字': 地 ti", 21 | "'平文式羅馬字': 地 chi" 22 | ].join('\n'), 23 | }], 24 | 25 | ['ヰヱヲ小假名', true, { 26 | description: ['true: 迥 ク𛅥ィ', 'false: 迥 クヱィ', '僅當開啓假名時生效'].join('\n'), 27 | }], 28 | 29 | ['音變', [1, { value: null, text: '無' }, '現代日語'], { 30 | description: [ 31 | "'無': 宙 チウ tiu;南 ダム dam;愁 スウ suu", 32 | "'現代日語': 宙 チュウ tyuu;南 ダン dan;愁 スウ suu", 33 | ].join('\n'), 34 | }], 35 | 36 | // 參考:尉遲治平. 日本悉曇家所傳古漢語調值. 37 | ['聲調', [1, { value: null, text: '無' }, '四聲', '四聲(數字)', '四聲(調值)', '六聲(調值)', '六聲(符號)', '八聲', '八聲(數字)', '八聲(調值)']], 38 | ]; 39 | 40 | // 2. 輔助函數 41 | 42 | const 假名表 = { 43 | a: 'ア', i: 'イ', u: 'ウ', e: 'エ', o: 'オ', 44 | ka: 'カ', ki: 'キ', ku: 'ク', ke: 'ケ', ko: 'コ', 45 | ga: 'ガ', gi: 'ギ', gu: 'グ', ge: 'ゲ', go: 'ゴ', 46 | sa: 'サ', si: 'シ', su: 'ス', se: 'セ', so: 'ソ', 47 | za: 'ザ', zi: 'ジ', zu: 'ズ', ze: 'ゼ', zo: 'ゾ', 48 | ta: 'タ', ti: 'チ', tu: 'ツ', te: 'テ', to: 'ト', 49 | da: 'ダ', di: 'ヂ', du: 'ヅ', de: 'デ', do: 'ド', 50 | na: 'ナ', ni: 'ニ', nu: 'ヌ', ne: 'ネ', no: 'ノ', 51 | pa: 'ハ', pi: 'ヒ', pu: 'フ', pe: 'ヘ', po: 'ホ', 52 | ba: 'バ', bi: 'ビ', bu: 'ブ', be: 'ベ', bo: 'ボ', 53 | ma: 'マ', mi: 'ミ', mu: 'ム', me: 'メ', mo: 'モ', 54 | ya: 'ヤ', yu: 'ユ', yo: 'ヨ', 55 | ra: 'ラ', ri: 'リ', ru: 'ル', re: 'レ', ro: 'ロ', 56 | wa: 'ワ', wi: 'ヰ', we: 'ヱ', wo: 'ヲ', 57 | }; 58 | 59 | const 拗音表 = { 60 | wya: 'ヰャ', wyo: 'ヰョ', 61 | ya: 'ャ', yu: 'ュ', yo: 'ョ', 62 | wa: 'ヮ', wi: '𛅤', we: '𛅥', wo: '𛅦', 63 | }; 64 | 65 | const 韻尾表 = { 66 | '': '', 'i': 'イ', 'u': 'ウ', 67 | 'm': 'ム', 'n': 'ン', 'ng': 'ゥ', // ng: 'ィ', 68 | 'p': 'フ', 't': 'ツ', 'k': 'ク', // k: 'キ', 69 | }; 70 | 71 | function roma2kata(s) { 72 | const r = /^([kgsztdnpbmyrw]?w??[yw]?)([aiueo])([ptkmngiu]*)$/g; // 將音節分為韻頭、主要元音及韻尾 73 | const match = r.exec(s); 74 | if (match === null) { 75 | throw new Error('無法轉換為假名:' + s); 76 | } 77 | const { 1: 韻頭, 2: 主要元音, 3: 韻尾 } = match; 78 | let 假名韻尾 = 韻尾表[韻尾]; 79 | if (主要元音 === 'e') { 80 | if (韻尾 === 'k') 假名韻尾 = 'キ'; 81 | if (韻尾 === 'ng') 假名韻尾 = 'ィ'; 82 | } 83 | if (韻頭.length <= 1) { 84 | return 假名表[韻頭 + 主要元音] + 假名韻尾; 85 | } 86 | const 填充元音 = 韻頭[1] === 'w' ? 'u' : 'i'; // 韻頭[1] 只能為 w 或 y 87 | return 假名表[韻頭[0] + 填充元音] + 拗音表[韻頭.slice(1) + 主要元音] + 假名韻尾; 88 | } 89 | 90 | function kata2hira(s) { 91 | const diff = 'ぁ'.charCodeAt(0) - 'ァ'.charCodeAt(0); 92 | return [...s].map(c => ({ 93 | '𛅤': '𛅐', 94 | '𛅥': '𛅑', 95 | '𛅦': '𛅒', 96 | }[c] ?? String.fromCharCode(c.charCodeAt(0) + diff))).join(''); 97 | } 98 | 99 | function small2large(s) { 100 | return [...s].map(c => ({ 101 | '𛅤': 'ヰ', 102 | '𛅥': 'ヱ', 103 | '𛅦': 'ヲ', 104 | }[c] ?? c)).join(''); 105 | } 106 | 107 | // 3. 推導規則 108 | 109 | function 聲母規則() { 110 | return when([ 111 | // 脣音 112 | ['幫滂並母', 'p'], 113 | ['明母', [ 114 | ['梗攝 非 (庚耕青韻 入聲)', 'm'], 115 | ['', 'b'], 116 | ]], 117 | 118 | // 舌音、半舌音 119 | ['端透定知徹澄母', 't'], 120 | ['泥孃母', [ 121 | ['梗攝', 'n'], 122 | ['', 'd'], 123 | ]], 124 | ['來母', 'r'], 125 | 126 | // 齒音、半齒音 127 | ['精莊章組', 's'], 128 | ['日母', 'z'], 129 | 130 | // 牙音、喉音 131 | ['見溪羣曉匣母', 'k'], 132 | ['疑母', 'g'], 133 | ['影云以母', ''] 134 | 135 | ], '無聲母規則'); 136 | } 137 | 138 | function 韻母規則() { 139 | return when([ 140 | ['通攝', [ 141 | ['一等', 'ong'], 142 | ['東韻', [ 143 | ['脣音', [['幫滂並母 入聲', 'uk'], ['', 'ong']]], 144 | ['精莊章組', 'yung'], 145 | ['', [ 146 | ['舒聲', 'yung'], 147 | ['入聲 影母', 'wik'], 148 | ['入聲', 'ik'] 149 | ]], 150 | ]], 151 | ['鍾韻', [ 152 | ['脣音', 'ong'], 153 | ['', 'yong'], 154 | ]], 155 | ]], 156 | 157 | ['止攝', [ 158 | ['合口', [ 159 | ['舌齒音', 'ui'], 160 | ['牙喉音', 'wi'], 161 | ]], 162 | ['', 'i'], 163 | ]], 164 | 165 | ['遇攝', [ 166 | ['魚韻', [ 167 | ['莊組', 'o'], 168 | ['', 'yo'], 169 | ]], 170 | ['虞韻', [ 171 | ['鈍音 或 莊組 或 來母', 'u'], 172 | ['知組', 'yuu'], 173 | ['', 'yu'], 174 | ]], 175 | ['一等', [ 176 | ['影母', 'wo'], 177 | ['', 'o'], 178 | ]], 179 | ]], 180 | 181 | ['蟹攝', [ 182 | ['三四等', [ 183 | ['廢韻 平上聲 章組', 'ai'], 184 | ['廢韻 合口', 'wai'], 185 | ['合口', 'wei'], 186 | ['', 'ei'], 187 | ]], 188 | ['一二等', [ 189 | ['合口', 'wai'], 190 | ['', 'ai'], 191 | ]], 192 | ]], 193 | 194 | ['臻攝', [ 195 | ['三四等', [ 196 | ['文韻', 'un'], 197 | ['合口', [ 198 | ['莊組', [['舒聲', 'on'], ['', 'it']]], 199 | ['舌齒音 非 來母', [['舒聲', 'yun'], ['', 'ot']]], 200 | ['云母', 'win'], 201 | ['', 'in'], 202 | ]], 203 | ['', 'in'], 204 | ]], 205 | ['一等', 'on'], 206 | ]], 207 | 208 | ['山攝', [ 209 | ['一二等 或 脣音 C類', [ 210 | ['合口', 'wan'], 211 | ['', 'an'], 212 | ]], 213 | ['三四等', [ 214 | ['合口', 'wen'], 215 | ['', 'en'], 216 | ]], 217 | ]], 218 | 219 | ['效攝', [ 220 | ['三四等', 'eu'], 221 | ['二等', 'au'], 222 | ['一等', [ 223 | ['脣音', 'ou'], 224 | ['', 'au'], 225 | ]], 226 | ]], 227 | 228 | ['果假攝', [ 229 | ['一二等 或 脣音 C類', [ 230 | ['合口', 'wa'], 231 | ['', 'a'], 232 | ]], 233 | ['三四等', [ 234 | ['合口', 'wa'], 235 | ['', 'ya'], 236 | ]], 237 | ]], 238 | 239 | ['宕江攝', [ 240 | ['三四等', [ 241 | ['合口', [ 242 | ['舌齒音', 'ang'], 243 | ['影云母', 'wang'], 244 | ['牙喉音', 'wyang'], 245 | ]], 246 | ['幫莊組', 'ang'], 247 | ['', 'yang'], 248 | ]], 249 | ['一二等', [ 250 | ['合口', 'wang'], 251 | ['', 'ang'], 252 | ]], 253 | ]], 254 | 255 | ['梗攝', [ 256 | ['三四等', [ 257 | ['合口', 'weng'], 258 | ['', 'eng'], 259 | ]], 260 | ['二等', [ 261 | ['合口', 'wang'], 262 | ['', 'ang'], 263 | ]], 264 | ]], 265 | 266 | ['曾攝', [ 267 | ['三四等', [ 268 | ['合口', [ 269 | ['牙音 或 曉匣母', 'wyong'], 270 | ['', 'yong'], 271 | ]], 272 | ['莊組', 'ong'], 273 | ['', 'yong'], 274 | ]], 275 | ['一等', 'ong'], 276 | ]], 277 | 278 | ['流攝', [ 279 | ['三四等', [ 280 | ['脣音 AB類', 'iu'], 281 | ['明母', 'ou'], 282 | ['幫莊組', 'uu'], 283 | ['', 'iu'], 284 | ]], 285 | ['一等', 'ou'], 286 | ]], 287 | 288 | ['深攝', 'im'], 289 | 290 | ['咸攝', [ 291 | ['一二等 或 脣音 C類', 'am'], 292 | ['三四等', 'em'], 293 | ]], 294 | ], '無韻母規則'); 295 | } 296 | 297 | let 聲母 = 聲母規則(); 298 | let 韻母 = 韻母規則(); 299 | 300 | if (is`入聲`) { 301 | if (韻母.endsWith('m')) 韻母 = 韻母.slice(0, -1) + 'p'; 302 | else if (韻母.endsWith('n')) 韻母 = 韻母.slice(0, -1) + 't'; 303 | else if (韻母.endsWith('ng')) 韻母 = 韻母.slice(0, -2) + 'k'; 304 | } 305 | 306 | function 聲調規則() { 307 | if (['四聲', '四聲(數字)', '四聲(調值)'].includes(選項.聲調)) { 308 | return { 309 | '平': ['꜀', '1', '11'], 310 | '上': ['꜂', '2', '55'], 311 | '去': ['꜄', '3', '15'], 312 | '入': ['꜆', '4', '1'], 313 | }[音韻地位.聲][['四聲', '四聲(數字)', '四聲(調值)'].indexOf(選項.聲調)]; 314 | } 315 | 316 | if (選項.聲調 === '六聲(調值)') { 317 | return when([ 318 | ['平聲 全濁', '11'], 319 | ['平聲', '51'], 320 | ['上去聲 全濁', '15'], 321 | ['上去聲', '55'], 322 | ['入聲 全濁', '1'], 323 | ['入聲', '5'], 324 | ], '無聲調規則'); 325 | } 326 | 327 | if (選項.聲調 === '六聲(符號)') { 328 | if (is`入聲 全濁`) { 329 | if (韻母.endsWith('p')) 韻母 = 韻母.slice(0, -1) + 'b'; 330 | else if (韻母.endsWith('t')) 韻母 = 韻母.slice(0, -1) + 'd'; 331 | else if (韻母.endsWith('k')) 韻母 = 韻母.slice(0, -1) + 'g'; 332 | return ''; 333 | } 334 | return when([ 335 | ['平聲 全濁', 'z'], 336 | ['平聲', ''], 337 | ['上去聲 全濁', 'h'], 338 | ['上去聲', 'x'], 339 | ['入聲', ''], 340 | ], '無聲調規則'); 341 | } 342 | 343 | if (['八聲', '八聲(數字)', '八聲(調值)'].includes(選項.聲調)) { 344 | return [ 345 | { // 陰 346 | '平': ['꜀', '1', '51'], 347 | '上': ['꜂', '2', '55'], 348 | '去': ['꜄', '3', '535'], 349 | '入': ['꜆', '4', '5'], 350 | }, 351 | { // 陽 352 | '平': ['꜁', '5', '11'], 353 | '上': ['꜃', '6', '15'], 354 | '去': ['꜅', '7', '315'], 355 | '入': ['꜇', '8', '1'], 356 | } 357 | ][+is`全濁`][音韻地位.聲][['八聲', '八聲(數字)', '八聲(調值)'].indexOf(選項.聲調)]; 358 | } 359 | 360 | return ''; 361 | } 362 | 363 | let 聲調 = 聲調規則(); 364 | 365 | if (韻母.startsWith('w') && is`非 牙喉音 或 A類 或 以母`) 韻母 = 韻母.slice(1); 366 | 367 | // 4. 音變規則 368 | 369 | if (選項.音變 === '現代日語') { 370 | if (韻母.startsWith('w')) 韻母 = 韻母.slice(1); // 園 wen -> en 371 | 372 | if (韻母.endsWith('p')) 韻母 = 韻母.slice(0, -1) + 'u'; // 鄴 gep -> geu 373 | else if (韻母.endsWith('m')) 韻母 = 韻母.slice(0, -1) + 'n'; // 南 dam -> dan 374 | else if (韻母.endsWith('eng')) 韻母 = 韻母.slice(0, -2) + 'i'; // 生 seng -> sei 375 | else if (韻母.endsWith('ng')) 韻母 = 韻母.slice(0, -2) + 'u'; // 相 syang -> syau 376 | 377 | if (韻母.endsWith('au')) 韻母 = 韻母.slice(0, -2) + 'ou'; // 高 kau -> kou 378 | else if (韻母.endsWith('iu')) 韻母 = 韻母.slice(0, -2) + 'yuu'; // 宙 tiu -> tyuu 379 | else if (韻母.endsWith('eu')) 韻母 = 韻母.slice(0, -2) + 'you'; // 遙 eu -> you 380 | 381 | if (聲母 === 'd' && /^[iy]/.test(韻母)) 聲母 = 'z'; // 膩 di -> zi, 紐 dyuu -> zyuu 382 | } 383 | 384 | let 聲韻; 385 | 386 | if (['平假名', '片假名'].includes(選項.書寫系統)) { 387 | 聲韻 = roma2kata(聲母 + 韻母); 388 | if (!選項.ヰヱヲ小假名) 聲韻 = small2large(聲韻); 389 | if (選項.書寫系統 === '平假名') 聲韻 = kata2hira(聲韻); 390 | } else { 391 | if (選項.音變 === '現代日語') { 392 | if (聲母 === 'p') 聲母 = 'h'; // 甫 pu -> hu 393 | 394 | if (韻母.endsWith('t')) 韻母 += 'u'; // 遏 at -> atu 395 | else if (韻母.endsWith('ek')) 韻母 += 'i'; // 席 sek -> seki 396 | else if (韻母.endsWith('k')) 韻母 += 'u'; // 澤 tak -> taku 397 | } 398 | 399 | if (選項.書寫系統 === '平文式羅馬字') { 400 | if (選項.音變 === '現代日語') { 401 | if (聲母 === 's' && 韻母.startsWith('i')) 聲母 = 'sh'; // 四 si -> shi 402 | else if (聲母 === 'z' && 韻母.startsWith('i')) 聲母 = 'j'; // 人 zin -> jin 403 | else if (聲母 === 't' && 韻母.startsWith('i')) 聲母 = 'ch'; // 地 ti -> chi 404 | else if (聲母 === 't' && 韻母.startsWith('u')) 聲母 = 'ts'; // 追 tui -> tsui 405 | else if (聲母 === 'h' && 韻母.startsWith('u')) 聲母 = 'f'; // 甫 hu -> fu 406 | else if (聲母 === 's' && 韻母.startsWith('y')) { 聲母 = 'sh'; 韻母 = 韻母.slice(1); } // 小 syou -> shou 407 | else if (聲母 === 'z' && 韻母.startsWith('y')) { 聲母 = 'j'; 韻母 = 韻母.slice(1); } // 繞 zyou -> jou 408 | else if (聲母 === 't' && 韻母.startsWith('y')) { 聲母 = 'ch'; 韻母 = 韻母.slice(1); } // 兆 tyou -> chou 409 | 410 | if (韻母.endsWith('tu')) 韻母 = 韻母.slice(0, -1) + 'su'; // 遏 atu -> atsu 411 | } 412 | } 413 | 414 | 聲韻 = 聲母 + 韻母; 415 | } 416 | 417 | return [...'꜀꜁꜂꜃'].includes(聲調) ? 聲調 + 聲韻 : 聲韻 + 聲調; 418 | -------------------------------------------------------------------------------- /zhongyuan.js: -------------------------------------------------------------------------------- 1 | /* 推導《中原音韻》 2 | * 3 | * 可選四家擬音方案: 4 | * 5 | * - 楊耐思. 中原音韻音系. 北京: 中國社會科學出版社, 1981. 6 | * - 寧繼福. 中原音韻表稿. 長春: 吉林文史出版社, 1985. 7 | * - 薛鳳生. 中原音韻音位系統. 魯國堯, 侍建國, 譯. 北京: 北京語言學院出版社, 1990. 8 | * - unt. 《中原音韻》音系簡述, 2021. https://zhuanlan.zhihu.com/p/353713058 9 | * 10 | * @author unt 11 | */ 12 | 13 | /** @type { 音韻地位['屬於'] } */ 14 | const is = (...x) => 音韻地位.屬於(...x); 15 | /** @type { 音韻地位['判斷'] } */ 16 | const when = (...x) => 音韻地位.判斷(...x); 17 | 18 | if (!音韻地位) return [ 19 | ['顯示', [5, 20 | '音位(薛鳳生, 1990)', 21 | '音位(unt, 2021)', 22 | '音值(楊耐思, 1981)', 23 | '音值(寧繼福, 1985)', 24 | '音值(unt, 2021)', 25 | ]], 26 | ['標記古入聲字|\n入聲在四聲標記(陰 ¹、陽 ²、上 ³、去 ⁴)後加 ʼ', true], 27 | ['包含部分例外音變', true], 28 | ['異讀分隔符|異讀分隔符(留空則爲換行)', ''], 29 | ].concat(選項.顯示?.includes('unt') === false ? [] : [ 30 | ['高元音開口呼|\n如選 ə 則支思韻寫 ɹ̩、ɻ̍', [1, 'ɨ', 'ə']], 31 | ['皆來韻古入聲字韻基', [1, 'ɛj', 'aj']], 32 | ['皆來韻梗二開見入|其中的見二開韻母\n韻基擬 aj 的情況下,是否有 j 介音無法確定', [2, 'aj', '(j)aj', 'jaj'], { hidden: 選項.皆來韻古入聲字韻基 !== 'aj' }], 33 | ]); 34 | 35 | const 例外 = 選項.包含部分例外音變; 36 | let 層次 = 0; // 本方案只涉及 0 和 1 兩個層次。對入聲來說,0 代表白讀,1 代表文讀 37 | 38 | function 調整音韻地位() { 39 | function 調整(表達式, 調整屬性, 邊緣地位種類 = []) { if (is(表達式)) 音韻地位 = 音韻地位.調整(調整屬性, 邊緣地位種類); } 40 | // 輕唇化例外 41 | 調整('明母 尤韻', { 等: '一', 類: null, 韻: '侯' }); 42 | 調整('明母 東韻', { 等: '一', 類: null }); 43 | 44 | 調整('云母 通攝 平聲', { 母: '匣' }, ['匣母三等']); // 熊 45 | if (!例外) return; 46 | 47 | // 流攝脣音入遇攝字 48 | 調整('明母 侯韻 上聲', { 韻: '模' }); // 母某牡畝(謀戊在《廣韻》中涉及的字太多,不加入) 49 | if (層次 === 0) 調整('尤韻 並母 平上聲', { 韻: '虞' }); // 浮、婦阜負(《中原音韻》僅魚模,元曲押魚模、尤侯) 50 | 調整('尤韻 幫滂母 去聲', { 韻: '虞' }); // 富、副 51 | 52 | // 蟹攝二等入假攝字 53 | if (when([ 54 | ['佳韻', [ 55 | ['並母 上聲', true], // 罷 56 | ['見母 開口 平聲', true], // 佳 57 | ['溪母 合口 平聲', true], // 咼 58 | ['見匣母 合口 去聲', true], // 卦掛、畫 59 | [層次 === 1 && '疑母 開口 平聲', true], // 涯\崖 60 | [層次 === 1 && '生母 開口 去聲', true], // 洒\曬 61 | ]], 62 | ['夬韻 匣母 合口 去聲', true], // 話 63 | ])) 調整('蟹攝', { 韻: '麻' }); 64 | 65 | 調整('端母 蕭韻 上聲', { 母: '泥' }); // 鳥 66 | 調整('生母 山韻 上聲', { 母: '初' }); // 産 67 | 調整('書母 通攝 舒聲', { 母: '昌' }); // 舂 68 | 調整('書母 鍾韻 入聲', { 母: '昌' }); // 束 69 | if (層次 === 0) 調整('書母 支韻 開口 去聲', { 母: '昌' }); // 翅\施 70 | 調整('見母 蕭韻 平聲', { 母: '曉' }); // 梟鴞驍 71 | 72 | if (層次 === 0) { 73 | 調整('果攝 開口 (定泥母 去聲 或 透母 平聲)', { 韻: '麻', 等: '二' }, ['端組類隔']); // 大那+他 74 | 調整('端母 庚韻 上聲', { 韻: '麻' }, ['端組類隔']); // 打 75 | } 76 | } 77 | 78 | function get聲母() { 79 | return when([ 80 | [例外, [ 81 | ['崇母 止攝 仄聲', 'ʂ'], // 士 82 | ['常母 平聲 (支韻 合口 或 魚尤宵韻)', 'tʂʰ'], // 垂蜍讎韶 83 | ['常母 深攝 平聲', 'ʂ'], // 忱煁 84 | ['船母 平聲 合口', 'tʂʰ'], // 船唇 85 | [層次 === 1 && '船母 曾攝 平聲', 'tʂʰ'], // 乗\繩 86 | 87 | [層次 === 1 && '匣母 寒韻 合口 平聲', ''], // 丸\桓 88 | ['以母 蟹攝 合口', 'ɻ'], // 鋭 89 | [層次 === 0 && '脂韻 以母 合口 平聲', 'ʋ'], // 惟\遺 90 | ['疑母', [ 91 | ['宕攝 三等 開口', 'ŋ'], // 仰、虐瘧 92 | ['山攝 三四等 開口 入聲', 'n'], // 囓臬糵 93 | ['咸攝 三四等 入聲', 'ŋ'], // 業鄴 94 | [層次 === 1 && '梗攝 二等 入聲', 'ŋ'], // 額 95 | ['效攝 一等 仄聲', 'ŋ'], // 傲奡鏊 96 | ]], // 俺《廣韻》未收,不考慮 97 | // x 爻。《中州樂府音韻類編》與哮小韻陰陽配對,《中州音韻》與遙小韻合併 98 | ]], 99 | 100 | ['幫組 C類', [ 101 | ['明母', 'ʋ'], ['', 'f'], 102 | ]], 103 | ['幫母 或 並母 仄聲', 'p'], ['滂並母', 'pʰ'], ['明母', 'm'], 104 | ['端母 或 定母 仄聲', 't'], ['透定母', 'tʰ'], ['泥孃母', 'n'], ['來母', 'l'], 105 | ['見母 或 羣母 仄聲', 'k'], ['溪羣母', 'kʰ'], ['影疑云以母', ''], ['曉匣母', 'x'], 106 | ['精母 或 從母 仄聲', 'ts'], ['清從母', 'tsʰ'], ['心邪俟母', 's'], 107 | 108 | ['常母 平聲 陽聲韻', 'tʂʰ'], 109 | ['知莊章母 或 澄崇母 仄聲', 'tʂ'], ['徹澄初崇昌母', 'tʂʰ'], ['生俟常書船母', 'ʂ'], ['日母', 'ɻ'], 110 | ], '無聲母規則', true); 111 | } 112 | 113 | function get韻母() { 114 | let 洪細 = when([ 115 | [例外, [ 116 | ['見影組 二等 開口 梗攝 平聲', [ 117 | ['曉母', ''], // 亨 118 | ['匣母 耕韻', ''], // 莖 119 | [層次 === 1 && '影母', ''], // 甖 120 | ]], 121 | ['通攝 三等', [ 122 | ['日母 入聲', 'j'], // 辱褥 123 | ['溪羣母 平聲', 'j'], // 穹芎、窮藭蛩卭笻 124 | [層次 === 1 && '影母 平聲', ''], // 癰廱壅\邕嗈雍 125 | ]] 126 | ]], 127 | 128 | ['幫組 C類 止蟹攝', 'j'], 129 | ['幫組 C類', ''], 130 | ['莊組', ''], 131 | 132 | ['(精章組 或 日母) 止攝 開口', ''], 133 | ['宕攝 合口 三等', [ 134 | [層次 === 1 && '入聲', 'j'], 135 | ['', ''], 136 | ]], 137 | ['通攝 三等', [ 138 | ['知章組 非 孃母 或 日母', [ 139 | ['舒聲', ''], 140 | [層次 === 1 && '入聲', ''], 141 | ]], 142 | ['見溪羣母 舒聲', ''], // 弓拱恐共 143 | ]], 144 | 145 | ['三四等 或 見影組 二等 非 合口', 'j'], 146 | ['', ''], 147 | ], '無洪細規則', true); 148 | 149 | let 開合 = when([ 150 | [例外, [ 151 | ['止蟹攝 四等 合口 匣母 平聲', ''], // 畦携 152 | ['止蟹攝 A類 合口 見母 去聲', ''], // 季 153 | ['脂韻 以母 合口 平聲', ''], // 遺 154 | [層次 === 1 && '以母 合口 山攝', ''], // 緣沿掾\捐鉛鳶 155 | [層次 === 1 && '曉匣母 先韻 合口', ''], // 懸縣血 156 | ['梗攝 三四等 合口 非 B類', [ 157 | ['入聲', ''], 158 | [層次 === 1 && '舒聲', ''], 159 | ]], 160 | 161 | ['見組 祭韻 合口', ''], // 鱖 162 | 163 | [層次 === 0 && '疑母 歌韻 開口 上聲', 'w'], // 我 164 | [層次 === 1 && '定母 宕江攝 入聲', ''], // 鐸 165 | 166 | ['止蟹攝 B類', [ 167 | ['幫母 去聲', ''], // 秘祕賁\詖 168 | ['滂母 上聲', ''], // 嚭 169 | ]], 170 | ['止蟹攝 A類', [ 171 | ['幫母 平聲', 'w'], // 卑(避諱) 172 | ['幫母 支韻 去聲', 'w'], // 臂 173 | ['並母 仄聲', 'w'], // 婢避幣\斃 174 | ['明母 去聲', 'w'], // 袂寐 175 | ]], 176 | ]], 177 | 178 | ['幫組', [ 179 | ['一等 非 通宕曾流效攝', 'w'], 180 | [層次 === 1 && '宕攝 入聲', 'w'], 181 | ['曾攝 一等 入聲', 'w'], 182 | ['文歌韻', 'w'], 183 | ['(止蟹攝 或 臻攝 入聲) B類', 'w'], // 蟹攝幫三實際上無 B 類 184 | ]], 185 | 186 | ['果江攝 銳音 或 宕攝 莊組', 'w'], 187 | [層次 === 1 && '宕攝 入聲 銳音', 'w'], 188 | ['合口', 'w'], 189 | ['', ''], 190 | ], '無開合規則', true); 191 | 192 | let 韻基 = when([ 193 | [例外, [ 194 | [層次 === 1 && '心母 止攝 開口 上聲 非 脂韻', 'jɨj'], // 璽枲徙\死(避諱。元曲“徙”押支思) 195 | [層次 === 1 && '昌母 止攝 開口 非 (之韻 上聲 或 支韻 去聲)', 'jɨj'], // 蚩媸鴟幟熾\齒(元曲押支思)\\翅施 196 | ['知母 開口 (脂韻 平聲 或 之韻 上聲)', 'ɨ'], // 胝(元曲無)、徵(元曲押支思) 197 | 198 | [層次 === 0 && '幫滂並母 尤韻 仄聲', 'ʌw'], // 缶覆 199 | [層次 === 0 && '滂母 侯韻 上聲', 'ʌw'], // 剖 200 | [層次 === 0 && '明母 侯韻 去聲', 'aw'], // 茂 201 | ['效攝 一等 明母', 'aw'], 202 | ['泰韻 疑母 合口', 'aj'], // 外 203 | ]], 204 | 205 | ['遇攝', 'u'], // 魚模韻 206 | ['止攝 開口 (精莊章組 或 日母)', 'ɨ'], // 支思韻 207 | ['果攝 (一等 或 幫組 三等)', 'ʌ'], // 歌戈韻 208 | ['假攝 (二等 或 莊組)', 'a'], // 家麻韻 209 | ['果假攝 三四等', 'ɛ'], // 車遮韻 210 | 211 | ['蟹攝 (一等 開口 或 二等 或 莊組) 或 止攝 莊組', 'aj'], // 皆來韻 212 | ['止蟹攝', 'ɨj'], // 齊微韻 213 | 214 | ['流攝', 'ɨw'], // 尤侯韻 215 | ['效攝 一等', 'ʌw'], // 蕭豪韻·一等。作爲共通音位,先將 ʌw 獨立,以便推導各家擬音 216 | ['效攝 (二等 或 莊組)', 'aw'], // 蕭豪韻·二等 217 | ['效攝 三四等', 'ɛw'], // 蕭豪韻·三四等 218 | 219 | ['舒聲', [ 220 | [例外, [ 221 | [層次 === 1 && '曾梗攝 一二等 非 開口 或 庚韻 三等 合口', 'uŋ'], 222 | ['溪母 登韻 上聲 或 以母 蒸韻 去聲', 'ɨn'], // 肯孕 223 | [層次 === 1 && '知母 清韻 或 昌母 蒸韻 去聲', 'ɨn'], // +貞稱(元曲押真文、庚青) 224 | ]], 225 | ['通攝', 'uŋ'], // 東鍾韻 226 | ['宕江攝', 'aŋ'], // 江陽韻 227 | ['曾梗攝', 'ɨŋ'], // 庚青韻 228 | 229 | ['臻攝', 'ɨn'], // 真文韻 230 | ['山攝 一等 非 開口', 'ʌn'], // 桓歡韻 231 | ['山攝 (一二等 或 莊組 或 幫組 C類)', 'an'], // 寒山韻 232 | ['山攝 三四等', 'ɛn'], // 先天韻 233 | 234 | ['深攝', is`幫組` ? 'ɨn' : 'ɨm'], // 侵尋韻 235 | ['咸攝 (一二等 或 莊組 或 幫組 C類)', is`幫組` ? 'an' : 'am'], // 監咸韻 236 | ['咸攝 三四等', is`幫組` ? 'ɛn' : 'ɛm'], // 廉纖韻 237 | ]], 238 | ['入聲', [ 239 | [例外, [ 240 | [層次 === 0 && '宕江攝 一等 明母', 'aw'], 241 | [層次 === 1 && '登韻 心母', 'ɨ'], // 塞(元曲押齊微) 242 | [層次 === 1 && '登韻 精母', 'aj'], // 則(元曲押齊微) 243 | [層次 === 1 && '曾梗攝 一等 溪母 開口', 'jaj'], // 刻(元曲押齊微、皆來) 244 | [層次 === 1 && '曾梗攝 二等 溪疑母 開口', 'ɛ'], // 客(元曲押皆來、車遮)、額(元曲只押皆來) 245 | [層次 === 0 && '文韻 並母', 'ʌ'], // 佛(元曲押魚模、歌戈) 246 | ['臻攝 一等 (開口 或 幫組 非 明母)', 'ʌ'], // 勃+麧(据今音) 247 | [層次 === 0 && '日母 深攝', 'u'], // 入 248 | ]], 249 | 250 | ['通攝', [ 251 | [層次 === 0 && '(精知章莊組 或 來日母) 東韻 三等', 'ɨw'], 252 | [層次 === 0 && '(知章莊組 或 日母) 鍾韻', 'ɨw'], // 燭褥+贖屬(元曲押魚模、尤侯)\辱(元曲只押魚模) 253 | ['', 'u'], 254 | ]], 255 | ['宕江攝', [ 256 | [層次 === 1, 'ʌ'], 257 | ['一等 或 鈍音 三等 非 開口', 'ʌw'], 258 | ['', 'aw'], 259 | ]], 260 | 261 | ['曾梗攝 (二等 或 莊組)', 'aj'], 262 | ['曾梗攝 B類 合口', 'u'], 263 | ['臻攝 莊組 合口', 'aj'], 264 | ['臻攝 (一等 或 幫組 C類 或 合口)', 'u'], 265 | ['臻深攝 莊組', 'ɨ'], 266 | ['曾梗臻深攝', 'ɨj'], 267 | ['山咸攝 一等 非 (銳音 開口)', 'ʌ'], 268 | ['山咸攝 (一二等 或 莊組 或 幫組 C類)', 'a'], 269 | ['山咸攝 三四等', 'ɛ'], 270 | ]], 271 | ], '無韻基規則', true); 272 | 273 | let 韻母 = 洪細 + 開合 + 韻基; 274 | 韻母 = 韻母.replace('wu', 'u'); 275 | 韻母 = 韻母.replace('jwɨj', 'wɨj'); 276 | 韻母 = 韻母.replace('jʌ', 'jwʌ'); 277 | return 韻母; 278 | } 279 | 280 | function get聲調() { 281 | return when([ 282 | [例外, [ 283 | ['匣母 蟹攝 上聲 開口', '³'], // 駭蟹\解獬 284 | ['羣母 臻攝 上聲 合口', '³'], // 窘 285 | 286 | ['羣母 梗攝 三等 開口 入聲', '⁴ʼ'], // 劇 287 | ['生母 山攝 合口 入聲', '⁴ʼ'], // 刷 288 | ['影疑母 通臻攝 一等 入聲 非 開口', '³ʼ'], // 屋沃兀 289 | [層次 === 0 && '影母 臻攝 A類 開口 入聲', '³ʼ'], // 一 290 | ]], 291 | ['平聲 (全清 或 次清)', '¹'], 292 | ['平聲 (全濁 或 次濁)', '²'], 293 | ['上聲 非 全濁', '³'], 294 | ['上去聲', '⁴'], 295 | ['入聲', [ 296 | ['全濁', '²ʼ'], 297 | ['次濁 或 影母', '⁴ʼ'], // 影母入聲《中原音韻》按次濁歸派 298 | ['', '³ʼ'], 299 | ]], 300 | ], '無聲調規則', true); 301 | } 302 | 303 | function get音節() { 304 | let 聲母 = get聲母(); 305 | let 韻母 = get韻母(); 306 | let 聲調 = get聲調(); 307 | const is脣音 = 'pmfʋ'.includes(聲母[0]); 308 | const is銳音 = 'tnlsʂɻ'.includes(聲母[0]); 309 | if (is脣音 && !'jwu'.includes(韻母[0])) 韻母 = 'β' + 韻母; // 以便推導薛鳳生和寧繼福擬音 310 | 311 | const 轉換規則字典 = { 312 | // 規則: (from, to, [condition, [else to]]) 313 | '音位(unt, 2021)': [ 314 | ['β', ''], 315 | ['ʌw', 'waw', is脣音 && !聲調.includes('ʼ'), 'aw'], 316 | ['ɨ', 選項.高元音開口呼], 317 | ['aj', 選項.皆來韻古入聲字韻基, 聲調.includes('ʼ')], 318 | ['jaj', 選項.皆來韻梗二開見入, 聲調.includes('ʼ')], 319 | ], 320 | '音值(unt, 2021)': [ 321 | ['β', ''], 322 | ['jɨ', 'i'], ['ij', 'i'], 323 | ['jw', 'ɥ'], 324 | ['wɨ', 'u', !韻母.includes('ŋ')], 325 | ['ɥɨ', 'y', !韻母.includes('ŋ'), 'ɥi'], 326 | ['ʌw', 'waw', is脣音 && !聲調.includes('ʼ'), 'aw'], 327 | ['wʌ', 'ɔ', is脣音 || is銳音 && 韻母 === 'wʌ', 'wɔ'], 328 | ['ɥʌ', 'jɔ'], 329 | ['ɨ', 選項.高元音開口呼], ['ə', 'ɹ̩', 韻母 === 'ə'], ['ɹ̩', 'ɻ̍', !聲母.includes('s')], 330 | ['aj', 選項.皆來韻古入聲字韻基, 聲調.includes('ʼ')], 331 | ['jaj', 選項.皆來韻梗二開見入, 聲調.includes('ʼ')], 332 | ], 333 | '音位(薛鳳生, 1990)': [ 334 | ['ʰ', 'h'], ['ʋ', 'v'], ['ʂ', 'sr'], ['ɻ', 'r'], 335 | ['ts', 'c'], ['x', 'h'], 336 | ['waw', 'ow'], ['β', 'w'], 337 | ['j', 'y'], 338 | ['ɨ', 'e', 韻母.includes('ŋ')], ['ɛ', 'e'], 339 | ['u', 'wɨ', !韻母.includes('ŋ'), 'wo'], ['ʌ', 'o'], 340 | ], 341 | '音值(楊耐思, 1981)': [ 342 | ['ʰ', 'ʻ'], ['ʋ', 'v'], ['ʂ', 'ʃ'], ['ɻ', 'ʒ'], 343 | ['β', ''], 344 | ['ɨ', 'ï', 韻母 === 'ɨ', 'ə'], 345 | ['j', 'i'], ['w', 'u'], 346 | ['əi', 'ei'], ['iei', 'i'], 347 | ['uau', 'au'], ['ʌu', 'au'], ['iau', 'iɛu', is銳音], 348 | ['ʌ', 'o'], ['iuo', 'io'], ['uon', 'on'], 349 | ['ia', 'i̯a', !韻母.includes('ŋ')], 350 | ], 351 | '音值(寧繼福, 1985)': [ 352 | ['β', '', 'jw'.includes(韻母.slice(-1)), 'w'], 353 | ['ʰ', 'ʻ'], ['ɻ', 'ɽ'], 354 | ['ɨ', 'ï', 韻母 === 'ɨ', 'ə'], 355 | ['j', 'i'], ['w', 'u'], 356 | ['uəi', 'ui'], ['əi', 'ei'], ['iei', 'i'], 357 | ['uʌu', 'au'], ['ʌu', 'ɑu'], 358 | ['uau', 'au'], ['iau', 'au', !聲調.includes('ʼ')], 359 | ['iɛu', 'iau'], 360 | ['ʌ', 'ɔ'], ['iuɔ', 'iɔ'], 361 | ], 362 | }; 363 | let 音节 = 聲母 + 韻母 + 聲調; 364 | 轉換規則字典[選項.顯示]?.forEach(規則 => { 365 | if (規則.length === 2 || 規則[2]) 音节 = 音节.replace(new RegExp(規則[0], 'g'), 規則[1]); 366 | else if (規則.length === 4) 音节 = 音节.replace(new RegExp(規則[0], 'g'), 規則[3]); 367 | }); 368 | 369 | if (!選項.標記古入聲字) 音节 = 音节.replace('ʼ', ''); 370 | return 音节; 371 | } 372 | 373 | const 音韻地位備份 = 音韻地位; 374 | const 結果 = [0, 1].map(i => { 375 | 層次 = i; 376 | 音韻地位 = 音韻地位備份; 377 | 調整音韻地位(); 378 | return get音節(); 379 | }); 380 | return [...new Set(結果)].join(選項.異讀分隔符 || '\n'); 381 | -------------------------------------------------------------------------------- /zaonhe.js: -------------------------------------------------------------------------------- 1 | /* 推導上海話 2 | * 3 | * https://zhuanlan.zhihu.com/p/386456940 4 | * 5 | * ——適改「上海市区方言志」音系 6 | * 7 | * 在墶「上海市区方言志」個基礎上向增加着一些理論白讀層搭着理論文讀層(參考「上海土白集字」),但是確保至少一個層次是「上海市区方言志」音系。 8 | * 提供了一些選項,其中「上海市区方言志」弗分尖團、弗分衣煙、弗分來蘭、弗分襪麥、弗分打黨、弗分肉月。 9 | * 10 | * @author Nyoeghau 11 | */ 12 | 13 | /** @type { 音韻地位['屬於'] } */ 14 | const is = (...x) => 音韻地位.屬於(...x); 15 | /** @type { 音韻地位['判斷'] } */ 16 | const when = (...x) => 音韻地位.判斷(...x); 17 | 18 | if (!音韻地位) 19 | return [ 20 | ['文白讀', [4, '文上白下', '僅白讀', '僅文讀', '主流層']], 21 | ['標調方式', [3, '數字調值', '折線', '附標', '八調序號', '弗標']], 22 | ['分尖團', [1, '分尖團', '區分⟨情、琴⟩', '區分⟨徐、齊⟩']], 23 | ['分衣煙|區分⟨衣、煙⟩', true], 24 | ['分來蘭|區分⟨來、蘭⟩', true], 25 | ['分袜麦|區分⟨袜、麦⟩', true], 26 | ['分打黨|區分⟨打、黨⟩', true], 27 | ['分肉月|區分⟨肉、月⟩', true], 28 | ['分國骨|區分⟨國、骨⟩', true], 29 | ['分于園|區分⟨于、園⟩', true], 30 | ['分干官|區分⟨干、官⟩', true], 31 | ['分困孔|區分⟨困、孔⟩', true], 32 | ['分羣窮|區分⟨羣、窮⟩', true], 33 | ]; 34 | 35 | const 元音 = 'iyɨʉɯuɪʏʊeøɘɵɤoəɛœɜɞʌɔæɐaɶäɑɒ'; 36 | const 元音Re = new RegExp('[' + 元音 + ']'); 37 | const 閉前元音 = 'iyɪʏ'; 38 | const 元音附標 = '̃̈'; 39 | const 顎化分尖團 = { 40 | n: 'ɲ', 41 | k: 'tɕ', 42 | kʰ: 'tɕʰ', 43 | h: 'ɕ', 44 | ŋ: 'ɲ', 45 | g: 'dʑ', 46 | }; 47 | const 顎化分情琴 = { 48 | ts: 'tɕ', 49 | tsʰ: 'tɕʰ', 50 | s: 'ɕ', 51 | z: 'ʑ', 52 | }; 53 | const 顎化弗分尖團 = { 54 | ʑ: 'dʑ', 55 | }; 56 | const 數字標調 = { 57 | 陰平: '⁵³', 58 | 陰去: '³³⁴', 59 | 陽去: '²³', 60 | 陰入: '⁵⁵', 61 | 陽入: '¹²', 62 | }; 63 | const 折線標調 = { 64 | 陰平: '˥˧', 65 | 陰去: '˧˧˦', 66 | 陽去: '˨˧', 67 | 陰入: '˥', 68 | 陽入: '˩˨', 69 | }; 70 | const 附標標調 = { 71 | 陰平: '᷇', 72 | 陰去: '̄', 73 | 陽去: '̌', 74 | 陰入: '̋', 75 | 陽入: '᷅', 76 | }; 77 | const 序號標調 = { 78 | 陰平: '¹', 79 | 陰去: '⁵', 80 | 陽去: '⁶', 81 | 陰入: '⁷', 82 | 陽入: '⁸', 83 | }; 84 | 85 | function 聲母規則(文讀) { 86 | let 聲母 = when([ 87 | // 顎化規則由獨立個 function 來做 88 | // '▽' 標識弗是主流層 89 | // 表一(of「上海市区方言志」) 90 | ['幫滂母 C類', 'f'], // 理論白讀 /p/ 91 | ['並母 C類', 文讀 ? 'v' : '▽▽b'], // 「防」等 92 | ['明母 C類 非 通流攝', 文讀 ? 'v' : '▽▽m'], // 「蚊」等 93 | ['幫母', 'p'], ['滂母', 'pʰ'], ['並母', 'b'], ['明母', 'm'], 94 | // 表二 95 | ['端母', 't'], ['透母', 'tʰ'], ['定母', 'd'], ['泥孃母', 'n'], ['來母', 'l'], 96 | // 表四 97 | ['見母', 'k'], ['溪母', 'kʰ'], ['曉母', 'h'], ['羣母', 'g'], [!文讀 && '日母', 'n'], 98 | // 表五 99 | ['日母 止攝 開口', '▽▽ɦ'], 100 | ['疑母', [ 101 | ['遇攝 一等 上聲', 文讀 ? '▽▽ʔ' : 'ŋ'], // 「五」。表中無 102 | ['合口 麻泰歌韻 或 開合中立 非 侯韻', 文讀 ? '▽▽ɦ' : 'ŋ'], // 「外」「瓦」「臥」。「吾」等 103 | ['合口', 文讀 ? 'ɦ' : '▽▽ŋ'], // 「魏」等 104 | [文讀 && '二三四等', '▽▽ɦ'], // 「言」等 105 | ['', 'ŋ'], 106 | ]], 107 | ['以母 脂韻 合口', 'v'], // 表中無 108 | // ['以母 上聲 通攝', 'ʔ'], // 似無規律。弗收 109 | // ['以苡已勇蛹涌恿甬俑踊慂𧻹悀埇𧗴'.includes(字頭) && '以母 上聲', 'ʔ'], 110 | ['云母 上去聲 梗攝', 'ʔ'], // 表中無 111 | ['匣云以母', 'ɦ'], 112 | ['影母', 'ʔ'], 113 | // 表三 114 | ['精知莊章母', 'ts'], ['清徹初昌母', 'tsʰ'], ['心生書母', 's'], ['從邪澄崇俟常船日母', 'z'], 115 | ], '無聲母規則'); 116 | return 聲母; 117 | } 118 | 119 | function 韻母規則(文讀) { 120 | let 韻母 = when([ 121 | // 特殊韻母 122 | [!文讀 && '無嘸无呒'.includes(字頭), '̩'], 123 | [!文讀 && '畝𠭇畮畞亩'.includes(字頭), '̩'], 124 | [!文讀 && '魚鱼'.includes(字頭), '̩'], 125 | [!文讀 && '吳吴'.includes(字頭), '̩'], 126 | [!文讀 && '疑母 模韻 上聲', '̩'], // 「五」「午」 127 | 128 | ['通攝 舒聲', [ 129 | ['三等 (孃疑母 或 喉音)', 'ioŋ'], 130 | ['三等 牙音', 文讀 ? 'oŋ' : 'ioŋ▽'], // 「龔」等 131 | ['三等 日母', 文讀 ? 'oŋ▽' : 'ioŋ'], // 「茸」等 132 | ['', 'oŋ'], // 脣音白讀僅「夢」。待考 133 | ]], 134 | ['通攝 入聲', [ 135 | ['三等 (孃母 或 牙喉音)', 'ioʔ'], 136 | ['三等 日母', 文讀 ? 'oʔ▽' : 'ioʔ'], // 「肉」等 137 | ['', 'oʔ'], 138 | ]], 139 | ['江攝 舒聲', [ 140 | [文讀 && '牙音', 'iã▽'], // 「江」等 141 | ['', 'ɑ̃'], 142 | ]], 143 | ['江攝 入聲', [ 144 | // [文讀 && '牙音 非 疑母', 'ioʔ▽'], // 「確」等。箇個同「上海市区方言志」音系 145 | [文讀 && '牙喉音', 'iɑʔ▽'], // 「確」等。箇個同「上海土白集字」音系。「樂」等。疑母文讀同匣母。「學」等 146 | ['', 'oʔ'], 147 | ]], 148 | ['止攝', [ 149 | [文讀 && '開口 日母', 'əɭ'], 150 | ['開口 (端組 或 來孃日母)', 'i'], 151 | ['開口 舌齒音', 'z̩'], 152 | ['開口 或 脣音', 'i'], 153 | ['合口 以母 脂韻', 'i'], 154 | ['合口 來孃母', 'e'], 155 | ['合口 日母', 文讀 ? 'ø' : 'y▽'], // 「蕊」 156 | ['合口 舌齒音', 文讀 ? 'ø' : 'z̩▽'], // 「吹」等 157 | [!文讀 && '合口 牙音 非 疑母', 'y▽'], // 「跪」等 158 | ['合口', 'ue'], 159 | ]], 160 | ['遇攝', [ 161 | [!文讀 && '魚韻 (來孃日母 或 精組)', 'i▽'], // 「呂」等。「女」(老派)。日母白讀類推無實例。「絮」等 162 | [!文讀 && '魚韻 莊組', 'z̩▽'], // 「鋤」等 163 | [!文讀 && '三等 日母', 'y▽'], // 白讀類推無實例 164 | ['三等 莊組', 'u'], 165 | ['C類 脣音', 'u'], 166 | ['三等 舌齒音 非 精組 非 來孃母', 'z̩'], 167 | ['三等', 'y'], // 牙喉音白讀 /e/「許」「鋸」。白讀 /i/「去」「渠」。屬於特殊存古,且弗唯一,故弗用 168 | ['一等', 'u'], 169 | ]], 170 | ['蟹攝', [ 171 | [!文讀 && '三等 合口 日母', 'i▽'], // 白讀類推無實例 172 | ['三四等 合口 舌齒音', 'ø'], 173 | ['三四等 合口', 'ue'], 174 | ['莊組 開口', 'a'], 175 | ['三等 舌齒音 非 精組 非 來母', 'z̩'], 176 | ['三四等', 'i'], 177 | ['二等 開口 匣母', 文讀 ? 'ie' : 'a▽'], // 「械」等。理論上還有 /ia/ 個白讀 178 | ['二等 開口 牙喉音', 文讀 ? 'ia' : 'a▽'], // 「解」等 179 | ['二等 合口 疑母', 文讀 ? 'ue' : 'a▽'], // 字典弗曾收「聵」 180 | ['二等 合口 牙喉音', 文讀 ? 'ua' : 'o▽'], // 「卦」等 181 | ['二等 合口 舌齒音', 'ø'], 182 | ['二等', 'a'], 183 | ['泰韻 開口 舌齒音', 'a'], 184 | ['泰韻 合口 疑母', 文讀 ? 'ue▽' : 'a'], // 「外」 185 | ['一等 合口 精組', 'ø'], 186 | ['一等 合口 牙喉音', 'ue'], 187 | ['一等', 'e'], 188 | ]], 189 | ['臻攝 舒聲', [ 190 | [文讀 && '三等 日母', 'əŋ▽'], // 「人」「閏」等 191 | ['三等 開口 知章莊組', 'əŋ'], 192 | ['C類 脣音', 'əŋ'], 193 | ['三等 (開口 或 脣音)', 'ɪɲ'], 194 | ['三等 合口 精組 或 日母', 'ɪɲ'], 195 | ['三等 合口 舌齒音', 'əŋ'], 196 | ['三等 合口', 'yɪɲ'], 197 | [!文讀 && '一等 合口 疑母', 'əŋ▽'], // 「諢」。白讀類推無實例 198 | ['一等 合口 牙喉音', 'uəŋ'], 199 | ['一等', 'əŋ'], 200 | ]], 201 | ['臻攝 入聲', [ 202 | [文讀 && '三等 日母', 'əʔ▽'], // 「日」等 203 | ['三等 開口 知章莊組', 'əʔ'], 204 | ['C類 脣音', 'əʔ'], 205 | ['三等 (開口 或 脣音)', 'iɪʔ'], 206 | ['三等 合口 (精組 或 來母)', 'iɪʔ'], 207 | ['三等 合口 舌齒音', 'əʔ'], 208 | ['三等 合口', 'yɪʔ'], 209 | ['一等 合口 疑母', 文讀 ? 'uəʔ▽' : 'əʔ'], // 「兀」 210 | ['一等 合口 牙喉音', 'uəʔ'], 211 | ['一等', 'əʔ'], 212 | ]], 213 | ['山攝 舒聲', [ 214 | ['三等 開口 知章組', 'ø'], 215 | ['三等 開口 日母', 文讀 ? 'ø' : 'iɪ▽'], // 「燃」等 216 | ['C類 脣音', 'æ'], 217 | ['三四等 (開口 或 脣音)', 'iɪ'], 218 | ['三等 合口 日母', 文讀 ? 'ø▽' : 'yø'], // 「軟」等 219 | ['三四等 合口 (精組 或 來母)', 'iɪ'], // 白讀 /æ/ 或 /ø/ 只有「全」特殊存古。故弗列 220 | ['三四等 合口 舌齒音', 'ø'], 221 | ['三四等 合口', 'yø'], 222 | [文讀 && '二等 開口 牙喉音', 'iɪ▽'], // 「間」等 223 | ['二等 合口 牙喉音', 'uæ'], 224 | ['二等 合口 舌齒音', 'ø'], 225 | ['二等', 'æ'], 226 | ['一等 開口 舌齒音', 'æ'], 227 | ['一等 合口 牙喉音', 'uø'], 228 | ['一等', 'ø'], 229 | ]], 230 | ['山攝 入聲', [ 231 | ['三等 知莊章組', 'əʔ'], 232 | [文讀 && '三等 日母', 'əʔ▽'], // 「熱」「爇」等 233 | ['C類 脣音', 'ɐʔ'], 234 | ['三四等 (開口 或 脣音)', 'iɪʔ'], 235 | ['三四等 合口 舌齒音', 'iɪʔ'], 236 | ['三四等 合口', 'yɪʔ'], 237 | [文讀 && '二等 開口 疑匣母', 'iɐʔ▽'], // 「齾」。「轄」 238 | ['二等 開口 舌齒音 日母', 文讀 ? 'əʔ▽' : 'iɪʔ'], // 字典弗曾收「𩭿」 239 | ['二等 合口 牙喉音 疑母', 文讀 ? 'uɐʔ▽' : 'ɐʔ'], // 字典弗曾收「刖」 240 | ['二等 合口 牙喉音', 'uɐʔ'], 241 | ['二等 合口 舌齒音', 'əʔ'], 242 | ['二等', 'ɐʔ'], 243 | ['一等 開口 舌齒音', 'ɐʔ'], 244 | ['一等 合口 牙喉音 見曉影母', 'uɐʔ'], 245 | ['一等 合口 牙喉音 疑母', 文讀 ? 'uəʔ▽' : 'əʔ'], // 字典弗曾收「枂」 246 | ['一等 合口 牙喉音', 'uəʔ'], 247 | ['一等', 'əʔ'], 248 | ]], 249 | ['效攝', [ 250 | [文讀 && '二等 牙喉音', 'iɔ▽'], // 「交」等 251 | ['三四等 非 知章組', 'iɔ'], 252 | ['', 'ɔ'], 253 | ]], 254 | ['果攝', [ 255 | [!文讀 && '一等 開口 (端組 或 喉音)', 'a▽'], // 「大」「何」等 256 | ['一等 或 三等 脣音', 'u'], // 「縛」 257 | ['開口', 文讀 ? 'ia▽' : 'a'], // 「茄」等 258 | ['合口', 文讀 ? 'ia▽' : 'io'], // 「瘸」等 259 | ]], 260 | ['假攝', [ 261 | [文讀 && '二等 牙喉音 合口', 'ua▽'], // 「花」等。脆麻花~ 262 | [文讀 && '二等 牙喉音', 'ia▽'], // 「下」等 263 | [文讀 && '二等', 'a▽'], // 「馬」「差」等 264 | ['二等 開口 牙音', 'a'], // 「家」等 265 | ['二等', 'o'], 266 | [文讀 && '牙喉音', 'ie▽'], // 「也」等 267 | [文讀 && '脣音 或 端精組', 'i▽'], // 「乜」。「姐」等 268 | ['舌齒音 日母', 文讀 ? 'a' : 'ia▽'], // 「惹」等 269 | ['舌齒音 章組', 文讀 ? 'o' : 'a▽'], // 「射」等 270 | ['', 'ia'], 271 | ]], 272 | ['宕攝 舒聲', [ 273 | ['三等 日母', 文讀 ? 'ɑ̃▽' : 'iã'], // 「壤」等 274 | ['三等 (精組 或 來孃母)', 'iã'], 275 | ['三等 知組', 'ã'], 276 | ['三等 (舌齒音 或 C類 脣音)', 'ɑ̃'], 277 | ['三等 (開口 或 脣音)', 'iã'], 278 | [!文讀 && '三等 合口 云母', 'iã▽'], // 「旺」等 279 | ['三等 合口', 'uɑ̃'], 280 | ['一等 合口 牙喉音', 'uɑ̃'], 281 | ['一等', 'ɑ̃'], 282 | ]], 283 | ['宕攝 入聲', [ 284 | ['三等 日母', 文讀 ? 'ɑʔ' : 'iɑʔ▽'], // 「箬」等 285 | ['三等 (精組 或 來孃母)', 'iɑʔ'], 286 | ['三等 (莊組 或 C類 脣音)', 'oʔ'], 287 | ['三等 舌齒音', 'ɑʔ'], 288 | ['三等 (開口 或 脣音)', 'iɑʔ'], 289 | ['三等 合口 牙喉音', 'ioʔ'], 290 | ['一等', 'oʔ'], 291 | ]], 292 | ['梗攝 舒聲', [ 293 | ['三四等 知章組', 文讀 ? 'əŋ' : 'ã▽'], // 「聲」「省」等 294 | [!文讀 && '三四等 開口 牙喉音', 'iã▽'], // 「映」等 295 | ['三四等 合口 牙喉音', 'ioŋ'], // 有異讀 /ɪɲ/ 296 | ['三四等 非 莊組', 'ɪɲ'], 297 | ['耕韻 開口 牙喉音', 文讀 ? 'ɪɲ' : 'ã▽'], // 「櫻」等 298 | ['耕韻 合口 喉音', 'oŋ'], 299 | ['二等 合口 牙喉音', 'uɑ̃'], 300 | ['二等 或 莊組', 文讀 ? 'əŋ▽' : 'ã'], // 「萌」「猛」「爭」「澄」「更」等 301 | ]], 302 | ['梗攝 入聲', [ 303 | ['三四等 知章組', 文讀 ? 'əʔ▽' : 'ɑʔ'], // 「適」等 304 | ['三四等 合口 牙喉音', 'ioʔ'], 305 | ['三四等', 'iɪʔ'], 306 | ['二等 合口 牙喉音', 'oʔ'], 307 | ['二等', 文讀 ? 'əʔ▽' : 'ɑʔ'], // 「脈」「白」「革」「客」「責」「澤」等。「脈」「白」「客」收於「上海土白集字」 308 | ]], 309 | ['曾攝 舒聲', [ 310 | [文讀 && '三等 日母', 'əŋ▽'], // 「仍」等 311 | ['三等 舌齒音 非 來日母', 文讀 ? 'əŋ' : 'ã▽'], // 「剩」等 312 | ['三等', 'ɪɲ'], 313 | ['一等 明母', 文讀 ? 'oŋ' : 'ɑ̃▽'], // 「懵」 314 | ['一等 脣音', 文讀 ? 'əŋ▽' : 'ã'], // 「崩」等 315 | ['一等 開口', 'əŋ'], // 無白讀例 316 | ['一等 合口', 'oŋ'], 317 | ]], 318 | ['曾攝 入聲', [ 319 | [文讀 && '三等 日母', 'əʔ▽'], // 「仍」等 320 | ['三等 舌齒音 非 精組 非 來孃日母', 'əʔ'], 321 | ['三等 合口', 'ioʔ'], 322 | ['三等', 'iɪʔ'], 323 | ['一等 明母', 'əʔ'], 324 | ['一等 脣音', 'oʔ'], 325 | ['一等 開口', 'əʔ'], 326 | ['一等 合口', 'oʔ'], 327 | ]], 328 | ['流攝', [ 329 | ['幽韻 明母', 文讀 ? 'iɤ▽' : 'iɔ'], // 「繆」等。白讀地位實不對應 330 | ['幽韻 脣音', 'iɔ'], // 地位實不對應 331 | ['三等 日母', 文讀 ? 'ɤ' : 'iɤ▽'], // 「柔」等 332 | ['三等 舌齒音 非 精組 非 來孃母', 'ɤ'], 333 | ['三等 C類 脣音 去聲', 'u'], 334 | ['三等 C類 脣音', 'ɤ'], 335 | ['三等', 'iɤ'], 336 | ['一等', 'ɤ'], 337 | ]], 338 | ['深攝 舒聲', [ 339 | ['日母', 文讀 ? 'əŋ' : 'ɪɲ▽'], // 「任」等 340 | ['舌齒音 非 精組 非 來孃母', 'əŋ'], 341 | ['', 'ɪɲ'], 342 | ]], 343 | ['深攝 入聲', [ 344 | ['日母', 文讀 ? 'əʔ' : 'iɪʔ▽'], // 「入」等 345 | ['舌齒音 非 精組 非 來孃母', 'əʔ'], 346 | ['', 'iɪʔ'], 347 | ]], 348 | ['咸攝 舒聲', [ 349 | ['三等 日母', 文讀 ? 'ø' : 'iɪ▽'], // 「染」等 350 | ['三等 舌齒音 非 來孃母 非 精組', 'ø'], 351 | ['C類 脣音', 'æ'], 352 | ['三四等', 'iɪ'], 353 | ['二等 孃母', 'ø'], // 地位實不對應。「喃」 354 | ['二等 來母', 'iɪ'], // 地位實不對應。「臉」 355 | [文讀 && '二等 牙喉音', 'iɪ▽'], // 「減」等 356 | ['二等', 'æ'], 357 | ['覃韻 端定來溪母', 'æ'], // 也有讀 /ø/ 個口音,但是弗在「上海市區方言志」 358 | ['談韻 舌齒音', 'æ'], 359 | ['一等', 'ø'], 360 | ]], 361 | ['咸攝 入聲', [ 362 | ['三等 日母', 文讀 ? 'əʔ▽' : 'iɪʔ'], // 無文讀例 363 | ['三等 舌齒音 非 來孃母 非 精組', 'əʔ'], 364 | ['C類 脣音', 'ɐʔ'], 365 | ['三四等', 'iɪʔ'], 366 | [文讀 && '二等 牙喉音', 'iɐʔ▽'], // 「甲」等 367 | ['二等', 'ɐʔ'], 368 | ['覃韻 舌齒音', 文讀 ? 'əʔ▽' : 'ɐʔ'], // 「答」等 369 | ['談韻 舌齒音', 'ɐʔ'], 370 | ['一等', 'əʔ'], 371 | ]], 372 | ], '無韻母規則'); 373 | 374 | let is主流層 = !韻母.includes('▽'); 375 | 韻母 = 韻母.replace('▽', ''); 376 | 韻母 = { 377 | 'iɪ': !選項.分衣煙 && 'i', 378 | 'æ': !選項.分來蘭 && 'e', 379 | 'uæ': !選項.分來蘭 && 'ue', 380 | 'ɑʔ': !選項.分袜麦 && 'ɐʔ', 381 | 'iɑʔ': !選項.分袜麦 && 'iɐʔ', 382 | 'ã': !選項.分打黨 && 'ɑ̃', 383 | 'iã': !選項.分打黨 && 'iɑ̃', 384 | 'ioʔ': !選項.分肉月 && 'yɪʔ', 385 | 'uəʔ': !選項.分國骨 && 'oʔ', 386 | 'yø': !選項.分于園 && 'y', 387 | 'io': !選項.分于園 && 'y', 388 | 'uø': !選項.分干官 && 'ø', 389 | 'uəŋ': !選項.分困孔 && 'oŋ', 390 | 'yɪɲ': !選項.分羣窮 && 'ioŋ', 391 | }[韻母] || 韻母; 392 | if (!is主流層) 韻母 += '▽'; 393 | return 韻母; 394 | } 395 | 396 | function 顎化規則(音節) { 397 | const match = 元音Re.exec(音節); 398 | if (match !== null) { 399 | if (閉前元音.includes(match[0])) { 400 | for (let 聲母 in 顎化分尖團) 音節 = 音節.replace(聲母, 顎化分尖團[聲母]); 401 | if (選項.分尖團 !== '分尖團') { 402 | for (let 聲母 in 顎化分情琴) 403 | 音節 = 音節.replace(聲母, 顎化分情琴[聲母]); 404 | } 405 | if (選項.分尖團 === '區分⟨徐、齊⟩') { 406 | if (is`從崇常母`) { 407 | for (let 聲母 in 顎化弗分尖團) 408 | 音節 = 音節.replace(聲母, 顎化弗分尖團[聲母]); 409 | } 410 | } 411 | } 412 | } 413 | return 音節; 414 | } 415 | 416 | function 主流層選擇規則(音們) { 417 | let 音們_非主流度們 = []; 418 | 音們.forEach(音 => { 419 | 音們_非主流度們.push({ 音, 非主流度: (音.match(/▽/g) || []).length }); 420 | }); 421 | return 音們_非主流度們.reduce((prev, curr) => { 422 | return prev.非主流度 < curr.非主流度 ? prev : curr; 423 | }).音; 424 | } 425 | 426 | function 聲調規則(音節) { 427 | const 聲調 = when([ 428 | ['云母 上去聲 梗攝', '陰去'], // 由於聲母例外 429 | // ['以母 上聲 通攝', '陰去'], // 由於聲母例外 430 | ['疑母 上聲 模韻', '陰去'], 431 | ['全清 或 次清', [['平聲', '陰平'], ['上去聲', '陰去'], ['入聲', '陰入']]], 432 | ['全濁 或 次濁', [['舒聲', '陽去'], ['入聲', '陽入']]], 433 | ], '無聲調規則'); 434 | 435 | if (選項.標調方式 === '附標') { 436 | let 標調位置; 437 | const match = 元音Re.exec(音節); 438 | if (match !== null) { 439 | 標調位置 = match.index; 440 | if (元音.includes(音節[標調位置 + 1])) 標調位置 += 1; // 弗要標在介音上 441 | if (元音附標.includes(音節[標調位置 + 1])) 標調位置 += 1; // 弗要標在附標下頭 442 | } else { 443 | 標調位置 = 音節.indexOf('̩'); 444 | } 445 | 標調位置 += 1; 446 | return 音節.slice(0, 標調位置) + 附標標調[聲調] + 音節.slice(標調位置); 447 | } else if (選項.標調方式 === '數字調值') { 448 | return 音節 + 數字標調[聲調]; 449 | } else if (選項.標調方式 === '折線') { 450 | return 音節 + 折線標調[聲調]; 451 | } else if (選項.標調方式 === '八調序號') { 452 | return 音節 + 序號標調[聲調]; 453 | } else { 454 | return 音節; 455 | } 456 | } 457 | 458 | function finalise(音節) { 459 | 音節 = 音節.replace(/▽/g, ''); 460 | 音節 = 顎化規則(音節); 461 | 音節 = 聲調規則(音節); 462 | return 音節; 463 | } 464 | 465 | let 文讀聲母 = 聲母規則(true); 466 | let 白讀聲母 = 聲母規則(false); 467 | 468 | let 文讀韻母 = 韻母規則(true); 469 | let 白讀韻母 = 韻母規則(false); 470 | 471 | let 文讀音 = 文讀聲母 + 文讀韻母; 472 | let 白讀音 = 白讀聲母 + 白讀韻母; 473 | 474 | let 結果; 475 | if (選項.文白讀 === '主流層') { 476 | 結果 = 主流層選擇規則([文讀音, 白讀音]); 477 | 結果 = finalise(結果); 478 | } else { 479 | if (選項.文白讀 === '僅白讀') 結果 = finalise(白讀音); 480 | else if (選項.文白讀 === '僅文讀') 結果 = finalise(文讀音); 481 | else if (文讀音 === 白讀音) 結果 = finalise(文讀音); 482 | else 結果 = finalise(文讀音) + '\n' + finalise(白讀音); 483 | } 484 | 485 | return 結果; 486 | -------------------------------------------------------------------------------- /unt_legacy.js: -------------------------------------------------------------------------------- 1 | /* unt 過往切韻擬音 2 | * 3 | * 最新版本請見「unt 擬音」方案。本方案包含 6 個過往版本: 4 | * 5 | * - 2019:切韻朗讀音 6 | * https://zhuanlan.zhihu.com/p/58227457 7 | * 8 | * - 2020:切韻擬音 J(原版) 9 | * https://zhuanlan.zhihu.com/p/305516512 & https://zhuanlan.zhihu.com/p/313005024 10 | * 11 | * - 2020:切韻擬音 J(2022 新版) 12 | * - 2020:切韻擬音 J(2024 新版) 13 | * 都由切韻擬音 L 改寫,與 L 的對比見 https://zhuanlan.zhihu.com/p/545490174 文末 14 | * 15 | * - 2022:切韻擬音 L 16 | * https://zhuanlan.zhihu.com/p/545490174 17 | * 18 | * - 2023:切韻通俗擬音 19 | * https://zhuanlan.zhihu.com/p/545490174 20 | * 21 | * 前 3 個版本已過時,僅作爲歷史存檔,不建議使用 22 | * 23 | * @author unt 24 | */ 25 | 26 | /** @type { 音韻地位['屬於'] } */ 27 | const is = (...x) => 音韻地位.屬於(...x); 28 | /** @type { 音韻地位['判斷'] } */ 29 | const when = (...x) => 音韻地位.判斷(...x); 30 | 31 | const is專業模式 = 選項.專業模式 ?? false; 32 | const isL = 選項.版本?.includes('L') ?? true; 33 | const isJ原版 = Boolean(選項.版本?.includes('J') && 選項.版本?.includes('原版')); // 2020 34 | const isJ新版 = Boolean(選項.版本?.includes('J') && !選項.版本?.includes('原版')); // 2022 或 2024 35 | const isJ2024 = Boolean(選項.版本?.includes('J') && !選項.版本?.includes('版')); // 2024 36 | const is朗讀音 = Boolean(選項.版本?.includes('朗讀音')); 37 | const is通俗 = Boolean(選項.版本?.includes('通俗')); 38 | 39 | function get選項列表() { 40 | const 後低元音選單 = !is朗讀音 ? (!is通俗 ? [1, 'a', 'ɑ'] : [2, 'ɑ', '歌陽唐ɑ 其他a']) : [1, 'ɑ']; 41 | 42 | const { _last版本, _last後低元音選單 } = 選項; 43 | const 版本changed = _last版本 && _last版本 !== 選項.版本; 44 | const 後低元音選單changed = _last後低元音選單 !== 後低元音選單.join('\n'); 45 | 46 | return [ 47 | ['_last版本', [1, 選項.版本 ?? '2022:切韻擬音 L'], { hidden: true }], 48 | ['_last後低元音選單', [1, 後低元音選單.join('\n')], { hidden: true }], 49 | ['版本|\n切韻擬音 J 和 L 均爲 2024 新版。如需調取過時版本、查看高級選項,請勾選「專業模式」', [is專業模式 ? 5 : 2, 50 | '2019:切韻朗讀音', 51 | '2020:切韻擬音 J(原版)', 52 | '2020:切韻擬音 J(2022 版)', 53 | '2020:切韻擬音 J', 54 | '2022:切韻擬音 L', 55 | '2023:切韻通俗擬音', 56 | ].filter((_, i) => is專業模式 || ![1, 2, 3].includes(i))], 57 | ['專業模式', is專業模式], 58 | 59 | '基本選項', 60 | ['後低元音', 後低元音選單, { reset: 後低元音選單changed }], 61 | ['聲調記號', [1, 62 | { text: '上ˊ 去ˋ', value: '\u0301\u0300' }, 63 | { text: '上ʔ 去h', value: 'ʔh' }, 64 | { text: '上ˀ 去ʰ', value: 'ˀʰ' }, 65 | '五度符號', 66 | '五度符號(帶拖腔)', 67 | '無', 68 | ].filter((_, i) => (is專業模式 || [0, 1, 2, 4, 6].includes(i)) && !(is朗讀音 && i === 4)), 69 | { description: is專業模式 && !is朗讀音 && 'ʔ 對應的上標字母 Unicode 未收,以 ˀ 代替' }, 70 | ], 71 | ['鈍C介音|鈍 C 介音', 72 | isL && [1, '開∅ 合w'] || 73 | // ɣ 代表軟腭近音(即 ɣ̞) 74 | isJ2024 && [1, '開ɣ 唇ʋ 合ɣw'] || 75 | // β、ʋ 都代表雙唇近音(即 β̞ = ʋ̟) 76 | isJ新版 && [1, '開j̈ 唇ʋ 合w', '開j̈ 唇ʋ 合ɥ̈', '開j̈ 唇β 合ɥ̈'] || 77 | isJ原版 && [1, '開j̈ 唇β 合ɥ̈'] || 78 | is朗讀音 && [1, '開j̈ 唇ɥ̈ 合ẅ'] || 79 | is通俗 && [1, '開ɨ 唇低ɨ 唇非低ʉ 合ʉ'], 80 | { reset: 版本changed }, 81 | ], 82 | ['見組非三等簡寫作軟腭音', isJ2024 ? false : is通俗 ? true : null, { reset: 版本changed }], 83 | ].concat(is專業模式 && !is通俗 ? [ 84 | '高級選項', 85 | ['前部聲母非三等', isL ? [1, 'ᵱ ɫ', 'pˤ lˤ', 'p̙ l̙'] : null], 86 | ['知組', isJ原版 ? [2, 'ʈ', 'tɹ'] : null], 87 | ['二等元音記號|\n雙下橫線僅在下等號顯示不正常時使用', !is朗讀音 ? [1, 88 | { text: '咽化 ◌ˤ', value: 'ˤ' }, 89 | { text: 'r 音鉤(帶空隙)◌˞', value: '˞\u2006' }, 90 | { text: 'r 音鉤(無空隙)◌˞', value: '˞' }, 91 | { text: '下等號 ◌͇', value: '͇' }, 92 | { text: '雙下橫線 ◌̳', value: '̳' }, 93 | ] : null], 94 | ['脂支魚虞', isJ2024 || isL ? [2, 95 | 'i ie ɨə uo', 96 | 'i ie ə o', 97 | 'i e ə o', 98 | ] : null], 99 | ['灰魂', isJ2024 || isL ? [1, 'wəj wən', 'oj on'] : null], 100 | ['豪覃', isJ2024 || isL ? [1, 'əw əm', 'ʌw ʌm'] : null], 101 | ['泰祭夬廢韻尾', isJ原版 ? [1, 'j', 'ɹ'] : is朗讀音 ? [1, 'jɕ'] : null], 102 | ['j 韻尾去聲作 ɹ', isJ新版 || isL ? false : null], // 脂韻去聲則後加 ɹ 103 | ['顯示純音位形式', isL ? false : null], 104 | ] : []); 105 | } 106 | 107 | const { 四等韻 } = TshetUinh.表達式; 108 | 109 | function 調整音韻地位() { 110 | function 調整(表達式, 調整屬性) { if (is(表達式)) 音韻地位 = 音韻地位.調整(調整屬性); } 111 | if (!is通俗) 調整('明母 尤韻', '侯韻 一等 不分類'); // 實爲一等 112 | if (!is通俗) 調整('銳音 幽韻', `尤韻 開合中立 ${is`端組` ? '四等' : ''}`); 113 | } 114 | 115 | function get聲母() { 116 | return when([ 117 | ['云母 開口 非 (侵鹽韻 入聲)', { 云: '' }], // 煜曄兩小韻爲“合口” 118 | [選項.顯示純音位形式 === true && '精組 非 三等', { 119 | 精: 'tᵴ', 清: 'tᵴʰ', 從: 'dᵶ', 心: 'ᵴ', 邪: 'ᵶ', // 精組僅在音位形式中區分三等非三等 120 | }], 121 | ['(鈍音 非 羣母 非 三等) 或 (來母 非 三等) 或 (匣母)', { 122 | // 羣母一律按三等寫,匣母一律按非三等寫 123 | 幫: 'ᵱ', 滂: 'ᵱʰ', 並: 'ᵬ', 明: 'ᵯ', 124 | 見: 'q', 溪: 'qʰ', 疑: 'ɴ', 125 | 影: 'ʡ', 曉: 'χ', 匣: 'ʁ', 126 | 來: 'ɫ', 127 | }], 128 | ['', { 129 | 幫: 'p', 滂: 'pʰ', 並: 'b', 明: 'm', 130 | 端: 't', 透: 'tʰ', 定: 'd', 泥: 'n', 來: 'l', 131 | 知: 'ʈ', 徹: 'ʈʰ', 澄: 'ɖ', 孃: 'ɳ', 132 | 見: 'k', 溪: 'kʰ', 羣: 'ɡ', 疑: 'ŋ', 云: 'w', 133 | 影: 'ʔ', 曉: 'x', 134 | 精: 'ts', 清: 'tsʰ', 從: 'dz', 心: 's', 邪: 'z', 135 | 莊: 'tʂ', 初: 'tʂʰ', 崇: 'dʐ', 生: 'ʂ', 俟: 'ʐ', 136 | 章: 'tɕ', 昌: 'tɕʰ', 常: 'dʑ', 書: 'ɕ', 船: 'ʑ', 日: 'ɲ', 以: 'j', 137 | }] 138 | ])[音韻地位.母]; 139 | } 140 | 141 | function get韻基() { 142 | const 韻 = { 143 | 臻: '真', 144 | 侯: '尤', 145 | 唐: '陽', 146 | }[音韻地位.韻] ?? 音韻地位.韻; 147 | const 所有韻 = [ 148 | // 介音(AB 韻只列在第一列,開合兼有的韻不列在最後一列) 149 | // 三 等:j|ɹ|∅|w 150 | // 非三等:e̯|ʕ|∅|o̯ 151 | ['ɨ', '   脂真幽侵|       |之蒸 微殷  |尤 東 文  '], 152 | ['ə', ' 青 齊先蕭添|佳耕江皆山 咸| 登 咍痕豪覃|模 冬灰魂  '], // 非三等 153 | ['ə', '   祭仙宵鹽|       |魚  廢元 嚴|虞 鍾   凡'], // 三等 154 | ['a', ' 清     |麻庚 夬刪肴銜|歌 陽泰寒 談'], 155 | ['ɨ', '       支'], // 前響二合元音 [ie] 156 | ]; 157 | const 韻尾列表 = is`舒聲` ? ['', ...'ŋɴjnwme'] : [...' kq t p']; 158 | 159 | let 匹配行 = 所有韻.find(e => e[1].includes(韻)); 160 | let 韻核 = 匹配行[0]; 161 | let 韻尾 = 韻尾列表[匹配行[1].split('|').find(v => v.includes(韻)).indexOf(韻)]; 162 | if (is`泰祭夬廢韻 去聲`) 韻尾 = 選項.泰祭夬廢韻尾 || 韻尾; 163 | if (選項['j 韻尾去聲作 ɹ'] && 韻尾 === 'j' && is`去聲`) 韻尾 = 'ɹ'; 164 | return [韻核, 韻尾]; 165 | } 166 | 167 | function get介音() { 168 | const A = 'j'; 169 | const B = 'ɹ'; 170 | const C = ''; 171 | let 等類介音 = when([ 172 | ['A類 或 四等', A], // 四等包含「地」「爹」等 173 | ['B類 或 二等', B], 174 | ['C類 或 一等', C], 175 | // 餘下為三等銳音(含以母) 176 | ['精組', A], 177 | ['', ''], 178 | ]); 179 | let 合口介音 = is`(合口 或 尤韻) 非 (幫組 或 云母)` ? 'w' : ''; 180 | return 等類介音 + 合口介音; 181 | } 182 | 183 | function get聲調() { 184 | if (選項.聲調記號 === '無') return ''; 185 | 186 | if (!選項.聲調記號.includes('五度符號')) { 187 | if (is`平入聲`) return ''; 188 | let 聲調記號 = 選項.聲調記號[+is`去聲`]; 189 | if (is朗讀音 && is`泰祭夬廢韻 去聲` && ['ʰ', 'h'].includes(聲調記號)) return ''; 190 | return 聲調記號; 191 | } 192 | 193 | const 五度符號列表 = is朗讀音 ? [ 194 | '˦', '˦˦˥', '˥˩', '˥', 195 | '˨˩', '˨˨˧', '˧˩˨', '˨˩', 196 | ] : 選項.聲調記號.includes('帶拖腔') ? [ 197 | '˦˦˨', '˦˥', '˦˩˨', '˦', 198 | '˨˨˩', '˨˦', '˨˩˨', '˨', 199 | ] : [ 200 | '˦', '˦˥', '˦˩', '˦', 201 | '˨', '˨˦', '˨˩', '˨', 202 | ]; 203 | const is陽調 = is`全濁` || is朗讀音 && is`次濁 平去聲`; 204 | return 五度符號列表['平上去入'.indexOf(音韻地位.聲) + is陽調 * 4]; 205 | } 206 | 207 | function 音位to音值(音節) { 208 | const is雙唇韻尾 = [...'wmp'].includes(音節.韻尾); 209 | const is展唇鈍韻尾 = [...'ŋk'].includes(音節.韻尾); 210 | const is圓唇鈍韻尾 = [...'ɴq'].includes(音節.韻尾); 211 | const is後部韻尾 = is展唇鈍韻尾 || is圓唇鈍韻尾 || !音節.韻尾; 212 | const is音節首含銳 = !['', 'w'].includes(音節.介音) || is`銳音 三等`; 213 | const is音節首含唇 = 音節.介音.includes('w') || is`幫組` || 音節.聲母 === 'w'; 214 | 215 | // (1) 韻核前化 216 | // 韻核前的非小舌化銳音使韻核前化,但後部韻尾使韻核不被前化(-K 只使 /ɨ/ 不被前化)。 217 | // 通俗擬音不強制前化。茝佁䑂 3 小韻也除外 218 | if (is音節首含銳 && !(is通俗 && is`東鍾之微魚虞廢殷元文歌陽尤嚴凡韻`) && !is`廢韻 非 去聲`) { 219 | if (!is後部韻尾) 音節.替換('韻核', 'ɨ', 'i'); 220 | if (!is圓唇鈍韻尾 && (音節.韻尾 || is`二等`)) 音節.替換('韻核', 'ə', 'e'); 221 | } 222 | 223 | // (2) 韻核圓唇化 224 | // a1. 圓唇鈍韻尾使韻核圓唇化 225 | // a2. 非三等音节相當於 a1 226 | // b1. 含唇音的三 等音節首使 ɨ 圓唇化(三 等 w 實現爲 u̯,同化 ɨ) 227 | // b2. 含唇音的非三等音節首使 ə 圓唇化(非三等 w 實現爲 o̯,同化 ə) 228 | // b3. 但展唇鈍韻尾和雙唇韻尾排斥圓唇,使韻核保持展唇 229 | if (is圓唇鈍韻尾 || !is`三等` && !音節.韻尾) { 230 | 音節.替換('韻核', 'ɨ', 'u'); 231 | 音節.替換('韻核', 'ə', 'o'); 232 | } 233 | if (is音節首含唇 && !is展唇鈍韻尾 && !is雙唇韻尾) { 234 | 音節.替換('韻核', 'ɨ', 'u'); 235 | if ((選項.灰魂?.includes('o') || !(isJ2024 || isL)) && !is`三等` || !音節.韻尾) 音節.替換('韻核', 'ə', 'o'); 236 | } // 豪韻唇音可能也是獨立的,但爲了簡便不處理它 237 | 238 | // (3) 韻核咽化 239 | // 非三等 ɹ 實現爲 ʕ,使韻核咽化 240 | if (!is`三等` && 音節.介音.includes('ɹ')) { 241 | 音節.韻核 += 'ˤ'; 242 | } 243 | 244 | // (4) 省略與韻核同質的介音、韻尾 245 | if (音節.韻核 === 'i') 音節.替換('韻尾', 'j', ''); 246 | if (音節.韻核 === 'i') 音節.替換('介音', 'j', ''); 247 | if (音節.韻核 === 'u') 音節.替換('介音', 'w', ''); 248 | if (音節.韻核 === 'e' && !is`三等`) 音節.替換('介音', 'j', ''); 249 | if (音節.韻核 === 'o') 音節.替換('介音', 'w', ''); 250 | if (音節.韻核.endsWith('ˤ')) 音節.替換('介音', 'ɹ', ''); 251 | 252 | // (5) 豪覃韻韻核寫作 ʌ 253 | if ((選項.豪覃?.includes('ʌ') || !(isJ2024 || isL)) && !is`三等` && is雙唇韻尾) 音節.替換('韻核', 'ə', 'ʌ'); 254 | 255 | // 後處理:按需顯示 ɑ,包括𦣛小韻(銳音歌三合) 256 | if (選項.後低元音.length === 1) 257 | if (!is音節首含銳 || is圓唇鈍韻尾 || is`歌韻 三等 合口`) 音節.替換('韻核', 'a', 選項.後低元音); 258 | // 後處理:脂支魚虞 259 | const i = ['i', 'ie', 'ə', 'o'].indexOf(音節.韻核 + 音節.韻尾); 260 | if (i !== -1 && is`三等`) { 261 | // 舊版本魚虞按前響二合元音處理 262 | const 韻基 = (選項.脂支魚虞 ?? (isJ2024 || isL ? 'i ie ə o' : 'i ie ɨə uo')).split(' ')[i]; 263 | 音節.韻核 = 韻基[0]; 264 | 音節.韻尾 = 韻基.slice(1); 265 | } 266 | } 267 | 268 | function 調整聲母(音節) { 269 | const 舊版聲母字典 = [ 270 | [選項.前部聲母非三等?.includes('ˤ'), { 271 | 'ᵱ': 'pˤ', 'ᵬ': 'bˤ', 'ᵯ': 'mˤ', 272 | 'ᵴ': 'sˤ', 'ᵶ': 'zˤ', 'ɫ': 'lˤ', 'ʡ': 'ʔˤ', 273 | }], 274 | [選項.前部聲母非三等?.includes('̙'), { 275 | 'ᵱ': 'p̙', 'ᵬ': 'b̙', 'ᵯ': 'm̙', 276 | 'ᵴ': 's̙', 'ᵶ': 'z̙', 'ɫ': 'l̙', 'ʡ': 'ʔ̙', 277 | }], 278 | [!isL, { 'ᵱ': 'p', 'ᵬ': 'b', 'ᵯ': 'm' }], 279 | [isJ原版 || is朗讀音 || is通俗 || isJ2024, { 'ʡ': 'ʔ' }], 280 | [isJ原版 || is朗讀音 || is通俗, { 'ɫ': 'l' }], 281 | [isJ原版 || is朗讀音, { 'x': 'h' }], 282 | [選項.見組非三等簡寫作軟腭音, { 'q': 'k', 'ɴ': 'ŋ', 'χ': 'x' }], 283 | [選項.見組非三等簡寫作軟腭音 && is通俗, { 'ʁ': 'ɣ' }], 284 | [選項.知組 === 'tɹ', { 'ʈ': 'tɹ', 'ɖ': 'dɹ', 'ɳ': 'nɹ' }], 285 | ].reduce((prev, cur) => Object.assign(prev, cur[0] ? cur[1] : {}), {}); 286 | 287 | for (const [k, v] of Object.entries(舊版聲母字典)) { 288 | if (音節.聲母.includes(k)) { 289 | 音節.替換('聲母', k, v); 290 | break; 291 | } 292 | } 293 | } 294 | 295 | function 調整鈍C介音(音節) { 296 | if (選項.鈍C介音.includes('∅')) return; 297 | const 鈍C介音列表 = 選項.鈍C介音.split(' ').map(e => e.replace(/[開合唇非低]/g, '')); 298 | const [鈍C開, 鈍C唇低, 鈍C唇非低, 鈍C合] = [...鈍C介音列表.slice(0, 2), ...鈍C介音列表.slice(-2)]; 299 | 音節.介音 = when([ 300 | [音節.韻核 !== 'i' && '三等 鈍音 非 云母', [ // J 的云母一律拼作 w 且後無多餘介音。通俗的云母爲零聲母,後面再處理 301 | [音節.介音 === 'w', 鈍C合], 302 | [音節.介音 === '', [ 303 | ['幫組', [ 304 | ['歌陽韻', 鈍C唇低], 305 | ['', 鈍C唇非低], 306 | ]], 307 | [isJ2024 && 音節.韻核 !== 'ɨ', 鈍C開], 308 | [!['ɨ', 'u'].includes(音節.韻核) || '東尤韻', 鈍C開], 309 | ]], 310 | ]], 311 | ['', 音節.介音], 312 | ], '', true); 313 | } 314 | 315 | function 調整二等元音(音節) { 316 | const 默認記號 = 'ˤ'; 317 | let 新記號 = 選項.二等元音記號; 318 | if (!新記號 || 新記號 === 默認記號 || !音節.韻核.includes(默認記號)) return; 319 | 320 | if (新記號[0] === '˞' && is`端組 或 來母 庚韻`) 新記號 = ''; 321 | 音節.替換('韻核', 默認記號, 新記號); 322 | if (音節.韻核 === 'e' && !音節.韻尾) { // 箉小韻,無附加符號時改作 -aj 323 | 音節.韻核 = 'a'; 324 | 音節.韻尾 = 'j'; 325 | } 326 | } 327 | 328 | function 音值toJ(音節) { 329 | if (選項.鈍C介音.includes('ɥ̈') && is`三等 非 東尤韻`) 音節.替換('韻核', 'u', 'ʉ'); 330 | if (isJ新版) return; 331 | 332 | if ( 333 | 音節.介音.includes('w') || 334 | 音節.介音 === 'ɥ̈' || 335 | [...'ʉuo'].includes(音節.韻核) && is`(合口 或 鍾韻) 非 (幫組 或 云母)` 336 | ) { 337 | 音節.聲母 += 'ʷ'; 338 | 音節.替換('聲母', 'ʰʷ', 'ʷʰ'); 339 | 音節.替換('聲母', 'jʷ', 'ɥ'); 340 | 音節.替換('介音', 'w', ''); 341 | 音節.替換('介音', 'j', 'ɥ'); 342 | } 343 | 344 | 音節.韻核 = when([ 345 | ['蒸韻', 'i'], 346 | ['臻韻 開口', 'ɹ̩'], 347 | ['侯韻 非 明母', 'ɘu'], 348 | 349 | ['清韻', 'iæ'], 350 | ['陽韻', (is`開口 或 A類` ? 'ɨ' : 'ʉ') + 'ɐ'], 351 | ['鍾韻', 'ʉɔ'], 352 | 353 | ['江韻', 'œˤ'], 354 | ['凡韻', 'œ'], 355 | ['', 音節.韻核], 356 | ]); 357 | 358 | 音節.介音 = when([ 359 | [音節.聲母.includes('ʷ') && 音節.韻核[0] !== 'ʉ' && '精組 三等', 'ɹ'], 360 | ['精組 三等 東尤韻', 'ɹ'], 361 | [音節.韻核[0] === 'i' && (['j', 'ɥ'].includes(音節.介音) || '知組 或 來母'), ''], 362 | [[...'iɨʉ'].includes(音節.韻核[0]) && (['j̈', 'ɥ̈'].includes(音節.介音) || '銳音'), ''], 363 | [選項.知組 !== 'tɹ' && '知組 三等', 'ɹ'], 364 | ['來母 三等', 'ɹ'], 365 | ['', 音節.介音], 366 | ]); 367 | 368 | 音節.韻尾 = { 369 | 微: 'i', 幽: 'u', 370 | 支: 'ɛ', 魚: 'ʌ', 虞: 'ɔ', 371 | }[音韻地位.韻] ?? 音節.韻尾; 372 | } 373 | 374 | function 音值to朗讀音(音節) { 375 | if (音節.聲母.replace('ʰ', '').length > 1) { 376 | 音節.聲母 = 音節.聲母[0] + '͡' + 音節.聲母.slice(1); 377 | } 378 | 379 | const hasW = 音節.聲母 === 'w' || 音節.介音.includes('w') || ['u', 'o'].includes(音節.韻核); 380 | 音節.替換('聲母', 'w', ''); 381 | 音節.介音 = when([ 382 | [`三四等 非 ${四等韻}`, [ // 包含「地」「爹」「丟」等 383 | ['幽韻', is`幫組` ? 'j' : 'ɥ'], 384 | [[...'iea'].includes(音節.韻核[0]) || '蒸韻 或 AB類', [ 385 | ['B類 或 知莊組 或 蒸韻 鈍音', hasW ? 'ɻɥ' : 'ɻj'], // B 386 | ['', hasW ? 'ɥ' : 'j'], // A 387 | ]], 388 | ['', is`幫組 或 尤韻` ? 'ɥ̈' : hasW ? 'ẅ' : 'j̈'], // C 389 | ]], 390 | ['二等 非 (端組 或 來母 庚韻)', is`知莊組` ? 'ɻ' + 音節.介音 : 音節.介音 + 'ɻ'], 391 | [音節.韻核 === 'o' && '一等 非 (冬模韻 或 幫組)', 'w'], 392 | ['', 音節.介音], 393 | ]); 394 | if (音節.聲母 === 'j' && ['j', 'ɥ'].includes(音節.介音)) 音節.聲母 = ''; 395 | 396 | if (音節.韻核.includes('ˤ')) { 397 | 音節.替換('韻核', 'eˤ', 'æ'); 398 | 音節.替換('韻核', 'oˤ', 'æ'); 399 | 音節.替換('韻核', 'aˤ', 'a'); 400 | } else { 401 | 音節.韻核 = when([ 402 | [`三四等 非 ${四等韻}`, [ 403 | ['蒸韻', 'i'], 404 | ['臻韻 開口', 'i˞ '], 405 | ['微韻', 'ɨ'], 406 | ['幽韻', 'ÿ'], 407 | [音節.韻核 === 'e' || '支清韻', 'ɛ'], 408 | ['魚韻', 'ə'], 409 | [音節.韻核 === 'o' || '虞韻', 'ɔ'], 410 | ['陽韻', 'ɐ'], 411 | ['嚴凡韻 幫組', 'ɞ'], 412 | ['', 音節.韻核], 413 | ]], 414 | ['侯韻 非 幫組', 'ɘu'], 415 | ['豪韻', 'ɑ'], 416 | ['覃韻 或 咍灰韻 開口', 'ɐ'], 417 | ['咍灰韻', 'ɔ̞'], 418 | [音節.韻核 === 'ə', 'ɘ'], 419 | ['', 音節.韻核], 420 | ]); 421 | } 422 | 423 | 音節.韻尾 = when([ 424 | ['通江攝', is`舒聲` ? 'ŋʷ' : 'kʷ'], 425 | ['宕攝', is`舒聲` ? 'ŋ' : 'k'], 426 | ['梗攝', is`舒聲` ? 'ɲ' : 'c'], 427 | ['支魚虞幽韻', ''], 428 | ['', 音節.韻尾], 429 | ]); 430 | 431 | [['j', 'i'], ['j̈', 'ɨ']].forEach(e => { 432 | if (音節.韻核[0] === e[1] && (音節.聲母 || 音節.介音[0] === 'ɻ')) 音節.替換('介音', e[0], ''); 433 | }); 434 | } 435 | 436 | function 音值to通俗(音節) { 437 | const is小舌尾 = is`尤侯虞模歌東冬鍾江陽唐韻`; 438 | 439 | if (音節.聲母 === 'w') { 440 | 音節.聲母 = ''; 441 | if (!音節.介音) 音節.介音 = 'ɨ'; 442 | 音節.介音 += 'u'; 443 | } 444 | if (is`蒸韻 B類`) 音節.韻核 = 'i'; 445 | 音節.替換('介音', 'j', 'i'); 446 | 音節.替換('介音', 'ɹ', 'ɨ'); 447 | 音節.替換('介音', 'w', 'u'); 448 | 音節.替換('介音', 'ʉ', 'ɨu'); 449 | if (is`銳音 三四等 非 ${四等韻}`) { 450 | if (!is`端精組`) 音節.介音 = (is`莊組` ? 'ɨ' : 'i') + 音節.介音; 451 | if (![...'iea'].includes(音節.韻核) || is小舌尾) 音節.替換('介音', 'i', 'ɨ'); 452 | } 453 | if (is`支魚虞韻`) { 454 | if (!['i', 'ɨ'].includes(音節.介音[0])) 音節.介音 = 音節.韻核.replace('u', 'ɨu') + 音節.介音; 455 | 音節.韻核 = 音節.韻尾; 456 | 音節.韻尾 = ''; 457 | } 458 | if (音節.韻核 === 'u' && is小舌尾) 音節.介音 = 音節.介音.replace('u', ''); 459 | if (音節.韻核 === 'i' && 音節.介音 === 'u') 音節.介音 = 'iu'; 460 | if (音節.韻核 === 'o' && 音節.介音 && !音節.介音.includes('u')) 音節.介音 += 'u'; 461 | 音節.替換('介音', 'iu', 'y'); 462 | 音節.替換('介音', 'ɨu', 'ʉ'); 463 | 464 | if (音節.韻核.includes('ˤ')) { 465 | 音節.替換('韻核', 'eˤ', 'ɛ'); 466 | 音節.替換('韻核', 'oˤ', 'ɔ'); 467 | 音節.替換('韻核', 'aˤ', 'æ'); 468 | } 469 | if (!is小舌尾) { 470 | if (音節.韻核 === 'o') 音節.介音 += 'u'; 471 | 音節.替換('韻核', 'u', 'ʉ'); 472 | 音節.替換('韻核', 'ʌ', is`豪韻` ? 'a' : 'ə'); 473 | 音節.替換('韻核', 'o', 'ə'); 474 | if (is`三四等`) 音節.替換('韻核', 'a', 'æ'); 475 | } 476 | if (選項.後低元音.includes('歌陽唐') && is小舌尾) 音節.替換('韻核', 'a', 'ɑ'); 477 | if (音節.介音 === 音節.韻核) 音節.介音 = ''; 478 | 479 | 音節.替換('韻尾', 'ɴ', 'ŋ'); 480 | 音節.替換('韻尾', 'q', 'k'); 481 | } 482 | 483 | function get音節() { 484 | const 音節 = { 485 | 聲母: get聲母(), 486 | 介音: get介音(), 487 | 聲調: get聲調(), 488 | 替換(propertyName, from, to) { this[propertyName] = this[propertyName].replace(from, to); } 489 | }; 490 | [音節.韻核, 音節.韻尾] = get韻基(); 491 | 492 | if (!選項.顯示純音位形式) 音位to音值(音節); 493 | 調整聲母(音節); 494 | 調整鈍C介音(音節); 495 | if (isJ新版 || isJ原版) 音值toJ(音節); 496 | else if (is朗讀音) 音值to朗讀音(音節); 497 | else if (is通俗) 音值to通俗(音節); 498 | 調整二等元音(音節); 499 | 500 | let 聲調記號插入位置 = ['̩', '͇', '̳', 'ɘu'].some(e => 音節.韻核.includes(e)) ? 2 : 1; 501 | 音節.韻基 = 音節.韻核 + 音節.韻尾; 502 | 音節.帶調韻基 = 選項.聲調記號.includes('\u0301') ? 503 | 音節.韻核.slice(0, 聲調記號插入位置) + 音節.聲調 + 音節.韻核.slice(聲調記號插入位置) + 音節.韻尾 : 504 | 音節.韻核 + 音節.韻尾 + 音節.聲調; 505 | 音節.韻母 = 音節.介音 + 音節.韻基; 506 | 音節.帶調韻母 = 音節.介音 + 音節.帶調韻基; 507 | return 音節; 508 | } 509 | 510 | if (!音韻地位) return get選項列表(); 511 | 調整音韻地位(); 512 | const 音節 = get音節(); 513 | return 音節.聲母 + 音節.帶調韻母; 514 | -------------------------------------------------------------------------------- /mongol.js: -------------------------------------------------------------------------------- 1 | /* 推導《蒙古字韻》 2 | * 3 | * 由切韻音系音韻地位完美推導《蒙古字韻》八思巴字及各家轉寫、擬音 4 | * 5 | * 《蒙古字韻》聲韻母及其搭配反映金、南宋時官話發音,但各音節轄字係由《平水韻》系韻書直接填入,不反映實際字音。聲調方面《蒙古字韻》全濁聲母獨立、全濁上獨立、入聲韻拼作對應的陰聲韻,皆爲沿襲等韻學習慣,亦不反映實際字音。不過正因如此,本推導方案的推導準確率達 100% 6 | * 7 | * 參考文獻: 8 | * 9 | * - 元刊本. 禮部韻略七音三十六母通考. https://www.digital.archives.go.jp/item/3215715 10 | * (載於《古今韻會舉要》書前,以下簡稱《通考》) 11 | * - 照那斯圖, 楊耐思. 1987. 蒙古字韻校本. 12 | * - 甯忌浮. 1997. 《古今韻會舉要》及相關韻書. 13 | * - Coblin, W. South. 2007. A Handbook of 'Phags-pa Chinese. https://doi.org/10.1515/9780824861261 14 | * - Shen, Zhongwei. 2008. Studies on the Menggu Ziyun. 15 | * - 沈鍾偉. 2015. 《蒙古字韻》集校. 16 | * - 宋洪民. 2017. 八思巴字資料與蒙古字韻. 17 | * - 濱田武志. 2019. 論《蒙古字韻》所反映的漢語方言音系. https://doi.org/10.1163/2405478x-01101006 18 | * - unt. 2023. 《蒙古字韻》音系. https://zhuanlan.zhihu.com/p/597563597 19 | * 20 | * 其他轉寫來源: 21 | * 22 | * - 照那斯圖. 1980. 論八思巴字. 23 | * - 吉池孝一. 2005. パスパ文字の字母表. 24 | * 25 | * @author unt 26 | */ 27 | 28 | /** @type { 音韻地位['屬於'] } */ 29 | const is = (...x) => 音韻地位.屬於(...x); 30 | /** @type { 音韻地位['判斷'] } */ 31 | const when = (...x) => 音韻地位.判斷(...x); 32 | 33 | const 字母韻後綴 = 選項?.後綴 ?? '₂'; 34 | 35 | if (!音韻地位) return [...[ 36 | ['顯示', [1, 37 | '八思巴字', 38 | '照那斯圖 1987 轉寫 ⭐', 39 | '吉池孝一 2005 轉寫', 40 | 'Coblin 2007 轉寫', 41 | 'Coblin 2007 擬音', 42 | '沈鐘偉 2008/2015 轉寫兼擬音', 43 | // TODO: '濱田武志 2019 擬音', 44 | 'unt 2023 轉寫 ⭐', 45 | 'unt 2023 擬音 ⭐', 46 | ]], 47 | ['ꡠ、ꡦ 的轉寫', [1, 48 | 'ꡠe ꡦė(八思巴字漢語風格)', 49 | 'ꡠė ꡦe(八思巴字蒙古語風格)', 50 | ], { hidden: 選項.顯示 !== '照那斯圖 1987 轉寫 ⭐' }], 51 | [['聲母附加數字|', 52 | '非敷 ꡰ f2 ≠ 奉 ꡤ f1', 53 | '審 ꡮ š2 ≠ 禪 ꡚ š1', 54 | '曉 ꡜ h2 ≠ 匣 ꡯ h1', 55 | '幺 ꡗ y2 ≠ 喻 ꡭ y1', 56 | ].join('\n') 57 | .replace(/1/g, 選項?.聲母附加數字?.split(' ')[0] ?? '1') 58 | .replace(/2/g, 選項?.聲母附加數字?.split(' ')[1] ?? '2'), 59 | [1, 60 | '₁ ₂', 61 | { value: '1 2', text: '1 2(原文)' }, 62 | ], { hidden: 選項.顯示 !== '吉池孝一 2005 轉寫' }], 63 | ['零聲母陽調下加|\n原文零聲母陽調整個音節加下劃線,此處用首個字母下加橫線代替', [2, 64 | { value: '\u0331', text: '◌\u0331 長音符(U+0331)' }, 65 | { value: '\u0332', text: '◌\u0332 橫線(U+0332)' }, 66 | { value: '\u035F', text: '◌\u035F 雙長音符(U+035F)' }, 67 | ], { hidden: 選項.顯示 !== '沈鐘偉 2008/2015 轉寫兼擬音' }], 68 | ], ...(選項?.顯示?.includes(' 擬音') ? [] : [ 69 | '', 70 | ['兩字母韻拼寫相同時加後綴區分', true, { 71 | description: [ 72 | '經韻 ꡦꡞꡃ ≠ 行韻 ꡦꡞꡃ', 73 | '弓韻 ꡦꡟꡃ ≠ 雄韻 ꡦꡟꡃ', 74 | '規韻 ꡦꡟꡠ ≠ 麾韻 ꡦꡟꡠ', 75 | '杴韻 ꡦꡠꡏ ≠ 嫌韻 ꡦꡠꡏ', 76 | ].map(e => 選項.兩字母韻拼寫相同時加後綴區分 === false ? e : e + 字母韻後綴).join('\n'), 77 | }], 78 | ['後綴', [1, '₂', '²', '2', '′'], { hidden: 選項.兩字母韻拼寫相同時加後綴區分 === false }], 79 | ['寶字採用特殊拼寫|寶字採用 ꡎꡖꡡ 拼寫\n「ꡎꡖꡡ 御寳上用此寳字」', false], 80 | ]), ...[ 81 | '音韻地位的選取', 82 | '', ['推導器 ≠ 傳統等韻學 = 原書 時', [2, '依推導器', '依原書']], 83 | '', ['推導器 = 傳統等韻學 ≠ 原書 時', [2, '依推導器', '依原書']], 84 | '', ['保留《蒙古字韻》小韻歸併錯誤', false], 85 | // 不保留《蒙古字韻》小韻拼寫錯誤 86 | ['不字讀重脣音', true], 87 | ]]; 88 | 89 | const use傳統 = 選項['推導器 ≠ 傳統等韻學 = 原書 時'] === '依原書'; 90 | const use創新 = 選項['推導器 = 傳統等韻學 ≠ 原書 時'] === '依原書'; 91 | 92 | function 調整音韻地位() { 93 | function 調整(表達式, 調整屬性, 字頭串 = null, 邊緣地位種類 = []) { 94 | if (typeof (字頭串) === 'string' && !字頭串.includes(字頭)) return; 95 | if (is(表達式)) 音韻地位 = 音韻地位.調整(調整屬性, 邊緣地位種類); 96 | } 97 | 98 | [[true, [ // 《廣韻》特殊小韻的正常調整 99 | ['云母 通曾攝 舒聲 非 開口', { 母: '匣' }, null, ['匣母三等']], // 雄熊 100 | ['云匣母 真韻 開口', { 母: '匣', 類: 'A' }, null, ['匣母三等']], // 礥 101 | ]], 102 | [use傳統, [ 103 | ['從母 真韻 去聲', { 母: '邪' }], // 賮燼藎贐 104 | ['影母 青韻 去聲', { 呼: '開' }], // 鎣瑩瀅 105 | ['清母 歌韻 去聲', { 呼: '合' }, '磋'], // 磋 106 | ['見母 仙韻 A類 開口 入聲', { 類: 'B' }], // 孑 107 | ['並母 灰韻 上聲', { 韻: '咍' }, '倍菩蓓萯䔒培痱傰', ['咍韻脣音']], // 倍 108 | ['滂母 灰韻 上聲', { 韻: '咍' }, null, ['咍韻脣音']], // (仿照倍) 109 | ['昌母 廢韻 平上聲 開口', { 母: '初', 韻: '皆', 等: '二' }], // 茝 110 | ['以母 廢韻 平上聲 開口', { 母: '疑', 韻: '皆', 等: '二' }], // (仿照茝) 111 | ['匣母 先韻 開口 上聲', { 韻: '真', 等: '三', 類: 'A' }, '礥𧥺㘋', ['匣母三等']], // 礥 112 | ['以母 脂韻 上聲 合口', { 韻: '宵', 呼: '開' }, '鷕'], // 鷕。此爲 TshetUinh.js v0.15 新修正地位 113 | ]], 114 | [use創新, [ // 《通考》也如此 115 | ['云母 咸攝 舒聲', { 母: '以', 類: null }], // 炎焱 116 | ['云母 蒸韻 合口 入聲', { 母: '影' }], // 域罭棫緎淢 117 | ['端母 庚韻 二等', { 韻: '麻' }, null, ['端組類隔']], // 打 118 | ['來母 歌韻 去聲', { 呼: '合' }], // 邏 119 | ['曉母 脂韻 A類 合口 去聲', { 韻: '灰', 等: '一', 類: null }, '䁤睢𥍋婎'], // 《通考》睢(《蒙古字韻》無字) 120 | // ['曉母 青韻 合口 入聲', { 韻: '庚', 等: '三', 類: 'B' }], // 《通考》殈(《蒙古字韻》無字) 121 | ['曉母 梗攝 三四等 合口 去聲', { 韻: '庚', 等: '三', 類: 'B' }], // 夐 122 | ['曉母 青韻 開口 入聲', { 呼: '合' }], // 赥䦧𥍠 123 | ['溪母 仙韻 A類 開口 去聲', { 類: 'B' }], // 譴遣 124 | ['曉母 咸攝 三等 入聲', { 韻: '添', 等: '四', 類: null }], // 脅愶㢵嗋熁 125 | ]], 126 | [選項['保留《蒙古字韻》小韻歸併錯誤'], [ 127 | ['曉母 (止攝 或 臻攝 入聲) A類', { 母: '匣' }, null, ['匣母三等']], // 屎欯、隳墮獝 128 | ['溪母 文韻 上聲', { 母: '云' }], // 𦄐 129 | ['曉母 陽韻 合口 入聲', { 母: '並' }], // 戄(ħwjaw 混入 vaw) 130 | ['澄母 仙韻 開口 入聲', { 母: '知' }], // 轍徹撤澈 131 | ['溪母 青韻 開口 上聲', { 呼: '合' }, '綮'], // 《廣韻》未收,《集韻》溪開四青上 132 | ['羣母 之韻 平聲', { 韻: '脂', 類: 'A' }, '蘄'], 133 | ['精母 咍韻 去聲', { 母: '明', 韻: '庚', 呼: null, 等: '二', 聲: '入' }, '載'], 134 | ['匣母 寒韻 合口 去聲', { 母: '端', 呼: '開' }, '漶'], 135 | ['明母 仙韻 B類 上聲', { 母: '滂' }, '葂莬'], // 《廣韻》未收 136 | ['影母 陽韻 合口 入聲', { 韻: '唐', 等: '一', 類: null }, '矱'], // 《廣韻》未收 137 | ['見母 幽韻 A類 上聲', { 韻: '尤', 類: 'C' }, '糺'], // 《廣韻》未收 138 | ['溪母 添韻 去聲', { 韻: '咸', 等: '二' }, '䈴'], // 《廣韻》未收 139 | ]], 140 | [選項.不字讀重脣音, [ // 《古今韻會舉要》也有此音 141 | ['幫母 文韻 入聲', { 韻: '魂', 等: '一', 類: null }, '不'], 142 | ]]].forEach(e => { 143 | if (e[0]) e[1].forEach(args => 調整(...args)); 144 | }); 145 | } 146 | 147 | 調整音韻地位(); 148 | 149 | const 韻圖等 = when([ 150 | ['幫組 C類', '輕'], 151 | 152 | // 切韻一二四等到韻圖不變 153 | ['非 三等', 音韻地位.等], 154 | 155 | // 按韻圖約定,幽韻一律歸四等 156 | ['幽韻', '四'], 157 | 158 | // 切韻三等,聲母是銳音的情況 159 | ['莊組', '二'], 160 | ['知章組 或 來日母', '三'], 161 | ['端精組 或 以母', '四'], 162 | 163 | // 切韻三等,聲母是鈍音的情況 164 | ['A類', '四'], 165 | ['', '三'], 166 | ]); 167 | 168 | // 接下來先推導 unt 擬音,再反推八思巴字 169 | function get聲母() { 170 | const is創新的二四等併入三等 = is`見溪羣曉母 臻攝 舒聲 A類 合口` || 171 | is`曉母 ((曾梗攝 開口 二三四等) 或 (山咸攝 (四等 或 A類)) 或 幽韻) 舒聲` || 172 | is`羣母 山攝 三四等 合口 舒聲`; 173 | let 聲母字典 = {}; 174 | if ((韻圖等 === '四' || 韻圖等 === '二' && !is`合口 或 江韻 舒聲`) && !(use創新 && is創新的二四等併入三等)) 聲母字典 = { 175 | 見: 'c', 溪: 'cʰ', 羣: 'ɟ', 曉: 'ç', 匣: 'ʝ', 176 | 影: 'ʔ', 疑: '', 云: '', 以: '', 177 | }; 178 | else if (韻圖等 === '輕') 聲母字典 = { 179 | 幫: 'f', 滂: 'f', 並: 'v', 明: 'ʋ', 180 | }; 181 | else if (韻圖等 === '三' || use創新 && is創新的二四等併入三等 || is`江韻 舒聲`) 聲母字典 = { 182 | 曉: 'x', 匣: use創新 ? 'ʝ' : 'ɣ', // 匣三(雄小韻)併入四等 183 | }; 184 | if (!(音韻地位.母 in 聲母字典)) 聲母字典 = { 185 | 幫: 'p', 滂: 'pʰ', 並: 'b', 明: 'm', 186 | 端: 't', 透: 'tʰ', 定: 'd', 泥: 'n', 來: 'l', 187 | 188 | 精: 'ts', 清: 'tsʰ', 從: 'dz', 心: 's', 邪: 'z', 189 | 知: 'tʂ', 徹: 'tʂʰ', 澄: 'dʐ', 孃: 'n', // 泥孃合併 190 | 莊: 'tʂ', 初: 'tʂʰ', 崇: 'dʐ', 生: 'ʂ', 俟: 'dʐ', // 俟母併入崇母 191 | 章: 'tʂ', 昌: 'tʂʰ', 船: 'dʐ', 書: 'ʂ', 常: 'ʐ', 日: 'ɻ', // 常船顛倒 192 | 見: 'k', 溪: 'kʰ', 羣: 'ɡ', 曉: 'χ', 匣: 'ʁ', 193 | 影: 'ʡ', 疑: 'ŋ', 云: 'ŋ', 以: 'ŋ', // ŋ 稍後處理 194 | }; 195 | return 聲母字典[音韻地位.母]; 196 | } 197 | 198 | function get韻母() { 199 | const is合口 = when([ 200 | ['效深咸攝 舒聲', '開'], // 流攝唇音歸合口 201 | ['遇通攝 或 痕韻 入聲', '合'], 202 | ['江韻 銳音', '合'], 203 | [use創新 && '明母 曾梗攝 一二等 舒聲', '合'], 204 | ['幫組', [ 205 | [韻圖等 === '三' && '止蟹攝 或 (臻深曾梗攝 入聲)', '合'], 206 | [韻圖等 === '輕' && '臻流果攝', '合'], 207 | ['二三四等', '開'], 208 | ['曾攝 舒聲 或 宕攝', '開'], 209 | [use創新 && '咍泰韻', '開'], 210 | ['', '合'], 211 | ]], 212 | ['', 音韻地位.呼 ?? '開'], 213 | ]) === '合'; 214 | const is三四等 = '三四'.includes(韻圖等) && !is`止攝 精組 開口` || 215 | is`見影組 江梗攝 二等 舒聲 非 合口` || 216 | 韻圖等 === '輕' && is`止蟹攝`; 217 | 218 | // 推導底層形式,僅 3 個元音:ɨ、ʌ、a 219 | let 韻基 = when([ 220 | ['臻深攝 入聲', [ 221 | [韻圖等 === '四' && '見影組 或 以母', 'ɨj'], 222 | [韻圖等 === '三' && '幫組', 'ɨj'], 223 | ['', 'ɨ'], 224 | ]], 225 | 226 | ['止攝 精莊組 開口 或 遇攝 或 通攝 入聲', 'ɨ'], 227 | ['果假攝 或 山咸攝 入聲', [ 228 | [韻圖等 === '輕' && '果攝', 'ʌ'], // 《通考》縛(《蒙古字韻》無字) 229 | [is三四等 || 韻圖等 === '一' && (is合口 || '非 (入聲 非 見影組)'), 'ʌ'], 230 | ['', 'a'], 231 | ]], 232 | 233 | ['通曾梗攝 舒聲', 'ɨŋ'], 234 | ['宕江攝 舒聲', 'aŋ'], 235 | 236 | ['止蟹攝 或 曾梗攝 入聲', [ 237 | [use創新 && 韻圖等 === '二' && '曾攝', 'ɨj'], 238 | [use創新 && 韻圖等 === '二' && '蟹攝 三四等', 'ɨj'], // 㯔毳。另有《廣韻》𠻜小韻 239 | [is三四等 || 韻圖等 === '一' && (is合口 || '入聲'), 'ɨj'], 240 | ['', 'aj'], 241 | ]], 242 | 243 | ['臻攝 舒聲', 'ɨn'], 244 | ['山攝 舒聲', [ 245 | [is三四等 || 韻圖等 === '一' && is合口, 'ʌn'], 246 | ['', 'an'], 247 | ]], 248 | 249 | ['流攝', 'ɨw'], 250 | ['效攝 或 宕江攝 入聲', [ 251 | [use創新 && '宕攝 莊組 三等', 'ʌw'], // 斮 252 | [is三四等, 'ʌw'], 253 | ['', 'aw'], 254 | ]], 255 | 256 | ['深攝 舒聲', 'ɨm'], 257 | ['咸攝 舒聲', [ 258 | [is三四等, 'ʌm'], 259 | ['', 'am'], 260 | ]], 261 | ], '無韻母規則', true); 262 | 263 | // 生成表層形式 264 | const is細音 = is三四等 || 265 | 韻圖等 === '二' && !is合口 && is`見影組 或 以母` || 266 | use創新 && is`莊組 蟹攝 三四等 開口` || // 《通考》殺(《集韻》生開三祭去,對應《廣韻》㡜小韻)(《蒙古字韻》無字) 267 | use創新 && is`宕攝 莊組 三等 入聲`; 268 | 韻基 = { 269 | ɨ: ['i', 'y', 'ɨ', 'u'], 270 | ʌ: ['jɛ', 'ɥɛ', 'ʌ', 'wɔ'], 271 | a: ['ja', 'wa', 'a', 'wa'], // 撮口呼併入合口呼 272 | }[韻基[0]][!is細音 * 2 + (is合口)] + 韻基.slice(1); 273 | if (韻基 === 'ij') 韻基 = 'i'; 274 | if (韻基 === 'y') 韻基 = 'ɥu'; 275 | if (韻基 === 'yj' && !(韻圖等 === '四' && is`見影組 或 以母`)) 韻基 = 'uj'; // 三等併入合口呼 276 | if (韻基 === 'ɥɛn' && 韻圖等 === '三' && is`見影組 或 來母`) 韻基 = 'ɥɔn'; // 條件變體 277 | if (韻基 === 'ɥɛn' && use創新 && is`羣曉母`) return 'ɥɔn'; // 四等併入三等 278 | return 韻基; 279 | } 280 | 281 | function get擬音() { 282 | let 擬音 = get聲母() + get韻母(); 283 | [ 284 | [/ŋ(?=ɥ|y|w|u)/, 'w'], 285 | ['jj', 'j'], ['ww', 'w'], 286 | ].forEach(e => 擬音 = 擬音.replace(...e)); 287 | return 擬音; 288 | } 289 | 290 | function 擬音to八思巴字(擬音) { 291 | if (選項.寶字採用特殊拼寫 && 擬音 === 'paw' && '寶寳宝珤'.includes(字頭)) { // 只考慮推導器的廣韻資料,不增加更多字頭 292 | return 'ꡎꡖꡡ'; 293 | } 294 | const 聲母字典 = { 295 | p: 'ꡎ', pʰ: 'ꡍ', b: 'ꡌ', m: 'ꡏ', f: 'ꡰ', v: 'ꡤ', ʋ: 'ꡓ', 296 | t: 'ꡊ', tʰ: 'ꡉ', d: 'ꡈ', n: 'ꡋ', l: 'ꡙ', // ꡇ 稍後處理 297 | ts: 'ꡒ', tsʰ: 'ꡑ', dz: 'ꡐ', s: 'ꡛ', z: 'ꡕ', 298 | tʂ: 'ꡆ', tʂʰ: 'ꡅ', dʐ: 'ꡄ', ʂ: 'ꡮ', ʐ: 'ꡚ', ɻ: 'ꡔ', 299 | c: 'ꡂꡦ', cʰ: 'ꡁꡦ', ɟ: 'ꡀꡦ', ç: 'ꡜꡦ', ʝ: 'ꡯꡦ', ɣ: 'ꡯꡦ', ʝjaŋ: 'ꡯjaŋ', ɣjaŋ: 'ꡯjaŋ', 300 | k: 'ꡂ', kʰ: 'ꡁ', ɡ: 'ꡀ', x: 'ꡜ', χ: 'ꡜ', ʁ: 'ꡣ', 301 | xwaŋ: 'ꡜɥaŋ', xuj: 'ꡜuj₂', // 怳、麾 302 | ʔ: 'ꡗ', ʔja: 'ꡗa', 303 | j: 'ꡭ', ɥ: 'ꡭɥ', i: 'ꡭi', y: 'ꡭy', jɛ: 'ꡭjɛ', 304 | ʡ: 'ꡖ', 305 | ŋ: 'ꡃ', wɥ: 'ꡝɥ', w: 'ꡝw', wy: 'ꡝy', wu: 'ꡝu', 306 | }; 307 | const 後處理替換列表 = [ 308 | // 【韻母到八思巴字】 309 | // 介音 + 韻核 310 | ['i', 'ꡞ'], ['ɨ', 'ꡜꡞ'], 311 | [/y|ꡦy(?=j)|ɥu/, 'ꡦꡟ'], ['u', 'ꡟ'], 312 | [/ꡦjɛ|(? e 327 | [/(?<=[ꡤ])ꡟꡓ/, 'ꡡꡓ'], // (v)uw -> ow 328 | [/(?<=[ꡣ])ꡧꡃ/, 'ꡡꡃ'], // (ʁ)waŋ -> oŋ(可能代表 o̯aŋ) 329 | 330 | // 照組(含孃母)後調整洪細 331 | [/(?<=[ꡆꡅ ꡮꡚꡔꡇ])ꡦꡟꡃ/, 'ꡟꡃ'], // juŋ -> uŋ(崇母除外) 332 | [/(?<=[ꡆꡅꡄꡮꡚꡔꡇ])ꡃ/, 'ꡜꡃ'], // aŋ -> ħaŋ 333 | [/(?<=[ꡆꡅꡄꡮꡚꡔꡇ])ꡦꡃ/, 'ꡃ'], // jaŋ -> aŋ 334 | [/(?<=[ꡔ])ꡟꡃ/, use創新 ? 'ꡦꡟꡃ' : 'ꡟꡃ'], // (ɻ)uŋ -> juŋ 335 | 336 | // ħ、ɣ、ʁ 後調整洪細 337 | [/(?<=^[ꡜꡯ])ꡞ(?!$)/, 'ꡦꡞ'], // (ħ|ɣ)i(C) -> ji 338 | [/(?<=^[ꡜꡣ])ꡜ/, ''], // (ħ|ʁ)ħ -> 刪除 339 | [/(?<=^[ꡜ])ꡦ(?=ꡋ)/, use創新 ? 'ꡠ' : 'ꡦ'], // (ħ)je(n) -> e 340 | [/(?<=^[ꡜꡯ])ꡦ(?=ꡋ|ꡏ)/, 'ꡦꡠ'], // (ħ|ɣ)je(n|m) -> jee 341 | [/(?<=^[ꡜꡯ])ꡠ(?=ꡋ|ꡏ)/, 'ꡦ'], // (ħ|ɣ)e(n|m) -> je 342 | [/(?<=^[ꡜ])ꡦ(?=ꡏ)/, use創新 ? 'ꡦꡠ' : 'ꡦ'], // (ħ)je(m) -> jee 343 | 344 | // 三四等的不規則分佈 345 | [/(? e 346 | [/(? e 347 | [/(?<=[ꡗ])ꡠ(?!ꡟ|ꡃ|ꡡ)/, 'ꡦ'], // (ʔ͡j)e -> je 348 | [/(?<=[ꡖꡭꡃ])ꡦ(?!ꡟ|ꡃ|ꡡ)/, 'ꡠ'], // (ʔ|j|ŋ)je -> e 349 | [/(?<=[ꡖ])ꡠ(?=$|ꡋ)/, use創新 ? 'ꡦ' : 'ꡠ'], // (ʔ)e(0|n) -> je 350 | [/(?<=[ꡍꡐ])ꡠ(?=ꡓ|ꡏ)/, use創新 ? 'ꡦ' : 'ꡠ'], // (pʰ|dz)e(w|m) -> je 351 | [/(?<=[ꡏꡔ])ꡦ(?=ꡋ)/, use創新 ? 'ꡠ' : 'ꡦ'], // (m|ɻ)je(n) -> e 352 | [/(?<=[ꡙ])ꡦ(?=ꡓ)/, use創新 ? 'ꡠ' : 'ꡦ'], // (l)je(w) -> e 353 | 354 | // 韻母特殊拼寫 355 | [/(?<=[ꡗꡭ])ꡦꡟꡠ/, 'ꡧꡞ'], // (ʔ͡j|j)jue -> wi 356 | [/(?<=[ꡖꡝ])ꡦꡟꡋ/, 'ꡧꡞꡋ'], // (ʔ|ɦ)jun -> win 357 | [/(?<=[ꡖ])ꡧꡦ$/, use創新 ? 'ꡧꡠ' : 'ꡧꡦ'], // (ʔ)jwe -> we。《通考》噦(《蒙古字韻》無字) 358 | [/(?<=[ꡀꡜꡣꡖꡝ])ꡦꡡꡋ/, 'ꡧꡦꡋ'], // (ɡ|ħ|ʁ|ʔ|ɦ)jon -> wjen 359 | [/ꡦꡦꡟꡃ/, 'ꡦꡟꡃ' + (選項.兩字母韻拼寫相同時加後綴區分 ? 字母韻後綴 : '')], 360 | [/(?<=ꡯ)ꡦꡞꡃ/, 'ꡦꡞꡃ' + (選項.兩字母韻拼寫相同時加後綴區分 ? 字母韻後綴 : '')], 361 | [/ꡟꡠ₂/, 'ꡦꡟꡠ' + (選項.兩字母韻拼寫相同時加後綴區分 ? 字母韻後綴 : '')], 362 | [/(?<=ꡯ)ꡦꡠꡏ/, 'ꡦꡠꡏ' + (選項.兩字母韻拼寫相同時加後綴區分 ? 字母韻後綴 : '')], 363 | 364 | // 聲母特殊拼寫 365 | [/ꡗ(?=ꡧꡞ|ꡧꡦ$)/, use創新 ? 'ꡖ' : 'ꡗ'], // 恚、抉 ʔ͡j > ʔ。《通考》也如此 366 | [/ꡝꡧ?(?=ꡟ(?!ꡠ)|ꡡ)/, ''], // 主要元音是 u、o 時,省略 w 母(ue 中的 u 不被視爲主要元音) 367 | 368 | [/ꡦ+/, 'ꡦ'], // 不use創新時的潛在情況 369 | ]; 370 | const 古代對立未合併列表 = [ 371 | [/ꡋ(?=ꡞ$|ꡟꡠ|ꡠꡏ)/, 'ꡇ', '孃母'], // 泥捼鮎 n > 尼諉黏 ɲ 372 | [/(?<=[ꡭ])ꡠ$/, 'ꡦ', '疑母'], // 謁 je > 齧 jje 373 | [/(?<=[ꡜ])ꡦꡟꡃ/, 'ꡧꡞꡃ', '梗攝 B類 平聲'], // 兄 ħwiŋ > 胷 ħjuŋ 374 | [/(?<=[ꡖ])ꡟꡃ/, 'ꡧꡟꡃ', '梗攝'], // 翁 ʔuŋ > 泓 ʔwuŋ 375 | [/(?<=[ꡆꡅ])ꡦꡋ/, 'ꡠꡋ', '知組 平去聲'], // 饘 tʂjen > 邅 tʂen 376 | [/(?<=[ꡅ])ꡠꡓ/, 'ꡦꡓ', '徹母 開口'], // 弨 tʂʰew > 超 tʂʰjew 377 | ]; 378 | 379 | let 聲母 = ''; 380 | Object.keys(聲母字典).forEach(e => { if (擬音.startsWith(e) && e.length > 聲母.length) 聲母 = e; }); 381 | 擬音 = 擬音.replace(聲母, 聲母字典[聲母]); 382 | 後處理替換列表.forEach(e => 擬音 = 擬音.replace(...e)); 383 | if (use創新) 古代對立未合併列表.forEach(e => { if (is(e[2])) 擬音 = 擬音.replace(e[0], e[1]); }); 384 | return 擬音; 385 | } 386 | 387 | function 八思巴字to轉寫(str) { 388 | const 八思巴字轉寫列表 = { 389 | '八思巴字': [ 390 | 'ꡂ', 'ꡁ', 'ꡀ', 'ꡃ', 391 | 'ꡊ', 'ꡉ', 'ꡈ', 'ꡋ', 392 | 'ꡆ', 'ꡅ', 'ꡄ', 'ꡇ', 393 | 'ꡎ', 'ꡍ', 'ꡌ', 'ꡏ', 'ꡰ', 'ꡤ', 'ꡓ', 394 | 'ꡒ', 'ꡑ', 'ꡐ', 'ꡛ', 'ꡕ', 'ꡮ', 'ꡚ', 395 | 'ꡜ', 'ꡣ', 'ꡯ', 'ꡖ', 'ꡗ', 'ꡝ', 'ꡭ', 'ꡙ', 'ꡔ', 396 | 'ꡞ', 'ꡟ', 'ꡠ', 'ꡡ', 'ꡦ', 'ꡧ', 'ꡨ', 397 | ], 398 | '照那斯圖 1987 轉寫 ⭐': [ 399 | 'g', 'kʻ', 'k', 'ŋ', 400 | 'd', 'tʻ', 't', 'n', 401 | 'dž', 'tšʻ', 'tš', 'ň', 402 | 'b', 'pʻ', 'p', 'm', 'hu̯', 'ħu̯', 'w', 403 | 'dz', 'tsʻ', 'ts', 's', 'z', 'š₂', 'š₁', 404 | 'h', 'ɣ', 'ħ', 'ꞏ', 'j̊', 'ʼ', 'j', 'l', 'ž', 405 | 'i', 'u', 'e', 'o', 'ė', 'u̯', 'i̯', 406 | ], 407 | '吉池孝一 2005 轉寫': [ 408 | 'g', 'kʻ', 'k', 'ŋ', 409 | 'd', 'tʻ', 't', 'n', 410 | 'ǰ', 'čʻ', 'č', 'ň', 411 | 'b', 'pʻ', 'p', 'm', 'f2', 'f1', 'v', 412 | 'j', 'cʻ', 'c', 's', 'z', 'š2', 'š1', 413 | 'h2', 'γ', 'h1', 'ꞏ', 'y2', 'ʼ', 'y1', 'l', 'ž', 414 | 'i', 'u', 'ė', 'o', 'e', 'ŭ', 'ĭ', 415 | ], 416 | 'Coblin 2007 轉寫': [ 417 | 'g', 'kh', 'k', 'ng', 418 | 'd', 'th', 't', 'n', 419 | 'j', 'ch', 'c', 'ñ', 420 | 'b', 'ph', 'p', 'm', 'Hw', 'hw', 'w', 421 | 'dz', 'tsh', 'ts', 's', 'z', 'sh', 'zh', 422 | 'ʰ', 'X', 'H', '\'', 'Y', 'x', 'y', 'l', 'Zh', 423 | 'i', 'u', 'e', 'o', 'ÿ', 'w', 'y', 424 | ], 425 | 'Coblin 2007 擬音': [ 426 | 'k', 'kʼ', 'ɡ', 'ŋ', 427 | 't', 'tʼ', 'd', 'n', 428 | 'tʂ', 'tʂʼ', 'dʐ', 'ȵ', 429 | 'p', 'pʼ', 'b', 'm', 'f', 'v', 'ʋ', 430 | 'ts', 'tsʼ', 'dz', 's', 'z', 'ʂ', 'ʐ', 431 | 'ʰ', 'ɣ', 'ɣj', 'ʔ', 'ʔj', 'ɦ', 'j', 'l', 'r', 432 | 'i', 'u', 'ɛ', 'ɔ', 'ÿ', 'w', 'j', 433 | ], 434 | '沈鐘偉 2008/2015 轉寫兼擬音': [ 435 | 'k', 'kʰ', 'ɡ', 'ŋ', 436 | 't', 'tʰ', 'd', 'n', 437 | 'tʃ', 'tʃʰ', 'dʒ', 'ɲ', 438 | 'p', 'pʰ', 'b', 'm', 'f', 'v', 'ʋ', 439 | 'ts', 'tsʰ', 'dz', 's', 'z', 'ʃ', 'ʒ', 440 | 'h', 'ɦ', 'ɦj', '0', 'j', '0̲', 'j̲', 'l', 'r', 441 | 'i', 'u', 'e', 'o', 'ɛ', 'w', 'j', 442 | ], 443 | 'unt 2023 轉寫 ⭐': [ 444 | 'k', 'kʰ', 'ɡ', 'ŋ', 445 | 't', 'tʰ', 'd', 'n', 446 | 'tʂ', 'tʂʰ', 'dʐ', 'ɲ', 447 | 'p', 'pʰ', 'b', 'm', 'f', 'v', 'w', 448 | 'ts', 'tsʰ', 'dz', 's', 'z', 'ʂ', 'ʐ', 449 | 'ħ', 'ʁ', 'ɣ', 'ʔ', 'ʔ͡j', 'ɦ', 'j', 'l', 'ɻ', 450 | 'i', 'u', 'e', 'o', 'jE', 'w', 'j', 451 | ], 452 | }; 453 | 454 | const 後處理替換列表字典 = { 455 | '照那斯圖 1987 轉寫 ⭐': [['ėa', 'ė'], ['bꞏo', 'boꞏo']].concat( 456 | 選項['ꡠ、ꡦ 的轉寫'] === 'ꡠė ꡦe(八思巴字蒙古語風格)' ? [['ė', 'E'], ['e', 'ė'], ['E', 'e'],] : [] 457 | ), 458 | '吉池孝一 2005 轉寫': [ 459 | [/(?<=[fšhy])1/g, 選項.聲母附加數字?.split(' ')[0]], 460 | [/(?<=[fšhy])2/g, 選項.聲母附加數字?.split(' ')[1]], 461 | ], 462 | 'Coblin 2007 轉寫': [[/^ʰ/, 'h'], ["b'o", "ba'o"]], 463 | 'Coblin 2007 擬音': [ 464 | [/ʋ$/, 'w'], 465 | [/^ʰ/, 'x'], ['ʰa', 'A'], ['ʰi', 'ə'], [/(?<=s|z)ə$/, 'ɿ'], [/ə$/, 'ʅ'], 466 | ['ÿaŋ', 'jaŋ'], ['ÿa', 'jɛ'], ['ÿ', 'j'], ['jj', 'j'], 467 | ['juŋ', 'yuŋ'], ['ju', 'y'], ['jɔ', 'yɔ'], [/jwj|jw|wj/, 'y'], 468 | [/^y/, 'jy'], 469 | ['uɛ', 'uɛ̌'], 470 | ], 471 | '沈鐘偉 2008/2015 轉寫兼擬音': [ 472 | [/ɦj(?=w?ɛ)/, 'ɦ'], 473 | ['ɛi', 'ji'], ['ɛe', 'je'], 474 | ['ɛu', 'y'], ['ɛo', 'ø'], ['ɛa', 'ɛ'], 475 | [/(? (str.includes(元音)))) { 488 | if (str.length > 1 && 'ꡏꡋꡃꡭꡓ'.includes(str.slice(-1))) { 489 | // 有韻尾則 a 補在韻尾前 490 | str = str.slice(0, -1) + 'a' + str.slice(-1); 491 | } else { 492 | // 否則 a 補在最後 493 | str += 'a'; 494 | } 495 | } 496 | const 八思巴字轉寫字典 = Object.fromEntries( 497 | 八思巴字轉寫列表.八思巴字.map((e, i) => [e, 八思巴字轉寫列表[選項.顯示][i]]) 498 | ); 499 | str = [...str].map(e => 八思巴字轉寫字典[e] ?? e).join(''); 500 | 後處理替換列表字典[選項.顯示].forEach(pair => str = str.replace(...pair)); 501 | return str; 502 | } 503 | 504 | const 擬音 = get擬音(); 505 | if (選項.顯示 === 'unt 2023 擬音 ⭐') return 擬音; 506 | const 八思巴字 = 擬音to八思巴字(擬音); 507 | return 選項.顯示 === '八思巴字' ? 八思巴字 : 八思巴字to轉寫(八思巴字); 508 | --------------------------------------------------------------------------------