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によって提供されるロケールに一致する文字列を生成します。year、month、dayオプションは、表示される部分とその形式を制御します。
2. useIntlを使用して命令的に日付をフォーマットする
useIntlフックを使用して、属性、ARIAラベル、またはデータ変換などのレンダリングされないコンテキストでフォーマットされた日付文字列が必要な場合にformatDateメソッドにアクセスします。
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オブジェクトを作成します。このアプローチでは、サーバー上または静的生成中に日付を事前フォーマットし、クライアント側の処理を軽減します。