Intl.NumberFormat API

JavaScriptの組み込み国際化APIで任意のロケールの数値をフォーマット

はじめに

アプリケーションで数値 1234567.89 を表示することを考えてみましょう。toString() を使用すると "1234567.89" が生成されますが、これは読みにくく、すべての人が小数点にピリオドを使用し、数値を左から右に読むことを前提としています。アメリカ人は "1,234,567.89" を期待し、ドイツ人は "1.234.567,89" を期待し、インドのユーザーは異なる桁区切りルールで "12,34,567.89" を期待します。

Intl.NumberFormat APIは、ロケール固有の規則に従って数値をフォーマットすることでこの問題を解決します。千の位区切り記号、小数点、数字のグループ化、通貨記号、パーセント記号、測定単位、および数字体系を処理します。これにより、手動での文字列操作やサードパーティのライブラリの必要性がなくなります。

このガイドでは、基本的な使用方法から通貨フォーマット、コンパクト表記、カスタム丸めモードなどの高度な機能まで、世界中のユーザーに対して数値を正しくフォーマットするために Intl.NumberFormat を使用する方法を説明します。

デフォルト設定で数値をフォーマットする

ロケール文字列を指定して new Intl.NumberFormat() を呼び出してフォーマッタを作成し、その format() メソッドに数値を渡して呼び出します。

const formatter = new Intl.NumberFormat('en-US');
formatter.format(1234567.89);
// "1,234,567.89"

フォーマッタはロケールに従って千の位区切り記号を追加し、小数点をフォーマットします。ロケールを指定しない場合、フォーマッタはランタイムのデフォルトロケールを使用します。これは通常、ユーザーのシステム設定に基づいています。

const formatter = new Intl.NumberFormat('de-DE');
formatter.format(1234567.89);
// "1.234.567,89"

ドイツの規則では、千の位区切り記号にピリオドを使用し、小数点にカンマを使用します。これはアメリカの規則とは逆です。フォーマッタはこれらの違いを自動的に処理します。

ロケールコードを理解する

ロケールコードは言語と任意で地域を識別し、language-REGION として記述されます。言語は enes のような2文字のISO 639-1コードを使用します。地域は USMX のような2文字のISO 3166-1コードを使用します。

new Intl.NumberFormat('en-US').format(1234.56);
// "1,234.56" (アメリカ英語)

new Intl.NumberFormat('en-GB').format(1234.56);
// "1,234.56" (イギリス英語)

new Intl.NumberFormat('es-ES').format(1234.56);
// "1234,56" (ヨーロッパスペイン語)

new Intl.NumberFormat('es-MX').format(1234.56);
// "1,234.56" (メキシコスペイン語)

両方の英語バリアントは同じフォーマットを使用しますが、スペイン語バリアントは異なります。ヨーロッパスペイン語は4桁の数値の千の位区切り記号を省略し、小数点にカンマを使用しますが、メキシコスペイン語はアメリカの規則に従います。

ユーザーの所在地や言語設定に基づいてロケールを選択してください。アプリケーションは通常、ユーザー設定、ブラウザの言語、またはIPジオロケーションからロケールを決定します。

フォーマットスタイルを選択する

styleオプションはフォーマットカテゴリを決定します。コンストラクタの第2引数としてオプションオブジェクトを渡します。

new Intl.NumberFormat('en-US', {
  style: 'decimal'
}).format(1234.56);
// "1,234.56"

decimalスタイルがデフォルトです。その他のスタイルにはcurrencypercentunitがあります。

記号とコードで通貨をフォーマットする

currencyスタイルを使用するには、ISO 4217通貨コードを指定するcurrencyオプションが必要です。

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).format(1234.56);
// "$1,234.56"

フォーマッタはドル記号を追加し、デフォルトで小数点以下2桁にフォーマットします。これはほとんどの通貨の標準です。異なるロケールでは記号の位置が異なります。

new Intl.NumberFormat('de-DE', {
  style: 'currency',
  currency: 'EUR'
}).format(1234.56);
// "1.234,56 €"

ドイツ語のフォーマットではユーロ記号を金額の後にスペースを入れて配置します。currencyオプションは表示する通貨を決定するもので、どのロケールの規則に従うかを決めるものではありません。ロケールはフォーマットの規則を決定し、通貨は記号を決定します。

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'EUR'
}).format(1234.56);
// "€1,234.56"

アメリカのフォーマット規則でユーロ記号を使用すると、ヨーロッパではなくアメリカの配置規則に従って、金額の前にユーロ記号が表示されます。

