単位付きの計測値リストをどのようにフォーマットしますか?
JavaScriptのIntl APIを使用して、5 km、10 km、15 kmなどの複数の計測値をロケールに適したリスト形式で表示する方法
はじめに
測定値を表示するアプリケーションでは、複数の値をまとめて表示する必要がよくあります。フィットネスアプリでは「5 km、10 km、15 km」のようにスプリットタイムを表示するかもしれません。天気アプリでは「20°C、22°C、25°C、23°C」のように週間の気温を表示するかもしれません。レシピでは「2カップ、大さじ1、小さじ3」のように材料の量を一覧表示するかもしれません。
これらのリストには2つの国際化の課題が組み合わさっています。まず、各測定値にはロケールに適した単位フォーマットが必要です。次に、リスト自体には対象言語に適した句読点と区切り記号が必要です。英語ではカンマを使用し、時には「and」のような接続詞を使います。他の言語では異なる区切り記号を使用し、異なる文法規則に従います。
JavaScriptはこの問題を解決するための2つのAPIを提供しています。Intl.NumberFormatは単位付きの個々の測定値をフォーマットします。Intl.ListFormatは複数の値を文法的に正しいリストに結合します。このレッスンでは、両方のAPIを一緒に使用して、どのロケールでもユーザーの期待に合った測定値のリストをフォーマットする方法を説明します。
測定値のリストには2つのフォーマット手順が必要
測定値のリストをフォーマットする場合、どちらのフォーマット手順も省略できません。測定値をフォーマットせずにリストをフォーマットすると、ロケールに適した区切り記号は得られますが、単位の表示が正しくありません。リストをフォーマットせずに測定値をフォーマットすると、正しい単位は得られますが、区切り記号が正しくありません。
const distances = [5, 10, 15];
// 間違い: リストはフォーマットされているが測定値はされていない
console.log(distances.join(', '));
// 出力: "5, 10, 15" (単位が欠けている)
// 間違い: 測定値はフォーマットされているがリストはされていない
const formatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer'
});
console.log(distances.map(d => formatter.format(d)).join(', '));
// 出力: "5 km, 10 km, 15 km" (ハードコードされたカンマは一部のロケールでは間違っている可能性がある)
正しいアプローチは、まず測定値をフォーマットし、次に結果の文字列配列をリストとしてフォーマットすることです。
const distances = [5, 10, 15];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer'
});
const formattedMeasurements = distances.map(d => numberFormatter.format(d));
// 結果: ["5 km", "10 km", "15 km"]
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedMeasurements));
// 出力: "5 km, 10 km, 15 km"
このパターンはあらゆる測定タイプとあらゆるロケールで機能します。各測定値をその単位でフォーマットし、次にフォーマットされた文字列の配列をリストとしてフォーマットします。
測定リストにはunit typeを使用する
Intl.ListFormatコンストラクタは、リスト項目の結合方法を制御するtypeオプションを受け付けます。type: 'unit'オプションは、技術的および科学的データの規則に従ってリストをフォーマットします。
const measurements = ['5 km', '10 km', '15 km'];
const unitList = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(unitList.format(measurements));
// 出力: "5 km, 10 km, 15 km"
type: 'unit'を持つリストは「and」や「or」などの接続詞を省略します。項目間には単純な区切り文字が使用されます。これは、技術的な文脈で測定値が一般的に記述される方法と一致します。
最後の項目の前に「and」を追加するtype: 'conjunction'と比較してみましょう。
const measurements = ['5 km', '10 km', '15 km'];
const conjunctionList = new Intl.ListFormat('en-US', {
type: 'conjunction'
});
console.log(conjunctionList.format(measurements));
// 出力: "5 km, 10 km, and 15 km"
接続形式は文章中では自然に読めますが、技術的な文脈では不適切に見えます。複数の測定値を表示する場合は、科学的および技術的な文書の標準的な規則に従うためにtype: 'unit'を使用してください。
リスト内の距離測定値をフォーマットする
距離測定にはkilometer、meter、mile、footなどの単位識別子を使用します。各距離を単位付きでフォーマットした後、それらをリストに結合します。
const distances = [5, 10, 15, 20];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer'
});
const formattedDistances = distances.map(d => numberFormatter.format(d));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedDistances));
// 出力: "5 km, 10 km, 15 km, 20 km"
同じパターンはマイルにも適用できます。
const distances = [3, 6, 9];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'mile'
});
const formattedDistances = distances.map(d => numberFormatter.format(d));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedDistances));
// 出力: "3 mi, 6 mi, 9 mi"
数値フォーマットオプションを設定することで、小数点以下の桁数を持つ距離をフォーマットできます。
const distances = [5.2, 10.7, 15.3];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer',
maximumFractionDigits: 1
});
const formattedDistances = distances.map(d => numberFormatter.format(d));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedDistances));
// 出力: "5.2 km, 10.7 km, 15.3 km"
リストフォーマッタが値を結合する前に、数値フォーマッタが丸めと小数点以下の桁数を処理します。
リスト内の重量測定値のフォーマット
重量測定値も同様のパターンに従い、kilogram、pound、ounce、gramなどの単位識別子を使用します。
const weights = [50, 75, 100];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilogram'
});
const formattedWeights = weights.map(w => numberFormatter.format(w));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedWeights));
// 出力: "50 kg, 75 kg, 100 kg"
重量をポンドで表示することもできます。
const weights = [110, 165, 220];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'pound'
});
const formattedWeights = weights.map(w => numberFormatter.format(w));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedWeights));
// 出力: "110 lb, 165 lb, 220 lb"
数値フォーマッタは各単位に対して正しい略語を自動的に使用します。
リスト内の温度測定値のフォーマット
温度測定値はcelsiusやfahrenheitなどの単位識別子を使用します。
const temperatures = [20, 22, 25, 23, 21];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'celsius'
});
const formattedTemperatures = temperatures.map(t => numberFormatter.format(t));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedTemperatures));
// 出力: "20°C, 22°C, 25°C, 23°C, 21°C"
温度フォーマッタは出力に度数記号を自動的に含めます。
華氏も同様に機能します。
const temperatures = [68, 72, 77, 73, 70];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'fahrenheit'
});
const formattedTemperatures = temperatures.map(t => numberFormatter.format(t));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedTemperatures));
// 出力: "68°F, 72°F, 77°F, 73°F, 70°F"
このパターンは異なる測定タイプ間で同一です。変わるのは単位識別子だけです。
リスト内の体積測定値のフォーマット
体積測定には、liter、gallon、milliliter、fluid-ounceなどの単位識別子を使用します。
const volumes = [1, 2, 3];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'liter'
});
const formattedVolumes = volumes.map(v => numberFormatter.format(v));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedVolumes));
// 出力: "1 L, 2 L, 3 L"
体積測定は小数値でも機能します。
const volumes = [0.5, 1.5, 2.5];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'liter',
maximumFractionDigits: 1
});
const formattedVolumes = volumes.map(v => numberFormatter.format(v));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedVolumes));
// 出力: "0.5 L, 1.5 L, 2.5 L"
数値フォーマッタはリストフォーマッタが値を処理する前に小数点の精度を処理します。
リスト内の速度測定値のフォーマット
速度測定には、kilometer-per-hourやmile-per-hourなどの複合単位を使用します。
const speeds = [50, 75, 100];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer-per-hour'
});
const formattedSpeeds = speeds.map(s => numberFormatter.format(s));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedSpeeds));
// 出力: "50 km/h, 75 km/h, 100 km/h"
マイル毎時も同様に機能します。
const speeds = [30, 45, 60];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'mile-per-hour'
});
const formattedSpeeds = speeds.map(s => numberFormatter.format(s));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedSpeeds));
// 出力: "30 mph, 45 mph, 60 mph"
複合単位は正しい略語と区切り文字で自動的にフォーマットされます。
ロケールによってリスト区切り文字のフォーマットが決まる
ロケールパラメータは、リスト項目の区切り方や句読点の付け方を制御します。言語によってリストのフォーマット規則が異なります。
const distances = [5, 10, 15];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer'
});
const formattedDistances = distances.map(d => numberFormatter.format(d));
const enList = new Intl.ListFormat('en-US', {
type: 'unit'
});
const frList = new Intl.ListFormat('fr-FR', {
type: 'unit'
});
const deList = new Intl.ListFormat('de-DE', {
type: 'unit'
});
console.log(enList.format(formattedDistances));
// 出力: "5 km, 10 km, 15 km"
console.log(frList.format(formattedDistances));
// 出力: "5 km, 10 km, 15 km"
console.log(deList.format(formattedDistances));
// 出力: "5 km, 10 km, 15 km"
キロメートルの略語はこれらのロケール間で類似していますが、スペースや区切り文字の規則は異なる場合があります。Intl.ListFormat APIはこれらのロケール固有のフォーマットルールを自動的に処理します。
言語によってはリストの区切り文字や句読点のパターンが異なります。このAPIを使用すると、各ロケールの特定のルールを知らなくても、リストが各ロケールの正しい規則に従うことが保証されます。
リスト形式のロケールと数値のロケールを一致させる
測定値のリストをフォーマットする際は、数値フォーマッタとリストフォーマッタの両方に同じロケールを使用してください。これにより、出力全体で一貫したフォーマットが保証されます。
const distances = [1000, 2000, 3000];
const locale = 'de-DE';
const numberFormatter = new Intl.NumberFormat(locale, {
style: 'unit',
unit: 'meter'
});
const formattedDistances = distances.map(d => numberFormatter.format(d));
const listFormatter = new Intl.ListFormat(locale, {
type: 'unit'
});
console.log(listFormatter.format(formattedDistances));
// 出力: "1.000 m, 2.000 m, 3.000 m"
ドイツ語のフォーマットでは、千の位区切りにピリオドを使用します。数値フォーマッタとリストフォーマッタは同じロケールを共有しているため、どちらもドイツ語の規則に従っています。
数値フォーマットとリストフォーマットに異なるロケールを使用すると、一貫性のない出力が生成されます。
const distances = [1000, 2000, 3000];
const numberFormatter = new Intl.NumberFormat('de-DE', {
style: 'unit',
unit: 'meter'
});
const formattedDistances = distances.map(d => numberFormatter.format(d));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedDistances));
// 出力: "1.000 m, 2.000 m, 3.000 m"
これにより、数値はドイツ語の規則を使用し、リストは英語の規則を使用するという混合フォーマットが生成されます。常に両方のフォーマッタに同じロケールを使用してください。
リスト表示スタイルの制御
styleオプションは、リストフォーマットの詳細度を制御します。このオプションは"long"、"short"、"narrow"の3つの値を受け付けます。
const measurements = ['5 km', '10 km', '15 km'];
const longList = new Intl.ListFormat('en-US', {
type: 'unit',
style: 'long'
});
const shortList = new Intl.ListFormat('en-US', {
type: 'unit',
style: 'short'
});
const narrowList = new Intl.ListFormat('en-US', {
type: 'unit',
style: 'narrow'
});
console.log(longList.format(measurements));
// 出力: "5 km, 10 km, 15 km"
console.log(shortList.format(measurements));
// 出力: "5 km, 10 km, 15 km"
console.log(narrowList.format(measurements));
// 出力: "5 km 10 km 15 km"
英語では、longとshortスタイルは単位リストに対して類似した出力を生成します。narrowスタイルは最小限のスペースを使用し、項目間の区切り文字を省略します。
異なるロケールではスタイル間でより多くの違いが見られます。各スタイルレベルの正確なフォーマットはロケールによって決定されます。
長い単位名との組み合わせ
数値フォーマッターで unitDisplay: 'long' を設定することで、略語ではなく完全な単位名で測定値をフォーマットできます。
const distances = [5, 10, 15];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer',
unitDisplay: 'long'
});
const formattedDistances = distances.map(d => numberFormatter.format(d));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedDistances));
// 出力: "5 kilometers, 10 kilometers, 15 kilometers"
数値フォーマッターは単数形と複数形を自動的に処理します。リストフォーマッターは、略語または完全な名前のどちらを使用しているかに関わらず、フォーマットされた文字列を結合します。
const distances = [1, 5];
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer',
unitDisplay: 'long'
});
const formattedDistances = distances.map(d => numberFormatter.format(d));
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
console.log(listFormatter.format(formattedDistances));
// 出力: "1 kilometer, 5 kilometers"
数値フォーマッターは1に対して「kilometer」、5に対して「kilometers」を使用します。リストフォーマッターはそれらを適切な区切り文字で結合します。
パフォーマンス向上のためのフォーマッターの再利用
Intl.NumberFormatとIntl.ListFormatインスタンスの作成には、ロケールデータの読み込みとオプションの処理が含まれます。複数の測定値リストをフォーマットする場合は、フォーマッターを一度作成して再利用しましょう。
const numberFormatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'kilometer'
});
const listFormatter = new Intl.ListFormat('en-US', {
type: 'unit'
});
const distanceLists = [
[5, 10, 15],
[20, 25, 30],
[35, 40, 45]
];
distanceLists.forEach(distances => {
const formattedDistances = distances.map(d => numberFormatter.format(d));
console.log(listFormatter.format(formattedDistances));
});
// 出力:
// "5 km, 10 km, 15 km"
// "20 km, 25 km, 30 km"
// "35 km, 40 km, 45 km"
このパターンでは、各フォーマッターを一度作成して複数回使用します。多数のリストをフォーマットする場合、パフォーマンスの差は顕著になります。
再利用可能なフォーマッター関数を作成する
2段階のフォーマットパターンを再利用可能な関数にカプセル化できます。
function formatMeasurementList(values, locale, unit) {
const numberFormatter = new Intl.NumberFormat(locale, {
style: 'unit',
unit: unit
});
const formattedValues = values.map(v => numberFormatter.format(v));
const listFormatter = new Intl.ListFormat(locale, {
type: 'unit'
});
return listFormatter.format(formattedValues);
}
console.log(formatMeasurementList([5, 10, 15], 'en-US', 'kilometer'));
// 出力: "5 km, 10 km, 15 km"
console.log(formatMeasurementList([50, 75, 100], 'en-US', 'kilogram'));
// 出力: "50 kg, 75 kg, 100 kg"
console.log(formatMeasurementList([20, 22, 25], 'en-US', 'celsius'));
// 出力: "20°C, 22°C, 25°C"
この関数はあらゆる測定タイプとロケールを処理します。追加のフォーマットオプションを受け入れるように拡張することもできます。
function formatMeasurementList(values, locale, unit, options = {}) {
const numberFormatter = new Intl.NumberFormat(locale, {
style: 'unit',
unit: unit,
...options
});
const formattedValues = values.map(v => numberFormatter.format(v));
const listFormatter = new Intl.ListFormat(locale, {
type: 'unit'
});
return listFormatter.format(formattedValues);
}
console.log(formatMeasurementList(
[5.123, 10.789, 15.456],
'en-US',
'kilometer',
{ maximumFractionDigits: 1 }
));
// 出力: "5.1 km, 10.8 km, 15.5 km"
console.log(formatMeasurementList(
[1, 5, 10],
'en-US',
'kilometer',
{ unitDisplay: 'long' }
));
// 出力: "1 kilometer, 5 kilometers, 10 kilometers"
この関数は追加オプションを数値フォーマッターに渡し、小数点以下の桁数、単位の表示方法、その他のフォーマット設定をコントロールできるようにします。
ユーザーのロケールに合わせたリストのフォーマット
特定のロケールをハードコーディングする代わりに、ユーザーのブラウザの言語設定を使用できます。navigator.languageプロパティはユーザーの優先ロケールを返します。
const userLocale = navigator.language;
const distances = [5, 10, 15];
const numberFormatter = new Intl.NumberFormat(userLocale, {
style: 'unit',
unit: 'kilometer'
});
const formattedDistances = distances.map(d => numberFormatter.format(d));
const listFormatter = new Intl.ListFormat(userLocale, {
type: 'unit'
});
console.log(listFormatter.format(formattedDistances));
// 出力はユーザーのロケールによって異なります
このアプローチでは、各ユーザーのフォーマット期待に従って測定リストを表示します。異なるユーザーは同じデータを自分のロケール規則に従ってフォーマットされた形で見ることができます。
アプリケーションでの計測リストの表示
このパターンは、ユーザーに複数の計測値を表示するあらゆる場所で使用できます。これには、ラップタイムを表示するフィットネスアプリケーション、気温予報を表示する天気アプリケーション、材料の量を表示するレシピアプリケーション、実験データを表示する科学アプリケーションなどが含まれます。
const splitTimes = [5, 10, 15, 20];
const numberFormatter = new Intl.NumberFormat(navigator.language, {
style: 'unit',
unit: 'kilometer',
maximumFractionDigits: 1
});
const formattedTimes = splitTimes.map(t => numberFormatter.format(t));
const listFormatter = new Intl.ListFormat(navigator.language, {
type: 'unit'
});
const result = listFormatter.format(formattedTimes);
document.getElementById('split-times').textContent = result;
// 表示例: "5 km, 10 km, 15 km, 20 km"(またはロケールに応じた表示)
フォーマットされた文字列は、他の文字列値と同様に機能します。テキストコンテンツ、属性、またはユーザーに情報を表示するあらゆるコンテキストに挿入できます。