"use strict"; const assign = require("object-assign"); // XXX: Symbol when web support. const PINYIN_STYLE = { NORMAL: 0, // 普通风格,不带声调。 TONE: 1, // 标准风格,声调在韵母的第一个字母上。 TONE2: 2, // 声调以数字形式在拼音之后,使用数字 0~4 标识。 TO3NE: 5, // 声调以数字形式在声母之后,使用数字 0~4 标识。 INITIALS: 3, // 仅需要声母部分。 FIRST_LETTER: 4, // 仅保留首字母。 }; const DEFAULT_OPTIONS = { style: PINYIN_STYLE.TONE, // 风格 segment: false, // 分词。 heteronym: false, // 多音字 }; // 声母表。 const INITIALS = "b,p,m,f,d,t,n,l,g,k,h,j,q,x,r,zh,ch,sh,z,c,s".split(","); // 韵母表。 //const FINALS = "ang,eng,ing,ong,an,en,in,un,er,ai,ei,ui,ao,ou,iu,ie,ve,a,o,e,i,u,v".split(","); // 带声调字符。 const PHONETIC_SYMBOL = require("./phonetic-symbol"); const RE_PHONETIC_SYMBOL = new RegExp("([" + Object.keys(PHONETIC_SYMBOL).join("") + "])", "g"); const RE_TONE2 = /([aeoiuvnm])([0-4])$/; /* * 格式化拼音为声母(Initials)形式。 * @param {String} * @return {String} */ function initials(pinyin) { for (let i = 0, l = INITIALS.length; i < l; i++) { if (pinyin.indexOf(INITIALS[i]) === 0) { return INITIALS[i]; } } return ""; } class Pinyin { constructor(dict) { this._dict = dict; } // @param {String} hans 要转为拼音的目标字符串(汉字)。 // @param {Object} options, 可选,用于指定拼音风格,是否启用多音字。 // @return {Array} 返回的拼音列表。 convert(hans, options) { if (typeof hans !== "string") { return []; } options = assign({}, DEFAULT_OPTIONS, options); let pys = []; let nohans = ""; for (let i = 0, firstCharCode, words, l = hans.length; i < l; i++) { words = hans[i]; firstCharCode = words.charCodeAt(0); if (this._dict[firstCharCode]) { // ends of non-chinese words. if (nohans.length > 0) { pys.push([nohans]); nohans = ""; // reset non-chinese words. } pys.push(this.single_pinyin(words, options)); } else { nohans += words; } } // 清理最后的非中文字符串。 if (nohans.length > 0) { pys.push([nohans]); nohans = ""; // reset non-chinese words. } return pys; } // 单字拼音转换。 // @param {String} han, 单个汉字 // @return {Array} 返回拼音列表,多音字会有多个拼音项。 single_pinyin(han, options) { if (typeof han !== "string") { return []; } if (han.length !== 1) { return this.single_pinyin(han.charAt(0), options); } let hanCode = han.charCodeAt(0); if (!this._dict[hanCode]) { return [han]; } let pys = this._dict[hanCode].split(","); if (!options.heteronym) { return [Pinyin.toFixed(pys[0], options.style)]; } // 临时存储已存在的拼音,避免多音字拼音转换为非注音风格出现重复。 let py_cached = {}; let pinyins = []; for (let i = 0, py, l = pys.length; i < l; i++) { py = Pinyin.toFixed(pys[i], options.style); if (py_cached.hasOwnProperty(py)) { continue; } py_cached[py] = py; pinyins.push(py); } return pinyins; } /** * 格式化拼音风格。 * * @param {String} pinyin TONE 风格的拼音。 * @param {ENUM} style 目标转换的拼音风格。 * @return {String} 转换后的拼音。 */ static toFixed(pinyin, style) { let tone = ""; // 声调。 let first_letter; let py; switch (style) { case PINYIN_STYLE.INITIALS: return initials(pinyin); case PINYIN_STYLE.FIRST_LETTER: first_letter = pinyin.charAt(0); if (PHONETIC_SYMBOL.hasOwnProperty(first_letter)) { first_letter = PHONETIC_SYMBOL[first_letter].charAt(0); } return first_letter; case PINYIN_STYLE.NORMAL: return pinyin.replace(RE_PHONETIC_SYMBOL, function ($0, $1_phonetic) { return PHONETIC_SYMBOL[$1_phonetic].replace(RE_TONE2, "$1"); }); case PINYIN_STYLE.TO3NE: return pinyin.replace(RE_PHONETIC_SYMBOL, function ($0, $1_phonetic) { return PHONETIC_SYMBOL[$1_phonetic]; }); case PINYIN_STYLE.TONE2: py = pinyin.replace(RE_PHONETIC_SYMBOL, function ($0, $1) { // 声调数值。 tone = PHONETIC_SYMBOL[$1].replace(RE_TONE2, "$2"); return PHONETIC_SYMBOL[$1].replace(RE_TONE2, "$1"); }); return py + tone; case PINYIN_STYLE.TONE: default: return pinyin; } } /** * 比较两个汉字转成拼音后的排序顺序,可以用作默认的拼音排序算法。 * * @param {String} hanA 汉字字符串 A。 * @return {String} hanB 汉字字符串 B。 * @return {Number} 返回 -1,0,或 1。 */ compare(hanA, hanB) { const pinyinA = this.convert(hanA, DEFAULT_OPTIONS); const pinyinB = this.convert(hanB, DEFAULT_OPTIONS); return String(pinyinA).localeCompare(String(pinyinB)); } static get STYLE_NORMAL() { return PINYIN_STYLE.NORMAL; } static get STYLE_TONE() { return PINYIN_STYLE.TONE; } static get STYLE_TONE2() { return PINYIN_STYLE.TONE2; } static get STYLE_TO3NE() { return PINYIN_STYLE.TO3NE; } static get STYLE_INITIALS() { return PINYIN_STYLE.INITIALS; } static get STYLE_FIRST_LETTER() { return PINYIN_STYLE.FIRST_LETTER; } static get DEFAULT_OPTIONS() { return DEFAULT_OPTIONS; } } module.exports = Pinyin;