1st、2nd、3rdのような序数の書式設定方法

JavaScriptを使用して、ロケールに適した接尾辞と書式で序数を表示する

はじめに

序数は、順序における位置やランクを示します。英語では、レースの順位やリスト内の項目を表すために1st、2nd、3rd、4thと記述します。これらの接尾辞は、序数を通常の数詞と区別するのに役立ちます。

言語によって、序数の表記規則は全く異なります。英語ではst、nd、rd、thなどの接尾辞を追加します。フランス語では1erや2eのような上付き文字を使用します。ドイツ語では1.や2.のように数字の後にピリオドを追加します。日本語では数字の前に「第」という文字を付けます。英語の序数接尾辞をハードコーディングすると、すべてのユーザーが同じ規則に従うことを前提としてしまいます。

JavaScriptはIntl.PluralRules APIを提供しており、ordinalタイプを使用してこれらの違いを自動的に処理できます。このレッスンでは、序数とは何か、なぜその書式が言語によって異なるのか、そしてあらゆるロケールで正しく書式設定する方法について説明します。

序数とは

序数は、順序における位置、ランク、または順番を表します。「いくつ」ではなく「どれ」という質問に答えます。1st、2nd、3rdという数字は、レースでの順位を表します。第一、第二、第三は、リスト内の項目を表します。

基数は、数量や量を表します。「いくつ」という質問に答えます。1、2、3という数字は、物の個数を表します。一、二、三は、量を表します。

同じ数値が文脈によって両方の目的で使用されます。数字の5は「りんご5個」では基数ですが、「5位」では序数です。多くの言語では序数を基数とは異なる方法で書式設定するため、この区別は重要です。

英語では、10未満の序数には独自の単語形式があります。first、second、third、fourth、fifthは異なる単語です。10を超えると、英語は接尾辞を追加して序数を形成します。eleventh、twelfth、twentieth、twenty-firstはパターンに従いますが、接尾辞が必要です。

序数を単語ではなく数字で表記する場合、英語ではst、nd、rd、またはthの接尾辞を追加します。これらの接尾辞は、数字の最後の桁に基づいた特定のルールに従います。

序数の書式設定がロケールによって異なる理由

異なる言語は、序数を表現するための異なるシステムを発展させてきました。これらの慣習は、各言語に固有の文法規則、表記体系、文化的慣行を反映しています。

英語では、序数は4つの異なる接尾辞を使用します。1で終わる数字はstを使用し、2で終わる数字はndを使用し、3で終わる数字はrdを使用し、その他すべての数字はthを使用します。ただし、11、12、または13で終わる数字はすべてthを使用します。これにより、1st、2nd、3rd、4th、11th、12th、13th、21st、22nd、23rdが作成されます。

フランス語では、序数は上付き文字の略語を使用します。最初の項目は、男性形の場合は1er、女性形の場合は1reを使用します。その他すべての序数は、2e、3e、4eのように上付き文字のeを使用します。書式設定には、接尾辞の文字だけでなく、上付き文字のタイポグラフィが含まれます。

ドイツ語では、序数は数字の後にピリオドを使用します。1.、2.、3.という表記は、first、second、thirdを表します。このピリオドは、読者が音読する際に適切な文法的語尾を心の中で追加する必要があることを示します。

スペイン語では、序数は性別による上付き文字の指示記号を使用します。男性形の序数は1.º、2.º、3.ºを使用し、女性形の序数は1.ª、2.ª、3.ªを使用します。ピリオドは数字と指示記号を区切ります。

日本語では、序数は数字の前に接頭辞「第」を追加します。first、second、thirdは第一、第二、第三として表示されます。この接頭辞により、意味が基数から序数に変わります。

数値とハードコードされた接尾辞を連結して序数文字列を構築すると、すべてのユーザーに英語の規則を強制することになります。これにより、異なる形式を期待するユーザーにとってアプリケーションが使いにくくなります。

