React Router v7で異なるロケールの日付をフォーマットする方法

地域固有の形式で日付を表示する

問題

日付には普遍的な表記形式がありません。数字の並び「10/12/2025」は、米国では10月12日を意味しますが、英国では12月10日を意味します。「Oct 12, 2025」のような形式でさえ、英語の月名と特定の順序規則を前提としており、ユーザーの期待と一致しない場合があります。アプリケーションが単一の固定形式で日付を表示すると、異なる規則に従う地域のユーザーにとって違和感があり、明確性と信頼性が低下します。

ロケールごとに、日、月、年の順序、区切り文字の選択、月名を完全に表記するか省略するかなど、日付表示の規則が異なります。これらの規則を無視すると、ユーザーは日付を慣れ親しんだ形式に頭の中で変換する必要があり、認知的負荷と誤解のリスクが増加します。

解決策

地域の規則を理解する国際化APIに表示ロジックを委譲することで、ユーザーのロケールに応じて日付の値をフォーマットします。日付文字列を手動で構築する代わりに、日付オブジェクトをフォーマット関数またはコンポーネントに渡し、アクティブなロケールに適した順序、区切り文字、月名を適用します。

React-intlは、標準的な日付フォーマットオプションを受け入れ、ロケールに対応した文字列を生成する宣言的コンポーネントと命令的メソッドの両方を提供します。含める日付部分とその詳細レベルを指定することで、出力スタイルを制御しながら、ライブラリが地域的な変動を処理します。このアプローチにより、日付フォーマットロジックを表示コンポーネントから分離し、アプリケーション全体で一貫性を確保します。

手順

1. FormattedDateを使用してコンポーネント内で日付をフォーマットする

react-intlのFormattedDateコンポーネントを使用して、ロケール固有の書式で日付をレンダリングします。このコンポーネントは、日付の値とIntl.DateTimeFormatOptionsに対応する書式オプションを受け取ります。

import { FormattedDate } from "react-intl";

export default function EventCard({ event }) {
  return (
    <article>
      <h2>{event.title}</h2>
      <time>
        <FormattedDate
          value={event.date}
          year="numeric"
          month="long"
          day="numeric"
        />
      </time>
      <p>{event.description}</p>
    </article>
  );
}

FormattedDateコンポーネントは、formatDateおよびIntl.DateTimeFormat APIを使用して、周囲のIntlProviderによって提供されるロケールに一致する文字列を生成します。yearmonth、およびdayオプションは、表示される部分とその書式を制御します。

2. useIntlを使用して命令的に日付を書式設定する

useIntlフックを使用してformatDateメソッドにアクセスし、属性、ariaラベル、データ変換などのレンダリングされないコンテキストで書式設定された日付文字列が必要な場合に使用します。

import { useIntl } from "react-intl";

export default function EventList({ events }) {
  const intl = useIntl();

  return (
    <ul>
      {events.map((event) => {
        const formattedDate = intl.formatDate(event.date, {
          year: "numeric",
          month: "short",
          day: "numeric",
        });

        return (
          <li key={event.id}>
            <a
              href={`/events/${event.id}`}
              aria-label={`${event.title} on ${formattedDate}`}
            >
              {event.title}
            </a>
          </li>
        );
      })}
    </ul>
  );
}

formatDate関数は、日付の値とオプションのIntl.DateTimeFormatOptionsを受け取り、ロケールに応じて書式設定された文字列を返します。これは、書式設定された日付を別の文字列に埋め込む必要がある場合や、プロップ値として使用する場合に便利です。

3. 再利用可能な日付フォーマッターヘルパーを作成する

一般的な日付書式パターンをintl.formatDateをラップするヘルパー関数に抽出し、アプリケーション全体で一貫性を確保します。

import { useIntl } from "react-intl";

export function useDateFormatter() {
  const intl = useIntl();

  return {
    formatShortDate: (date: Date | number) =>
      intl.formatDate(date, {
        year: "numeric",
        month: "numeric",
        day: "numeric",
      }),

    formatLongDate: (date: Date | number) =>
      intl.formatDate(date, {
        year: "numeric",
        month: "long",
        day: "numeric",
      }),

    formatDateTime: (date: Date | number) =>
      intl.formatDate(date, {
        year: "numeric",
        month: "short",
        day: "numeric",
        hour: "numeric",
        minute: "2-digit",
      }),
  };
}

このフックは書式設定ロジックを一元化し、アプリケーション全体で一貫した日付スタイルを簡単に適用できるようにします。コンポーネントは、必要な詳細レベルに基づいて適切なメソッドを呼び出します。

4. ルートローダーで日付を書式設定する

データ読み込み中に日付の書式設定が必要な場合は、リクエストからロケールにアクセスし、createIntlを使用してローダーデータを返す前に日付を書式設定します。

import type { Route } from "./+types/event";
import { createIntl, createIntlCache } from "react-intl";

const cache = createIntlCache();

export async function loader({ request }: Route.LoaderArgs) {
  const url = new URL(request.url);
  const locale = url.searchParams.get("locale") || "en";

  const event = await fetchEvent();

  const intl = createIntl({ locale, messages: {} }, cache);

  return {
    event,
    formattedDate: intl.formatDate(event.date, {
      year: "numeric",
      month: "long",
      day: "numeric",
    }),
  };
}

export default function Event({ loaderData }: Route.ComponentProps) {
  return (
    <article>
      <h1>{loaderData.event.title}</h1>
      <time>{loaderData.formattedDate}</time>
    </article>
  );
}

createIntl関数は、Reactコンポーネントの外部で日付をフォーマットできるintlオブジェクトを作成します。このアプローチは、サーバー上または静的生成時に日付を事前フォーマットし、クライアント側の処理を削減します。