JavaScriptで数値フォーマットする標準API「Intl.NumberFormat」 (カンマ区切り、円・ドル表記、漢数字など)

ほぼすべてのブラウザやNode.jsでサポートされている数値をフォーマットするAPI「[Intl.NumberFormat](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat)」を紹介する記事です。

JavaScriptで数値をカンマ区切りにしたいときにGoogleで検索してみると、正規表現を使っていたり、ループで回して3桁ごとにカンマ付ける実装を紹介する記事を多く見つけました。
しかし、それらは古い記事ということもあり、2018年現在は数値のフォーマットを実装する必要はありません。EcmaScriptで仕様策定されておりほとんどのブラウザやNode.jsで使える関数があるので紹介します。
今回紹介するコードの実行結果はすべてChromeでの結果になります。

# TL;DR
* 「[Intl.NumberFormat.prototype.format](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/format)」を使います。
* まだChromeとFirefoxだけですが、「[Intl.NumberFormat.prototype.formatToParts](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/formatToParts)」を使うとカスタムフォーマットもできます。

# [Intl](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl)
i18nやl10nといった国際化対応するためのAPIです。
詳しくは「[Intl - JavaScript|MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl)」をお読みください。
EcmaScriptの仕様については「[ここ](https://www.ecma-international.org/ecma-402/1.0/)」を読んで下さい。

# [Intl.NumberFormat](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat)
通過や文字など国や地域によって数の表記は違います。このAPIはその国ごとの表記を指定した表記に変換してくれるAPIを提供してくれるオブジェクトです。
詳しくはMDNの記事をお読み下さい。日本語の記事は情報が古いので[英語版](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat)を参考にしてください。

以下のようにlocaleをコンストラクタの引数に指定することができます。
引数の指定が無い場合は`'us-EN'`が設定されます。

```js
const formatter = new Intl.NumberFormat('ja-JP');
```

# [Intl.NumberFormat.prototype.format](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/format)
数値を変換する関数です。Intl.NumberFormatのメソッドです。
IEを含むほぼすべてのブラウザで使用可能です。
よく使われそうな例を紹介します。

## カンマ区切り
冒頭で説明したカンマ区切りです。３桁ごとに区切って表示する場合に使うことができます。
以下は`'ja-JP'`を指定していますが、`'us-EN'`や引数なしでも同じように3桁でカンマ区切りします。

```js
const formatter = new Intl.NumberFormat('ja-JP');
formatter.format(1000);
// => "1,000"
```

## 通貨
数値をサクッと円やドル表記したいことはあると思います。
円の表記は以下のようにNumberFormatの第２引数にオブジェクトを渡すことで実現可能です。
第２引数のオプションについてもやはりMDNの「[Intl.NumberFormat](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat)」に詳しく載っていますので興味ある方はそちらをお読み下さい。

```js
const formatter = new Intl.NumberFormat('ja-JP', {
  style: 'currency',
  currency: 'JPY'
});
formatter.format(1000);
// => "¥1,000"
```

もちろん、ドルやユーロなども対応しています。

```js
let formatter = new Intl.NumberFormat('us-EN', {
  style: 'currency',
  currency: 'USD'
});
formatter.format(1000);
// => "$1,000.00"

let formatter = new Intl.NumberFormat('de-DE', {
  style: 'currency',
  currency: 'EUR'
});
formatter.format(1000)
// => "1.000,00 €"
```


## 漢数字
数値を漢数字の文字列に変換することも可能です。
以下は「中国で使用される簡体字で書かれる中国語の１０進数の漢数字」という指定をしています。
`zh-Hans-CN`までが「中国で使用される簡体字で書かれる中国語」を指し、`u-nu-hanidec`が「１０進数の漢数字」を指します。

```js
const formatter = new Intl.NumberFormat('zh-Hans-CN-u-nu-hanidec');
formatter.format(1234567890);
// => "一,二三四,五六七,八九〇"
```

# [Intl.NumberFormat.prototype.formatToParts](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/formatToParts)
上記のように`format`関数で数値のフォーマットが行えますが、新しいAPIである`formatToParts`に関しても紹介しておきます。

執筆時点(2018/04/15)ではChrome63とFirefox58で使えるようになっています。
Node.jsも次期バージョンのv10では使えます。
EcmaScriptの仕様は「[ここ](https://tc39.github.io/ecma402/#sec-formatnumbertoparts)」にあります。

数値のカスタムフォーマットを行うことが可能になります。
`formatToParts`の戻り値は{type: string, value: string}のオブジェクトの配列です。

```js
const formatter = new Intl.NumberFormat('ja-JP', {
  style: 'currency',
  currency: 'JPY'
});
formatter.formatToParts(1000);
// => [ { type: 'currency', value: '¥' },
//      { type: 'integer', value: '1' },
//      { type: 'group', value: ',' },
//      { type: 'integer', value: '000' } ]
```

オブジェクトの配列なので、あとは好き勝手いじることが可能です。
以下はMDNに掲載されている例をJPYにしてみた例です。

```js
const num = 1000;
const formatter = new Intl.NumberFormat('ja-JP', {
  style: 'currency',
  currency: 'JPY'
});
const yen = formatter.formatToParts(num).map(({type, value}) => {
  switch (type) {
    case 'currency': return "<strong>" + value + "</strong>";
    default: return value;
  }
}).reduce((string, part) => {return string + part});
console.log(yen);
// => "<strong>¥</strong>1,000"
```

# 最後に
数値のフォーマット関数は標準で用意されているAPIを使いましょう。

# 補足(2018/04/17)
はてブでSafariで使えないというコメントがあったので補足します。
[Intl.NumberFormat.prototype.format](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/format)はSafari10から対応しています。
[Intl.NumberFormat.prototype.formatToParts](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/formatToParts)は使えません。
MDNの日本語版のブラウザ対応表が更新されていませんでした。英語版では10から対応と記載がありました。Safari11.1でも確認済みです。
また、MDNの[Intl.NumberFormat.prototype.format](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/format)の日本語版のSafariのサポートバージョンも更新しておきました。

# 参考
- [Intl](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl)
- [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat)
- [
ウェブサイトをグローバル化するために便利なIntl APIの話 - Qiita](https://qiita.com/teyosh/items/b126f21a16b795885067)

最後までお読みいただきありがとうございました。不備や質問がございましたらコメント欄に記載またはTwitterで [@shisama_](https://twitter.com/shisama_) 宛にメンション飛ばして下さい。