В экосистеме FormatJS форматтеры представляют собой слой преобразования данных в локализованное строковое представление. Они используются на этапе рендеринга сообщений и позволяют адаптировать вывод под культурные и языковые особенности.
Базовые форматтеры покрывают стандартные сценарии: числа, даты, время, валюты, списки. Однако реальные приложения часто требуют более сложной логики форматирования, выходящей за рамки встроенных возможностей. В таких случаях применяются кастомные форматтеры.
Кастомный форматтер — это функция, интегрируемая в механизм
IntlMessageFormat, которая расширяет стандартный набор
типов форматирования и позволяет определять собственные правила
преобразования значений.
FormatJS использует объект конфигурации, в который можно передать
пользовательские форматтеры через formats.
Базовая структура выглядит следующим образом:
import { IntlMessageFormat } from 'intl-messageformat';
const formats = {
number: {
percentage: {
style: 'percent',
minimumFractionDigits: 2
}
}
};
const message = new IntlMessageFormat(
'Завершено: {progress, number, percentage}',
'ru',
formats
);
message.format({ progress: 0.756 });
В данном примере создаётся кастомный числовой формат
percentage, который переопределяет стандартное поведение
форматирования числа.
Числовые форматтеры в FormatJS основаны на
Intl.NumberFormat. Кастомизация позволяет задавать
предустановленные профили отображения чисел.
Пример расширенного набора числовых форматов:
const formats = {
number: {
currencyUSD: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
maximumFractionDigits: 2
},
compactShort: {
notation: 'compact',
compactDisplay: 'short'
}
}
};
Использование:
const mf = new IntlMessageFormat(
'Баланс: {amount, number, currencyUSD}',
'en',
formats
);
mf.format({ amount: 12345.67 });
Кастомные числовые форматтеры часто применяются для:
Дата и время являются одним из наиболее частых источников
локализационных ошибок. FormatJS позволяет определять именованные
шаблоны поверх Intl.DateTimeFormat.
const formats = {
date: {
shortDateTime: {
year: '2-digit',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
},
longDate: {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
}
}
};
Применение:
const mf = new IntlMessageFormat(
'Создано: {createdAt, date, longDate}',
'ru',
formats
);
mf.format({ createdAt: new Date() });
Такие форматтеры позволяют централизованно управлять представлением времени без дублирования логики в компонентах.
Списки в FormatJS используют Intl.ListFormat, однако
кастомизация позволяет задать предопределённые стили объединения
элементов.
const formats = {
list: {
semicolon: {
style: 'short',
type: 'unit',
separator: '; '
},
narrow: {
style: 'narrow',
type: 'conjunction'
}
}
};
Пример использования:
const mf = new IntlMessageFormat(
'Теги: {tags, list, semicolon}',
'ru',
formats
);
mf.format({ tags: ['JS', 'i18n', 'FormatJS'] });
Подобные форматтеры полезны при отображении тегов, категорий и системных меток.
Стандартный механизм FormatJS ограничен конфигурацией
Intl.*Format. Однако возможна регистрация полностью
кастомных форматтеров через расширение API.
Подход заключается в создании функции, которая принимает значение и возвращает строку:
const customFormats = {
custom: {
uppercase: (value) => String(value).toUpperCase(),
maskEmail: (value) => {
const [name, domain] = value.split('@');
return `${name[0]}***@${domain}`;
}
}
};
Интеграция через обёртку:
function formatMessage(message, locale, formats, values) {
return message.replace(
/\{(\w+),\s*custom,\s*(\w+)\}/g,
(_, key, formatName) => {
const value = values[key];
return customFormats.custom[formatName](value);
}
);
}
Использование:
formatMessage(
'Пользователь: {email, custom, maskEmail}',
'ru',
customFormats,
{ email: 'user@example.com' }
);
Такой подход применяется, когда стандартные форматтеры не покрывают бизнес-логику.
Одним из ключевых принципов построения локализационной системы является повторное использование форматтеров. FormatJS позволяет создавать библиотеки форматтеров на уровне приложения.
export const formats = {
number: {
percentStrict: {
style: 'percent',
minimumFractionDigits: 1,
maximumFractionDigits: 1
}
},
date: {
isoLike: {
year: 'numeric',
month: '2-digit',
day: '2-digit'
}
}
};
Такая структура обеспечивает:
FormatJS позволяет не только добавлять новые форматы, но и переопределять существующие. Это используется при необходимости адаптации стандартов под требования продукта.
Пример изменения поведения числового форматтера:
const formats = {
number: {
integer: {
maximumFractionDigits: 0,
useGrouping: false
}
}
};
В результате все сообщения, использующие
number, integer, будут выводить числа без разделителей и
десятичных знаков.
В сложных интерфейсах форматирование может зависеть от контекста. FormatJS не поддерживает условную логику внутри форматтера напрямую, однако это реализуется через предварительную обработку данных.
function formatAmount(value, currency) {
const formats = {
number: {
dynamicCurrency: {
style: 'currency',
currency
}
}
};
const mf = new IntlMessageFormat(
'Сумма: {value, number, dynamicCurrency}',
'ru',
formats
);
return mf.format({ value });
}
Такой подход позволяет динамически менять поведение форматтера без изменения шаблонов сообщений.
В крупных приложениях форматтеры обычно выносятся в отдельный слой конфигурации интернационализации.
Типичная структура:
/i18n
formats/
number.js
date.js
list.js
index.js
Файл агрегации:
import number from './formats/number';
import date from './formats/date';
import list from './formats/list';
export const formats = {
number,
date,
list
};
Такой подход обеспечивает централизованное управление всеми правилами форматирования и упрощает масштабирование локализации на новые языки и регионы.
Несмотря на гибкость, кастомные форматтеры имеют ряд ограничений:
Эти ограничения компенсируются строгой архитектурной дисциплиной и централизованным управлением форматами.