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"(または古いブラウザでは "red, green, blue")

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