JavaScriptでA、B、Cのようなリストをフォーマットする方法は?

Intl.ListFormatを使用して、ロケール固有の接続詞と区切り文字で配列をフォーマットします。

はじめに

ユーザーにアイテムのリストを表示する際、カンマと「and」のような接続詞で結合する必要があります。言語によってリストのフォーマット規則は異なります。英語ではカンマと「and」を使用し、スペイン語では「y」、フランス語では「et」を使用し、中国語では全く異なる句読点を使用します。

Intl.ListFormat APIは、配列を適切な区切り文字と接続詞を使用してロケールに適した文字列にフォーマットします。これにより、リストフォーマットにおける文化的な違いが自動的に処理されます。

手動リストフォーマットの問題点

join()メソッドを使用して、配列要素をカンマで結合できます。

const fruits = ["apples", "oranges", "bananas"];
const list = fruits.join(", ");
console.log(list);
// "apples, oranges, bananas"

このアプローチには2つの問題があります。第一に、最後のアイテムの前に接続詞が追加されません。第二に、他の言語では機能しない英語の句読点を使用しています。

最後のアイテムの前に手動で「and」を追加することもできます。

const fruits = ["apples", "oranges", "bananas"];
const lastFruit = fruits[fruits.length - 1];
const otherFruits = fruits.slice(0, -1);
const list = otherFruits.join(", ") + ", and " + lastFruit;
console.log(list);
// "apples, oranges, and bananas"

このコードは英語でのみ機能します。スペイン語ユーザーには「apples, oranges y bananas」ではなく「apples, oranges, and bananas」と表示されます。フランス語ユーザーには「et」ではなく「and」と表示されます。句読点と接続詞の規則は言語によって異なります。

Intl.ListFormatを使用したリストのフォーマット

Intl.ListFormatコンストラクタは、配列をロケールに適したリスト文字列に変換するフォーマッタを作成します。

const formatter = new Intl.ListFormat("en");
const fruits = ["apples", "oranges", "bananas"];
console.log(formatter.format(fruits));
// "apples, oranges, and bananas"

フォーマッタは、指定されたロケールに適した区切り文字と接続詞を使用します。コンストラクタの最初の引数としてロケールを渡します。

const enFormatter = new Intl.ListFormat("en");
const esFormatter = new Intl.ListFormat("es");
const frFormatter = new Intl.ListFormat("fr");

const fruits = ["apples", "oranges", "bananas"];

console.log(enFormatter.format(fruits));
// "apples, oranges, and bananas"

console.log(esFormatter.format(fruits));
// "apples, oranges y bananas"

console.log(frFormatter.format(fruits));
// "apples, oranges et bananas"

フォーマッタは、各ロケールの句読点と接続詞の規則を自動的に適用します。

「and」を使用したリストの書式設定

Intl.ListFormatのデフォルト動作では、接続詞「and」または他の言語での同等の表現を使用します。これは接続詞書式設定と呼ばれます。

const formatter = new Intl.ListFormat("en", { type: "conjunction" });
const items = ["bread", "milk", "eggs"];
console.log(formatter.format(items));
// "bread, milk, and eggs"

typeオプションは、項目間に表示される接続詞を制御します。"conjunction"の値は「and」を使用するリストを生成します。これはデフォルト値であるため、省略できます。

リストタイプオプションの理解

typeオプションは、項目の接続方法を制御する3つの値を受け入れます。

"conjunction"タイプは「and」または同等の表現を使用します。

const formatter = new Intl.ListFormat("en", { type: "conjunction" });
console.log(formatter.format(["red", "green", "blue"]));
// "red, green, and blue"

"disjunction"タイプは「or」または同等の表現を使用します。

const formatter = new Intl.ListFormat("en", { type: "disjunction" });
console.log(formatter.format(["red", "green", "blue"]));
// "red, green, or blue"

"unit"タイプは、接続詞なしで測定値または数量のリストを書式設定します。

const formatter = new Intl.ListFormat("en", { type: "unit" });
console.log(formatter.format(["5 pounds", "12 ounces"]));
// "5 pounds, 12 ounces"

unitタイプは、技術データまたは測定データに適した最小限の句読点を使用します。

スタイルオプションの理解

styleオプションは、書式設定された出力の長さと形式性を制御します。3つの値を受け入れます。

"long"スタイルは、完全な単語と標準的な句読点を使用します。これがデフォルトです。