序数型のIntl.PluralRulesを理解する

Intl.PluralRules APIは、指定されたロケールにおいて数値がどの複数形カテゴリに属するかを判定します。このAPIは単数形と複数形の単語形式を選択するために一般的に使用されますが、序数も処理します。

コンストラクタはロケール識別子とオプションオブジェクトを受け取ります。typeオプションを"ordinal"に設定すると、基数ではなく序数を扱うことができます。

const rules = new Intl.PluralRules('en-US', { type: 'ordinal' });

これにより、英語の序数パターンを理解するルールオブジェクトが作成されます。select()メソッドは、渡された任意の数値に対してカテゴリ名を返します。

const rules = new Intl.PluralRules('en-US', { type: 'ordinal' });

console.log(rules.select(1));
// Output: "one"

console.log(rules.select(2));
// Output: "two"

console.log(rules.select(3));
// Output: "few"

console.log(rules.select(4));
// Output: "other"

返されるカテゴリは言語学的な用語であり、実際の接尾辞ではありません。カテゴリ「one」は英語でst接尾辞を取る数値に対応します。カテゴリ「two」はnd接尾辞に対応します。カテゴリ「few」はrd接尾辞に対応します。カテゴリ「other」はth接尾辞に対応します。

これらのカテゴリ名をロケールに適した接尾辞にマッピングします。APIは各数値がどのカテゴリに属するかを判定する複雑なルールを処理します。

序数フォーマッタ関数の構築

序数をフォーマットするには、Intl.PluralRulesと複数形カテゴリから接尾辞へのマッピングを組み合わせます。数値を受け取り、フォーマットされた文字列を返すフォーマッタ関数を作成します。

function formatOrdinal(number, locale) {
  const rules = new Intl.PluralRules(locale, { type: 'ordinal' });
  const category = rules.select(number);

  const suffixes = {
    one: 'st',
    two: 'nd',
    few: 'rd',
    other: 'th'
  };

  const suffix = suffixes[category];
  return `${number}${suffix}`;
}

console.log(formatOrdinal(1, 'en-US'));
// Output: "1st"

console.log(formatOrdinal(2, 'en-US'));
// Output: "2nd"

console.log(formatOrdinal(3, 'en-US'));
// Output: "3rd"

console.log(formatOrdinal(4, 'en-US'));
// Output: "4th"

この関数は実行されるたびに新しいPluralRulesインスタンスを作成します。パフォーマンスを向上させるには、ルールオブジェクトを一度作成し、複数の数値に対して再利用します。

const rules = new Intl.PluralRules('en-US', { type: 'ordinal' });

const suffixes = {
  one: 'st',
  two: 'nd',
  few: 'rd',
  other: 'th'
};

function formatOrdinal(number) {
  const category = rules.select(number);
  const suffix = suffixes[category];
  return `${number}${suffix}`;
}

console.log(formatOrdinal(1));
// Output: "1st"

console.log(formatOrdinal(21));
// Output: "21st"

console.log(formatOrdinal(22));
// Output: "22nd"

console.log(formatOrdinal(23));
// Output: "23rd"

APIは、最後の桁に関わらずすべてthを使用する11、12、13などの数値を正しく処理します。

console.log(formatOrdinal(11));
// Output: "11th"

console.log(formatOrdinal(12));
// Output: "12th"

console.log(formatOrdinal(13));
// Output: "13th"

複数形ルールは、ロケールのすべての特殊ケースと例外をエンコードします。これらのエッジケースを処理するための条件ロジックを記述する必要はありません。

異なるロケールの序数のフォーマット

複数形カテゴリとその意味は、ロケールによって異なります。英語よりも少ないカテゴリを持つ言語もあれば、どの数値が各カテゴリに該当するかについて異なるルールを持つ言語もあります。

ウェールズ語は異なる分類システムを使用します。ルールはより多くのカテゴリを識別し、それぞれがウェールズ語の異なる序数形式に対応します。

