1st、2nd、3rdのような序数の表記方法

JavaScriptを使用して、地域に適した接尾辞とフォーマットで序数を表示する方法

はじめに

序数は、順序や順位を示します。英語では、レースでの順位やリスト内のアイテムの位置を表すために1st、2nd、3rd、4thと書きます。これらの接尾辞は、序数を通常の基数と区別するのに役立ちます。

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

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

序数とは

序数は位置、順位、または順序を表します。「いくつあるか」ではなく「どの位置か」という質問に答えます。1st、2nd、3rdはレースでの順位を表します。第一、第二、第三はリスト内のアイテムを表します。

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

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

日本語では、序数は「第1」「第2」「第3」「第4」「第5」のように表記されます。10以上の序数も同様に「第」を付けて表します。「第11」「第12」「第20」「第21」などです。

数字で序数を書く場合、英語では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を使用します。他のすべての序数は上付き文字のeを使用し、2e、3e、4eのようになります。この書式には、接尾辞の文字だけでなく、上付き文字の表記法も含まれています。

ドイツ語では、序数は数字の後にピリオドを使用します。1.、2.、3.という表記は、第1、第2、第3を表します。このピリオドは、読者が声に出して読むときに適切な文法的語尾を精神的に追加すべきことを示しています。

スペイン語では、序数は性別によって異なる上付き指標を使用します。男性形の序数は1.º、2.º、3.ºを使用し、女性形の序数は1.ª、2.ª、3.ªを使用します。ピリオドは数字と指標を分離します。

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

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

序数タイプの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));
// 出力: "one"

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

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

console.log(rules.select(4));
// 出力: "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'));
// 出力: "1st"

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

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

console.log(formatOrdinal(4, 'en-US'));
// 出力: "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));
// 出力: "1st"

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

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

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

このAPIは、最後の数字に関わらず「th」を使用する11、12、13などの数字も正しく処理します。

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

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

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

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

異なるロケールの序数の整形

複数形カテゴリとその意味はロケールによって変わります。英語よりカテゴリが少ない言語もあれば、各カテゴリに分類される数値のルールが異なる言語もあります。

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

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

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

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

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

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

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

console.log(cyRules.select(5));
// 出力: "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'));
// 出力: "1st"

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

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

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

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

序数複数カテゴリの理解

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

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

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

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

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

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

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

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

console.log(options.pluralCategories);
// 出力: ["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));
// 出力はユーザーのロケールによって異なります

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

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

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: ソフトウェアをインストールする」、「2nd step: 設定を構成する」、「3rd step: アプリケーションを起動する」と表示します。

長いシーケンスのリスト項目では、単なる列挙よりも位置の強調が重要な場合、序数フォーマットが役立ちます。

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

新しい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"

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