const formatter = new Intl.ListFormat("en", { style: "long" });
console.log(formatter.format(["Alice", "Bob", "Carol"]));
// "Alice, Bob, and Carol"

"short"スタイルは、利用可能な場合は省略形を使用します。

const formatter = new Intl.ListFormat("en", { style: "short" });
console.log(formatter.format(["Alice", "Bob", "Carol"]));
// "Alice, Bob, & Carol"

"narrow"スタイルは、可能な限り最もコンパクトな形式を使用します。

const formatter = new Intl.ListFormat("en", { style: "narrow" });
console.log(formatter.format(["Alice", "Bob", "Carol"]));
// "Alice, Bob, Carol"

narrowスタイルは、接続詞を完全に省略することがよくあります。正確な出力はロケールによって異なります。

異なるロケールでのリスト書式設定

各ロケールには独自のリスト書式設定ルールがあります。フォーマッターはこれらのルールを自動的に適用します。

英語ではカンマを使用し、「and」の前にオックスフォードカンマを含めます。

const formatter = new Intl.ListFormat("en");
console.log(formatter.format(["coffee", "tea", "juice"]));
// "coffee, tea, and juice"

スペイン語ではカンマと接続詞「y」を使用します。

const formatter = new Intl.ListFormat("es");
console.log(formatter.format(["café", "té", "jugo"]));
// "café, té y jugo"

フランス語ではカンマと接続詞「et」を使用します。

const formatter = new Intl.ListFormat("fr");
console.log(formatter.format(["café", "thé", "jus"]));
// "café, thé et jus"

中国語では「and」を表す文字「和」と、区切り文字として列挙カンマ「、」を使用します。

const formatter = new Intl.ListFormat("zh");
console.log(formatter.format(["咖啡", "茶", "可乐"]));
// "咖啡、茶和可乐"

ドイツ語ではカンマと接続詞「und」を使用します。

const formatter = new Intl.ListFormat("de");
console.log(formatter.format(["Kaffee", "Tee", "Saft"]));
// "Kaffee, Tee und Saft"

フォーマッターは、各言語のルールを知らなくても、これらの違いを処理します。

formatToPartsで個別のパーツを取得する

formatToParts()メソッドは、フォーマットされたリストの各パーツを表すオブジェクトの配列を返します。これは、異なるパーツを個別にスタイル設定する必要がある場合に便利です。

const formatter = new Intl.ListFormat("en");
const parts = formatter.formatToParts(["red", "green", "blue"]);
console.log(parts);

出力は、typevalueプロパティを持つオブジェクトの配列です。

[
  { type: "element", value: "red" },
  { type: "literal", value: ", " },
  { type: "element", value: "green" },
  { type: "literal", value: ", and " },
  { type: "element", value: "blue" }
]

各リスト項目のタイプは"element"です。区切り文字と接続詞のタイプは"literal"です。これを使用してカスタムスタイルを適用できます。

const formatter = new Intl.ListFormat("en");
const parts = formatter.formatToParts(["red", "green", "blue"]);

const html = parts
  .map((part) => {
    if (part.type === "element") {
      return `<strong>${part.value}</strong>`;
    }
    return part.value;
  })
  .join("");

console.log(html);
// "<strong>red</strong>, <strong>green</strong>, and <strong>blue</strong>"

このアプローチにより、ロケールに適した区切り文字と接続詞を維持しながら、フォーマットを正確に制御できます。

ブラウザサポート

Intl.ListFormat APIは、すべてのモダンブラウザで利用できます。Chrome、Firefox、Safari、Edgeを含む主要ブラウザで2021年4月からサポートされています。

APIを使用する前に、利用可能かどうかを確認できます。

if (typeof Intl.ListFormat !== "undefined") {
  const formatter = new Intl.ListFormat("en");
  console.log(formatter.format(["a", "b", "c"]));
} else {
  console.log("Intl.ListFormat is not supported");
}

古いブラウザの場合は、フォールバックを提供するか、ポリフィルを使用する必要があります。フォールバックには、シンプルなjoin()メソッドを使用できます。

function formatList(items, locale) {
  if (typeof Intl.ListFormat !== "undefined") {
    const formatter = new Intl.ListFormat(locale);
    return formatter.format(items);
  }
  return items.join(", ");
}

console.log(formatList(["red", "green", "blue"], "en"));
// "red, green, and blue" (or "red, green, blue" in older browsers)

これにより、Intl.ListFormatをサポートしていないブラウザでもコードが動作することが保証されます。