/* ベトナム語の音節を分析してHTML表示するオブジェクト @author : id:TagengoGakushuu 2016.03. GPL License */ // 配列の便利メソッド if( ( ! Array.prototype.map ) || ( ! Array.prototype.includes ) || ( ! Array.prototype.flatten ) ){ Array.prototype.map = function(func){ var _ret = []; this.each(function(item, ind){ _ret.push( func( item, ind ) ); }); return _ret; }; Array.prototype.each = function(func){ for( var i = 0; i < this.length; i ++ ){ func( this[ i ], i ); }; }; Array.prototype.includes = function(val){ var ret = false; this.each(function(item){ ret = ret || ( item == val ); }); return ret; }; Array.prototype.flatten = function(){ var ret = []; // 1階層だけ,ならす this.each(function(item){ ret = ret.concat( item ); }); //log( "flatten -> " + ret ); return ret; }; Array.prototype.outer_product = function( arr2 ){ // 2つの配列を掛け合わせて,全ての組み合わせを作り出す。(外積) // 文字列として組み合わせを生成する。 return this.map(function(item1){ return arr2.map(function(item2){ return item1 + item2; }); }).flatten(); }; } // ベトナム語の定数などを格納した便利オブジェクト var VtUtil = { // このオブジェクトを使用する前に必要な初期化 init : function(){ // 韻母が介母音+単母音であるようなパターン // 介母音または単母音に声調符号が付く this.arr_kaiboin_patterns = ( VtUtil.arr_sharp_u.flatten().outer_product( VtUtil.arr_aimai_a.flatten() ) .concat( VtUtil.arr_sharp_u.flatten().outer_product( VtUtil.arr_vwl_y.flatten() ) ) .concat( VtUtil.arr_sharp_u.flatten().outer_product( VtUtil.arr_narrow_e.flatten() ) ) .concat( VtUtil.arr_sharp_u.flatten().outer_product( VtUtil.arr_long_aimai_o.flatten() ) ) .concat( VtUtil.arr_wide_o.flatten().outer_product( VtUtil.arr_long_a.flatten() ) ) .concat( VtUtil.arr_wide_o.flatten().outer_product( VtUtil.arr_short_a.flatten() ) ) .concat( VtUtil.arr_wide_o.flatten().outer_product( VtUtil.arr_wide_e.flatten() ) ) ); //log( this.arr_kaiboin_patterns ) // 複合母音のパターン // 1文字目または2文字目に声調符号が付く this.arr_fukugouboin_patterns = ( VtUtil.arr_vwl_i.flatten().outer_product( VtUtil.arr_long_a.flatten() ) .concat( VtUtil.arr_vwl_i.flatten().outer_product( VtUtil.arr_narrow_e.flatten() ) ) .concat( VtUtil.arr_vwl_y.flatten().outer_product( VtUtil.arr_narrow_e.flatten() ) ) .concat( VtUtil.arr_sharp_u.flatten().outer_product( VtUtil.arr_long_a.flatten() ) ) .concat( VtUtil.arr_sharp_u.flatten().outer_product( VtUtil.arr_sharp_o.flatten() ) ) .concat( VtUtil.arr_vwl_wi.flatten().outer_product( VtUtil.arr_long_a.flatten() ) ) .concat( VtUtil.arr_vwl_wi.flatten().outer_product( VtUtil.arr_long_aimai_o.flatten() ) ) ); // 単母音のパターン this.arr_tanboin_patterns = ( VtUtil.arr_long_a.flatten() .concat( VtUtil.arr_short_a.flatten() ) .concat( VtUtil.arr_aimai_a.flatten() ) .concat( VtUtil.arr_vwl_i.flatten() ) .concat( VtUtil.arr_vwl_y.flatten() ) .concat( VtUtil.arr_sharp_u.flatten() ) .concat( VtUtil.arr_vwl_wi.flatten() ) .concat( VtUtil.arr_wide_e.flatten() ) .concat( VtUtil.arr_narrow_e.flatten() ) .concat( VtUtil.arr_wide_o.flatten() ) .concat( VtUtil.arr_sharp_o.flatten() ) .concat( VtUtil.arr_long_aimai_o.flatten() ) ); // giをジーと読むパターン,全一致 this.arr_gi_entire_match_patterns = ["g", "G"] .outer_product( VtUtil.arr_vwl_i.flatten() ) .outer_product( [ "", "n" ] ) ; // giをジーと読むパターン,語頭一致 this.arr_gi_head_match_patterns = ["g", "G"] .outer_product( VtUtil.arr_vwl_i.flatten() ) .outer_product( VtUtil.arr_narrow_e.flatten() ) ; }, // 頭子音, 3文字のもの arr_top_consonants_3c : [ "ngh", "Ngh" ], // 頭子音, 3文字のもの,対応する発音記号 arr_top_consonants_3c_sound : [ "ŋ" ], // 頭子音, 3文字のもの,対応するカナガイド arr_top_consonants_3c_sound_kana : [ "ング" ], // 頭子音,2文字のもの arr_top_consonants_2c : [ "ch", "gh", "gi", "kh", "ng", "nh", "ph", "th", "tr", "Ch", "Gh", "Gi", "Kh", "Ng", "Nh", "Ph", "Th", "Tr" ], // 頭子音,2文字のもの, 対応する発音記号 arr_top_consonants_2c_sound : [ "tʃ", "ɣ", "z", "x", "ŋ", "ɲ", "f", "tʰ", "tʃ" ], // 頭子音,2文字のもの, 対応するカナガイド arr_top_consonants_2c_sound_kana : [ "チャ行", "ガ行,息を激しく,g=gh", "★ザ行,giで一文字,の頭子音", "カ行,息を激しく", "ング", "★ニャ行", "ファ行", "タ行,息を激しく", "★チャ行" ], // 頭子音,1文字のもの arr_top_consonants_1c : [ "b", "c", "d", "đ", "g", "h", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "x", "B", "C", "D", "Đ", "G", "H", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "V", "X" ], // 頭子音,1文字のもの, 対応する発音記号 arr_top_consonants_1c_sound : [ "b", "k", "z", "ɗ", "ɣ", "h", "k", "l", "m", "n", "p", "k", "z", "s", "t", "v", "s" ], // 頭子音,1文字のもの, 対応するカナガイド arr_top_consonants_1c_sound_kana : [ "バ行", "カ行,c=k", "★ザ行", "ダ行", "ガ行,息を激しく,g=gh", "ハ行", "カ行,c=k", "ラ行,舌先は歯茎", "マ行", "ナ行", "パ行", "クヮ行", "★ザ行", "サ行,s=x", "タ行", "ヴァ行", "サ行,s=x" ], // 頭子音がgiで始まりジーと読む場合の発音記号 special_top_consonant_g_of_gi_sound : "z", // 頭子音がgiで始まりジーと読む場合。カナガイド special_top_consonant_g_of_gi_sound_kana : "★ザ行,本来はgiで,一文字の頭子音", // 母音,声調順 // 長いア arr_long_a : [ [ "a", "à", "á", "ả", "ã", "ạ" ], [ "A", "À", "Á", "Ả", "Ã", "Ạ" ] ], // 短いア arr_short_a : [ [ "ă", "ằ", "ắ", "ẳ", "ẵ", "ặ" ], [ "Ă", "Ằ", "Ắ", "Ẳ", "Ẵ", "Ặ" ] ], // 曖昧ウ arr_aimai_a : [ [ "â", "ầ", "ấ", "ẩ", "ẫ", "ậ" ], [ "Â", "Ầ", "Ấ", "Ẩ", "Ẫ", "Ậ" ] ], // Iのイ arr_vwl_i : [ [ "i", "ì", "í", "ỉ", "ĩ", "ị" ], [ "I", "Ì", "Í", "Ỉ", "Ĩ", "Ị" ] ], // Yのイ arr_vwl_y : [ [ "y", "ỳ", "ý", "ỷ", "ỹ", "ỵ" ], [ "Y", "Ỳ", "Ý", "Ỷ", "Ỹ", "Ỵ" ] ], // 突き出すウ arr_sharp_u : [ [ "u", "ù", "ú", "ủ", "ũ", "ụ" ], [ "U", "Ù", "Ú", "Ủ", "Ũ", "Ụ" ] ], // ウィ arr_vwl_wi : [ [ "ư", "ừ", "ứ", "ử", "ữ", "ự" ], [ "Ư", "Ừ", "Ứ", "Ử", "Ữ", "Ự" ] ], // 大きく開くエ arr_wide_e : [ [ "e", "è", "é", "ẻ", "ẽ", "ẹ" ], [ "E", "È", "É", "Ẻ", "Ẽ", "Ẹ" ] ], // イに近いエ arr_narrow_e : [ [ "ê", "ề", "ế", "ể", "ễ", "ệ" ], [ "Ê", "Ề", "Ế", "Ể", "Ễ", "Ệ" ] ], // 大きく開くオ arr_wide_o : [ [ "o", "ò", "ó", "ỏ", "õ", "ọ" ], [ "O", "Ò", "Ó", "Ỏ", "Õ", "Ọ" ] ], // 突き出すオ arr_sharp_o : [ [ "ô", "ồ", "ố", "ổ", "ỗ", "ộ" ], [ "Ô", "Ồ", "Ố", "Ổ", "Ỗ", "Ộ" ] ], // 長い曖昧ウ arr_long_aimai_o : [ [ "ơ", "ờ", "ớ", "ở", "ỡ", "ợ" ], [ "Ơ", "Ờ", "Ớ", "Ở", "Ỡ", "Ợ" ] ], // 末子音のパターン arr_matsushiin : [ "p", "m", "t", "c", "ch", "n", "nh", "ng", "o", "u", "i", "y" ], // 末子音のパターン, 発音記号 arr_matsushiin_sound_kigou : [ "p̚", "m", "t̚", "k̚", "(j)k", "n", "(ʲ)ɲ", "ŋ", "w", "w", "j", "j" ], // 末子音のパターン,カナガイド arr_matsushiin_sound_kana : [ "ップ,破裂しない", "ム", "ッ(ト),破裂しない", "ック,破裂しない", "★(ィ)ック,唇を横に引く", "ン(ヌ)", "★ィン", "ング", "ゥ,軽い音,ウとオの中間", "ゥ,軽い音,ウとオの中間", "ィ,軽い音", "ィ,軽い音" ], // 複合母音のパターンの声調なし版 arr_fukugouboin_patterns_without_tone : [ "ia", "iê", "yê", "ua", "uô", "ưa", "ươ" ], // 複合母音のパターン, 発音記号 arr_fukugouboin_patterns_without_tone_sound_kigou : [ "iə", "iə", "iə", "uə", "uə", "ɯə", "ɯə" ], // 複合母音のパターン, カナガイド arr_fukugouboin_patterns_without_tone_sound_kana : [ "イーァ,ァは曖昧", "★イーァ,ァは曖昧", "★イーァ,ァは曖昧", "ウーァ,ウは突き出す,ァは曖昧", "ウーァ,ウは突き出す,ァは曖昧", "ウァ,ウは唇を横に,ァは曖昧", "★ウァ,ウは唇を横に,ァは曖昧" ], // 単母音のパターンの声調なし版 arr_tanboin_patterns_without_tone : [ "a", "ă", "â", "i", "y", "u", "ư", "e", "ê", "o", "ô", "ơ" ], // 単母音のパターンの声調なし版, 発音記号 arr_tanboin_patterns_without_tone_sound_kigou : [ "αː", "α", "ə", "i", "iː", "u", "ɯ", "ɛː", "e", "ɔ", "o", "əː" ], // 単母音のパターンの声調なし版, カナガイド arr_tanboin_patterns_without_tone_sound_kana : [ "アー,大きく開く,長く伸ばす", "ア,大きく開く,短く", "★ァ,あいまい,アウオの中間", "イ,唇を横に", "イー,唇を横に", "ウ,唇を突き出す", "★ウィ,唇を横に", "エー,大きく開く,長く伸ばす", "エ,口は狭く", "オー,大きく開く", "★オ,唇を突き出す", "★ァー,あいまい,アウオの中間" ], // 単母音のパターンの声調なし版,文字の名称 arr_tanboin_patterns_without_tone_name : [ "普通の a", "ブレーヴェ付き a", "ハット付き a", "普通の i", "普通の y", "普通の u", "ホーン付き u", "普通の e", "ハット付き e", "普通の o", "ハット付き o", "ホーン付き o" ], // giをジーと読む場合の主母音 special_shuboin_i_of_gi_sound_kigou : "iː", // giをジーと読む場合の主母音, カナガイド special_shuboin_i_of_gi_sound_kana : "イー,唇を横に", // giをジーと読む場合の主母音,文字の名称 special_shuboin_i_of_gi_sound_name : "i (※giで一文字)", // 声調符号マーク arr_tone_marks : [ "なし", "`", "´", " ̉", "˜", "  ̣" ], // 声調のガイド説明 arr_tone_exps : [ "高いまま平ら", "低く伸ばす", "上に上がる", "低い波", "上がる波", "低い重み" ], // 声調の図示データ arr_tone_charts : [ [ "――→", "   ", "   ", "   ", "   " ], [ "   ", "   ", "   ", "―― ", "  \" ], [ "  /", " / ", "/  ", "   ", "   " ], [ "   ", "   ", "   ", "\ /", " V " ], [ "  /", " × ", "/  ", "   ", "   " ], [ "   ", "   ", "   ", "\  ", " × " ] ], // 声調の図示データ,簡易版(MSゴシック用) arr_tone_charts_simple : [ [ "――→", "   ", "   " ], [ "   ", "   ", "――→" ], [ " / ", "/  ", "   " ], [ "   ", "   ", "\_/" ], [ " ×→", "/  ", "   " ], [ "   ", "   ", "↓  " ] ], // ----- 文字データの定義おわり ----- // ----- 以下,処理内容 ----- // 文章を受け取って,音節の配列HTMLにする split_into_wd_arr_html : function( obj ){ // 文字列および表示オプション var vt_sent = obj.vt_sent; var disp_style_detail_flag = obj.disp_style_detail; var disp_style_force_break_num = obj.disp_style_force_break_num; // 生の文章をサニタイズして,音節の配列に変換 var arr_syls = this.split_sent_into_syls( vt_sent ); // 各音節を分析して情報を付与 var _this = this; arr_syls = arr_syls.map(function(syl_info){ return _this.analyze_syl_info( syl_info ); }); // 各音節の情報をHTML化 var result_html = this.create_entire_html_from_all_syls({ arr_syls : arr_syls, disp_style_detail_flag : disp_style_detail_flag, disp_style_force_break_num : disp_style_force_break_num }); return result_html; // + "hoge" + vt_sent; }, // 生の文章をサニタイズして,音節の配列に変換 split_sent_into_syls : function( vt_sent ){ var ret = []; // 前処理 // 改行コードを統一 vt_sent = vt_sent.replace( /\r\n/g, "\n" ); // 1文字ずつカーソルをずらしながら, // ベトナム語の正規のアルファベットかどうかを判定しつつ // 単語のまとまりを保管してゆく。 var ch; var syl = null; var state_in_syl_flag = false; // いま音節の中にいるフラグ for( var cur_pos = 0; cur_pos < vt_sent.length; cur_pos ++ ){ // この位置の文字を取得 ch = vt_sent.charAt( cur_pos ); // ベトナム語の正規のアルファベットか? if( VtUtil.is_regular_alphabet( ch ) ){ // 音節はすでに始まっているか? if( state_in_syl_flag ){ syl += ch; } else { // 音節ではない部分から移動して,新しく音節が始まった場合 state_in_syl_flag = true; // 蓄積済みのデータがあれば保管 if( syl ){ ret.push({ str : syl, valid_sound : true }); } // 新しい音節の始まり syl = ch; } }else{ // 音節はすでに始まっているか? if( state_in_syl_flag ){ // 蓄積済みのデータを保管 ret.push({ str : syl, valid_sound : true }); // もう音節ではない state_in_syl_flag = false; syl = null; } // 新しい文字は,空白などの「分割文字」か? if( [ " ", " ", "\t" ].includes( ch ) ){ // スキップ } else // 新しい文字は,改行か? if( "\n" == ch ){ //log("改行文字を発見"); // 改行として情報を保管 ret.push({ str : ch, valid_sound : false, is_breakline : true }); } else { // ピリオドなどの句読点は保管しておく ret.push({ str : ch, valid_sound : false }); } } } // ループ終了時点で,蓄積済みのデータがあれば保管 if( syl ){ ret.push({ str : syl, valid_sound : true }); } return ret; //return [1,2,3]; }, // ベトナム語の正規のアルファベットか? is_regular_alphabet : function( ch ){ return ( // この1文字は母音文字か this.is_one_vowel_char( ch ) || // この1文字は子音文字か this.is_one_consonant_char( ch ) ); }, // ある1文字は母音文字か is_one_vowel_char : function( ch ){ var all_vowels = [ this.arr_long_a, this.arr_short_a, this.arr_aimai_a, this.arr_vwl_i, this.arr_vwl_y, this.arr_sharp_u, this.arr_vwl_wi, this.arr_wide_e, this.arr_narrow_e, this.arr_wide_o, this.arr_sharp_o, this.arr_long_aimai_o ]; return all_vowels.map(function(arr_vwl){ return ( // 小文字 arr_vwl[0].includes(ch) || // 大文字 arr_vwl[1].includes(ch) ); }).includes(true); }, // ある1文字は子音文字か is_one_consonant_char : function( ch ){ return ( // 1文字の頭子音か? this.arr_top_consonants_1c.includes( ch ) // 2文字以上の頭子音は検査不要 ///|| // 末子音か? // >母音または1文字の頭子音で網羅し尽くされている ); }, // 1つの音節を分析して情報を付与 analyze_syl_info : function( syl_info ){ // 音節構造を分解 // 下記のアルゴリズムに従う。 // // ベトナム語の一音節を解析し,母音と子音の構造を抽出するアルゴリズム(自動処理の手順ステップ) - 語学と多言語学習のメモブログ // http://tagengo-gakushuu.hatenablog.jp/entry/2016/02/26/%E3%83%99%E3%83%88%E3%83%8A%E3%83%A0%E8%AA%9E%E3%81%AE%E4%B8%80%E9%9F%B3%E7%AF%80%E3%82%92%E8%A7%A3%E6%9E%90%E3%81%97%EF%BC%8C%E6%AF%8D%E9%9F%B3%E3%81%A8%E5%AD%90%E9%9F%B3%E3%81%AE%E6%A7%8B // 一般的な記号や改行コードなど,音でない要素は除外する if( ! syl_info.valid_sound ){ return syl_info; } // 声母+韻母に分解 var seibo_inbo_arr = this.split_syllable_into_head_and_main( syl_info.str ); syl_info.seibo_str = seibo_inbo_arr[0]; // 声母を保管 syl_info.inbo_str = seibo_inbo_arr[1]; // 韻母を保管 // 韻母を分解して,介母音+主母音+末子音にする var inbo_detail_info = this.split_inbo_into_details( syl_info.inbo_str, syl_info ); // 韻母の分析に成功したか if( inbo_detail_info.split_success ){ inbo_detail_arr = inbo_detail_info.arr; syl_info.kaiboin_str = inbo_detail_arr[0]; // 介母音を保管 syl_info.shuboin_str = inbo_detail_arr[1]; // 主母音を保管 syl_info.matsushiin_str = inbo_detail_arr[2]; // 末子音を保管 // 声母と韻母に発音記号を付与 syl_info = this.add_sound_guide_to_one_syl( syl_info ); } else{ // 外来語は途中で分析に失敗する syl_info.ignore_flag = true; } return syl_info; }, // ある音節を,声母と韻母に分解して返す。 split_syllable_into_head_and_main : function( vt_wd ){ // 結果を格納 var ret = [ null, null ]; // 音節の先頭が,3文字子音にマッチ? var has_top_con_3c = this.arr_top_consonants_3c.map(function(con){ return ( vt_wd.indexOf( con ) == 0 ); }).includes( true ); if( has_top_con_3c ){ // 先頭の3文字とそれ以外で分解 ret[0] = vt_wd.substring( 0, 3 ); ret[1] = vt_wd.substring( 3, vt_wd.length ); // 終了 return ret; } // 語頭が gi の場合は特殊なケースとして個別処理 if( this.heading_gi_reads_zi( vt_wd ) ){ log( "giをジーと読むケース:" + vt_wd ); // 擬似的にiを主母音とし,ここでは子音をGiではなくGと表記する。 ret[0] = vt_wd.substring( 0, 1 ); ret[1] = vt_wd.substring( 1, vt_wd.length ); // 終了 return ret; } // 音節の先頭が,2文字子音にマッチ? var has_top_con_2c = this.arr_top_consonants_2c.map(function(con){ return ( vt_wd.indexOf( con ) == 0 ); }).includes( true ); if( has_top_con_2c ){ // 先頭の2文字とそれ以外で分解 ret[0] = vt_wd.substring( 0, 2 ); ret[1] = vt_wd.substring( 2, vt_wd.length ); // 終了 return ret; } // 音節の先頭が,1文字子音にマッチ? var has_top_con_1c = this.arr_top_consonants_1c.map(function(con){ return ( vt_wd.indexOf( con ) == 0 ); }).includes( true ); if( has_top_con_1c ){ // 先頭の1文字とそれ以外で分解 ret[0] = vt_wd.substring( 0, 1 ); ret[1] = vt_wd.substring( 1, vt_wd.length ); // 終了 return ret; } // 頭子音がない場合 ret[0] = null; ret[1] = vt_wd; return ret; }, // 先頭のgiがジーと読むパターンかどうかを判定 heading_gi_reads_zi : function( vt_wd ){ return ( // 語全体で全一致? this.arr_gi_entire_match_patterns.includes( vt_wd ) || // 語頭のみ一致? this.arr_gi_head_match_patterns .map(function(ghmp){ return ( vt_wd.indexOf( ghmp ) == 0 ); }).includes(true) ); }, // 韻母を分解して,介母音+主母音+末子音にする split_inbo_into_details : function( inbo_str, syl_info ){ // 途中で分析が失敗することもある。 // 文章が外来語を含んでいると,音節の構造が例外となるため。 // その場合は解析失敗というマークをつけて返す。 // 結果を格納 var ret = { split_success : null, // 分析成功フラグ arr : [ null, null, null ] }; // 韻母の最初の2文字が,介母音付きのパターンに該当するか? var has_kaiboin = this.arr_kaiboin_patterns.map(function(kbp){ return ( inbo_str.indexOf( kbp ) == 0 ); }).includes( true ); // もしくは,声母がqか? has_kaiboin = has_kaiboin || ( ["Q", "q"].includes( syl_info.seibo_str ) ); if( has_kaiboin ){ // 先頭の1文字を,介母音とする ret.arr[0] = inbo_str.substring( 0, 1 ); // FIXED: 介母音+二重母音というパターンでエラーになっている。 // 2~3文字目が複合母音か? var has_fukugouboin = false; var str_tail; if( inbo_str.length > 2 ){ var fukugou_test_str = inbo_str.substring( 1, 3 ); // 複合母音のパターンとマッチ has_fukugouboin = has_fukugouboin || this.arr_fukugouboin_patterns.map(function(fbp){ return ( fukugou_test_str.indexOf( fbp ) == 0 ); }).includes( true ); if( has_fukugouboin ){ // 主母音は複合母音であるとして格納 ret.arr[1] = fukugou_test_str; // 残りの文字列を格納 str_tail = inbo_str.substring( 3, inbo_str.length ); //log("介母音に続く複合母音を発見:" + ret.arr[0] + ", " + fukugou_test_str + ", " + str_tail ); } } // 複合母音ではなかった場合 if( ! has_fukugouboin ){ // 単母音を主母音として格納 ret.arr[1] = inbo_str.substring( 1, 2 ); // 残りの文字列を格納 str_tail = inbo_str.substring( 2, inbo_str.length ); } // 末子音はあるか? if( str_tail.length > 0 ){ // 残った部分は有効な末子音か? // そうでなければ外来語とみなす。 if( this.arr_matsushiin.includes( str_tail ) ){ // 末子音とみなす ret.arr[2] = str_tail; // 解析に成功して終了 ret.split_success = true; return ret; } else { // 解析に失敗して終了 ret.split_success = false; log( "介母音と主母音があるが,末子音の解析に失敗。" + inbo_str + ", " + ret.arr[0] + ", " + ret.arr[1] ); return ret; } } else { // 介母音はあるが,末子音は無い ret.arr[2] = null; // 解析に成功して終了 ret.split_success = true; return ret; } } // 介母音なし ret.arr[0] = null; // 韻母の最初の2文字が,複合母音のパターンに該当するか? var has_fukugouboin = this.arr_fukugouboin_patterns.map(function(fbp){ return ( inbo_str.indexOf( fbp ) == 0 ); }).includes( true ); if( has_fukugouboin ){ // 先頭の2文字を主母音とする ret.arr[1] = inbo_str.substring( 0, 2 ); // 末子音はあるか? var str_tail = inbo_str.substring( 2, inbo_str.length ); if( str_tail.length > 0 ){ // 残った部分は有効な末子音か? // そうでなければ外来語とみなす。 if( this.arr_matsushiin.includes( str_tail ) ){ // 末子音とみなす ret.arr[2] = str_tail; // 解析に成功して終了 ret.split_success = true; return ret; } else { // 解析に失敗して終了 ret.split_success = false; log( "介母音はなく複合母音があるが,末子音の解析に失敗。" + inbo_str ); return ret; } } else { // 複合母音としての主母音はあるが,末子音は無い ret.arr[2] = null; // 解析に成功して終了 ret.split_success = true; return ret; } } // 韻母の最初が介母音ではなく,複合母音でもない場合 // 韻母の最初の文字が,単母音のパターンに該当するか? var has_tanboin = this.arr_tanboin_patterns.map(function(tbp){ return ( inbo_str.indexOf( tbp ) == 0 ); }).includes( true ); if( has_tanboin ){ // 先頭の1文字を主母音とする ret.arr[1] = inbo_str.substring( 0, 1 ); // 末子音はあるか? var str_tail = inbo_str.substring( 1, inbo_str.length ); if( str_tail.length > 0 ){ // 残った部分は有効な末子音か? // そうでなければ外来語とみなす。 if( this.arr_matsushiin.includes( str_tail ) ){ // 末子音とみなす ret.arr[2] = str_tail; // 解析に成功して終了 ret.split_success = true; return ret; } else { // 解析に失敗して終了 ret.split_success = false; log( "単母音があるが,末子音の解析に失敗。" + inbo_str ); return ret; } } else { // 単母音としての主母音はあるが,末子音は無い ret.arr[2] = null; // 解析に成功して終了 ret.split_success = true; return ret; } } // ここまでのパターンにマッチしなかった場合 // 解析に失敗して終了 ret.split_success = false; log( "韻母が,介母音・複合母音・単母音のいずれからも始まらず,分析に失敗。" + inbo_str ); return ret; }, // 声母と韻母に発音ガイド情報を付与 add_sound_guide_to_one_syl : function( syl_info ){ // 頭子音 if( syl_info.seibo_str ){ syl_info = this.get_seibo_sound_kigou( syl_info ); } // 韻母 // 介母音 if( syl_info.kaiboin_str ){ syl_info.kaiboin_sound_kigou = "w"; syl_info.kaiboin_sound_kana = "弱いゥ"; } // 主母音 syl_info = this.get_shuboin_sound_kigou( syl_info ); // 末子音 if( syl_info.matsushiin_str ){ syl_info = this.get_matsushiin_sound_kigou( syl_info ); } // 全体をまとめた発音記号 var entire_sound_kigou = ""; if( syl_info.seibo_str ){ entire_sound_kigou += syl_info.seibo_sound_kigou; } if( syl_info.kaiboin_str ){ entire_sound_kigou += syl_info.kaiboin_sound_kigou; } entire_sound_kigou += syl_info.shuboin_sound_kigou; if( syl_info.matsushiin_str ){ entire_sound_kigou += syl_info.matsushiin_sound_kigou; } syl_info.entire_sound_kigou = entire_sound_kigou; return syl_info; }, // 声母の文字列に,発音ガイドを付与する get_seibo_sound_kigou : function( syl_info ){ // giで始まるケースは特殊なので, 頭子音の読み方のみ調整する。 if( this.heading_gi_reads_zi( syl_info.str ) ){ syl_info.seibo_sound_kigou = this.special_top_consonant_g_of_gi_sound; syl_info.seibo_sound_kana = this.special_top_consonant_g_of_gi_sound_kana; // 終了 return syl_info; } // 頭子音 var s = syl_info.seibo_str; // 発音記号の配列上でのインデックス var sound_ind; // 3文字? if( s.length == 3 ){ sound_ind = this.arr_top_consonants_3c.indexOf( s ) % ( this.arr_top_consonants_3c.length / 2 ); syl_info.seibo_sound_kigou = this.arr_top_consonants_3c_sound[ sound_ind ]; syl_info.seibo_sound_kana = this.arr_top_consonants_3c_sound_kana[ sound_ind ]; } else // 2文字? if( s.length == 2 ){ sound_ind = this.arr_top_consonants_2c.indexOf( s ) % ( this.arr_top_consonants_2c.length / 2 ); syl_info.seibo_sound_kigou = this.arr_top_consonants_2c_sound[ sound_ind ]; syl_info.seibo_sound_kana = this.arr_top_consonants_2c_sound_kana[ sound_ind ]; } else // 1文字 { sound_ind = this.arr_top_consonants_1c.indexOf( s ) % ( this.arr_top_consonants_1c.length / 2 ); syl_info.seibo_sound_kigou = this.arr_top_consonants_1c_sound[ sound_ind ]; syl_info.seibo_sound_kana = this.arr_top_consonants_1c_sound_kana[ sound_ind ]; } return syl_info; }, // 主母音の文字列に,発音ガイドを付与する get_shuboin_sound_kigou : function( syl_info ){ // 主母音から声調記号を取り払い,全て小文字にする var s = this.normalize_vowels_into_toneless_smalls( syl_info.shuboin_str ); syl_info.normalized_shuboin_str = s; // giをジーと伸ばす音の場合に, 発音記号を補正。ただし gie^ の場合はノータッチ if( this.arr_gi_entire_match_patterns.includes( syl_info.str ) ) { // i: とする syl_info.shuboin_sound_kigou = this.special_shuboin_i_of_gi_sound_kigou; syl_info.shuboin_sound_kana = this.special_shuboin_i_of_gi_sound_kana; } else { // 発音記号の配列上でのインデックス var sound_ind; // 複合母音か? if( s.length == 2 ){ sound_ind = this.arr_fukugouboin_patterns_without_tone.indexOf( s ); syl_info.shuboin_sound_kigou = this.arr_fukugouboin_patterns_without_tone_sound_kigou[ sound_ind ]; syl_info.shuboin_sound_kana = this.arr_fukugouboin_patterns_without_tone_sound_kana[ sound_ind ]; } else { // 単母音 sound_ind = this.arr_tanboin_patterns_without_tone.indexOf( s ); syl_info.shuboin_sound_kigou = this.arr_tanboin_patterns_without_tone_sound_kigou[ sound_ind ]; syl_info.shuboin_sound_kana = this.arr_tanboin_patterns_without_tone_sound_kana[ sound_ind ]; } } // 文字の名称を付与 var _this = this; syl_info.shuboin_char_names = s.split("").map(function(ch){ // 一文字ずつ単母音として見た場合の名称を付与 var sound_ind_for_name = _this.arr_tanboin_patterns_without_tone.indexOf( ch ); return _this.arr_tanboin_patterns_without_tone_name[ sound_ind_for_name ]; }); // giをジーと伸ばす音の場合に,文字の名称を補正。gie^ の場合も含む。 if( this.heading_gi_reads_zi( syl_info.str ) ) { // giの一部であると注記 syl_info.shuboin_char_names[0] = this.special_shuboin_i_of_gi_sound_name; } // 主母音から声調を認識して,声調情報を付与 syl_info = this.get_tone_info_from_shuboin( syl_info ); return syl_info; }, // 末子音の文字列に,発音ガイドを付与する get_matsushiin_sound_kigou : function( syl_info ){ // 末子音 var s = syl_info.matsushiin_str; // 発音記号の配列上でのインデックス var sound_ind; sound_ind = this.arr_matsushiin.indexOf( s ); syl_info.matsushiin_sound_kigou = this.arr_matsushiin_sound_kigou[ sound_ind ]; syl_info.matsushiin_sound_kana = this.arr_matsushiin_sound_kana[ sound_ind ]; return syl_info; }, // 母音の並びから声調記号を取り払い,全て小文字にする normalize_vowels_into_toneless_smalls : function( s ){ // 主母音の発音ガイドを表示するために必要 // 1文字ずつ置換 var new_s = s.split("").map(function(orig_ch){ var new_ch = orig_ch; // 全ての単母音マスタを参照しながら,声調のない文字に置き換える [ VtUtil.arr_long_a, VtUtil.arr_short_a, VtUtil.arr_aimai_a, VtUtil.arr_vwl_i, VtUtil.arr_vwl_y, VtUtil.arr_sharp_u, VtUtil.arr_vwl_wi, VtUtil.arr_wide_e, VtUtil.arr_narrow_e, VtUtil.arr_wide_o, VtUtil.arr_sharp_o, VtUtil.arr_long_aimai_o ].each(function(one_ch_arr){ // この文字に該当? if( one_ch_arr.flatten().includes( orig_ch ) ){ // 小文字の声調なしに置換 new_ch = one_ch_arr[0][0]; } }); return new_ch; }).join(""); return new_s; }, // 主母音から声調を認識して,声調情報を付与 get_tone_info_from_shuboin : function( syl_info ){ // 単母音マスタと全照合して,マッチしたものがマスタ内配列の何番目なのかを見る。 // 声調のインデックス番号。デフォルトで最初。 var ret_tone_index = 0; // 主母音の全文字について syl_info.shuboin_str.split("").each(function(shuboin_ch){ // 全単母音と照合 [ VtUtil.arr_long_a, VtUtil.arr_short_a, VtUtil.arr_aimai_a, VtUtil.arr_vwl_i, VtUtil.arr_vwl_y, VtUtil.arr_sharp_u, VtUtil.arr_vwl_wi, VtUtil.arr_wide_e, VtUtil.arr_narrow_e, VtUtil.arr_wide_o, VtUtil.arr_sharp_o, VtUtil.arr_long_aimai_o ].each(function(one_master_arr){ // この一つの単母音について one_master_arr.flatten().each(function(master_ch, index){ // 何番目とマッチするか? var current_tone_index = index % 6; //log( current_tone_index + "," + shuboin_ch + ", " + master_ch ); if( ( shuboin_ch == master_ch ) && ( current_tone_index > 0 ) ) { //log( "主母音" + shuboin_ch + ", マッチした声調番号" + current_tone_index ); ret_tone_index = current_tone_index; } }); }); }); // 何もマッチしなければ0のまま // 声調番号を格納 syl_info.tone_index = ret_tone_index; // 声調符号の文字 syl_info.tone_mark_str = this.arr_tone_marks[ ret_tone_index ]; // 声調ガイド説明 syl_info.tone_exp = this.arr_tone_exps[ ret_tone_index ]; return syl_info; }, // ----- 以下,HTMLの生成処理 ----- // HTMLエスケープする。 h : function( s ){ return ( s .replace( />/g, ">" ) .replace( /" + "" + syl_info.str + " " + "" + "" + "
外来語
" + syl_info.str + "" + "" ; } else if( syl_info.valid_sound ){ // ベトナム語として有効な音節か? // 有効な音節の場合 // 全体の発音を示す ret += "" + "" ; // 声母と韻母に分ける ret += "" + "
" + "" + syl_info.str + " " + "[ " + syl_info.entire_sound_kigou + " ]" + "
" + "
声母
(頭子音)
" ; if( syl_info.seibo_str ){ // 声母を表示 ret += syl_info.seibo_str; // 声母の発音カナ ret += "
" + this.get_sound_kana_html( syl_info.seibo_sound_kana ) + "
" ; // 声母の発音記号 ret += "
発音記号
"; ret += "
[ " + syl_info.seibo_sound_kigou + " ]
" ; } else { ret += " "; } ret += "
" + "
韻母
" // + syl_info.inbo_str + this.get_inbo_html_from_one_syl( syl_info ) + "
" ; } else { // 音節ではない記号などの場合 ret += "" + "" + "" + "
" + "" + this.h( syl_info.str ) + " " + "
" + "
記号
" + this.h( syl_info.str ) + "
" ; } ret += "" ; return ret; //return "(" + syl_info.str + ")"; //return "(fuga)"; }, // カナガイドのHTMLを生成 get_sound_kana_html : function( kana_info ){ var ret = ""; // 最初に発音注意マークがあるか? if( kana_info.charAt(0) == "★" ){ ret += "
発音注意
" ; kana_info = kana_info.substring(1, kana_info.length); } // 分解 var arr = kana_info.split(","); // 最初の行はメインの説明 ret += "
" + arr[0] + "
" ; // 2行目以降があるか? arr.shift(); if( arr.length > 0 ){ ret += "
" + arr.join("
") + "
" ; } return ret; }, // 韻母の構造をHTMLとして返す get_inbo_html_from_one_syl : function( syl_info ){ var ret = ""; /* // デバッグ用 ret += "(" + syl_info.kaiboin_str + ")"; ret += "(" + syl_info.shuboin_str + ")"; ret += "(" + syl_info.matsushiin_str + ")"; return ret; */ //log("韻母の構造をHTMLで表示:" + syl_info.inbo_str); // ----- 介母音 ----- ret += "" + "" + "
" + "
介母音
" ; if( syl_info.kaiboin_str ){ //log("介母音あり"); // 介母音を表示 ret += syl_info.kaiboin_str; // 介母音の発音カナ ret += "
" + this.get_sound_kana_html( syl_info.kaiboin_sound_kana ) + "
" ; // 介母音の発音記号 ret += "
発音記号
"; ret += "
[ " + syl_info.kaiboin_sound_kigou + " ]
" ; } else { //log("介母音なし"); ret += " "; } //log("介母音HTMLの構築が完了"); // ----- 主母音 ----- ret += "
" + "
主母音
" + syl_info.shuboin_str // 主母音は必ずある ; // 主母音の読み方について説明 ret += "" + "" + "" + "
読み方
" ; // 声調の矢印チャート ret += this.get_tone_chart_html_from_one_syl( syl_info ) //" "; ; ret += "
" + syl_info.tone_exp + "
" ; ret += "
"; // 主母音の発音カナ ret += "
" + this.get_sound_kana_html( syl_info.shuboin_sound_kana ) + "
" ; // 主母音の発音記号 ret += "
発音記号
"; ret += "
[ " + syl_info.shuboin_sound_kigou + " ]
" ; ret += "
" ; // 主母音の読み方終わり // 主母音の表記について説明 ret += "" + "" + "" + "
書き方
" ; // 声調だけの説明 ret += "
声調符号
" ; ret += "
" + syl_info.tone_mark_str //+ syl_info.tone_index + "
" ; ret += "
" + syl_info.tone_exp + "
" ; ret += "
"; // 声調記号なしで表示 ret += "
声調符号を消すと
"; ret += "
" + syl_info.normalized_shuboin_str + "
" ; ret += "
" + syl_info.shuboin_char_names.join(" と
") ; if( syl_info.shuboin_char_names.length > 1 ){ ret += "
(二重母音)"; } ret += "
" ; ret += "
" ; // 主母音の書き方終わり ret += "
" + "
末子音
" ; //log("主母音HTMLの構築が完了"); // ----- 末子音 ----- if( syl_info.matsushiin_str ){ // 末子音を表示 ret += syl_info.matsushiin_str; // 末子音の発音カナ ret += "
" + this.get_sound_kana_html( syl_info.matsushiin_sound_kana ) + "
" ; // 末子音の発音記号 ret += "
発音記号
"; ret += "
[ " + syl_info.matsushiin_sound_kigou + " ]
" ; //log("末子音HTMLの構築が完了"); } else { ret += " "; } ret += "
" ; return ret; }, // 1つの音節について,声調を図示するチャートテーブルをHTMLで返す get_tone_chart_html_from_one_syl : function( syl_info ){ var ret = ""; // この声調番号の図示データを取得 var lines = this.arr_tone_charts[ syl_info.tone_index ]; // 目盛りデータ var memoris = ["高", "", "", "", "低"]; // tableを構築 ret += "
" + "
音の高さ
" + "" + lines.map(function(line, index){ // セルに収める return "" + "" + line.split("").map(function(ch){ return ""; }).join("") + "" ; }).join("") + "
" + memoris[ index ] + " " + ch + "
" + "
" ; return ret; }, // ----- シンプルモードHTMLの生成処理 ----- // 簡易表示モード create_entire_html_from_all_syls_simple_mode : function( arr_syls, disp_style_force_break_num ){ var lines = []; var line = []; var wd_cnt = 0; // 改行ごとの単位に分ける arr_syls.each(function(syl_info){ // 改行か? if( syl_info.is_breakline ){ // 既存の行を保管 lines.push(line); // 新規行 line = []; wd_cnt = 0; } else { // 改行以外の要素だった場合 // 行内の許容単語数を超えた? if( wd_cnt >= disp_style_force_break_num ){ // 既存の行を保管 lines.push(line); // 新規行 line = []; wd_cnt = 0; } line.push( syl_info ); wd_cnt ++; } }); // 未改行の最終行の分を保管 if( line.length > 0 ){ lines.push( line ); } // 最も長い行の列数 var max_col_num = 0; lines.each(function(line){ if( line.length > max_col_num ){ max_col_num = line.length; } }); // 全行を一個のTABLEタグ化する var _this = this; var ret = ""; ret += lines.map(function(line){ var s = ""; // まず原文 s += ""; for( var i = 0; i < max_col_num; i ++ ){ if( line.length >= i + 1 ){ s += "" ; }else{ s += "" ; } } s += ""; // 発音記号の行 s += ""; for( var i = 0; i < max_col_num; i ++ ){ if( ( line.length >= i + 1 ) && line[i].valid_sound && ( ! line[i].ignore_flag ) ){ s += "" ; }else{ s += "" ; } } s += ""; // 声調図の行1 s += ""; for( var i = 0; i < max_col_num; i ++ ){ if( ( line.length >= i + 1 ) && line[i].valid_sound && ( ! line[i].ignore_flag ) ){ s += "" ; }else{ s += "" ; } } s += ""; // 声調図の行2 s += ""; for( var i = 0; i < max_col_num; i ++ ){ if( ( line.length >= i + 1 ) && line[i].valid_sound && ( ! line[i].ignore_flag ) ){ s += "" ; }else{ s += "" ; } } s += ""; // 声調図の行3 s += ""; for( var i = 0; i < max_col_num; i ++ ){ if( ( line.length >= i + 1 ) && line[i].valid_sound && ( ! line[i].ignore_flag ) ){ s += "" ; }else{ s += "" ; } } s += ""; // この行の結果を返却 return s; }).join(""); ret += "
" + _this.h( line[i].str ) + "" + "
" + line[i].entire_sound_kigou + "" + "
" + _this.arr_tone_charts_simple[ line[i].tone_index ][0] + "" + "
" + _this.arr_tone_charts_simple[ line[i].tone_index ][1] + "" + "
" + _this.arr_tone_charts_simple[ line[i].tone_index ][2] + "" + "
"; return ret; }, // 1つの音節について,声調を図示するチャートテーブルの簡易版をHTMLで返す get_simple_tone_chart_html_from_one_syl : function( syl_info ){ //log("声調番号:" + syl_info.tone_index + ", " + syl_info.str ); // この声調番号の図示データ(簡易版)を取得 var lines = this.arr_tone_charts_simple[ syl_info.tone_index ]; return lines.join("
"); } }; VtUtil.init(); // 初期化しておく