通貨表示フォーマットを制御する

currencyDisplayオプションは、フォーマットされた文字列内での通貨の表示方法を変更します。

const amount = 1234.56;

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencyDisplay: 'symbol'
}).format(amount);
// "$1,234.56"

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencyDisplay: 'code'
}).format(amount);
// "USD 1,234.56"

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencyDisplay: 'name'
}).format(amount);
// "1,234.56 US dollars"

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencyDisplay: 'narrowSymbol'
}).format(amount);
// "$1,234.56"

symbolオプションはデフォルトで、$のような通貨記号を表示します。codeオプションは3文字の通貨コードを表示します。nameオプションは通貨名をスペルアウトします。narrowSymbolオプションはロケールの狭い通貨記号を使用します。これは曖昧になる可能性がありますが、スペースを節約します。

symbolnarrowSymbolの違いは、記号を共有する通貨で明らかになります。

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'CAD',
  currencyDisplay: 'symbol'
}).format(100);
// "CA$100.00"

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'CAD',
  currencyDisplay: 'narrowSymbol'
}).format(100);
// "$100.00"

カナダドルはsymbolオプションでは米ドルと区別するためにCA$と表示されますが、narrowSymbolでは単に$と表示されます。

会計表記法で負の通貨金額をフォーマットする

currencySignオプションは負の金額の表示方法を制御します。

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencySign: 'standard'
}).format(-1234.56);
// "-$1,234.56"

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  currencySign: 'accounting'
}).format(-1234.56);
// "($1,234.56)"

standardオプションはデフォルトであり、マイナス記号を使用します。accountingオプションは会計慣行に従って負の金額を括弧で囲みます。これにより、財務報告書において負の数字がより視覚的に区別しやすくなります。

パーセンテージのフォーマット

percentスタイルは数値を100倍してパーセント記号を追加します。

new Intl.NumberFormat('en-US', {
  style: 'percent'
}).format(0.1234);
// "12%"

new Intl.NumberFormat('en-US', {
  style: 'percent'
}).format(0.1256);
// "13%"

フォーマッタはデフォルトで最も近い整数に丸めます。桁数オプションで小数点以下の桁数を制御できます。

new Intl.NumberFormat('en-US', {
  style: 'percent',
  minimumFractionDigits: 2
}).format(0.1234);
// "12.34%"

乗算された形式ではなく、パーセンテージの小数形式を渡してください。フォーマッタが乗算を処理します。

単位を使用した測定値のフォーマット

unitスタイルには単位識別子を持つunitオプションが必要です。

new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer-per-hour'
}).format(100);
// "100 km/h"

new Intl.NumberFormat('en-GB', {
  style: 'unit',
  unit: 'mile-per-hour'
}).format(100);
// "100 mph"

利用可能な単位には、長さの測定(meterkilometermile)、時間の長さ(secondminutehour)、デジタルストレージ(bytekilobytemegabyte)、温度(celsiusfahrenheit)など多数あります。kilometer-per-hourのような複合単位は、単純な単位をハイフンで結合します。

unitDisplayオプションは単位の形式を制御します。

const distance = 1234.5;

new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'long'
}).format(distance);
// "1,234.5 kilometers"

new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'short'
}).format(distance);
// "1,234.5 km"

new Intl.NumberFormat('en-US', {
  style: 'unit',
  unit: 'kilometer',
  unitDisplay: 'narrow'
}).format(distance);
// "1,234.5km"

longオプションは単位名をスペルアウトします。shortオプションは略語を使用します。narrowオプションは最もコンパクトな形式を使用しますが、曖昧になる可能性があります。

コンパクト表記で大きな数値を表示する

notation オプションは数値の表現方法を変更します。compact 値は大きな数値にロケール固有の短縮形を使用します。

new Intl.NumberFormat('en-US', {
  notation: 'compact'
}).format(1234567);
// "1.2M"

new Intl.NumberFormat('en-US', {
  notation: 'compact'
}).format(987654321);
// "988M"

コンパクト表記はデフォルトで小数点以下1桁に丸め、千単位には K、百万単位には M、十億単位には B などの接尾辞を追加します。この形式はソーシャルメディアのフォロワー数、動画の視聴回数、分析ダッシュボードなどで見られます。

compactDisplay オプションは接尾辞の長さを制御します。

new Intl.NumberFormat('en-US', {
  notation: 'compact',
  compactDisplay: 'short'
}).format(1234567);
// "1.2M"

