В i18next форматирование значений является частью механизма интерполяции и позволяет преобразовывать данные непосредственно в строках переводов. Форматтеры применяются на этапе подстановки переменных и отвечают за приведение чисел, дат, строк и других типов данных к локализованному виду без усложнения логики приложения.
Основная идея заключается в разделении перевода и отображения данных: ключи переводов содержат шаблоны, а форматтеры определяют правила преобразования значений.
Интерполяция в i18next работает через плейсхолдеры:
t('welcome_user', { name: 'Alex' })
Шаблон перевода:
{
"welcome_user": "Hello, {{name}}"
}
Форматтеры расширяют этот механизм:
{
"price": "Price: {{value, currency}}"
}
Здесь currency — форматтер, применяемый к
value.
Внутри i18next форматтеры вызываются через функцию:
format: (value, format, lng, options) => {}
Где:
value — исходное значениеformat — имя или тип форматированияlng — текущий языкoptions — дополнительные параметры интерполяцииПростейшая реализация:
import i18next from 'i18next';
i18next.init({
interpolation: {
format: (value, format, lng) => {
if (format === 'uppercase') return String(value).toUpperCase();
return value;
}
}
});
Использование:
i18next.t('key', { name: 'john' });
// при шаблоне {{name, uppercase}} → JOHN
Часто форматирование делегируется встроенному Intl.
format: (value, format, lng) => {
if (format === 'number') {
return new Intl.NumberFormat(lng).format(value);
}
return value;
}
Шаблон:
{
"users": "Users: {{count, number}}"
}
if (format === 'currency') {
return new Intl.NumberFormat(lng, {
style: 'currency',
currency: 'USD'
}).format(value);
}
Более гибкий вариант через options:
format: (value, format, lng, options) => {
if (format === 'currency') {
return new Intl.NumberFormat(lng, {
style: 'currency',
currency: options?.currency || 'USD'
}).format(value);
}
return value;
}
if (format === 'date') {
return new Intl.DateTimeFormat(lng, {
year: 'numeric',
month: 'long',
day: '2-digit'
}).format(value);
}
Использование:
{
"today": "Today is {{date, date}}"
}
i18next поддерживает последовательное применение форматов:
{
"value": "{{amount, number, currency}}"
}
Логика обработки:
number — базовое преобразованиеcurrency — дополнительное оформлениеРеализация цепочки:
format: (value, format, lng) => {
const formats = format.split(',');
return formats.reduce((acc, f) => {
const fmt = f.trim();
if (fmt === 'number') {
return new Intl.NumberFormat(lng).format(acc);
}
if (fmt === 'currency') {
return new Intl.NumberFormat(lng, {
style: 'currency',
currency: 'USD'
}).format(acc);
}
return acc;
}, value);
}
В i18next форматтеры часто выносятся в отдельные плагины или модули. Это позволяет:
const customFormatter = {
type: 'formatter',
init: () => {},
add(name, fn) {
this[name] = fn;
}
};
Регистрация:
i18next.init({
interpolation: {
format: customFormatter
}
});
Одним из наиболее мощных подходов является использование ICU
MessageFormat через i18next-icu.
ICU позволяет выразить:
Пример шаблона:
{
"items": "{count, plural, one {# item} other {# items}}"
}
Интеграция:
import ICU from 'i18next-icu';
import i18next from 'i18next';
i18next.use(ICU).init({
lng: 'en'
});
ICU полностью заменяет необходимость ручных форматтеров для сложных случаев.
Форматтеры часто используются для значений, не требующих перевода:
Пример процентов:
if (format === 'percent') {
return new Intl.NumberFormat(lng, {
style: 'percent'
}).format(value);
}
Форматтеры получают lng, что позволяет учитывать
особенности языка:
if (lng === 'ru') {
return new Intl.NumberFormat('ru-RU').format(value);
}
Это особенно важно для:
Форматтеры часто становятся точкой контроля над данными:
Пример проверки:
if (typeof value !== 'number') {
return value;
}
Форматирование через Intl может быть затратным при
частых вызовах. Оптимизация включает:
const cache = new Map();
function getNumberFormatter(lng) {
if (!cache.has(lng)) {
cache.set(lng, new Intl.NumberFormat(lng));
}
return cache.get(lng);
}
Использование:
if (format === 'number') {
return getNumberFormatter(lng).format(value);
}
В React-приложениях форматтеры используются внутри переводов:
const { t } = useTranslation();
t('price', { value: 1200 });
Шаблон:
{
"price": "{{value, currency}}"
}
Форматирование остаётся в i18next, а UI получает уже готовую строку.
В сложных системах вводятся предметные форматтеры:
if (format === 'weight') {
return `${value} kg`;
}
if (format === 'status') {
const map = {
active: 'Active',
disabled: 'Disabled'
};
return map[value] || value;
}
Распространённые подходы:
i18next-icu)Форматтеры часто комбинируются с postprocessor-цепочкой для сложной трансформации текста.
Корректная организация форматтеров обычно предполагает:
Такое разделение упрощает масштабирование интернационализации и снижает связанность модулей приложения.