背景
自分のサイトに、電話番号を入れると、読み取るだけで発信画面が開く QR コードを作るツールを置いています。
QR の中身は tel:0312345678 のような文字列で、これを tel URI と呼ぶ。
スマホのカメラが tel: で始まる文字列を読むと、電話アプリに番号が入った状態で開く、という仕組みだ。
名刺に載せたくて作ったのですが、自分でいろいろな書き方の番号を入れてみたら、思ったより素直に壊れました…。
結論
数字以外を消す前に、normalize('NFKC') で全角を半角にそろえます。
「+81 (0)3」のような書き方の (0) は、国番号の後ろでは落とします。
内線番号は、数字だけ残すと本番号にくっつくので、「内線」より後ろを先に捨てます。
ツールの処理
ツールでは、入力欄の文字列 raw を下記で整えてから、先頭に tel: を付けています。
先頭の + だけを覚えておいて、あとは数字以外を全部消す、というだけの処理です。
function normNo(raw) {
raw = (raw || '').trim();
var plus = raw.charAt(0) === '+' ? '+' : '';
var digits = raw.replace(/[^0-9]/g, '');
return plus + digits;
}
/[^0-9]/g は「半角の 0〜9 以外の文字すべて」という意味の正規表現です。
ハイフンや空白、かっこは、これでまとめて消えます。
いろいろ入れてみた
下記の6通りを入れて、できる tel: を確かめました(Node.js v24.11.1 で、ツールの関数を写して実行)。
"03-1234-5678" -> tel:0312345678
"03-1234-5678" -> tel:
"03-1234-5678(内線 210)" -> tel:0312345678210
"+81 (0)3-1234-5678" -> tel:+810312345678
"+81 3 1234 5678" -> tel:
"+81-3-1234-5678" -> tel:+81312345678
全角で入れた2つは、空になりました。
全角の「0」は、見た目は数字でも半角の「0」とは別の文字(U+FF10)なので、[0-9] に一致しない。
\d も試しましたが、JavaScript の \d は u フラグを付けても半角の 0〜9 だけなので、結果は同じでした。
ツール側は空になると「電話番号を入力してください。」と出すので、入力したのに怒られる、という見え方になります。
+81 (0)3-1234-5678 は +810312345678 になりました。
この (0) は、国内からかけるときだけ付ける先頭の 0 を「ここに入ります」と示す書き方です。
国番号の +81 を付けて国際形式でかけるときは、その 0 は付けないので、正しくは +81312345678 だ。
内線の例は、210 が本番号の後ろにつながって、存在しない13桁の番号になっています。
直すならこう
下記のように、NFKC で全角を半角にし、内線より後ろを捨て、国番号の後ろの (0) を落としてから数字だけ残します。
NFKC は、Unicode の「互換文字」を標準的な形にそろえる正規化で、全角英数字は半角になる。
function normTel(raw) {
var s = String(raw || '').normalize('NFKC').trim();
s = s.split(/内線|ext\.?|x(?=\s*\d)/i)[0];
var plus = s.charAt(0) === '+' ? '+' : '';
if (plus) s = s.replace(/^\+(\d+)\s*\(0\)/, '+$1');
return plus + s.replace(/[^0-9]/g, '');
}
同じ入力に、ext. の例を1つ足して実行した結果は下記です。
"03-1234-5678" -> tel:0312345678
"03-1234-5678" -> tel:0312345678
"03-1234-5678(内線 210)" -> tel:0312345678
"+81 (0)3-1234-5678" -> tel:+81312345678
"+81 3 1234 5678" -> tel:+81312345678
"03-1234-5678 ext. 210" -> tel:0312345678
全角の2つも、+81 (0)3 も、期待どおりの番号になりました。
全角の「+」も NFKC で半角の「+」になるので、国番号の判定もそのまま通っています。
ツールのほうも、この形に差し替える予定です。
内線を捨てる理由
tel URI には内線を表す書き方もあるらしいのですが、スマホで読み取ったときにどう扱われるかは端末しだいな気がします。
QR から発信画面を開く用途なら、代表番号で確実につながるほうが大事だと思っています。
なので、内線は QR に入れず、名刺の文字のほうで伝える割り切りにしました。
(0) を国番号の直後に限って落としているのも、それ以外の場所のかっこは市外局番の区切りとして使われることがあるからです。
作ったツールはここに置いています。
https://hashitosystem.com/tools/telqr/
電話番号から数字を取り出すときは、数字以外を消す前に normalize('NFKC') を通し、国番号の後ろの (0) と内線を先に処理します。
本記事はAI補助で執筆した、個人開発の紹介記事です。