3-5や100-200のような範囲の書式設定方法
JavaScriptを使用してロケールに適した書式で数値範囲を表示する
はじめに
数値範囲はユーザーインターフェース全体に表示されます。価格範囲は$100-$200、ページ番号は1-10、数量の推定値は3-5個のアイテムとして表示されます。これらの範囲は、値が2つの端点の間にあることを伝え、単一の正確な数値ではなく、境界のある情報をユーザーに提供します。
範囲値間の区切り記号をハードコーディングすると、すべてのユーザーが同じ表記規則に従うことを前提としています。英語話者は通常、範囲にハイフンまたはenダッシュを使用しますが、他の言語では異なる記号や単語を使用します。ドイツ語では数値間に「bis」を使用し、一部の言語では区切り記号の前後にスペースを配置します。
JavaScriptはformatRange()のIntl.NumberFormatメソッドを提供し、範囲の書式設定を自動的に処理します。このメソッドは、数値と区切り記号の両方にロケール固有の規則を適用し、世界中のユーザーに対して範囲が正しく表示されるようにします。
数値範囲にロケール固有の書式設定が必要な理由
異なる文化は範囲を表現するための異なる規則を発展させてきました。これらの規則には、区切り記号とその前後のスペースの両方が含まれます。
米国英語では、範囲は通常、スペースなしのenダッシュを使用します:3-5、100-200。一部のスタイルガイドでは、ダッシュの前後にスペースが表示されます:3 - 5。正確な規則は、コンテキストと出版基準によって異なります。
ドイツ語では、範囲は区切り記号として「bis」を使用することがよくあります:3 bis 5、100 bis 200。この単語ベースのアプローチは、句読点に依存するのではなく、範囲の関係を明示的にします。
スペイン語では、範囲は英語のようにダッシュを使用するか、「a」という単語を使用できます:3-5または3 a 5。選択は、特定のスペイン語圏の地域とコンテキストによって異なります。
通貨や単位を含む範囲をフォーマットする場合、複雑さが増します。価格範囲は米国英語では$100-$200と表示されますが、ドイツ語では100 €-200 €、または記号が一度だけ表示される100-200 €となります。ロケールによって通貨記号の配置や繰り返しの処理方法が異なります。
手動での範囲フォーマットには、これらの規則を把握し、ロケール固有のロジックを実装する必要があります。Intl APIはこの知識をカプセル化し、ロケールに基づいて適切なフォーマットを適用します。
formatRangeを使用した数値範囲のフォーマット
formatRange()メソッドは2つの数値を受け取り、範囲を表すフォーマット済み文字列を返します。希望するロケールとオプションでIntl.NumberFormatインスタンスを作成し、開始値と終了値を指定してformatRange()を呼び出します。
const formatter = new Intl.NumberFormat("en-US");
console.log(formatter.formatRange(3, 5));
// Output: "3–5"
console.log(formatter.formatRange(100, 200));
// Output: "100–200"
console.log(formatter.formatRange(1000, 5000));
// Output: "1,000–5,000"
フォーマッターは範囲内の両方の数値に桁区切り記号を適用し、それらの間に適切な区切り記号を使用します。米国英語の場合、これはスペースなしのenダッシュです。
ロケール識別子を変更することで、異なるロケール向けに同じ範囲をフォーマットできます。
const usFormatter = new Intl.NumberFormat("en-US");
console.log(usFormatter.formatRange(100, 200));
// Output: "100–200"
const deFormatter = new Intl.NumberFormat("de-DE");
console.log(deFormatter.formatRange(100, 200));
// Output: "100–200"
const esFormatter = new Intl.NumberFormat("es-ES");
console.log(esFormatter.formatRange(100, 200));
// Output: "100-200"
各ロケールは区切り記号とスペーシングに独自の規則を適用します。APIはロケールのタイポグラフィ標準に基づいて、これらの詳細を自動的に処理します。
通貨範囲のフォーマット
範囲フォーマットは通貨を含むあらゆる数値フォーマットオプションで機能します。通貨範囲をフォーマットする場合、フォーマッターは通貨記号の配置と範囲区切り記号の両方を処理します。
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0
});
console.log(formatter.formatRange(100, 200));
// Output: "$100 – $200"
console.log(formatter.formatRange(1000, 5000));
// Output: "$1,000 – $5,000"
フォーマッターは範囲内の各数値の前に通貨記号を配置します。これにより、両方の値が通貨金額を表すことが明確になります。
ロケールによって通貨記号の配置方法が異なります。
const usFormatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0
});
console.log(usFormatter.formatRange(100, 200));
// Output: "$100 – $200"
const deFormatter = new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR",
maximumFractionDigits: 0
});
console.log(deFormatter.formatRange(100, 200));
// Output: "100–200 €"
ドイツ語のフォーマッターは、各数値の前ではなく範囲の後にユーロ記号を配置します。これは通貨範囲に関するドイツ語の表記規則に従っています。
範囲の値がほぼ等しい場合の動作
開始値と終了値がフォーマット後に同じ数値に丸められる場合、フォーマッターは範囲を省略し、近似記号を追加することがあります。
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0
});
console.log(formatter.formatRange(100, 200));
// Output: "$100 – $200"
console.log(formatter.formatRange(100, 120));
// Output: "$100 – $120"
console.log(formatter.formatRange(100.2, 100.8));
// Output: "~$100"
3番目の例は、同じ整数に丸められる2つの値を示しています。範囲情報を伝えない「$100 – $100」を表示する代わりに、フォーマッターは「~$100」を出力します。チルダ記号は値が近似値であることを示します。
この動作は、フォーマットオプションによって開始値と終了値が同一に見える場合に適用されます。
const formatter = new Intl.NumberFormat("en-US", {
maximumFractionDigits: 1
});
console.log(formatter.formatRange(2.9, 3.1));
// Output: "~3"
console.log(formatter.formatRange(2.94, 2.96));
// Output: "~2.9"
フォーマッターは必要な場合にのみ近似記号を挿入します。値が異なる数値に丸められる場合は、標準的な範囲として表示されます。
小数点以下の桁数を含む範囲のフォーマット
範囲のフォーマットは、フォーマッターオプションの小数点以下の桁数設定を保持します。範囲内の両方の値の精度を制御できます。
const formatter = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
console.log(formatter.formatRange(3.5, 5.7));
// Output: "3.50–5.70"
console.log(formatter.formatRange(100, 200));
// Output: "100.00–200.00"
フォーマッターは範囲内の両方の数値に小数点以下の桁数設定を適用します。これにより、範囲表示全体で一貫した精度が保証されます。
小数点フォーマットを通貨やその他のスタイルと組み合わせることができます。
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
console.log(formatter.formatRange(99.99, 199.99));
// Output: "$99.99 – $199.99"
異なる言語での数値範囲のフォーマット
範囲のフォーマットは、各ロケールの数値、区切り文字、スペースの規則に適応します。
const enFormatter = new Intl.NumberFormat("en-US");
console.log(enFormatter.formatRange(1000, 5000));
// Output: "1,000–5,000"
const deFormatter = new Intl.NumberFormat("de-DE");
console.log(deFormatter.formatRange(1000, 5000));
// Output: "1.000–5.000"
const frFormatter = new Intl.NumberFormat("fr-FR");
console.log(frFormatter.formatRange(1000, 5000));
// Output: "1 000–5 000"
const jaFormatter = new Intl.NumberFormat("ja-JP");
console.log(jaFormatter.formatRange(1000, 5000));
// Output: "1,000~5,000"
英語では桁区切りにカンマを使用し、範囲には半角ダッシュを使用します。ドイツ語では桁区切りにピリオドを使用し、半角ダッシュを使用します。フランス語では桁区切りにスペースを使用し、半角ダッシュを使用します。日本語では桁区切りにカンマを使用し、範囲には波ダッシュ(~)を使用します。
これらの違いは通貨フォーマットにも及びます。
const enFormatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
console.log(enFormatter.formatRange(100, 200));
// Output: "$100.00 – $200.00"
const deFormatter = new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR"
});
console.log(deFormatter.formatRange(100, 200));
// Output: "100,00–200,00 €"
const jaFormatter = new Intl.NumberFormat("ja-JP", {
style: "currency",
currency: "JPY"
});
console.log(jaFormatter.formatRange(100, 200));
// Output: "¥100~¥200"
各ロケールは、通貨記号の配置、小数点区切り文字、範囲区切り文字に独自のルールを適用します。APIはこれらすべてのバリエーションを自動的に処理します。
formatRangeとコンパクト表記の組み合わせ
範囲フォーマットはコンパクト表記と連携し、1K-5Kや1M-5Mのような範囲を表示できます。
const formatter = new Intl.NumberFormat("en-US", {
notation: "compact"
});
console.log(formatter.formatRange(1000, 5000));
// Output: "1K–5K"
console.log(formatter.formatRange(1000000, 5000000));
// Output: "1M–5M"
console.log(formatter.formatRange(1200, 4800));
// Output: "1.2K–4.8K"
フォーマッターは範囲内の両方の値にコンパクト表記を適用します。これにより、範囲情報を伝えながら出力を簡潔に保ちます。
範囲が異なる桁数レベルにまたがる場合、フォーマッターは各値を適切に処理します。
const formatter = new Intl.NumberFormat("en-US", {
notation: "compact"
});
console.log(formatter.formatRange(500, 1500));
// Output: "500–1.5K"
console.log(formatter.formatRange(900000, 1200000));
// Output: "900K–1.2M"
開始値はコンパクト表記を使用せず、終了値は使用する場合や、異なる桁数インジケーターを使用する場合があります。フォーマッターは各値のサイズに基づいてこれらの決定を行います。
カスタムスタイリングのためのformatRangeToPartsの使用
formatRangeToParts()メソッドは、フォーマットされた範囲の部分を表すオブジェクトの配列を返します。これにより、範囲の個々のコンポーネントをスタイル設定または操作できます。
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0
});
const parts = formatter.formatRangeToParts(100, 200);
console.log(parts);
出力は、それぞれtype、value、sourceプロパティを持つオブジェクトの配列です。
[
{ type: "currency", value: "$", source: "startRange" },
{ type: "integer", value: "100", source: "startRange" },
{ type: "literal", value: " – ", source: "shared" },
{ type: "currency", value: "$", source: "endRange" },
{ type: "integer", value: "200", source: "endRange" }
]
typeプロパティは、その部分が何を表すかを識別します:通貨記号、整数、小数点区切り文字、またはリテラルテキスト。valueプロパティには、フォーマットされたテキストが含まれます。sourceプロパティは、その部分が開始値、終了値に属するか、またはそれらの間で共有されているかを示します。
これらの部分を使用して、異なるコンポーネントに異なるスタイルを適用したカスタムHTMLを作成できます。
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0
});
const parts = formatter.formatRangeToParts(100, 200);
let html = "";
parts.forEach(part => {
if (part.type === "currency") {
html += `<span class="currency-symbol">${part.value}</span>`;
} else if (part.type === "integer") {
html += `<span class="amount">${part.value}</span>`;
} else if (part.type === "literal") {
html += `<span class="separator">${part.value}</span>`;
} else {
html += part.value;
}
});
console.log(html);
// Output: <span class="currency-symbol">$</span><span class="amount">100</span><span class="separator"> – </span><span class="currency-symbol">$</span><span class="amount">200</span>
この手法により、正しいロケール固有のフォーマットを保持しながら、CSSクラスの適用、ツールチップの追加、またはその他のカスタム動作を実装できます。
formatRangeでエッジケースを処理する
formatRange()メソッドには、無効な入力に対するエラー処理が含まれています。いずれかのパラメータがundefinedの場合、TypeErrorがスローされます。いずれかのパラメータがNaNであるか、数値に変換できない場合、RangeErrorがスローされます。
const formatter = new Intl.NumberFormat("en-US");
try {
console.log(formatter.formatRange(100, undefined));
} catch (error) {
console.log(error.name);
// Output: "TypeError"
}
try {
console.log(formatter.formatRange(NaN, 200));
} catch (error) {
console.log(error.name);
// Output: "RangeError"
}
ユーザー入力や外部ソースからのデータを扱う場合は、formatRange()に渡す前に、値が有効な数値であることを検証してください。
このメソッドは、数値、BigInt値、または有効な数値を表す文字列を受け入れます。
const formatter = new Intl.NumberFormat("en-US");
console.log(formatter.formatRange(100, 200));
// Output: "100–200"
console.log(formatter.formatRange(100n, 200n));
// Output: "100–200"
console.log(formatter.formatRange("100", "200"));
// Output: "100–200"
文字列入力は数値として解析され、浮動小数点変換の問題なく精度が保持されます。
formatRangeと手動フォーマットの使い分け
ユーザーに範囲を表示する場合はformatRange()を使用してください。これは、価格範囲、数量範囲、測定範囲、ページ番号、またはその他の境界値に適用されます。このメソッドは、区切り文字のロジックを実装することなく、正しいロケール固有のフォーマットを保証します。
範囲として意味的に関連していない複数の個別の値を表示する必要がある場合は、formatRange()を使用しないでください。たとえば、「$100、$150、$200」のような価格のリストを表示する場合は、範囲として扱うのではなく、各値に対して通常のformat()呼び出しを使用する必要があります。
また、値間の関係が数値範囲でない場合もformatRange()を使用しないでください。比較や差異を表示する場合は、範囲フォーマットではなく、そのコンテキストに適したフォーマットを使用してください。