JavaScriptでローマ字をカタカナに変換する関数


概要

入力を正規化する必要があったので、ローマ字からカタカナに変換する関数を作成しました。

IMEと似た感じで変換されます(表にないものはそのまま出ます)。

aiueoアイウエオ

hankachiハンカチ

tonari no kyaku ha yoku kakikuu kyakudaトナリ ノ キャク ハ ヨク カキクウ キャクダ

toukyou tokkyo kyokakyokuトウキョウ トッキョ キョカキョク

qawsedrftgyhujikolpクァwセdrftgyフジコlp

高輪Gateway高輪ガテワy


コード

TSで書いているので、JSで使う場合型情報を落としてください。

促音やへの対応は行っています。

抜けや不具合があったらご報告ください。

※ Google IMEで調べたところ、wが連続したときだけ他と違う挙動を示しましたが、面倒だったので他と同じようにただの促音になります。

interface ITree {
[key: string]: ITree | string;
}

const tree: ITree = {
a: 'ア', i: 'イ', u: 'ウ', e: 'エ', o: 'オ',
k: {
a: 'カ', i: 'キ', u: 'ク', e: 'ケ', o: 'コ',
y: { a: 'キャ', i: 'キィ', u: 'キュ', e: 'キェ', o: 'キョ' },
},
s: {
a: 'サ', i: 'シ', u: 'ス', e: 'セ', o: 'ソ',
h: { a: 'シャ', i: 'シ', u: 'シュ', e: 'シェ', o: 'ショ' },
y: { a: 'キャ', i: 'キィ', u: 'キュ', e: 'キェ', o: 'キョ' },
},
t: {
a: 'タ', i: 'チ', u: 'ツ', e: 'テ', o: 'ト',
h: { a: 'テャ', i: 'ティ', u: 'テュ', e: 'テェ', o: 'テョ' },
y: { a: 'チャ', i: 'チィ', u: 'チュ', e: 'チェ', o: 'チョ' },
s: { a: 'ツァ', i: 'ツィ', u: 'ツ', e: 'ツェ', o: 'ツォ' },
},
c: {
a: 'カ', i: 'シ', u: 'ク', e: 'セ', o: 'コ',
h: { a: 'チャ', i: 'チ', u: 'チュ', e: 'チェ', o: 'チョ' },
y: { a: 'チャ', i: 'チィ', u: 'チュ', e: 'チェ', o: 'チョ' },
},
q: {
a: 'クァ', i: 'クィ', u: 'ク', e: 'クェ', o: 'クォ',
},
n: {
a: 'ナ', i: 'ニ', u: 'ヌ', e: 'ネ', o: 'ノ', n: 'ン',
y: { a: 'ニャ', i: 'ニィ', u: 'ニュ', e: 'ニェ', o: 'ニョ' },
},
h: {
a: 'ハ', i: 'ヒ', u: 'フ', e: 'ヘ', o: 'ホ',
y: { a: 'ヒャ', i: 'ヒィ', u: 'ヒュ', e: 'ヒェ', o: 'ヒョ' },
},
f: {
a: 'ファ', i: 'フィ', u: 'フ', e: 'フェ', o: 'フォ',
y: { a: 'フャ', u: 'フュ', o: 'フョ' },
},
m: {
a: 'マ', i: 'ミ', u: 'ム', e: 'メ', o: 'モ',
y: { a: 'ミャ', i: 'ミィ', u: 'ミュ', e: 'ミェ', o: 'ミョ' },
},
y: { a: 'ヤ', i: 'イ', u: 'ユ', e: 'イェ', o: 'ヨ' },
r: {
a: 'ラ', i: 'リ', u: 'ル', e: 'レ', o: 'ロ',
y: { a: 'リャ', i: 'リィ', u: 'リュ', e: 'リェ', o: 'リョ' },
},
w: { a: 'ワ', i: 'ウィ', u: 'ウ', e: 'ウェ', o: 'ヲ' },
g: {
a: 'ガ', i: 'ギ', u: 'グ', e: 'ゲ', o: 'ゴ',
y: { a: 'ギャ', i: 'ギィ', u: 'ギュ', e: 'ギェ', o: 'ギョ' },
},
z: {
a: 'ザ', i: 'ジ', u: 'ズ', e: 'ゼ', o: 'ゾ',
y: { a: 'ジャ', i: 'ジィ', u: 'ジュ', e: 'ジェ', o: 'ジョ' },
},
j: {
a: 'ジャ', i: 'ジ', u: 'ジュ', e: 'ジェ', o: 'ジョ',
y: { a: 'ジャ', i: 'ジィ', u: 'ジュ', e: 'ジェ', o: 'ジョ' },
},
d: {
a: 'ダ', i: 'ヂ', u: 'ヅ', e: 'デ', o: 'ド',
h: { a: 'デャ', i: 'ディ', u: 'デュ', e: 'デェ', o: 'デョ' },
y: { a: 'ヂャ', i: 'ヂィ', u: 'ヂュ', e: 'ヂェ', o: 'ヂョ' },
},
b: {
a: 'バ', i: 'ビ', u: 'ブ', e: 'ベ', o: 'ボ',
y: { a: 'ビャ', i: 'ビィ', u: 'ビュ', e: 'ビェ', o: 'ビョ' },
},
v: {
a: 'ヴァ', i: 'ヴィ', u: 'ヴ', e: 'ヴェ', o: 'ヴォ',
y: { a: 'ヴャ', i: 'ヴィ', u: 'ヴュ', e: 'ヴェ', o: 'ヴョ' },
},
p: {
a: 'パ', i: 'ピ', u: 'プ', e: 'ペ', o: 'ポ',
y: { a: 'ピャ', i: 'ピィ', u: 'ピュ', e: 'ピェ', o: 'ピョ' },
},
x: {
a: 'ァ', i: 'ィ', u: 'ゥ', e: 'ェ', o: 'ォ',
y: {
a: 'ャ', i: 'ィ', u: 'ュ', e: 'ェ', o: 'ョ',
},
t: {
u: 'ッ',
s: {
u: 'ッ',
},
},
},
l: {
a: 'ァ', i: 'ィ', u: 'ゥ', e: 'ェ', o: 'ォ',
y: {
a: 'ャ', i: 'ィ', u: 'ュ', e: 'ェ', o: 'ョ',
},
t: {
u: 'ッ',
s: {
u: 'ッ',
},
},
},
};

