Как форматировать даты для разных локалей в React Router v7

Отображайте даты в региональных форматах

Проблема

У дат нет универсального письменного формата. Например, числовая последовательность 10/12/2025 означает 12 октября в США, но 10 декабря в Великобритании. Даже такие форматы, как «12 окт. 2025», предполагают английские названия месяцев и определённый порядок, который может не совпадать с ожиданиями пользователя. Если приложение показывает даты только в одном фиксированном формате, это выглядит чуждо для пользователей из других регионов, снижая понятность и доверие.

В разных локалях существуют свои правила отображения дат: порядок дня, месяца и года, выбор разделителей, написание месяца полностью или сокращённо. Игнорирование этих особенностей заставляет пользователя мысленно переводить даты в привычный формат, что увеличивает когнитивную нагрузку и риск ошибки.

Решение

Форматируйте даты в соответствии с локалью пользователя, передавая логику отображения специализированным API интернационализации, которые знают региональные стандарты. Вместо ручной сборки строк дат передавайте объекты дат в функции или компоненты, которые применяют правильный порядок, разделители и названия месяцев для выбранной локали.

React-intl предоставляет как декларативные компоненты, так и императивные методы, которые принимают стандартные параметры форматирования дат и возвращают строки с учётом локали. Указывая, какие части даты и уровень детализации нужны, вы контролируете стиль вывода, а библиотека учитывает региональные различия. Такой подход отделяет логику форматирования дат от компонентов отображения и обеспечивает единообразие по всему приложению.

Шаги

1. Форматируйте даты в компонентах с помощью FormattedDate

Используйте компонент FormattedDate из react-intl для отображения дат с учетом локали. Компонент принимает значение даты и параметры форматирования, которые соответствуют 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 использует API formatDate и Intl.DateTimeFormat для создания строки, соответствующей локали, заданной окружающим IntlProvider. Опции year, month и 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 создаёт intl-объект, который может форматировать даты вне компонентов React. Такой подход позволяет предварительно форматировать даты на сервере или при статической генерации, снижая нагрузку на клиент.