new Intl.NumberFormat('en-US', {
  notation: 'compact',
  compactDisplay: 'long'
}).format(1234567);
// "1.2 million"

short オプションはデフォルトで記号を使用します。long オプションは桁数を表す単語をスペルアウトします。異なるロケールでは異なる接尾辞が使用されます。

new Intl.NumberFormat('zh-CN', {
  notation: 'compact'
}).format(123456789);
// "1.2亿"

中国語では 亿 を1億の単位として使用し、言語の数値グループ化システムを反映しています。

非常に大きな数値や小さな数値を科学的記数法で表現する

scientific 表記は数値を係数と10の累乗の積として表現します。

new Intl.NumberFormat('en-US', {
  notation: 'scientific'
}).format(123456789);
// "1.235E8"

new Intl.NumberFormat('en-US', {
  notation: 'scientific'
}).format(0.00000123);
// "1.23E-6"

この形式は非常に大きな数値(天文学的距離、分子数)や非常に小さな数値(粒子質量、ナノスケールの測定値)に適しています。指数は常に1の倍数として表示されます。

技術的なアプリケーションにエンジニアリング表記を使用する

engineering 表記は科学的記数法に似ていますが、指数を3の倍数に制限します。

new Intl.NumberFormat('en-US', {
  notation: 'engineering'
}).format(123456789);
// "123.457E6"

new Intl.NumberFormat('en-US', {
  notation: 'engineering'
}).format(1234);
// "1.234E3"

エンジニアリング表記はSI単位接頭辞(キロ、メガ、ギガ)と一致するため、工学や物理学の分野で標準となっています。係数の範囲は1から999です。

小数点以下の桁数を制御する

minimumFractionDigitsmaximumFractionDigitsオプションは、小数点以下に表示される桁数を制御します。

new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
}).format(1234.5);
// "1,234.50"

new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
}).format(1234.567);
// "1,234.57"

最小値は必要に応じて末尾のゼロを表示させます。最大値はより長い小数を丸めます。通貨フォーマッタはデフォルトで小数点以下2桁です。小数フォーマッタはデフォルトで最小値0、最大値3です。

new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 0,
  maximumFractionDigits: 0
}).format(1234.567);
// "1,235"

両方を0に設定すると、最も近い整数に丸められます。

有効数字で全体の精度を制御する

minimumSignificantDigitsmaximumSignificantDigitsオプションは、小数点の位置に関係なく全体の精度を制御します。

new Intl.NumberFormat('en-US', {
  minimumSignificantDigits: 3,
  maximumSignificantDigits: 3
}).format(1234.567);
// "1,230"

new Intl.NumberFormat('en-US', {
  minimumSignificantDigits: 3,
  maximumSignificantDigits: 3
}).format(0.001234);
// "0.00123"

有効数字は先頭のゼロを除くすべての桁をカウントします。最初の例では3桁に丸められ、1230が生成されます。2番目の例では先頭のゼロの後に3桁を保持し、0.00123が生成されます。

両方が指定された場合、有効数字オプションは小数点以下の桁数オプションよりも優先されます。

丸めモードで丸め戦略を選択する

roundingModeオプションは、切り捨てが必要な場合の数値の丸め方を決定します。

const value = 1.5;

new Intl.NumberFormat('en-US', {
  maximumFractionDigits: 0,
  roundingMode: 'halfExpand'
}).format(value);
// "2"

new Intl.NumberFormat('en-US', {
  maximumFractionDigits: 0,
  roundingMode: 'halfTrunc'
}).format(value);
// "1"

halfExpandモードはデフォルトであり、0.5をゼロから離れる方向に丸めます。これは学校で教えられる一般的な丸め戦略です。halfTruncモードは0.5をゼロに向かって丸めます。

その他のモードには以下があります:

  • ceil:常に正の無限大に向かって丸める
  • floor:常に負の無限大に向かって丸める
  • expand:常にゼロから離れる方向に丸める
  • trunc:常にゼロに向かって丸める
  • halfCeil:0.5を正の無限大に向かって丸める
  • halfFloor:0.5を負の無限大に向かって丸める
  • halfEven:0.5を最も近い偶数に丸める
const prices = [1.5, 2.5, 3.5];

prices.map(price =>
  new Intl.NumberFormat('en-US', {
    maximumFractionDigits: 0,
    roundingMode: 'halfEven'
  }).format(price)
);
// ["2", "2", "4"]

halfEvenモード(銀行家の丸めとも呼ばれる)は、繰り返し計算における丸めバイアスを減らします。0.5を丸める場合、最も近い偶数を選択します。これにより、1.52.5の両方が2になりますが、3.54になります。