export function convertRomanToKana(original: string) {
const str = original.replace(/[A-Za-z]/, s => String.fromCharCode(s.charCodeAt(0) - 65248)).toLowerCase(); // 全角→半角→小文字
let result = '';
let tmp = '';
let index = 0;
const len = str.length;
let node = tree;
const push = (char: string, toRoot = true) => {
result += char;
tmp = '';
node = toRoot ? tree : node;
};
while (index < len) {
const char = str.charAt(index);
if (char.match(/[a-z]/)) { // 英数字以外は考慮しない
if (char in node) {
const next = node[char];
if (typeof next === 'string') {
push(next);
} else {
tmp += original.charAt(index);
node = next;
}
index++;
continue;
}
const prev = str.charAt(index - 1);
if (prev && (prev === 'n' || prev === char)) { // 促音やnへの対応
push(prev === 'n' ? 'ン' : 'ッ', false);
}
if (node !== tree && char in tree) { // 今のノードがルート以外だった場合、仕切り直してチェックする
push(tmp);
continue;
}
}
push(tmp + char);
index++;
}
tmp = tmp.replace(/n$/, 'ン'); // 末尾のnは変換する
push(tmp);
return result;
}


参考

変換表はこちらの記事を元に叩き台を作成しました。

https://qiita.com/le_panda_noir/items/5be2639f77905879432a

書かれている通り、表を作成するのは地味に面倒でした…(IMEのカナ変換に生まれて初めて感謝しました)。

全角→半角変換はこちらの記事を参考にしました。

https://qiita.com/yamikoo@github/items/5dbcc77b267a549bdbae