JavaScriptで配列をロケール固有の区切り文字でフォーマットする方法

Intl.ListFormatを使用して、任意の言語に適したカンマ、スペース、句読点を自動的に適用します。

はじめに

配列を読みやすい文字列に変換する際、項目をカンマやその他の句読点で区切る必要があります。言語によって使用される区切り文字は異なります。英語ではカンマとスペースを使用し、日本語では読点「、」を使用し、アラビア語では異なる句読点と語順を使用します。

Intl.ListFormat APIは、配列をロケールに適した区切り文字を使用した文字列に変換します。これにより、すべての言語でリストがユーザーにとって自然に見えるようになります。

配列の区切り文字がロケールによって異なる理由

すべての言語がリスト項目の区切りにカンマを使用すると思われるかもしれませんが、これは正しくありません。

英語では、項目をカンマとスペースで区切ります。

// English: "red, green, blue"

日本語では、スペースなしで読点「、」を使用します。

// Japanese: "赤、緑、青"

中国語でも同じ読点「、」文字を使用します。

// Chinese: "红、绿、蓝"

アラビア語では異なるカンマ文字「،」を使用し、右から左に読みます。

// Arabic: "أحمر، أخضر، أزرق"

これらの違いはIntl.ListFormatで自動的に処理されます。各言語の句読点ルールを知る必要はありません。

join()の問題点

Array.prototype.join()メソッドは、指定した区切り文字を使用して配列を文字列に変換します。

const colors = ["red", "green", "blue"];
console.log(colors.join(", "));
// "red, green, blue"

これは英語の句読点をハードコードしています。カンマとスペースの区切り文字は他の言語では機能しません。

const colors = ["赤", "緑", "青"];
console.log(colors.join(", "));
// "赤, 緑, 青" (wrong - should use 、 instead of ,)

ロケールに基づいて区切り文字を手動で切り替えることはできません。なぜなら、すべての言語とその句読点ルールのマッピングを維持する必要があり、このマッピングは不完全で保守が困難だからです。

ロケール対応の区切り文字にIntl.ListFormatを使用する

Intl.ListFormatコンストラクタは、任意のロケールに対して正しい区切り文字を適用するフォーマッタを作成します。

const formatter = new Intl.ListFormat("en");
const colors = ["red", "green", "blue"];
console.log(formatter.format(colors));
// "red, green, and blue"

フォーマッタは、指定されたロケールに適した句読点を自動的に使用します。最初の引数としてロケールコードを渡します。

const enFormatter = new Intl.ListFormat("en");
const jaFormatter = new Intl.ListFormat("ja");
const arFormatter = new Intl.ListFormat("ar");

const colors = ["red", "green", "blue"];

console.log(enFormatter.format(colors));
// "red, green, and blue"

console.log(jaFormatter.format(["赤", "緑", "青"]));
// "赤、緑、青"

console.log(arFormatter.format(["أحمر", "أخضر", "أزرق"]));
// "أحمر، أخضر، أزرق"

ブラウザが句読点のルールを提供します。ロケール固有のコードを保守する必要はありません。

言語による区切り文字の違い

フォーマッタは、ロケールに基づいて異なる区切り文字を適用します。以下の例は、同じ配列が異なる形式でフォーマットされる様子を示しています。

英語では、カンマ、スペース、および「and」という単語を使用します。

const formatter = new Intl.ListFormat("en");
console.log(formatter.format(["apple", "orange", "banana"]));
// "apple, orange, and banana"

スペイン語では、カンマ、スペース、および「y」という単語を使用します。

const formatter = new Intl.ListFormat("es");
console.log(formatter.format(["manzana", "naranja", "plátano"]));
// "manzana, naranja y plátano"

フランス語では、カンマ、スペース、および「et」という単語を使用します。

const formatter = new Intl.ListFormat("fr");
console.log(formatter.format(["pomme", "orange", "banane"]));
// "pomme, orange et banane"