const enRules = new Intl.PluralRules('en-US', { type: 'ordinal' });
const cyRules = new Intl.PluralRules('cy', { type: 'ordinal' });

console.log(enRules.select(1));
// Output: "one"

console.log(cyRules.select(1));
// Output: "one"

console.log(enRules.select(2));
// Output: "two"

console.log(cyRules.select(2));
// Output: "two"

console.log(enRules.select(5));
// Output: "other"

console.log(cyRules.select(5));
// Output: "many"

複数のロケールをサポートするには、各ロケールに対して異なる接尾辞マッピングが必要です。カテゴリは同じままですが、接尾辞は変わります。

const ordinalSuffixes = {
  'en-US': {
    one: 'st',
    two: 'nd',
    few: 'rd',
    other: 'th'
  },
  'fr-FR': {
    one: 'er',
    other: 'e'
  }
};

function formatOrdinal(number, locale) {
  const rules = new Intl.PluralRules(locale, { type: 'ordinal' });
  const category = rules.select(number);
  const suffixes = ordinalSuffixes[locale];
  const suffix = suffixes[category] || suffixes.other;
  return `${number}${suffix}`;
}

console.log(formatOrdinal(1, 'en-US'));
// Output: "1st"

console.log(formatOrdinal(1, 'fr-FR'));
// Output: "1er"

console.log(formatOrdinal(2, 'en-US'));
// Output: "2nd"

console.log(formatOrdinal(2, 'fr-FR'));
// Output: "2e"

このアプローチは、各ロケールの接尾辞文字列を制御する場合にうまく機能します。ただし、サポートするすべてのロケールの接尾辞データを維持する必要があります。

序数複数形カテゴリの理解

Intl.PluralRules APIは、6つの可能なカテゴリ名を使用します。異なるロケールは、これらのカテゴリの異なるサブセットを使用します。

カテゴリは、zero、one、two、few、many、otherです。すべての言語が6つのカテゴリすべてを区別するわけではありません。英語の序数は、one、two、few、otherの4つのみを使用します。

カテゴリ名は数値に直接対応しません。カテゴリ「one」には、11を除く1で終わるすべての数値である1、21、31、41が含まれます。カテゴリ「two」には、12を除く2で終わるすべての数値である2、22、32、42が含まれます。

ロケールが使用するカテゴリを確認するには、resolvedOptions()メソッドを呼び出し、pluralCategoriesプロパティを調べます。

const rules = new Intl.PluralRules('en-US', { type: 'ordinal' });
const options = rules.resolvedOptions();

console.log(options.pluralCategories);
// Output: ["one", "two", "few", "other"]

これは、英語の序数が4つのカテゴリを使用することを示しています。他のロケールでは異なるカテゴリのセットを使用します。

const rules = new Intl.PluralRules('fr-FR', { type: 'ordinal' });
const options = rules.resolvedOptions();

console.log(options.pluralCategories);
// Output: ["one", "other"]

フランス語の序数は、oneとotherのみを区別します。このよりシンプルな分類は、フランス語のよりシンプルな接尾辞ルールを反映しています。

ユーザーのロケールに合わせた序数のフォーマット

特定のロケールをハードコーディングする代わりに、ブラウザからユーザーの優先言語を使用できます。navigator.languageプロパティは、ユーザーの最優先言語設定を返します。

const userLocale = navigator.language;
const rules = new Intl.PluralRules(userLocale, { type: 'ordinal' });

const suffixes = {
  one: 'st',
  two: 'nd',
  few: 'rd',
  other: 'th'
};

function formatOrdinal(number) {
  const category = rules.select(number);
  const suffix = suffixes[category] || suffixes.other;
  return `${number}${suffix}`;
}

console.log(formatOrdinal(1));
// Output varies by user's locale

このアプローチは、ユーザーの言語設定に自動的に適応します。ただし、アプリケーションがサポートする各ロケールに対して適切な接尾辞マッピングを提供する必要があります。

