i18next предоставляет механизм форматирования значений прямо в
процессе интерполяции, позволяя преобразовывать строки, числа, даты и
любые пользовательские структуры данных без усложнения логики
приложения. Основной точкой расширения выступает функция
interpolation.format, через которую внедряются кастомные
форматтеры.
Интерполяция в i18next работает как подстановка значений в строковые шаблоны:
i18next.t('welcome', { name: 'Alex' });
Шаблон:
{
"welcome": "Hello, {{name}}"
}
Добавление форматирования расширяет эту модель:
{
"price": "Price: {{value, currency}}"
}
Здесь currency — идентификатор форматтера, который
обрабатывает значение перед подстановкой.
Ключевой механизм — функция interpolation.format,
определяемая при инициализации i18next:
import i18next from 'i18next';
i18next.init({
interpolation: {
format: (value, format, lng) => {
return value;
}
}
});
Параметры функции:
value — значение, переданное в интерполяциюformat — имя форматтера из строки переводаlng — текущий языкИменно format определяет логику преобразования
значения.
Типичный подход — использование switch для обработки
разных форматов:
i18next.init({
interpolation: {
format: (value, format, lng) => {
switch (format) {
case 'uppercase':
return String(value).toUpperCase();
case 'lowercase':
return String(value).toLowerCase();
case 'capitalize':
return String(value).charAt(0).toUpperCase() + String(value).slice(1);
default:
return value;
}
}
}
});
Использование в переводах:
{
"title": "{{text, uppercase}}"
}
Для числовых значений применяется Intl.NumberFormat,
обеспечивающий локализацию валют, процентов и чисел.
i18next.init({
interpolation: {
format: (value, format, lng) => {
if (format === 'currency') {
return new Intl.NumberFormat(lng, {
style: 'currency',
currency: 'USD'
}).format(value);
}
if (format === 'percent') {
return new Intl.NumberFormat(lng, {
style: 'percent'
}).format(value);
}
return value;
}
}
});
Пример использования:
{
"balance": "Balance: {{amount, currency}}"
}
Работа с датами строится на Intl.DateTimeFormat:
i18next.init({
interpolation: {
format: (value, format, lng) => {
if (value instanceof Date) {
if (format === 'short') {
return new Intl.DateTimeFormat(lng).format(value);
}
if (format === 'long') {
return new Intl.DateTimeFormat(lng, {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(value);
}
}
return value;
}
}
});
Шаблоны переводов:
{
"today": "Today is {{date, long}}"
}
Несмотря на отсутствие встроенной композиции форматтеров, логика может быть расширена вручную:
i18next.init({
interpolation: {
format: (value, format) => {
const formats = format.split(',');
return formats.reduce((acc, f) => {
switch (f.trim()) {
case 'trim':
return String(acc).trim();
case 'uppercase':
return String(acc).toUpperCase();
default:
return acc;
}
}, value);
}
}
});
Пример:
{
"label": "{{text, trim, uppercase}}"
}
Форматтеры могут работать не только со строками и числами, но и со структурами данных:
i18next.init({
interpolation: {
format: (value, format) => {
if (format === 'json') {
return JSON.stringify(value, null, 2);
}
if (format === 'length') {
return Array.isArray(value) ? value.length : 0;
}
return value;
}
}
});
Использование:
{
"items": "Total items: {{list, length}}"
}
Параметр lng позволяет адаптировать форматирование под
текущую локаль:
i18next.init({
interpolation: {
format: (value, format, lng) => {
if (format === 'date') {
return new Intl.DateTimeFormat(lng === 'ru' ? 'ru-RU' : 'en-US').format(value);
}
return value;
}
}
});
Такой подход обеспечивает согласованность локализации между переводами и форматированием.
Механизм форматтеров встроен в слой интерполяции, поэтому:
Часто используемая практика — вынос сложной логики в отдельные утилиты:
const formatters = {
currency: (value, lng) =>
new Intl.NumberFormat(lng, { style: 'currency', currency: 'USD' }).format(value),
uppercase: (value) => String(value).toUpperCase()
};
i18next.init({
interpolation: {
format: (value, format, lng) => {
const fn = formatters[format];
return fn ? fn(value, lng) : value;
}
}
});
В связке с UI-фреймворками форматтеры сохраняют ту же модель работы, так как выполняются на уровне i18next:
const price = i18next.t('price', { amount: 1200 });
Перевод:
{
"price": "{{amount, currency}}"
}
Форматирование остаётся централизованным и не зависит от слоя представления.
При росте приложения форматтеры часто структурируются как модуль:
export const createFormatters = (lng) => ({
dateShort: (value) =>
new Intl.DateTimeFormat(lng).format(value),
numberCompact: (value) =>
new Intl.NumberFormat(lng, { notation: 'compact' }).format(value),
boolean: (value) => (value ? 'true' : 'false')
});
Подключение:
i18next.init({
interpolation: {
format: (value, format, lng) => {
const fns = createFormatters(lng);
return fns[format] ? fns[format](value) : value;
}
}
});
Такой подход формирует из форматтеров отдельный слой представления данных, изолированный от бизнес-логики приложения.