金融アプリケーションでは、料金の切り上げにはceilを、返金の切り下げにはfloorを使用します。統計アプリケーションでは、累積的な丸め誤差を最小限に抑えるためにhalfEvenを使用します。

桁区切りをグループ化オプションで制御する

useGroupingオプションは、桁区切り記号を表示するかどうかを制御します。

new Intl.NumberFormat('en-US', {
  useGrouping: true
}).format(123456);
// "123,456"

new Intl.NumberFormat('en-US', {
  useGrouping: false
}).format(123456);
// "123456"

true値がデフォルトです。false値はすべての区切り記号を削除します。文字列値を使用するとより細かく制御できます。

new Intl.NumberFormat('en-US', {
  useGrouping: 'always'
}).format(1234);
// "1,234"

new Intl.NumberFormat('en-US', {
  useGrouping: 'min2'
}).format(1234);
// "1234"

always値はすべてのケースで区切り記号を使用します。min2値は4桁の数値では区切り記号を省略します。auto値はロケールの設定に従い、通常はmin2の動作と一致します。

コンパクト表記では、コンパクトな数値は内部の区切り記号をほとんど必要としないため、デフォルトでmin2が使用されます。

符号表示オプションで符号を明示的に表示する

signDisplayオプションは、正負の符号をいつ表示するかを制御します。

new Intl.NumberFormat('en-US', {
  signDisplay: 'auto'
}).format(100);
// "100"

new Intl.NumberFormat('en-US', {
  signDisplay: 'always'
}).format(100);
// "+100"

auto値がデフォルトで、負の数にはマイナス記号を表示しますが、正の数にはプラス記号を表示しません。always値は両方を表示します。

new Intl.NumberFormat('en-US', {
  signDisplay: 'exceptZero'
}).format(0);
// "0"

new Intl.NumberFormat('en-US', {
  signDisplay: 'always'
}).format(0);
// "+0"

exceptZero値はalwaysの動作でも、ゼロ値の符号を省略します。これにより、+0-0の紛らわしい表示を防ぎます。

new Intl.NumberFormat('en-US', {
  signDisplay: 'never'
}).format(-100);
// "100"

never値は符号を完全に削除し、絶対値のみを表示します。

金融アプリケーションでは、プラス記号で利益を強調するためにalwaysを使用します。温度表示では、+0°-0°の表示を避けるためにexceptZeroを使用します。

フォーマットされた出力をパーツに分解する

formatToParts() メソッドはフォーマットされた文字列の各コンポーネントを表すオブジェクトの配列を返します。

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).formatToParts(1234.56);

結果は以下のようになります:

[
  { type: 'currency', value: '$' },
  { type: 'integer', value: '1' },
  { type: 'group', value: ',' },
  { type: 'integer', value: '234' },
  { type: 'decimal', value: '.' },
  { type: 'fraction', value: '56' }
]

各オブジェクトにはコンポーネントを識別する type と文字列を含む value があります。typecurrencyintegergroupdecimalfractionliteralminusSignplusSignpercentSign などがあります。

これにより個々のコンポーネントのカスタムスタイリングが可能になります。

const parts = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).formatToParts(1234.56);

const formatted = parts.map(part => {
  if (part.type === 'currency') {
    return `<span class="currency">${part.value}</span>`;
  }
  if (part.type === 'integer') {
    return `<span class="integer">${part.value}</span>`;
  }
  return part.value;
}).join('');

// <span class="currency">$</span><span class="integer">1</span>,<span class="integer">234</span>.56

これによりスタイル付きコンポーネントを持つHTMLが生成されます。同じアプローチで、負の金額に異なる色を適用したり、通貨記号を拡大したり、個々の数字をアニメーション化したりすることができます。

数値範囲のフォーマット

formatRange() メソッドは2つの数値を範囲としてフォーマットします。

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).formatRange(100, 200);
// "$100.00 – $200.00"

フォーマッターはロケール固有の範囲区切り記号(英語ではenダッシュ)を使用し、両方の通貨記号を含めます。値が同じ文字列にフォーマットされる場合、フォーマッターはチルダ付きの単一の値を返します。

new Intl.NumberFormat('en-US', {
  notation: 'compact'
}).formatRange(1200, 1800);
// "~1K"

コンパクト表記では 12001800 の両方が 1K としてフォーマットされるため、フォーマッターは約 1K と表示します。

