# Punycode

**Punycode**（ピュニコード、プニコード）とは、国際化ドメイン名で使われる文字符号化方式で、RFC 3492 で定義されている。Unicode で書かれた文字列をDNSで使用可能な、アルファベット（大文字小文字を区別しない）、数字、ハイフンのみの文字列に変換する。

## 概要

ドメイン名として Punycode を使用する際は、ピリオド（`.`）で区切られたドメイン名の階層レベルごとにプレフィックスとして「`xn--`」を使用し、エンコードされた文字列を続ける。大文字と小文字は区別されない。

| 可読なドメイン名 | Punycodeでのドメイン名 |
| --- | --- |
| `ドメイン名例.jp` | `xn--eckwd4c7cu47r2wf.jp` |
| `ウィキペディア.ドメイン名例.jp` | `xn--cckbak0byl6e.xn--eckwd4c7cu47r2wf.jp` |
| `可愛いね.そうでもないよ` | `xn--n8j5d625jn9k.xn--n8jd2ewbp7lub`  |

## エンコーディング手順

この節では、 Punycode のエンコーディング手順を、「bücher」がどのようにして「`bcher-kva`」と変換されるかを例にとって、説明する。

### ASCII文字の分離

最初に、入力文字列中にあるすべてのASCII文字を残し、ASCII文字以外の文字を取り去る。また、ASCII文字以外の文字がある場合は、最後に区切り文字（ハイフン、 `-`）を追加する。

| 入力文字列 | 処理後 |
| --- | --- |
| `bücher` | `bcher-` |
| `日本Japan` | `Japan-` |
| `MajiでKoiする5秒前` | `MajiKoi5-` |

### 非ASCII文字の挿入をコード番号としてエンコード

次のエンコーディング手順を理解するために、先にデコーダの動作を理解する必要がある。

デコーダは、2つの状態変数 *i* と *n* を持つオートマトンである。 *i* は分離した後のASCII文字列への挿入位置を表し、その範囲は0 (文字列の先頭への挿入を表す) からASCII文字列の長さ (文字列の末尾への挿入を表す) である。従って、この例では0–5となる。*n*は挿入する文字のコードポイントである。

|  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 分離後文字列 |  | b |  | c |  | h |  | e |  | r |  |
| 挿入位置*i* | 0 |  | 1 |  | 2 |  | 3 |  | 4 |  | 5 |

*i* は0から始まり、*n* は128 (非ASCII文字の最初のコードポイント) から始まる。状態遷移は単調であり、遷移すると *i* が1だけ増加する。ただし *i* がすでに最大値の場合は *n* が1増加し、*i* は0に戻る。つまり、挿入するべき箇所がなくなると、挿入すべき文字が1つ後の物となる。要するに、各状態遷移の際、*n*で表されるコードポイントを文字列に挿入するか、挿入をスキップするか、という動きとなる。

コード番号は、エンコーダによって生成される数値であり、デコーダが文字を挿入する前にスキップすべき挿入可能位置がいくつあるかを数値化したものである。"ü" のUnicodeコードポイントはU+00FC, 10進数で表すと252である。よって、ü の字を文字列の1文字目の後ろに挿入するには、ü より前にある124 (251 - 127 = 124) 種類の非ASCII文字が、"bcher" の中に6か所ある挿入ポイントに挿入されるのをスキップし、さらに0文字目 (つまり文字列の先頭) にüが挿入されるのをスキップする必要がある。したがって、デコーダには必要な1文字を挿入するために、(124 × 6) + 1 = 745回の非ASCII文字挿入をスキップするようデコーダに伝える必要がある。

### コード番号をASCII文字列として再変換

Punycode は、このコード番号を表すためにリトルエンディアンの一般化可変長整数を使用する。例として、コード番号745を「`kva`」と表す方法を示す。

一般化可変長整数では、各々の桁に異なる閾値を設け、これより小さい数字の表れる桁を最大桁とすることで、数字列の区切りを決める。 Punycode で用いられるリトルエンディアンの場合、小さい桁から表記されるため、先読みなしで任意の桁数の数字を表記できる。

Punycode の場合は、各桁に使える数字として36種の文字を使用する。アルファベット（大文字小文字を区別しない）の'`a`'から'`z`'が0から25を表し、数字の'`0`'から'`9`'が26から35を表す (このため、後述する式に出てくる底 (base) は、36となる)。

|  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 10進数 | 0 | 1 | 2 | 3 | … | 24 | 25 | 26 | … | 34 | 35 |
| 一般化可変長整数 | a | b | c | d | … | y | z | 0 | … | 8 | 9 |

したがって、「`kva`」は「10 21 0」を表す。ここで閾値は (1 1 26) であるとする。最初の桁 (**`k`**つまり**10**) の重みは1である。1桁目は1–35までの範囲の値をとるため、2桁目 (**`v`**つまり**21**) の重みは35となる。2桁目は同様に1–35の範囲の値をとるため、3桁目 (**`a`**つまり**0**) の重みは 352 = 1225 である。また、3桁目の値**0**は3桁目の閾値**26**よりも小さいため、ここで1つ目の数字列は終了であることがわかる。したがって「`kva`」は 10 × 1 + 21 × 35 + 0 × 1225 = 745 を表していることがわかる。

各桁の重み*w*(*j*)および閾値*t*(*j*)の値は、以下の式で計算される:

 WIKINIUM\_MATH\_PH\_0
 WIKINIUM\_MATH\_PH\_2
 WIKINIUM\_MATH\_PH\_4
（ただし*t*(*j*)は上式を*tmin*以下なら*tmin*、*tmax*以上なら*tmax*とクランプしたものである。 *bias* は状態変数である。）
*base* = 36, *tmin* = 1, *tmax* = 26
2つ目の文字を挿入する際には、さらにエンコードされたコード番号を繋げていけばよいが、挿入のたびにbiasの値が調節され、各桁の閾値と重みが変わることに注意する必要がある。biasの値が変わらない範囲であれば、単純に追加するだけでよい。例えば、"bücher" に2つ目の非ASCII文字を挿入しようとすると、前述のとおりデコーダは前に戻れないため、最初の挿入結果は "büücher" で、コードは "`bcher-kvaa`" となる。次の挿入可能位置は "bücüher" で "`bcher-kvab`" となる。同様に、続きの "bücherü" は "`bcher-kvae`" となり、次に来るのは "ýbücher" で "`bcher-kvaf`" である。

* 国際化ドメイン名
* Nameprep

## 外部サイト

* RFC 3492
* Online Punycode/IDN Decoder/Encoder
* IDN Library—Libidn
* ICU IDNA Demonstration An online demonstration of how ICU performs IDN operations
* Punycode for Domains Convert Unicode to Punycode
* List of TLDs considered by the Mozilla developers to have an effective anti-spoofing policy for name registration
* 日本語JPドメイン名のPunycode変換・逆変換 - 日本語.jp
* Punycoder - the Punycode converter (IDN converter) 🔧