В экосистеме i18next работа с датами и временем строится не как отдельная подсистема, а как расширение механизма интерполяции. Базовая идея заключается в том, что перевод строки может содержать динамические значения, которые подставляются в момент выполнения, включая даты, числа и вычисляемые строки.
Стандартная интерполяция выглядит следующим образом:
i18next.t('lastVisit', {
date: new Date()
});
Ресурс перевода:
{
"lastVisit": "Последний визит: {{date}}"
}
По умолчанию объект Date преобразуется в строку через
toString(), что почти всегда неприемлемо для локализации.
Поэтому применяется форматирование через кастомные функции или
встроенные форматтеры.
Механизм форматирования в i18next позволяет применять функции к интерполяционным значениям.
Регистрация форматтера:
i18next.services.formatter.add('datetime', (value, lng, options) => {
return new Intl.DateTimeFormat(lng, options).format(value);
});
Использование в переводах:
{
"lastVisit": "Последний визит: {{date, datetime}}"
}
Передача параметров формата:
i18next.t('lastVisit', {
date: new Date(),
formatParams: {
date: {
year: 'numeric',
month: 'long',
day: '2-digit'
}
}
});
Современный стандарт форматирования дат основан на
Intl.DateTimeFormat. Он обеспечивает локализацию без
сторонних зависимостей.
Пример прямого форматирования:
const formatter = new Intl.DateTimeFormat('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
formatter.format(new Date());
Интеграция с i18next обычно реализуется через обёртку форматтера:
i18next.services.formatter.add('date', (value, lng, options) => {
return new Intl.DateTimeFormat(lng, options).format(value);
});
Формат даты определяется двумя независимыми параметрами:
Такой подход позволяет переиспользовать один и тот же ключ перевода:
{
"createdAt": "Создано: {{date, date}}"
}
i18next.t('createdAt', {
date: new Date(),
lng: 'ru',
formatParams: {
date: {
dateStyle: 'long',
timeStyle: 'short'
}
}
});
Часовые пояса в JavaScript не хранятся в объекте Date, а
вычисляются при форматировании. В i18n-слое это создаёт важный слой
абстракции: форматтер обязан учитывать целевую локаль, но не всегда —
временную зону пользователя.
Форматирование с явным часовым поясом:
new Intl.DateTimeFormat('ru-RU', {
timeZone: 'Europe/Moscow',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}).format(new Date());
В связке с i18next это используется в пользовательских форматтерах:
i18next.services.formatter.add('datetimeTz', (value, lng, options) => {
return new Intl.DateTimeFormat(lng, {
...options,
timeZone: options.timeZone || 'UTC'
}).format(value);
});
ICU-сообщения (International Components for Unicode) предоставляют расширенный синтаксис для интерполяции и форматирования.
При использовании ICU-плагина для i18next поддерживаются конструкции:
{
"eventDate": "Событие состоится {date, date, long}"
}
Поддерживаемые типы:
datetimenumberpluralICU обеспечивает стандартизированную модель, где форматирование отделено от кода и описывается декларативно.
Системы реального уровня редко ограничиваются Intl.
Часто вводятся дополнительные слои:
import dayjs from 'dayjs';
i18next.services.formatter.add('dayjs', (value, lng, options) => {
return dayjs(value).format(options.format || 'YYYY-MM-DD');
});
Использование:
{
"upd ated": "Обновлено: {{date, dayjs}}"
}
import moment from 'moment';
i18next.services.formatter.add('moment', (value, lng, options) => {
return moment(value).format(options.format);
});
Несмотря на распространённость, данный подход постепенно вытесняется из-за избыточного веса библиотеки и проблем с tree-shaking.
При серверном рендеринге значения дат передаются как строки, чтобы избежать различий между сервером и клиентом.
Проблемный вариант:
{
date: new Date()
}
Корректный вариант:
{
date: new Date().toISOString()
}
На клиенте происходит восстановление:
new Date(dateString)
В связке с i18next форматтеры применяются уже после гидратации, что обеспечивает стабильный результат.
Относительное время («2 минуты назад») не входит в базовый i18next,
но часто реализуется через Intl.RelativeTimeFormat.
const rtf = new Intl.RelativeTimeFormat('ru', { numeric: 'auto' });
rtf.format(-2, 'minute');
Интеграция:
i18next.services.formatter.add('relativeTime', (value, lng, options) => {
const rtf = new Intl.RelativeTimeFormat(lng, options);
return rtf.format(value.amount, value.unit);
});
Пример использования:
{
"lastSeen": "Был в сети {{time, relativeTime}}"
}
Для стабильного форматирования даты требуется унификация входного типа:
Поддерживаемые формы:
Datenumber)Нормализация:
function normalizeDate(value) {
if (value instanceof Date) return value;
if (typeof value === 'number') return new Date(value);
return new Date(Date.parse(value));
}
Форматтер:
i18next.services.formatter.add('date', (value, lng, options) => {
const date = normalizeDate(value);
return new Intl.DateTimeFormat(lng, options).format(date);
});
Форматирование дат является дорогостоящей операцией при массовом рендеринге списков. В рамках i18next применяется несколько стратегий оптимизации:
Intl.DateTimeFormat(lng + options)Пример кэша:
const cache = new Map();
function getFormatter(lng, options) {
const key = lng + JSON.stringify(options);
if (!cache.has(key)) {
cache.se t(key, new Intl.DateTimeFormat(lng, options));
}
return cache.get(key);
}
Некоторые языки используют альтернативные календари (исламский,
буддийский, японский). Intl.DateTimeFormat поддерживает
параметр calendar.
new Intl.DateTimeFormat('ar-SA-u-ca-islamic', {
day: 'numeric',
month: 'long',
year: 'numeric'
}).format(new Date());
Интеграция через i18next:
i18next.services.formatter.add('calendarDate', (value, lng, options) => {
return new Intl.DateTimeFormat(lng, {
...options
}).format(value);
});
При смене языка форматирование дат автоматически меняется за счёт
передачи lng в форматтер:
i18next.changeLanguage('de');
Дальнейшие вызовы:
i18next.t('createdAt', { date: new Date() });
дают локализованный результат без изменения бизнес-логики.
Форматирование дат в i18n-слое имеет ряд структурных ограничений:
Intl в среде выполненияЭти ограничения компенсируются внешними библиотеками и строгой нормализацией входных данных, а также единым форматтер-слоем внутри i18next