formatRangeToParts() メソッドは範囲のパーツを返します。

new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).formatRangeToParts(100, 200);

結果は以下のようになります:

[
  { type: 'currency', value: '$', source: 'startRange' },
  { type: 'integer', value: '100', source: 'startRange' },
  { type: 'decimal', value: '.', source: 'startRange' },
  { type: 'fraction', value: '00', source: 'startRange' },
  { type: 'literal', value: ' – ', source: 'shared' },
  { type: 'currency', value: '$', source: 'endRange' },
  { type: 'integer', value: '200', source: 'endRange' },
  { type: 'decimal', value: '.', source: 'endRange' },
  { type: 'fraction', value: '00', source: 'endRange' }
]

source プロパティは、そのパーツが開始値、終了値、または範囲区切り記号のどれから来たかを識別します。

価格範囲、日付範囲、測定範囲は、適切なロケール対応フォーマットのためにこのメソッドを使用します。

解決されたオプションを検査する

resolvedOptions() メソッドは、フォーマッタが実際に使用しているオプションを示すオブジェクトを返します。

const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});

formatter.resolvedOptions();

以下が返されます:

{
  locale: 'en-US',
  numberingSystem: 'latn',
  style: 'currency',
  currency: 'USD',
  currencyDisplay: 'symbol',
  currencySign: 'standard',
  minimumIntegerDigits: 1,
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
  useGrouping: 'auto',
  notation: 'standard',
  signDisplay: 'auto',
  roundingMode: 'halfExpand'
}

このオブジェクトには、明示的に設定されたオプションとデフォルト値が含まれています。locale は、システムが異なるが互換性のあるロケールに解決した場合、要求されたロケールと異なる場合があります。numberingSystem は、フォーマッタが使用する数字文字を示します。

このメソッドは、すべてのアクティブな設定を明らかにすることで、予期しないフォーマットのデバッグに役立ちます。

パフォーマンス向上のためにフォーマッタを再利用する

NumberFormat インスタンスの作成には、ロケールデータの読み込みとオプション処理が含まれます。同じ設定で複数の値をフォーマットする場合は、インスタンスを再利用してください。

// 非効率的:各値に対して新しいフォーマッタを作成
prices.map(price =>
  new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD'
  }).format(price)
);

// 効率的:フォーマッタを一度だけ作成
const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});

prices.map(price => formatter.format(price));

2番目のアプローチは、多くの値をフォーマットする場合、大幅に高速です。ループやコンポーネントのレンダリング関数の外部でフォーマッタを作成してください。

最新のJavaScriptエンジンは内部的にNumberFormatインスタンスをキャッシュしますが、明示的な再利用はより良いパフォーマンスと明確なコードを提供します。

ブラウザサポートを確認する

Intl.NumberFormat APIはすべての最新ブラウザでサポートされています。Chrome、Firefox、Safari、Edgeは2016年以降、基本的なAPIをサポートしています。formatRange()formatRangeToParts()、拡張されたroundingModeオプションなどの高度な機能は最近サポートされるようになりましたが、現在のブラウザバージョンで利用可能です。

以下を使用してサポートを確認できます:

if (typeof Intl !== 'undefined' && Intl.NumberFormat) {
  // NumberFormatがサポートされている
  const formatter = new Intl.NumberFormat('en-US');
}

特定の機能を確認する:

const formatter = new Intl.NumberFormat('en-US');

if (typeof formatter.formatRange === 'function') {
  // formatRangeがサポートされている
}

古いブラウザのサポートが必要なアプリケーションでは、@formatjs/intl-numberformatのようなポリフィルを使用できますが、ほとんどの最新アプリケーションではフォールバックなしでネイティブAPIを使用できます。

NumberFormatを使用するタイミング

Intl.NumberFormatの使用ケース:

  • UIコンテキストでユーザーに数値を表示する場合
  • Eコマースアプリケーションでの通貨金額のフォーマット
  • 分析ダッシュボードでのパーセンテージの表示
  • フォロワー数、閲覧数、その他のソーシャルメトリクスの表示
  • 測定値や科学的数値のフォーマット
  • 複数のロケールをサポートする国際化アプリケーションの構築

Intl.NumberFormatを使用すべきでないケース:

  • 内部計算やデータ処理
  • データベースへの値の保存
  • APIのためのデータシリアライズ
  • ユーザー入力を数値に戻すパース処理

NumberFormatはプレゼンテーション層のツールです。計算やストレージでは生の数値を保持し、ユーザーに表示する際にのみフォーマットを適用してください。