こんにちは、おかちゃんせんせいです!
これまで番外編として、業務効率化のために作業を自動化する話を何回かピックアップしました。



もちろん自動化すればいいってものでもないですが、自動化することでミスや作業時間を減らし、他に生産的な仕事に時間を割くことができるようになることもあります。
自動化という意味では、プログラムを組まなくてもGoogleスプレッドシートの関数だけで、なんとかやりくりする方法もあります。
今回は、Googleスプレッドシートでマクロを組む際に使用する開発言語Google Apps Script(GAS)についての記事を特集します。
目次
メールアドレス作成の際に必要となるアカウント名
今回GASで実現したいことは、
メールアドレス作成の際に必要となるアカウント名を、特定のデータがあれば自動作成すること
です。
本当はそれ以外にもやりたいことはありますが、まずは上述の部分であれば一定のルールのもとで自動化できると考えました。
今のところは、フリガナを見てローマ字に変換して、メールアドレスのアカウント名にしていましたが……
ローマ字で表現するときに、
「H」なのか「F」なのか?
「N」なのか「M」なのか?
「Z」なのか「J」なのか?
よくわからなくなってしまうことがあるので、そんなときには下記サイトにかなり助けられました。
ただ、毎回サイト開いてコピペするのも面倒だし、同じ作業の繰り返し、かつ、一定のルールがあるから自動化できないかと思い、ネットを検索。
カタカナではないですが、GASでローマ字変換するスクリプトが公開されているサイトがありましたので、参考にさせていただきました。
問題は『ヘボン式』になっていないこと
ただ、スクリプトでひらがなの部分をカタカナに変えて、
「はい、終わり!」
というわけにはいかなかったのです。
なぜかというと、参考サイトで紹介されているスクリプトはあくまで訓令式ローマ字に変換するものであって、ヘボン式ではないから。
社内ではアカウント名をヘボン式で表記するルールがあったため、そのままでは使えません。
ちなみに、ヘボン式とはパスポートなどにも使えるローマ字表記で、たとえば下記のような例外ルールがあります。
※その他、個別・例外ルール
ヘボン式変換君サイトより引用
①「 ん 」 ・・・ 「 N 」に変換。ただし、「 NB 」・「 NM 」・「 NP 」となる場合は「 N→M 」で表記。
(例: かんだ→KANDA 、 なんば→NAMBA 、 へんみ→HEMMI 、 らんぽ→RAMPO)
②「 っ 」 ・・・ 後に続く子音を重ねる。ただし、「 CH 」が続く場合は「 TCH 」となる。
(例: べっぷ→BEPPU 、 こっち→KOTCHI )
③長音について ・・・ 母音の重なりを1文字で表記します。「 おお = OH 」表現も可能なようですが、任意表記のようなので変換君では考慮していません。
(例: とおる→TORU 、 おおにし→ONISHI )
他にも微妙に違う表記方法の文字があるので、そういったことは下記サイトを確認しながら編集することに決めました。
サンプルコード
自分が考えたコードではないので、詳しい説明は割愛しますが、、、
要は、まず変換したい文字列(string)を受け取って、splitで分割。該当するカタカナの組み合わせが見つかったら(3文字→2文字→1文字)、紐付けたローマ字で変換していく流れ。
基本的な流れは、すべて参考サイトからの引用になりますが、カタカナの組み合わせはヘボン式用に追加・修正しています。
/**
* カタカナ(フリガナ)をヘボン式ローマ字(人名・地名向けの短縮表記)に変換する関数
*
* @param {string} input - カタカナ
* @return {string} ローマ字
*/
function kana2romaji(input) {
// 1文字ずつ配列化して走査します(バッファで2~3文字の合成も見る)
const arrayedString = String(input).split("");
// 最終的な出力
let value = "";
// -----------------------------
// 3文字合成(triTable)
// 例:「リュウ」→ "ryu" のように “○ュウ/○ョウ” を一度に処理
// -----------------------------
const triTable = {
// ○ュウ 系(短縮)
"キュウ": "kyu", "シュウ": "shu", "チュウ": "chu",
"ニュウ": "nyu", "ヒュウ": "hyu", "ミュウ": "myu", "リュウ": "ryu",
// ○ョウ 系(短縮)
"キョウ": "kyo", "ショウ": "sho", "チョウ": "cho",
"ニョウ": "nyo", "ヒョウ": "hyo", "ミョウ": "myo", "リョウ": "ryo",
"ギョウ": "gyo", "ジョウ": "jo", "ヂョウ": "dyo",
"ビョウ": "byo","ピョウ": "pyo",
// ○ュウ(濁音など)
"ギュウ": "gyu", "ジュウ": "ju", "ビュウ": "byu", "ヂュウ": "dyu", "ピュウ": "pyu",
// ヴ系(3文字合成の保険)
"ウ゛ァ": "va","ウ゛ィ": "vi","ウ゛ゥ": "vu","ウ゛ェ": "ve","ウ゛ォ": "vo"
};
// -----------------------------
// 2文字合成(biTable)
// 例:「トウ」→ "to", 「チェ」→ "che"
// -----------------------------
const biTable = {
// 長音の短縮(人名表記に合わせて “ou” にせず短く)
// 「○オ」
"オオ": "o","コオ": "ko","ソオ": "so","トオ": "to","ノオ": "no",
"ホオ": "ho","モオ": "mo","ヨオ": "yo","ロオ": "ro",
// 「○ウ」
"クウ": "ku","スウ": "su","ツウ": "tsu","ヌウ": "nu","フウ": "fu",
"ムウ": "mu","ユウ": "yu","ルウ": "ru",
"コウ": "ko","ソウ": "so","トウ": "to","ノウ": "no",
"ホウ": "ho","モウ": "mo","ヨウ": "yo","ロウ": "ro",
"ゴウ": "go","ドウ": "do","ボウ": "bo","ポウ": "po",
// 外来音・拗音(自然な綴り)
"ジェ": "je","チェ": "che","シェ": "she",
"ティ": "ti","ディ": "di","デュ": "dyu","トゥ": "tu","ドゥ": "du",
"ファ": "fa","フィ": "fi","フェ": "fe","フォ": "fo",
"ウィ": "wi","ウェ": "we","ウォ": "wo",
"ヴァ": "va","ヴィ": "vi","ヴ": "vu","ヴェ": "ve","ヴォ": "vo",
// 通常の拗音
"ギャ": "gya","ギュ": "gyu","ギョ": "gyo",
"シャ": "sha","シュ": "shu","ショ": "sho",
"キャ": "kya","キュ": "kyu","キョ": "kyo",
"ジャ": "ja","ジュ": "ju","ジョ": "jo",
"チャ": "cha","チュ": "chu","チョ": "cho",
"ヂャ": "dya","ヂュ": "dyu","ヂョ": "dyo",
"デャ": "dha","デョ": "dho",
"ニャ": "nya","ニュ": "nyu","ニョ": "nyo",
"ヒャ": "hya","ヒュ": "hyu","ヒョ": "hyo",
"ビャ": "bya","ビュ": "byu","ビョ": "byo",
"ピャ": "pya","ピュ": "pyu","ピョ": "pyo",
"ミャ": "mya","ミュ": "myu","ミョ": "myo",
"リャ": "rya","リュ": "ryu","リョ": "ryo",
// 促音(ッ)=重子音
"ッカ": "kka","ッキ": "kki","ック": "kku","ッケ": "kke","ッコ": "kko",
"ッサ": "ssa","ッシ": "sshi","ッス": "ssu","ッセ": "sse","ッソ": "sso",
"ッタ": "tta","ッチ": "tchi","ッツ": "ttu","ッテ": "tte","ット": "tto",
"ッナ": "nna","ッニ": "nni","ッヌ": "nnu","ッネ": "nne","ッノ": "nno",
"ッハ": "hha","ッヒ": "hhi","ッフ": "ffu","ッヘ": "hhe","ッホ": "hho",
"ッマ": "mma","ッミ": "mmi","ッム": "mmu","ッメ": "mme","ッモ": "mmo",
"ッヤ": "yya","ッユ": "yyu","ッヨ": "yyo",
"ッラ": "rra","ッリ": "rri","ッル": "rru","ッレ": "rre","ッロ": "rro",
"ッワ": "wwa",
"ッガ": "gga","ッギ": "ggi","ッグ": "ggu","ッゲ": "gge","ッゴ": "ggo",
"ッザ": "zza","ッジ": "jji","ッズ": "zzu","ッゼ": "zze","ッゾ": "zzo",
"ッダ": "dda","ッヂ": "ddi","ッヅ": "ddu","ッデ": "dde","ッド": "ddo",
"ッバ": "bba","ッビ": "bbi","ッブ": "bbu","ッベ": "bbe","ッボ": "bbo",
"ッパ": "ppa","ッピ": "ppi","ップ": "ppu","ッペ": "ppe","ッポ": "ppo"
};
// -----------------------------
// 単音(uniTable)
// 例:「カ」→ "ka"、「ン」→ "n"、伸ばし棒「ー」は無視
// -----------------------------
const uniTable = {
"ア":"a","イ":"i","ウ":"u","エ":"e","オ":"o",
"カ":"ka","キ":"ki","ク":"ku","ケ":"ke","コ":"ko",
"サ":"sa","シ":"shi","ス":"su","セ":"se","ソ":"so",
"タ":"ta","チ":"chi","ツ":"tsu","テ":"te","ト":"to",
"ナ":"na","ニ":"ni","ヌ":"nu","ネ":"ne","ノ":"no",
"ハ":"ha","ヒ":"hi","フ":"fu","ヘ":"he","ホ":"ho",
"マ":"ma","ミ":"mi","ム":"mu","メ":"me","モ":"mo",
"ヤ":"ya","ユ":"yu","ヨ":"yo",
"ラ":"ra","リ":"ri","ル":"ru","レ":"re","ロ":"ro",
"ワ":"wa","ヲ":"wo","ン":"n",
"ガ":"ga","ギ":"gi","グ":"gu","ゲ":"ge","ゴ":"go",
"ザ":"za","ジ":"ji","ズ":"zu","ゼ":"ze","ゾ":"zo",
"ダ":"da","ヂ":"ji","ヅ":"zu","デ":"de","ド":"do",
"バ":"ba","ビ":"bi","ブ":"bu","ベ":"be","ボ":"bo",
"パ":"pa","ピ":"pi","プ":"pu","ペ":"pe","ポ":"po",
"ァ":"xa","ィ":"xi","ゥ":"xu","ェ":"xe","ォ":"xo",
"ー":"" // 伸ばし棒は短縮表記では無視(例:ファミリー→famiri)
};
// 「母音(拗音含む)」判定に使うセット
const isVowelKana = ch =>
["ア","イ","ウ","エ","オ","ァ","ィ","ゥ","ェ","ォ","ヤ","ユ","ヨ","ャ","ュ","ョ"].includes(ch);
// 入力全体がいきなり 3 文字/2 文字の合成パターンに一致する場合の早期リターン
if (triTable[input] !== undefined) return triTable[input];
if (biTable[input] !== undefined) return biTable[input];
// 先頭文字が 2 文字/3 文字合成の“候補になり得るか”の目印セット
const biCheck = {};
for (const key in biTable) biCheck[key[0]] = true;
const triCheck = {};
for (const key in triTable) {
triCheck[key[0] + key[1]] = true; // tri の先頭 2 文字
biCheck[key[0]] = true; // tri の先頭 1 文字も「保留対象」に含める
}
// ここから本体:1~3文字のバッファ `buf` を使って逐次変換
let buf = "";
for (let i = 0; i < arrayedString.length; i++) {
const ch = arrayedString[i];
buf += ch;
if (buf.length === 3) {
// 3文字バッファ
if (triTable[buf] !== undefined) {
value += triTable[buf];
} else {
// 3文字ではマッチしない → 先頭2文字 or 1文字ずつで処理
const a = buf[0], b = buf[1], c = buf[2];
if (biTable[a + b] !== undefined) {
value += biTable[a + b];
} else {
value += (uniTable[a] !== undefined ? uniTable[a] : a);
value += (uniTable[b] !== undefined ? uniTable[b] : b);
}
value += (uniTable[c] !== undefined ? uniTable[c] : c);
}
buf = "";
} else if (buf.length === 2) {
// 2文字バッファ
if (triCheck[buf] !== undefined) {
// 3文字合成の可能性があるので、もう1文字待つ
} else if (biTable[buf] !== undefined) {
// 2文字合成に一致 → ただし長音短縮の“先読み”例外を確認
const next = arrayedString[i + 1];
const yRowLongU = (buf === "ヤウ" || buf === "ユウ" || buf === "ヨウ"); // Yu 系は短縮優先
if (buf[1] === "ウ" && next && isVowelKana(next) && !yRowLongU) {
// 直後が母音 → 短縮しない(例:ノウ + エ)
value += (uniTable[buf[0]] !== undefined ? uniTable[buf[0]] : buf[0]); // 1文字目だけ確定
buf = buf[1]; // 「ウ」を次ループで通常処理
} else {
// 通常は短縮(例:トウ→to、ヨウ→yo)
value += biTable[buf];
buf = "";
}
} else {
// 2文字では未定義 → 1文字目だけ確定し、2文字目が“実在する連結候補”なら保留
const a = buf[0], b = buf[1];
value += (uniTable[a] !== undefined ? uniTable[a] : a);
const next = arrayedString[i + 1];
const pair = next ? (b + next) : null;
const hasBiWithNext = pair ? (biTable[pair] !== undefined) : false; // 例:ド + ウ →「ドウ」
const hasTriWithNext = pair ? (triCheck[pair] !== undefined) : false; // 例:キ + ョ →「キョ」(tri先頭2字)
if (hasBiWithNext || hasTriWithNext) {
buf = b; // 次の周回で 2文字/3文字合成に挑戦
} else {
value += (uniTable[b] !== undefined ? uniTable[b] : b);
buf = "";
}
}
} else if (i === arrayedString.length - 1) {
// 残り1文字で入力が終わるときは確定
value += (uniTable[ch] !== undefined ? uniTable[ch] : ch);
buf = "";
} else if (biCheck[buf] !== undefined) {
// 2文字/3文字合成に発展する可能性があるので一旦キープ
// 何もしない(次の文字を待つ)
} else {
// 合成の見込みがない単独文字はその場で確定
value += (uniTable[ch] !== undefined ? uniTable[ch] : ch);
buf = "";
}
}
// ループ終了後に、バッファに取り残しがあれば出力(末尾落ち防止)
if (buf.length === 1) {
value += (uniTable[buf] !== undefined ? uniTable[buf] : buf);
buf = "";
} else if (buf.length === 2) {
if (biTable[buf] !== undefined) {
value += biTable[buf];
} else {
const a = buf[0], b = buf[1];
value += (uniTable[a] !== undefined ? uniTable[a] : a);
value += (uniTable[b] !== undefined ? uniTable[b] : b);
}
buf = "";
}
return value;
}
(2022/09/27追記)
テストサンプルをいくつか試していましたら、子音が続く場合(例:テッタ)に正常に動作しないことが判明しました。
修正内容は、下記2箇所です。
・uniTableからャ・ュ・ョ・ッを除外
・buf.length == 2の場合に子音が続くケースを追加条件分岐
(2023/05/31追記)
・文字数の判定で最後が3文字の場合に、bufに値が残ってしまう不具合を解消
(2024/01/25追記)
・クドウの場合、kudouになってしまう不具合を解消
(2024/06/03追記)
・スバラシイの場合、suundefinedrashiiになってしまう不具合を解消
⇒「ば」がunderfinedになっていたので修正
(2024/11/14追記)
・最後の一文字がローマ字変換されない不具合を修正
(2025/03/05追記)
・半角スペースおよび全角スペースがあるとunderfinedになる現象を改修
(2025/09/03改修)
・一部正しく変換できないフリガナ(例. イノウエ)があったため改修し、コードの体裁を全体的に整理
改良版サンプルコード
※追記(2024年09月10日)※
2024年9月時点で質問してくださった方の内容を反映させるために、
引数がカタカナでもひらがなでもどちらの場合でも最終的にローマ字に変換する関数を作成しました。
追加した関数はこちらのconvertCharacters関数です。
/**
* カタカナ⇔ひらがなに変換する関数
*
* @param {string} input - カタカナ or ひらがな
* @param {string} kind - 変換したい入力文字種(カタカナ:0、ひらがな:1)
* @return {string} カタカナ→ひらがな or ひらがな→カタカナ
*/
function convertCharacters (input,kind) {
if(input == null) return "";
// ひらがな、カタカナの一覧を文字列として取得する
// ※文字位置はそれぞれ対応している(例:あ(2番目)⇔ア(2番目))
const hiragana = "ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろわをんゎゐゑゕゖ";
const katakana = "ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロワヲンヮヰヱヵヶ";
// デフォルト:変換前(ひらがな)、変換後(カタカナ)
let before = hiragana;
let after = katakana;
const charArr = input.split('');
// 入力文字にひらがなが含まれておらず、かつ、入力文字がひらがなの場合
// 変換前をカタカナ、変換後をひらがなとする
if(hiragana.indexOf(charArr[0]) === -1 && kind === 1){
before = katakana;
after = hiragana;
// 入力文字にひらがなが含まれておらず、かつ、入力文字がカタカナ
// または、入力文字にひらがなが含まれておらず、かつ、入力文字がひらがなの場合
// 特に変換する必要はないため入力文字を返す
}else if(hiragana.indexOf(charArr[0]) === -1 && kind === 0
|| hiragana.indexOf(charArr[0]) !== -1 && kind === 1){
return input;
}
console.log(input);
// 入力した文字を一文字ずつ取り出して変換する
const afterText = charArr.map(function(char) {
var index = before.indexOf(char);
return index >= 0 ? after[index] : char;
}).join('');
console.log(afterText);
return afterText;
}
そして、完成形が下記になります。
カタカナ・ひらがな、どちらのバージョンでも対応したい場合には下記コードを活用してください。
/**
* カタカナ(フリガナ)をヘボン式ローマ字(人名・地名向けの短縮表記)に変換する関数
*
* @param {string} input - カタカナ
* @return {string} ローマ字
*/
function kana2romaji(input) {
// 1文字ずつ配列化して走査します(バッファで2~3文字の合成も見る)
const arrayedString = String(input).split("");
// 最終的な出力
let value = "";
// -----------------------------
// 3文字合成(triTable)
// 例:「リュウ」→ "ryu" のように “○ュウ/○ョウ” を一度に処理
// -----------------------------
const triTable = {
// ○ュウ 系(短縮)
"キュウ": "kyu", "シュウ": "shu", "チュウ": "chu",
"ニュウ": "nyu", "ヒュウ": "hyu", "ミュウ": "myu", "リュウ": "ryu",
// ○ョウ 系(短縮)
"キョウ": "kyo", "ショウ": "sho", "チョウ": "cho",
"ニョウ": "nyo", "ヒョウ": "hyo", "ミョウ": "myo", "リョウ": "ryo",
"ギョウ": "gyo", "ジョウ": "jo", "ヂョウ": "dyo",
"ビョウ": "byo","ピョウ": "pyo",
// ○ュウ(濁音など)
"ギュウ": "gyu", "ジュウ": "ju", "ビュウ": "byu", "ヂュウ": "dyu", "ピュウ": "pyu",
// ヴ系(3文字合成の保険)
"ウ゛ァ": "va","ウ゛ィ": "vi","ウ゛ゥ": "vu","ウ゛ェ": "ve","ウ゛ォ": "vo"
};
// -----------------------------
// 2文字合成(biTable)
// 例:「トウ」→ "to", 「チェ」→ "che"
// -----------------------------
const biTable = {
// 長音の短縮(人名表記に合わせて “ou” にせず短く)
// 「○オ」
"オオ": "o","コオ": "ko","ソオ": "so","トオ": "to","ノオ": "no",
"ホオ": "ho","モオ": "mo","ヨオ": "yo","ロオ": "ro",
// 「○ウ」
"クウ": "ku","スウ": "su","ツウ": "tsu","ヌウ": "nu","フウ": "fu",
"ムウ": "mu","ユウ": "yu","ルウ": "ru",
"コウ": "ko","ソウ": "so","トウ": "to","ノウ": "no",
"ホウ": "ho","モウ": "mo","ヨウ": "yo","ロウ": "ro",
"ゴウ": "go","ドウ": "do","ボウ": "bo","ポウ": "po",
// 外来音・拗音(自然な綴り)
"ジェ": "je","チェ": "che","シェ": "she",
"ティ": "ti","ディ": "di","デュ": "dyu","トゥ": "tu","ドゥ": "du",
"ファ": "fa","フィ": "fi","フェ": "fe","フォ": "fo",
"ウィ": "wi","ウェ": "we","ウォ": "wo",
"ヴァ": "va","ヴィ": "vi","ヴ": "vu","ヴェ": "ve","ヴォ": "vo",
// 通常の拗音
"ギャ": "gya","ギュ": "gyu","ギョ": "gyo",
"シャ": "sha","シュ": "shu","ショ": "sho",
"キャ": "kya","キュ": "kyu","キョ": "kyo",
"ジャ": "ja","ジュ": "ju","ジョ": "jo",
"チャ": "cha","チュ": "chu","チョ": "cho",
"ヂャ": "dya","ヂュ": "dyu","ヂョ": "dyo",
"デャ": "dha","デョ": "dho",
"ニャ": "nya","ニュ": "nyu","ニョ": "nyo",
"ヒャ": "hya","ヒュ": "hyu","ヒョ": "hyo",
"ビャ": "bya","ビュ": "byu","ビョ": "byo",
"ピャ": "pya","ピュ": "pyu","ピョ": "pyo",
"ミャ": "mya","ミュ": "myu","ミョ": "myo",
"リャ": "rya","リュ": "ryu","リョ": "ryo",
// 促音(ッ)=重子音
"ッカ": "kka","ッキ": "kki","ック": "kku","ッケ": "kke","ッコ": "kko",
"ッサ": "ssa","ッシ": "sshi","ッス": "ssu","ッセ": "sse","ッソ": "sso",
"ッタ": "tta","ッチ": "tchi","ッツ": "ttu","ッテ": "tte","ット": "tto",
"ッナ": "nna","ッニ": "nni","ッヌ": "nnu","ッネ": "nne","ッノ": "nno",
"ッハ": "hha","ッヒ": "hhi","ッフ": "ffu","ッヘ": "hhe","ッホ": "hho",
"ッマ": "mma","ッミ": "mmi","ッム": "mmu","ッメ": "mme","ッモ": "mmo",
"ッヤ": "yya","ッユ": "yyu","ッヨ": "yyo",
"ッラ": "rra","ッリ": "rri","ッル": "rru","ッレ": "rre","ッロ": "rro",
"ッワ": "wwa",
"ッガ": "gga","ッギ": "ggi","ッグ": "ggu","ッゲ": "gge","ッゴ": "ggo",
"ッザ": "zza","ッジ": "jji","ッズ": "zzu","ッゼ": "zze","ッゾ": "zzo",
"ッダ": "dda","ッヂ": "ddi","ッヅ": "ddu","ッデ": "dde","ッド": "ddo",
"ッバ": "bba","ッビ": "bbi","ッブ": "bbu","ッベ": "bbe","ッボ": "bbo",
"ッパ": "ppa","ッピ": "ppi","ップ": "ppu","ッペ": "ppe","ッポ": "ppo"
};
// -----------------------------
// 単音(uniTable)
// 例:「カ」→ "ka"、「ン」→ "n"、伸ばし棒「ー」は無視
// -----------------------------
const uniTable = {
"ア":"a","イ":"i","ウ":"u","エ":"e","オ":"o",
"カ":"ka","キ":"ki","ク":"ku","ケ":"ke","コ":"ko",
"サ":"sa","シ":"shi","ス":"su","セ":"se","ソ":"so",
"タ":"ta","チ":"chi","ツ":"tsu","テ":"te","ト":"to",
"ナ":"na","ニ":"ni","ヌ":"nu","ネ":"ne","ノ":"no",
"ハ":"ha","ヒ":"hi","フ":"fu","ヘ":"he","ホ":"ho",
"マ":"ma","ミ":"mi","ム":"mu","メ":"me","モ":"mo",
"ヤ":"ya","ユ":"yu","ヨ":"yo",
"ラ":"ra","リ":"ri","ル":"ru","レ":"re","ロ":"ro",
"ワ":"wa","ヲ":"wo","ン":"n",
"ガ":"ga","ギ":"gi","グ":"gu","ゲ":"ge","ゴ":"go",
"ザ":"za","ジ":"ji","ズ":"zu","ゼ":"ze","ゾ":"zo",
"ダ":"da","ヂ":"ji","ヅ":"zu","デ":"de","ド":"do",
"バ":"ba","ビ":"bi","ブ":"bu","ベ":"be","ボ":"bo",
"パ":"pa","ピ":"pi","プ":"pu","ペ":"pe","ポ":"po",
"ァ":"xa","ィ":"xi","ゥ":"xu","ェ":"xe","ォ":"xo",
"ー":"" // 伸ばし棒は短縮表記では無視(例:ファミリー→famiri)
};
// 「母音(拗音含む)」判定に使うセット
const isVowelKana = ch =>
["ア","イ","ウ","エ","オ","ァ","ィ","ゥ","ェ","ォ","ヤ","ユ","ヨ","ャ","ュ","ョ"].includes(ch);
// 入力全体がいきなり 3 文字/2 文字の合成パターンに一致する場合の早期リターン
if (triTable[input] !== undefined) return triTable[input];
if (biTable[input] !== undefined) return biTable[input];
// 先頭文字が 2 文字/3 文字合成の“候補になり得るか”の目印セット
const biCheck = {};
for (const key in biTable) biCheck[key[0]] = true;
const triCheck = {};
for (const key in triTable) {
triCheck[key[0] + key[1]] = true; // tri の先頭 2 文字
biCheck[key[0]] = true; // tri の先頭 1 文字も「保留対象」に含める
}
// ここから本体:1~3文字のバッファ `buf` を使って逐次変換
let buf = "";
for (let i = 0; i < arrayedString.length; i++) {
const ch = arrayedString[i];
buf += ch;
if (buf.length === 3) {
// 3文字バッファ
if (triTable[buf] !== undefined) {
value += triTable[buf];
} else {
// 3文字ではマッチしない → 先頭2文字 or 1文字ずつで処理
const a = buf[0], b = buf[1], c = buf[2];
if (biTable[a + b] !== undefined) {
value += biTable[a + b];
} else {
value += (uniTable[a] !== undefined ? uniTable[a] : a);
value += (uniTable[b] !== undefined ? uniTable[b] : b);
}
value += (uniTable[c] !== undefined ? uniTable[c] : c);
}
buf = "";
} else if (buf.length === 2) {
// 2文字バッファ
if (triCheck[buf] !== undefined) {
// 3文字合成の可能性があるので、もう1文字待つ
} else if (biTable[buf] !== undefined) {
// 2文字合成に一致 → ただし長音短縮の“先読み”例外を確認
const next = arrayedString[i + 1];
const yRowLongU = (buf === "ヤウ" || buf === "ユウ" || buf === "ヨウ"); // Yu 系は短縮優先
if (buf[1] === "ウ" && next && isVowelKana(next) && !yRowLongU) {
// 直後が母音 → 短縮しない(例:ノウ + エ)
value += (uniTable[buf[0]] !== undefined ? uniTable[buf[0]] : buf[0]); // 1文字目だけ確定
buf = buf[1]; // 「ウ」を次ループで通常処理
} else {
// 通常は短縮(例:トウ→to、ヨウ→yo)
value += biTable[buf];
buf = "";
}
} else {
// 2文字では未定義 → 1文字目だけ確定し、2文字目が“実在する連結候補”なら保留
const a = buf[0], b = buf[1];
value += (uniTable[a] !== undefined ? uniTable[a] : a);
const next = arrayedString[i + 1];
const pair = next ? (b + next) : null;
const hasBiWithNext = pair ? (biTable[pair] !== undefined) : false; // 例:ド + ウ →「ドウ」
const hasTriWithNext = pair ? (triCheck[pair] !== undefined) : false; // 例:キ + ョ →「キョ」(tri先頭2字)
if (hasBiWithNext || hasTriWithNext) {
buf = b; // 次の周回で 2文字/3文字合成に挑戦
} else {
value += (uniTable[b] !== undefined ? uniTable[b] : b);
buf = "";
}
}
} else if (i === arrayedString.length - 1) {
// 残り1文字で入力が終わるときは確定
value += (uniTable[ch] !== undefined ? uniTable[ch] : ch);
buf = "";
} else if (biCheck[buf] !== undefined) {
// 2文字/3文字合成に発展する可能性があるので一旦キープ
// 何もしない(次の文字を待つ)
} else {
// 合成の見込みがない単独文字はその場で確定
value += (uniTable[ch] !== undefined ? uniTable[ch] : ch);
buf = "";
}
}
// ループ終了後に、バッファに取り残しがあれば出力(末尾落ち防止)
if (buf.length === 1) {
value += (uniTable[buf] !== undefined ? uniTable[buf] : buf);
buf = "";
} else if (buf.length === 2) {
if (biTable[buf] !== undefined) {
value += biTable[buf];
} else {
const a = buf[0], b = buf[1];
value += (uniTable[a] !== undefined ? uniTable[a] : a);
value += (uniTable[b] !== undefined ? uniTable[b] : b);
}
buf = "";
}
return value;
}
/**
* カタカナ⇔ひらがなに変換する関数
*
* @param {string} input - カタカナ or ひらがな
* @param {string} kind - 変換したい入力文字種(カタカナ:0、ひらがな:1)
* @return {string} カタカナ→ひらがな or ひらがな→カタカナ
*/
function convertCharacters (input,kind) {
if(input == null) return "";
// ひらがな、カタカナの一覧を文字列として取得する
// ※文字位置はそれぞれ対応している(例:あ(2番目)⇔ア(2番目))
const hiragana = "ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろわをんゎゐゑゕゖ";
const katakana = "ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロワヲンヮヰヱヵヶ";
// デフォルト:変換前(ひらがな)、変換後(カタカナ)
let before = hiragana;
let after = katakana;
const charArr = input.split('');
// 入力文字にひらがなが含まれておらず、かつ、入力文字がひらがなの場合
// 変換前をカタカナ、変換後をひらがなとする
if(hiragana.indexOf(charArr[0]) === -1 && kind === 1){
before = katakana;
after = hiragana;
// 入力文字にひらがなが含まれておらず、かつ、入力文字がカタカナ
// または、入力文字にひらがなが含まれておらず、かつ、入力文字がひらがなの場合
// 特に変換する必要はないため入力文字を返す
}else if(hiragana.indexOf(charArr[0]) === -1 && kind === 0
|| hiragana.indexOf(charArr[0]) !== -1 && kind === 1){
return input;
}
console.log(input);
// 入力した文字を一文字ずつ取り出して変換する
const afterText = charArr.map(function(char) {
var index = before.indexOf(char);
return index >= 0 ? after[index] : char;
}).join('');
console.log(afterText);
return afterText;
}
(2024/09/10追記)
・引数がひらがなでもカタカナでもどちらの場合でも対応できるバージョンを作成
(2025/09/03改修)
・一部正しく変換できないフリガナ(例. イノウエ)があったため改修し、コードの体裁を全体的に整理
まだコードが出来上がったばかりで十分検証ができていませんので、もし不備がありましたらご指摘いただけますと幸いです!
最後まで読んでいただき、ありがとうございました!
トウキョウがうまく変換できませんでした
ご指摘いただきありがとうございます!
不具合を修正し、トウキョウ→tokyoと表示されるようにいたしました。
「くどう」が「kudou」になってしまっております。
調整いただけると助かります。
ご指摘いただきありがとうございます!
不具合を解消いたしましたので、ご確認よろしくお願いいたします。
スバラシイ→suundefinedrashiiとなってしまいます。
調整していただけると幸いです。
みそに様
不具合についてご指摘いただきありがとうございます。
修正いたしましたので、現在更新したコードで動作ご確認よろしくお願いいたします。
初めまして
拡張機能からスクリプトをコピーして保存。
その後、実行してみるとエラーで、
TypeError: Cannot read properties of undefined (reading ‘split’)
と出てしまいます。
これは何が原因なのでしょうか。
hiro様
はじめまして。
おそらくkana2romaji関数をそのまま実行されたのではないでしょうか。
kana2romajiには引数stringに文字列を渡してあげる必要があります。
hiro様がカナからローマ字に変換したい文字列をkana2romajiに渡してみていただければと思います。
下記が活用例になります。
function main(){
const kana = “ヤマダタロウ”;
const romaji = kana2romaji(kana);
console.log(romaji);
}
早々のお返事ありがとうございます。
全くの素人なのですが、スプレッドシート内でセル内のひらがなをローマ字に変換する関数ができないかなと模索しております。
セル内に関数で特定のセルのひらがなをローマ字にすることはできないのでしょうか?
そちらの内容と、本日更新した記事の内容を基にすれば実現可能です。
https://hajimarinomachi.com/gas-spreedsheet-function/
また、ご質問いただいた内容を反映させるため、
先ほどひらがなでもカタカナでも対応できるバージョンを公開しました。
合わせてご確認ください。
早々のお返事ありがとうございます。
全くの素人なのですが、スプレッドシート内でセル内のひらがなをローマ字に変換する関数ができないかなと模索しております。
セル内に関数で特定のセルのひらがなをローマ字にすることはできないのでしょうか?
お世話になります。
以下のキーワードがうまく表示されず困ってしまっております。ご相談可能でございますでしょうか。
ヤハギ = yaha(gi がでない)
オオハシ = oha(shi がでない)
カヨ = ka(yo がでない)
お世話になります。
不具合のご指摘ありがとございます。
ご指摘いただいた内容について、修正いたしました。
現在のスクリプトで再度検証をよろしくお願いいたします。
反映を確認できました、、!
早急にご対応いただきましてありがとうございました。
氏名の変換に利用させてもらっています。
「スペース」があると「undefined」になるものがあります。
イシバシ マサヤishibashiundefinedmasaya
「スペース」を削除すると正常です。
イシバシマサヤishibashimasaya
「スペース」ありでも正常に変換されるものもあります。
コンドウ ヨシミkondo yoshimi
たかただ様
コメントいただきありがとうございます。
スクリプトはスペースを考慮していなかったため、スペースがあるとunderfinedになってしまっていました。
そこで、半角スペースまたは全角スペースがある場合には、スペースを保持して変換するように改修いたしました。
改修版を公開しておりますので、ご確認ください。
おかちゃんせんせい様
早々に改修いただき、ありがとうございました。
完璧です。
以下のように表示されないところがあります
キクチ ハルカ → kiku haruka
ナンブ マナト → nambu manato
イシバシ マサヤ → ishiba masaya
ナガオ シズル → naga shizuru
ヒラオ カンタ → hira kanta
P.S.
ナンブ マナト → nambu manato
「nanbu」が希望です