Home Reference Source

src/language/Japanese.js

/**
 * The script is part of MojiJS.
 * 
 * AUTHOR:
 *  natade (http://twitter.com/natadea)
 * 
 * LICENSE:
 *  The MIT license https://opensource.org/licenses/MIT
 */

import Unicode from "../encode/Unicode.js";

/**
 * 日本語を扱うクラス
 * @ignore
 */
export default class Japanese {

	/**
	 * カタカナをひらがなに変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toHiragana(text) {
		/**
		 * @param {string} ch 
		 */
		const func = function(ch) {
			return(String.fromCharCode(ch.charCodeAt(0) - 0x0060));
		};
		return (text.replace(/[\u30A1-\u30F6]/g, func));
	}

	/**
	 * ひらがなをカタカナに変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toKatakana(text) {
		/**
		 * @param {string} ch 
		 */
		const func = function(ch) {
			return(String.fromCharCode(ch.charCodeAt(0) + 0x0060));
		};
		return (text.replace(/[\u3041-\u3096]/g, func));
	}
	
	/**
	 * スペースを半角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toHalfWidthSpace(text) {
		return (text.replace(/\u3000/g, String.fromCharCode(0x0020)));
	}
	
	/**
	 * スペースを全角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toFullWidthSpace(text) {
		return (text.replace(/\u0020/g, String.fromCharCode(0x3000)));
	}
	
	/**
	 * 英数記号を半角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toHalfWidthAsciiCode(text) {
		let out = text;
		out = out.replace(/\u3000/g, "\u0020");				//全角スペース
		out = out.replace(/[\u2018-\u201B]/g, "\u0027");	//シングルクォーテーション
		out = out.replace(/[\u201C-\u201F]/g, "\u0022");	//ダブルクォーテーション
		/**
		 * @param {string} ch 
		 */
		const func = function(ch) {
			const code = ch.charCodeAt(0);
			return (String.fromCharCode(code - 0xFEE0));
		};
		return (out.replace(/[\uFF01-\uFF5E]/g, func));
	}
	
	/**
	 * 英数記号を全角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toFullWidthAsciiCode(text) {
		let out = text;
		out = out.replace(/\u0020/g, "\u3000");	//全角スペース
		out = out.replace(/\u0022/g, "\u201D");	//ダブルクォーテーション
		out = out.replace(/\u0027/g, "\u2019");	//アポストロフィー
		/**
		 * @param {string} ch 
		 */
		const func = function(ch) {
			const code = ch.charCodeAt(0);
			return (String.fromCharCode(code + 0xFEE0));
		};
		return (out.replace(/[\u0020-\u007E]/g, func));
	}
	
	/**
	 * アルファベットを半角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toHalfWidthAlphabet(text) {
		/**
		 * @param {string} ch 
		 */
		const func = function(ch) {
			return (String.fromCharCode(ch.charCodeAt(0) - 0xFEE0));
		};
		return (text.replace(/[\uFF21-\uFF3A\uFF41-\uFF5A]/g, func));
	}
	
	/**
	 * アルファベットを全角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toFullWidthAlphabet(text) {
		/**
		 * @param {string} ch 
		 */
		const func = function(ch) {
			return (String.fromCharCode(ch.charCodeAt(0) + 0xFEE0));
		};
		return (text.replace(/[A-Za-z]/g, func));
	}
	
	/**
	 * 数値を半角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toHalfWidthNumber(text) {
		/**
		 * @param {string} ch 
		 */
		const func = function(ch) {
			return(String.fromCharCode(ch.charCodeAt(0) - 0xFEE0));
		};
		return (text.replace(/[\uFF10-\uFF19]/g, func));
	}
	
	/**
	 * 数値を全角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toFullWidthNumber(text) {
		/**
		 * @param {string} ch 
		 */
		const func = function(ch) {
			return(String.fromCharCode(ch.charCodeAt(0) + 0xFEE0));
		};
		return (text.replace(/[0-9]/g, func));
	}
	
	/**
	 * カタカナを半角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toHalfWidthKana(text) {
		/**
		 * @type {Object<number, string>}
		 */
		const map = {
			0x3001	:	"\uFF64"	,	//	、
			0x3002	:	"\uFF61"	,	//	。	。
			0x300C	:	"\uFF62"	,	//	「	「
			0x300D	:	"\uFF63"	,	//	」	」
			0x309B	:	"\uFF9E"	,	//	゛	゙
			0x309C	:	"\uFF9F"	,	//	゜	゚
			0x30A1	:	"\uFF67"	,	//	ァ	ァ
			0x30A2	:	"\uFF71"	,	//	ア	ア
			0x30A3	:	"\uFF68"	,	//	ィ	ィ
			0x30A4	:	"\uFF72"	,	//	イ	イ
			0x30A5	:	"\uFF69"	,	//	ゥ	ゥ
			0x30A6	:	"\uFF73"	,	//	ウ	ウ
			0x30A7	:	"\uFF6A"	,	//	ェ	ェ
			0x30A8	:	"\uFF74"	,	//	エ	エ
			0x30A9	:	"\uFF6B"	,	//	ォ	ォ
			0x30AA	:	"\uFF75"	,	//	オ	オ
			0x30AB	:	"\uFF76"	,	//	カ	カ
			0x30AC	:	"\uFF76\uFF9E"	,	//	ガ	ガ
			0x30AD	:	"\uFF77"	,	//	キ	キ
			0x30AE	:	"\uFF77\uFF9E"	,	//	ギ	ギ
			0x30AF	:	"\uFF78"	,	//	ク	ク
			0x30B0	:	"\uFF78\uFF9E"	,	//	グ	グ
			0x30B1	:	"\uFF79"	,	//	ケ	ケ
			0x30B2	:	"\uFF79\uFF9E"	,	//	ゲ	ゲ
			0x30B3	:	"\uFF7A"	,	//	コ	コ
			0x30B4	:	"\uFF7A\uFF9E"	,	//	ゴ	ゴ
			0x30B5	:	"\uFF7B"	,	//	サ	サ
			0x30B6	:	"\uFF7B\uFF9E"	,	//	ザ	ザ
			0x30B7	:	"\uFF7C"	,	//	シ	シ
			0x30B8	:	"\uFF7C\uFF9E"	,	//	ジ	ジ
			0x30B9	:	"\uFF7D"	,	//	ス	ス
			0x30BA	:	"\uFF7D\uFF9E"	,	//	ズ	ズ
			0x30BB	:	"\uFF7E"	,	//	セ	セ
			0x30BC	:	"\uFF7E\uFF9E"	,	//	ゼ	ゼ
			0x30BD	:	"\uFF7F"	,	//	ソ	ソ
			0x30BE	:	"\uFF7F\uFF9E"	,	//	ゾ	ゾ
			0x30BF	:	"\uFF80"	,	//	タ	タ
			0x30C0	:	"\uFF80\uFF9E"	,	//	ダ	ダ
			0x30C1	:	"\uFF81"	,	//	チ	チ
			0x30C2	:	"\uFF81\uFF9E"	,	//	ヂ	ヂ
			0x30C3	:	"\uFF6F"	,	//	ッ	ッ
			0x30C4	:	"\uFF82"	,	//	ツ	ツ
			0x30C5	:	"\uFF82\uFF9E"	,	//	ヅ	ヅ
			0x30C6	:	"\uFF83"	,	//	テ	テ
			0x30C7	:	"\uFF83\uFF9E"	,	//	デ	デ
			0x30C8	:	"\uFF84"	,	//	ト	ト
			0x30C9	:	"\uFF84\uFF9E"	,	//	ド	ド
			0x30CA	:	"\uFF85"	,	//	ナ	ナ
			0x30CB	:	"\uFF86"	,	//	ニ	ニ
			0x30CC	:	"\uFF87"	,	//	ヌ	ヌ
			0x30CD	:	"\uFF88"	,	//	ネ	ネ
			0x30CE	:	"\uFF89"	,	//	ノ	ノ
			0x30CF	:	"\uFF8A"	,	//	ハ	ハ
			0x30D0	:	"\uFF8A\uFF9E"	,	//	バ	バ
			0x30D1	:	"\uFF8A\uFF9F"	,	//	パ	パ
			0x30D2	:	"\uFF8B"	,	//	ヒ	ヒ
			0x30D3	:	"\uFF8B\uFF9E"	,	//	ビ	ビ
			0x30D4	:	"\uFF8B\uFF9F"	,	//	ピ	ピ
			0x30D5	:	"\uFF8C"	,	//	フ	フ
			0x30D6	:	"\uFF8C\uFF9E"	,	//	ブ	ブ
			0x30D7	:	"\uFF8C\uFF9F"	,	//	プ	プ
			0x30D8	:	"\uFF8D"	,	//	ヘ	ヘ
			0x30D9	:	"\uFF8D\uFF9E"	,	//	ベ	ベ
			0x30DA	:	"\uFF8D\uFF9F"	,	//	ペ	ペ
			0x30DB	:	"\uFF8E"	,	//	ホ	ホ
			0x30DC	:	"\uFF8E\uFF9E"	,	//	ボ	ボ
			0x30DD	:	"\uFF8E\uFF9F"	,	//	ポ	ポ
			0x30DE	:	"\uFF8F"	,	//	マ	マ
			0x30DF	:	"\uFF90"	,	//	ミ	ミ
			0x30E0	:	"\uFF91"	,	//	ム	ム
			0x30E1	:	"\uFF92"	,	//	メ	メ
			0x30E2	:	"\uFF93"	,	//	モ	モ
			0x30E3	:	"\uFF6C"	,	//	ャ	ャ
			0x30E4	:	"\uFF94"	,	//	ヤ	ヤ
			0x30E5	:	"\uFF6D"	,	//	ュ	ュ
			0x30E6	:	"\uFF95"	,	//	ユ	ユ
			0x30E7	:	"\uFF6E"	,	//	ョ	ョ
			0x30E8	:	"\uFF96"	,	//	ヨ	ヨ
			0x30E9	:	"\uFF97"	,	//	ラ	ラ
			0x30EA	:	"\uFF98"	,	//	リ	リ
			0x30EB	:	"\uFF99"	,	//	ル	ル
			0x30EC	:	"\uFF9A"	,	//	レ	レ
			0x30ED	:	"\uFF9B"	,	//	ロ	ロ
			0x30EE	:	"\uFF9C"	,	//	ヮ	ワ
			0x30EF	:	"\uFF9C"	,	//	ワ	ワ
			0x30F0	:	"\uFF72"	,	//	ヰ	イ
			0x30F1	:	"\uFF74"	,	//	ヱ	エ
			0x30F2	:	"\uFF66"	,	//	ヲ	ヲ
			0x30F3	:	"\uFF9D"	,	//	ン	ン
			0x30F4	:	"\uFF73\uFF9E"	,	//	ヴ	ヴ
			0x30F5	:	"\uFF76"	,	//	ヵ	カ
			0x30F6	:	"\uFF79"	,	//	ヶ	ケ
			0x30F7	:	"\uFF9C\uFF9E"	,	//	ヷ	ヷ
			0x30F8	:	"\uFF72\uFF9E"	,	//	ヸ	イ゙
			0x30F9	:	"\uFF74\uFF9E"	,	//	ヹ	エ゙
			0x30FA	:	"\uFF66\uFF9E"	,	//	ヺ	ヺ
			0x30FB	:	"\uFF65"	,	//	・	・
			0x30FC	:	"\uFF70"		//	ー	ー
		};
		/**
		 * @param {string} ch 
		 */
		const func = function(ch) {
			if(ch.length === 1) {
				return(map[ch.charCodeAt(0)]);
			}
			else {
				return(map[ch.charCodeAt(0)] + map[ch.charCodeAt(1)]);
			}
		};
		return (text.replace(/[\u3001\u3002\u300C\u300D\u309B\u309C\u30A1-\u30FC][\u309B\u309C]?/g, func));
	}

	/**
	 * カタカナを全角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toFullWidthKana(text) {
		/**
		 * @type {Object<number, number>}
		 */
		const map = {
			0xFF61	:	0x3002	,	//	。	。
			0xFF62	:	0x300C	,	//	「	「
			0xFF63	:	0x300D	,	//	」	」
			0xFF64	:	0x3001	,	//	、
			0xFF65	:	0x30FB	,	//	・	・
			0xFF66	:	0x30F2	,	//	ヲ	ヲ
			0xFF67	:	0x30A1	,	//	ァ	ァ
			0xFF68	:	0x30A3	,	//	ィ	ィ
			0xFF69	:	0x30A5	,	//	ゥ	ゥ
			0xFF6A	:	0x30A7	,	//	ェ	ェ
			0xFF6B	:	0x30A9	,	//	ォ	ォ
			0xFF6C	:	0x30E3	,	//	ャ	ャ
			0xFF6D	:	0x30E5	,	//	ュ	ュ
			0xFF6E	:	0x30E7	,	//	ョ	ョ
			0xFF6F	:	0x30C3	,	//	ッ	ッ
			0xFF70	:	0x30FC	,	//	ー	ー
			0xFF71	:	0x30A2	,	//	ア	ア
			0xFF72	:	0x30A4	,	//	イ	イ
			0xFF73	:	0x30A6	,	//	ウ	ウ
			0xFF74	:	0x30A8	,	//	エ	エ
			0xFF75	:	0x30AA	,	//	オ	オ
			0xFF76	:	0x30AB	,	//	カ	カ
			0xFF77	:	0x30AD	,	//	キ	キ
			0xFF78	:	0x30AF	,	//	ク	ク
			0xFF79	:	0x30B1	,	//	ケ	ケ
			0xFF7A	:	0x30B3	,	//	コ	コ
			0xFF7B	:	0x30B5	,	//	サ	サ
			0xFF7C	:	0x30B7	,	//	シ	シ
			0xFF7D	:	0x30B9	,	//	ス	ス
			0xFF7E	:	0x30BB	,	//	セ	セ
			0xFF7F	:	0x30BD	,	//	ソ	ソ
			0xFF80	:	0x30BF	,	//	タ	タ
			0xFF81	:	0x30C1	,	//	チ	チ
			0xFF82	:	0x30C4	,	//	ツ	ツ
			0xFF83	:	0x30C6	,	//	テ	テ
			0xFF84	:	0x30C8	,	//	ト	ト
			0xFF85	:	0x30CA	,	//	ナ	ナ
			0xFF86	:	0x30CB	,	//	ニ	ニ
			0xFF87	:	0x30CC	,	//	ヌ	ヌ
			0xFF88	:	0x30CD	,	//	ネ	ネ
			0xFF89	:	0x30CE	,	//	ノ	ノ
			0xFF8A	:	0x30CF	,	//	ハ	ハ
			0xFF8B	:	0x30D2	,	//	ヒ	ヒ
			0xFF8C	:	0x30D5	,	//	フ	フ
			0xFF8D	:	0x30D8	,	//	ヘ	ヘ
			0xFF8E	:	0x30DB	,	//	ホ	ホ
			0xFF8F	:	0x30DE	,	//	マ	マ
			0xFF90	:	0x30DF	,	//	ミ	ミ
			0xFF91	:	0x30E0	,	//	ム	ム
			0xFF92	:	0x30E1	,	//	メ	メ
			0xFF93	:	0x30E2	,	//	モ	モ
			0xFF94	:	0x30E4	,	//	ヤ	ヤ
			0xFF95	:	0x30E6	,	//	ユ	ユ
			0xFF96	:	0x30E8	,	//	ヨ	ヨ
			0xFF97	:	0x30E9	,	//	ラ	ラ
			0xFF98	:	0x30EA	,	//	リ	リ
			0xFF99	:	0x30EB	,	//	ル	ル
			0xFF9A	:	0x30EC	,	//	レ	レ
			0xFF9B	:	0x30ED	,	//	ロ	ロ
			0xFF9C	:	0x30EF	,	//	ワ	ワ
			0xFF9D	:	0x30F3	,	//	ン	ン
			0xFF9E	:	0x309B	,	//	゛	゙
			0xFF9F	:	0x309C		//	゜	゚
		};
		/**
		 * @param {string} str 
		 */
		const func = function(str) {
			if(str.length === 1) {
				return (String.fromCharCode(map[str.charCodeAt(0)]));
			}
			else {
				const next = str.charCodeAt(1);
				const ch   = str.charCodeAt(0);
				if(next === 0xFF9E) {
					// Shift-JISにない濁点(ヷ、ヸ、ヹ、ヺ)は意図的に無視
					// ヴ
					if (ch === 0xFF73) {
						return (String.fromCharCode(0x3094));
					}
					// ガ-ド、バ-ボ
					else if(
						((0xFF76 <= ch) && (ch <= 0xFF84)) ||
						((0xFF8A <= ch) && (ch <= 0xFF8E))	) {
						return (String.fromCharCode(map[ch] + 1));
					}
				}
				// 半濁点
				else if(next === 0xFF9F) {
					// パ-ポ
					if((0xFF8A <= ch) && (ch <= 0xFF8E)) {
						return (String.fromCharCode(map[ch] + 2));
					}
				}
				return (String.fromCharCode(map[ch]) + String.fromCharCode(map[next]));
			}
		};
		return (text.replace(/[\uFF61-\uFF9F][\uFF9E\uFF9F]?/g, func));
	}
	
	/**
	 * 半角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toHalfWidth(text) {
		return Japanese.toHalfWidthKana(Japanese.toHalfWidthAsciiCode(text));
	}
	
	/**
	 * 全角に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toFullWidth(text) {
		return Japanese.toFullWidthKana(Japanese.toFullWidthAsciiCode(text));
	}

	/**
	 * ローマ字からひらがなに変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toHiraganaFromRomaji(text) {
		/**
		 * ローマ字から変換マップ
		 * .y[aiuoe] は除いている
		 * @type {Object<string, string>}
		 */
		const map = {
			"a" : "あ" ,
			"i" : "い" ,
			"u" : "う" ,
			"e" : "え" ,
			"o" : "お" ,
			"ka" : "か" ,
			"ki" : "き" ,
			"ku" : "く" ,
			"ke" : "け" ,
			"ko" : "こ" ,
			"ga" : "が" ,
			"gi" : "ぎ" ,
			"gu" : "ぐ" ,
			"ge" : "げ" ,
			"go" : "ご" ,
			"sa" : "さ" ,
			"si" : "し" ,
			"su" : "す" ,
			"se" : "せ" ,
			"so" : "そ" ,
			"za" : "ざ" ,
			"zi" : "じ" ,
			"zu" : "ず" ,
			"ze" : "ぜ" ,
			"zo" : "ぞ" ,
			"ta" : "た" ,
			"ti" : "ち" ,
			"tu" : "つ" ,
			"te" : "て" ,
			"to" : "と" ,
			"da" : "だ" ,
			"di" : "ぢ" ,
			"du" : "づ" ,
			"de" : "で" ,
			"do" : "ど" ,
			"na" : "な" ,
			"ni" : "に" ,
			"nu" : "ぬ" ,
			"ne" : "ね" ,
			"no" : "の" ,
			"ha" : "は" ,
			"hi" : "ひ" ,
			"hu" : "ふ" ,
			"he" : "へ" ,
			"ho" : "ほ" ,
			"ba" : "ば" ,
			"bi" : "び" ,
			"bu" : "ぶ" ,
			"be" : "べ" ,
			"bo" : "ぼ" ,
			"pa" : "ぱ" ,
			"pi" : "ぴ" ,
			"pu" : "ぷ" ,
			"pe" : "ぺ" ,
			"po" : "ぽ" ,
			"ma" : "ま" ,
			"mi" : "み" ,
			"mu" : "む" ,
			"me" : "め" ,
			"mo" : "も" ,
			"ya" : "や" ,
			"yi" : "い" ,
			"yu" : "ゆ" ,
			"ye" : "いぇ" ,
			"yo" : "よ" ,
			"ra" : "ら" ,
			"ri" : "り" ,
			"ru" : "る" ,
			"re" : "れ" ,
			"ro" : "ろ" ,
			"wa" : "わ" ,
			"wi" : "うぃ" ,
			"wu" : "う" ,
			"we" : "うぇ" ,
			"wo" : "を" ,
			"la" : "ぁ" ,
			"li" : "ぃ" ,
			"lu" : "ぅ" ,
			"le" : "ぇ" ,
			"lo" : "ぉ" ,
			"lya" : "ゃ" ,
			"lyi" : "ぃ" ,
			"lyu" : "ゅ" ,
			"lye" : "ぇ" ,
			"lyo" : "ょ" ,
			"ltu" : "っ" ,
			"ltsu" : "っ" ,
			"xa" : "ぁ" ,
			"xi" : "ぃ" ,
			"xu" : "ぅ" ,
			"xe" : "ぇ" ,
			"xo" : "ぉ" ,
			"xya" : "ゃ" ,
			"xyi" : "ぃ" ,
			"xyu" : "ゅ" ,
			"xye" : "ぇ" ,
			"xyo" : "ょ" ,
			"xtu" : "っ" ,
			"xtsu" : "っ" ,
			// 環境依存をなくすために、SJISにあるカタカナにしています。
			"va" : "ヴぁ" ,
			"vi" : "ヴぃ" ,
			"vu" : "ヴ" ,
			"ve" : "ヴぇ" ,
			"vo" : "ヴぉ" ,
			"qa" : "くぁ" ,
			"qi" : "くぃ" ,
			"qu" : "く" ,
			"qe" : "くぇ" ,
			"qo" : "くぉ" ,
			"qwa" : "くぁ" ,
			"qwi" : "くぃ" ,
			"qwu" : "くぅ" ,
			"qwe" : "くぇ" ,
			"qwo" : "くぉ" ,
			"gwa" : "ぐぁ" ,
			"gwi" : "ぐぃ" ,
			"gwu" : "ぐぅ" ,
			"gwe" : "ぐぇ" ,
			"gwo" : "ぐぉ" ,
			"sha" : "しゃ" ,
			"shi" : "し" ,
			"shu" : "しゅ" ,
			"she" : "しぇ" ,
			"sho" : "しょ" ,
			"swa" : "すぁ" ,
			"swi" : "すぃ" ,
			"swu" : "すぅ" ,
			"swe" : "すぇ" ,
			"swo" : "すぉ" ,
			"cha" : "ちゃ" ,
			"chi" : "ち" ,
			"chu" : "ちゅ" ,
			"che" : "ちぇ" ,
			"cho" : "ちょ" ,
			"tha" : "ちゃ" ,
			"thi" : "ち" ,
			"thu" : "てゅ" ,
			"the" : "てぇ" ,
			"tho" : "てょ" ,
			"tsa" : "つぁ" ,
			"tsi" : "つぃ" ,
			"tsu" : "つ" ,
			"tse" : "つぇ" ,
			"tso" : "つぉ" ,
			"twa" : "とぁ" ,
			"twi" : "とぃ" ,
			"twu" : "とぅ" ,
			"twe" : "とぇ" ,
			"two" : "とぉ" ,
			"fa" : "ふぁ" ,
			"fi" : "ふぃ" ,
			"fu" : "ふ" ,
			"fe" : "ふぇ" ,
			"fo" : "ふぉ" ,
			"fwa" : "ふぁ" ,
			"fwi" : "ふぃ" ,
			"fwu" : "ふぅ" ,
			"fwe" : "ふぇ" ,
			"fwo" : "ふぉ" ,
			"ja" : "じゃ" ,
			"ji" : "じ" ,
			"ju" : "じゅ" ,
			"je" : "じぇ" ,
			"jo" : "じょ" ,
			"n" : "ん" ,
			"nn" : "ん" ,
			"-" : "ー" ,
			"?" : "?" ,
			"!" : "!",
			"," : "、",
			"." : "。" 
		};
		/**
		 * ya, yi, yu, ye, yo
		 * @type {Object<string, string>}
		 */
		const y_komoji_map = {
			"a" : "ゃ",
			"i" : "ぃ",
			"u" : "ゅ",
			"e" : "ぇ",
			"o" : "ょ"
		};
		/**
		 * @param {string} str 
		 */
		const func = function(str) {
			const output = [];
			let y_komoji = null;
			let romaji = str.toLowerCase();
			if(romaji.length > 2) {
				// 同じ文字の繰り返しなら「っ」に変更
				if(romaji.charCodeAt(0) === romaji.charCodeAt(1)) {
					// ただし繰り返し文字がnの場合は「ん」として扱う
					if(romaji.substring(0, 1) === "n") {
						output.push("ん");
						romaji = romaji.substring(2);
					}
					else {
						output.push("っ");
						romaji = romaji.substring(1);
					}
				}
			}
			if(romaji.length === 3) {
				const char_1 = romaji.substring(0, 1);
				const char_2 = romaji.substring(1, 2);
				// 2文字目がyで始まる場合(ただし、lya, xya などを除く)は
				// 小文字リストから選んで、最後に小文字をつける
				// sya -> si につけかえて辞書から探す
				if((char_2 === "y") && (char_1 !== "l") && (char_1 !== "x")) {
					y_komoji = y_komoji_map[romaji.substring(2)];
					romaji = romaji.substring(0, 1) + "i";
				}
			}
			const data = map[romaji];
			if(!data) {
				return str;
			}
			output.push(data);
			if(y_komoji) {
				output.push(y_komoji);
			}
			return output.join("");
		};
		// 上から下への優先度で変換する。
		// ([xl]?[kgsztdnhbpmyrwlxvqfj])(\1)?y?[aiuoe] ... yが入り込む可能性がある文字。前の文字を繰り返して「tta -> った」にも対応。
		// [xl]?(gw|ch|cch|sh|ssh|ts|tts|th|tth)?[aiuoe] ... yを使用しない文字
		// nn? ... ん
		// [?!-] ... 記号
		return (text.replace(/([xl]?[kgsztdnhbpmyrwlxvqfj])(\1)?y?[aiuoe]|[xl]?([gqstf]w|ch|cch|sh|ssh|ts|tts|th|tth)?[aiuoe]|nn?|[?!-.,]/gi, func));
	}

	/**
	 * ローマ字からカタカナに変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toKatakanaFromRomaji(text) {
		return Japanese.toKatakana(Japanese.toHiraganaFromRomaji(text));
	}

	/**
	 * ひらがなからローマ字に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toRomajiFromHiragana(text) {
		/**
		 * ひらがなからローマ字への変換マップ
		 * @type {Object<string, string>}
		 */
		const map = {
			"あ" : "a" ,
			"い" : "i" ,
			"う" : "u" ,
			"え" : "e" ,
			"お" : "o" ,
			"か" : "ka" ,
			"き" : "ki" ,
			"く" : "ku" ,
			"け" : "ke" ,
			"こ" : "ko" ,
			"が" : "ga" ,
			"ぎ" : "gi" ,
			"ぐ" : "gu" ,
			"げ" : "ge" ,
			"ご" : "go" ,
			"さ" : "sa" ,
			"し" : "shi" ,
			"す" : "su" ,
			"せ" : "se" ,
			"そ" : "so" ,
			"ざ" : "za" ,
			"じ" : "ji" ,
			"ず" : "zu" ,
			"ぜ" : "ze" ,
			"ぞ" : "zo" ,
			"た" : "ta" ,
			"ち" : "chi" ,
			"つ" : "tsu" ,
			"て" : "te" ,
			"と" : "to" ,
			"だ" : "da" ,
			"ぢ" : "di" ,
			"づ" : "du" ,
			"で" : "de" ,
			"ど" : "do" ,
			"な" : "na" ,
			"に" : "ni" ,
			"ぬ" : "nu" ,
			"ね" : "ne" ,
			"の" : "no" ,
			"は" : "ha" ,
			"ひ" : "hi" ,
			"ふ" : "fu" ,
			"へ" : "he" ,
			"ほ" : "ho" ,
			"ば" : "ba" ,
			"び" : "bi" ,
			"ぶ" : "bu" ,
			"べ" : "be" ,
			"ぼ" : "bo" ,
			"ぱ" : "pa" ,
			"ぴ" : "pi" ,
			"ぷ" : "pu" ,
			"ぺ" : "pe" ,
			"ぽ" : "po" ,
			"ま" : "ma" ,
			"み" : "mi" ,
			"む" : "mu" ,
			"め" : "me" ,
			"も" : "mo" ,
			"や" : "ya" ,
			"ゆ" : "yu" ,
			"いぇ" : "ye" ,
			"よ" : "yo" ,
			"ら" : "ra" ,
			"り" : "ri" ,
			"る" : "ru" ,
			"れ" : "re" ,
			"ろ" : "ro" ,
			"わ" : "wa" ,
			"うぃ" : "wi" ,
			"うぇ" : "we" ,
			"うぉ" : "wo" ,
			"を" : "wo" ,
			"ゐ" : "wi" ,
			"ゑ" : "we" ,
			"ん" : "n" ,
			"ぁ" : "lya" ,
			"ぃ" : "lyi" ,
			"ぅ" : "lyu" ,
			"ぇ" : "lye" ,
			"ぉ" : "lyo" ,
			"ゃ" : "lya" ,
			"ゅ" : "lyu" ,
			"ょ" : "lyo" ,
			// 環境依存をなくすために、SJISにあるカタカナにしています。
			"ヴぁ" : "va" ,
			"ヴぃ" : "vi" ,
			"ヴ" : "vu" ,
			"ヴぇ" : "ve" ,
			"ヴぉ" : "vo" ,
			"ゔぁ" : "va" ,
			"ゔぃ" : "vi" ,
			"ゔ" : "vu" ,
			"ゔぇ" : "ve" ,
			"ゔぉ" : "vo" ,
			"きゃ" : "kya" ,
			"きぃ" : "kyi" ,
			"きゅ" : "kyu" ,
			"きぇ" : "kye" ,
			"きょ" : "kyo" ,
			"ぎゃ" : "gya" ,
			"ぎぃ" : "gyi" ,
			"ぎゅ" : "gyu" ,
			"ぎぇ" : "gye" ,
			"ぎょ" : "gyo" ,
			"くぁ" : "qa" ,
			"くぃ" : "qi" ,
			"くぅ" : "qu" ,
			"くぇ" : "qe" ,
			"くぉ" : "qo" ,
			"ぐぁ" : "gwa" ,
			"ぐぃ" : "gwi" ,
			"ぐぅ" : "gwu" ,
			"ぐぇ" : "gwe" ,
			"ぐぉ" : "gwo" ,
			"しゃ" : "sha" ,
			// "しぃ" : "shii" ,
			"しゅ" : "shu" ,
			"しぇ" : "she" ,
			"しょ" : "sho" ,
			"じゃ" : "ja" ,
			// "じぃ" : "jii" ,
			"じゅ" : "ju" ,
			"じぇ" : "je" ,
			"じょ" : "jo" ,
			"ちゃ" : "cha" ,
			// "ちぃ" : "chii"
			"ちゅ" : "chu" ,
			"ちぇ" : "che" ,
			"ちょ" : "cho" ,
			"つぁ" : "tsa" ,
			"つぃ" : "tsi" ,
			"つぇ" : "tse" ,
			"つぉ" : "tso" ,
			"てぁ" : "tha" ,
			"てぃ" : "thi" ,
			"てゅ" : "thu" ,
			"てぇ" : "the" ,
			"てょ" : "tho" ,
			"にゃ" : "nya" ,
			"にぃ" : "nyi" ,
			"にゅ" : "nyu" ,
			"にぇ" : "nye" ,
			"にょ" : "nyo" ,
			"ひゃ" : "hya" ,
			"ひぃ" : "hyi" ,
			"ひゅ" : "hyu" ,
			"ひぇ" : "hye" ,
			"ひょ" : "hyo" ,
			"びゃ" : "bya" ,
			"びぃ" : "byi" ,
			"びゅ" : "byu" ,
			"びぇ" : "bye" ,
			"びょ" : "byo" ,
			"ぴゃ" : "pya" ,
			"ぴぃ" : "pyi" ,
			"ぴゅ" : "pyu" ,
			"ぴぇ" : "pye" ,
			"ぴょ" : "pyo" ,
			"ふぁ" : "fa" ,
			"ふぃ" : "fi" ,
			"ふぇ" : "fe" ,
			"ふぉ" : "fo" ,
			"みゃ" : "mya" ,
			"みぃ" : "myi" ,
			"みゅ" : "myu" ,
			"みぇ" : "mye" ,
			"みょ" : "myo" ,
			"りゃ" : "rya" ,
			"りぃ" : "ryi" ,
			"りゅ" : "ryu" ,
			"りぇ" : "rye" ,
			"りょ" : "ryo" ,
			"ー" : "-" ,
			"?" : "?" ,
			"!" : "!" ,
			"、" : "," ,
			"。" : "." 
		};

		/**
		 * @type {Object<string, string>}
		 */
		const komoji_map = {
			"ぁ" : "la",
			"ぃ" : "li",
			"ぅ" : "lu",
			"ぇ" : "le",
			"ぉ" : "lo",
			"ゃ" : "lya",
			"ゅ" : "lyu",
			"ょ" : "lyo"
		};

		/**
		 * @param {string} str 
		 */
		const func = function(str) {
			let tgt = str;
			let is_xtu = false; 
			// 1文字目に「っ」があるか
			if(/^っ/.test(tgt)) {
				is_xtu = true;
				tgt = tgt.replace(/^っ*/, "");
			}
			// 変換
			let trans = map[tgt];
			// 変換に失敗した場合は
			if(!trans) {
				if(trans.length === 1) {
					// 1文字なのでこれ以上変換不能
					return str;
				}
				const char_1 = trans.substring(0, 1);
				const char_2 = trans.substring(1, 2);
				// 最後の文字が小文字である
				if(!komoji_map[char_2]) {
					// これ以上変換不能
					return str;
				}
				tgt = char_1;
				const last_text = komoji_map[char_2];
				// 再度変換テスト
				trans = map[tgt];
				if(!trans) {
					// これ以上変換不能
					return str;
				}
				trans += last_text;
			}
			if(is_xtu) {
				trans = trans.substring(0, 1) + trans;
			}
			return trans;
		};
		// [っ]*[あいうえおか-ぢつ-もやゆよら-ろわゐゑをんヴ][ぁぃぅぇぉゃゅょ]? ... 促音+子音母音
		// [ぁぃぅぇぉゃゅょゎっ] ... 小文字のみ
		// [?!-。、] ... 記号
		return (text.replace(/[っ]*[あいうえおか-ぢつ-もやゆよら-ろわゐゑをんヴゔ][ぁぃぅぇぉゃゅょ]?|[ぁぃぅぇぉゃゅょゎっ]|[?!-。、]/g, func));
	}

	/**
	 * カタカナからローマ字に変換
	 * @param {String} text - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static toRomajiFromKatakana(text) {
		return Japanese.toRomajiFromHiragana(Japanese.toHiragana(text));
	}

	/**
	 * 指定したコードポイントの横幅を取得します
	 * - 結合文字と異体字セレクタは、0としてカウントします。
	 * - 半角は1としてカウントします。これらは、ASCII文字、半角カタカナとします。
	 * - 全角は2としてカウントします。上記以外を全角として処理します。
	 * @param {Number} cp 調査するコードポイント
	 * @returns {Number} 文字の横幅
	 */
	static getWidthFromCodePoint(cp) {
		if(Unicode.isCombiningMarkFromCodePoint(cp) || Unicode.isVariationSelectorFromCodePoint(cp)) {
			return 0;
		}
		else if((cp < 0x80) || ((0xFF61 <= cp) && (cp < 0xFFA0))) {
			return 1;
		}
		else {
			return 2;
		}
	}

	/**
	 * 指定したテキストの横幅を半角/全角でカウント
	 * - 結合文字と異体字セレクタは、0としてカウントします。
	 * - 半角は1としてカウントします。これらは、ASCII文字、半角カタカナとします。
	 * - 全角は2としてカウントします。上記以外を全角として処理します。
	 * @param {String} text - カウントしたいテキスト
	 * @returns {Number} 文字の横幅
	 */
	static getWidth(text) {
		const utf32_array = Unicode.toUTF32Array(text);
		let count = 0;
		for(let i = 0; i < utf32_array.length; i++) {
			count += Japanese.getWidthFromCodePoint(utf32_array[i]);
		}
		return count;
	}

	/**
	 * 異体字セレクタと結合文字を考慮して文字列を文字の配列に変換する
	 * @param {String} text - 変換したいテキスト
	 * @returns {Array<Array<number>>} UTF32(コードポイント)の配列が入った配列
	 */
	static toMojiArrayFromString(text) {
		const utf32 = Unicode.toUTF32Array(text);
		/**
		 * @type {Array<Array<number>>}
		 */
		const mojiarray = [];
		let moji = [];
		for(let i = 0; i < utf32.length; i++) {
			const cp = utf32[i];
			if((i > 0) && !Unicode.isVariationSelectorFromCodePoint(cp) && !Unicode.isCombiningMarkFromCodePoint(cp)) {
				mojiarray.push(moji);
				moji = [];
			}
			moji.push(cp);
		}
		mojiarray.push(moji);
		return mojiarray;
	}

	/**
	 * 異体字セレクタと結合文字を考慮して文字列を文字の配列に変換する
	 * @param {Array<Array<number>>} mojiarray - UTF32(コードポイント)の配列が入った配列
	 * @returns {string} UTF32(コードポイント)の配列が入った配列
	 */
	static toStringFromMojiArray(mojiarray) {
		/**
		 * @type {Array<number>}
		 */
		const utf32 = [];
		for(let i = 0; i < mojiarray.length; i++) {
			for(let j = 0; j < mojiarray[i].length; j++) {
				utf32.push(mojiarray[i][j]);
			}
		}
		return Unicode.fromUTF32Array(utf32);
	}

	/**
	 * 指定したテキストの横幅を半角/全角で換算した場合の切り出し
	 * - 結合文字と異体字セレクタは、0としてカウントします。
	 * - 半角は1としてカウントします。これらは、ASCII文字、半角カタカナとします。
	 * - 全角は2としてカウントします。上記以外を全角として処理します。
	 * @param {String} text - 切り出したいテキスト
	 * @param {Number} offset - 切り出し位置
	 * @param {Number} size - 切り出す長さ
	 * @returns {String} 切り出したテキスト
	 * @ignore
	 */
	static cutTextForWidth(text, offset, size) {
		const moji_array = Japanese.toMojiArrayFromString(text);
		const SPACE = [ 0x20 ] ; // ' '
		/**
		 * @type {Array<Array<number>>}
		 */
		const output = [];
		let is_target = false;
		let position = 0;
		let cut_size = size;
		if(offset < 0) {
			cut_size += offset;
			offset = 0;
		}
		if(cut_size <= 0) {
			return "";
		}
		for(let i = 0; i < moji_array.length; i++) {
			const ch = moji_array[i][0];
			const ch_size = ((ch < 0x80) || ((0xFF61 <= ch) && (ch < 0xFFA0))) ? 1 : 2;
			if(position >= offset) {
				is_target = true;
				if(cut_size >= ch_size) {
					output.push(moji_array[i]);
				}
				else {
					output.push(SPACE);
				}
				cut_size -= ch_size;
				if(cut_size <= 0) {
					break;
				}
			}
			position += ch_size;
			// 2バイト文字の途中をoffset指定していた場合になる。
			if(((position - 1) >= offset) && !is_target) {
				cut_size--;
				output.push(SPACE);
			}
		}
		return Japanese.toStringFromMojiArray(output);
	}

}