特定の接尾辞マッピングがないロケールの場合、デフォルトの動作にフォールバックするか、接尾辞なしで数値を表示できます。

function formatOrdinal(number, locale = navigator.language) {
  const rules = new Intl.PluralRules(locale, { type: 'ordinal' });
  const category = rules.select(number);

  const localeMapping = ordinalSuffixes[locale];

  if (!localeMapping) {
    return String(number);
  }

  const suffix = localeMapping[category] || localeMapping.other || '';
  return `${number}${suffix}`;
}

この関数は、ロケールに接尾辞マッピングが存在しない場合、数値のみを返します。

序数の一般的な使用例

序数は、ユーザーインターフェースのいくつかの一般的なコンテキストで表示されます。これらの使用例を理解することで、数値を序数としてフォーマットするタイミングを判断できます。

ランキングやリーダーボードは、ユーザーの順位を表示します。ゲームアプリケーションでは、「place 1」、「place 2」、「place 3」ではなく、「1st place」、「2nd place」、「3rd place」と表示されます。

function formatRanking(position) {
  const rules = new Intl.PluralRules('en-US', { type: 'ordinal' });
  const category = rules.select(position);

  const suffixes = {
    one: 'st',
    two: 'nd',
    few: 'rd',
    other: 'th'
  };

  const suffix = suffixes[category];
  return `${position}${suffix} place`;
}

console.log(formatRanking(1));
// Output: "1st place"

console.log(formatRanking(42));
// Output: "42nd place"

日付のフォーマットでは、月の日付に序数を使用することがあります。一部のロケールでは、「January 1」ではなく「January 1st」と記述します。

ステップバイステップの手順では、各ステップに番号を付けるために序数を使用します。チュートリアルでは、「1st step: Install the software」、「2nd step: Configure settings」、「3rd step: Start the application」と表示されます。

長いシーケンスのリスト項目は、単なる列挙よりも位置を強調することが重要な場合、序数フォーマットの恩恵を受けます。

パフォーマンスのためのルールオブジェクトの再利用

新しいIntl.PluralRulesインスタンスの作成には、ロケールデータの読み込みとオプションの処理が含まれます。同じロケールで複数の序数をフォーマットする場合は、ルールオブジェクトを一度作成して再利用してください。

const rules = new Intl.PluralRules('en-US', { type: 'ordinal' });

const suffixes = {
  one: 'st',
  two: 'nd',
  few: 'rd',
  other: 'th'
};

function formatOrdinal(number) {
  const category = rules.select(number);
  const suffix = suffixes[category];
  return `${number}${suffix}`;
}

const positions = [1, 2, 3, 4, 5];

positions.forEach(position => {
  console.log(formatOrdinal(position));
});
// Output:
// "1st"
// "2nd"
// "3rd"
// "4th"
// "5th"

このアプローチは、数値ごとに新しいルールオブジェクトを作成するよりも効率的です。数百または数千の値を含む配列をフォーマットする場合、パフォーマンスの差は顕著になります。

特定のロケール用に設定された関数を返すフォーマッターファクトリーを作成することもできます。

function createOrdinalFormatter(locale, suffixMapping) {
  const rules = new Intl.PluralRules(locale, { type: 'ordinal' });

  return function(number) {
    const category = rules.select(number);
    const suffix = suffixMapping[category] || suffixMapping.other || '';
    return `${number}${suffix}`;
  };
}

const formatEnglishOrdinal = createOrdinalFormatter('en-US', {
  one: 'st',
  two: 'nd',
  few: 'rd',
  other: 'th'
});

console.log(formatEnglishOrdinal(1));
// Output: "1st"

console.log(formatEnglishOrdinal(2));
// Output: "2nd"

このパターンは、ルールオブジェクトとサフィックスマッピングをまとめてカプセル化し、アプリケーション全体でフォーマッターを簡単に再利用できるようにします。