Как форматировать даты для разных локалей в 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} на ${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. Этот подход предварительно форматирует даты на сервере или во время статической генерации, уменьшая нагрузку на клиентскую сторону.