ドイツ語では、カンマ、スペース、および「und」という単語を使用します。

const formatter = new Intl.ListFormat("de");
console.log(formatter.format(["Apfel", "Orange", "Banane"]));
// "Apfel, Orange und Banane"

日本語では、読点「、」および文字「と」を使用します。

const formatter = new Intl.ListFormat("ja");
console.log(formatter.format(["りんご", "オレンジ", "バナナ"]));
// "りんご、オレンジ、バナナ"

中国語では、頓号「、」および単語「和」を使用します。

const formatter = new Intl.ListFormat("zh");
console.log(formatter.format(["苹果", "橙子", "香蕉"]));
// "苹果、橙子和香蕉"

韓国語では、カンマおよび助詞「및」を使用します。

const formatter = new Intl.ListFormat("ko");
console.log(formatter.format(["사과", "오렌지", "바나나"]));
// "사과, 오렌지 및 바나나"

フォーマッタは、これらすべての違いを自動的に処理します。すべての言語に対して同じコードを記述します。

ユーザーのロケールを使用する

ブラウザの設定からユーザーの優先ロケールを検出し、それを使用してリストをフォーマットできます。

const userLocale = navigator.language;
const formatter = new Intl.ListFormat(userLocale);
const items = ["first", "second", "third"];
console.log(formatter.format(items));

これにより、リストはユーザーの期待に合った区切り文字を使用します。フランス語のブラウザ設定を持つユーザーにはフランス語の句読点が表示されます。日本語の設定を持つユーザーには日本語の句読点が表示されます。

接続詞なしで配列をフォーマットする

デフォルトの Intl.ListFormat の動作では、最後の項目の前に「and」のような接続詞が追加されます。unit タイプを使用することで、これを無効にできます。

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

unit タイプは接続語を追加せず、区切り文字のみを使用します。これは、技術的なリスト、測定値、または接続詞が適切でないデータに便利です。

const enFormatter = new Intl.ListFormat("en", { type: "unit" });
const jaFormatter = new Intl.ListFormat("ja", { type: "unit" });

console.log(enFormatter.format(["Item A", "Item B", "Item C"]));
// "Item A, Item B, Item C"

console.log(jaFormatter.format(["項目A", "項目B", "項目C"]));
// "項目A、項目B、項目C"

接続詞がない場合でも、区切り文字の句読点はロケールのルールに従います。

再利用可能なフォーマッターを作成する

フォーマッターを一度作成し、複数の配列に再利用できます。これは、配列ごとに新しいフォーマッターを作成するよりも効率的です。

const formatter = new Intl.ListFormat("en");

console.log(formatter.format(["red", "green"]));
// "red and green"

console.log(formatter.format(["a", "b", "c", "d"]));
// "a, b, c, and d"

console.log(formatter.format(["one"]));
// "one"

同じフォーマッターは、任意の長さの配列に対して機能します。配列内の項目数に基づいて、正しい区切り文字と接続詞を適用します。

空の配列を処理する

空の配列をフォーマットすると、フォーマッターは空の文字列を返します。

const formatter = new Intl.ListFormat("en");
console.log(formatter.format([]));
// ""

異なる動作が必要な場合は、フォーマットする前に空の配列をチェックする必要があります。

function formatList(items, locale) {
  if (items.length === 0) {
    return "No items";
  }
  const formatter = new Intl.ListFormat(locale);
  return formatter.format(items);
}

console.log(formatList([], "en"));
// "No items"

console.log(formatList(["apple"], "en"));
// "apple"

これにより、空の配列がユーザーにどのように表示されるかを制御できます。

ブラウザサポート

Intl.ListFormat API は、すべてのモダンブラウザで利用可能です。2021年4月以降、Chrome、Firefox、Safari、Edge でサポートされています。

使用する前に 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)

これにより、最新のブラウザで最高のエクスペリエンスを提供しながら、すべてのブラウザでコードが動作することを保証します。