Intl — это встроенный набор API JavaScript для
интернационализации, включающий форматирование дат, чисел, списков,
множественного числа и других языковых конструкций. Несмотря на
стандартизированность, поддержка отдельных частей Intl
исторически внедрялась поэтапно, а в некоторых окружениях остаётся
неполной.
Ключевая сложность заключается в том, что наличие объекта
Intl само по себе не гарантирует доступность всех его
возможностей. Разные браузеры и версии Node.js поддерживают разные
наборы конструкторов и опций. Поэтому определение необходимости
полифилов — это не бинарная проверка, а анализ конкретных возможностей
среды выполнения.
Первичный уровень проверки сводится к определению существования глобального объекта:
if (typeof Intl === "undefined") {
// требуется полифил всей библиотеки Intl
}
Такой сценарий встречается в крайне старых окружениях (например,
устаревшие Android WebView или ранние версии Node.js). Однако
современная практика показывает, что отсутствие Intl —
редкий случай. Более значимой задачей становится проверка частичной
поддержки.
Intl состоит из набора независимых модулей:
Intl.DateTimeFormatIntl.NumberFormatIntl.CollatorIntl.PluralRulesIntl.RelativeTimeFormatIntl.ListFormatIntl.SegmenterIntl.DisplayNamesНаличие каждого конструктора должно проверяться отдельно:
const hasDateTimeFormat = typeof Intl.DateTimeFormat === "function";
const hasNumberFormat = typeof Intl.NumberFormat === "function";
const hasRelativeTimeFormat = typeof Intl.RelativeTimeFormat === "function";
Такой подход позволяет выявить частично устаревшие окружения, где базовое форматирование чисел и дат доступно, но отсутствуют более новые API.
Даже при наличии конструктора не гарантируется корректная поддержка
локалей. Для этого используется метод
supportedLocalesOf:
if (Intl.DateTimeFormat.supportedLocalesOf(["ru-RU"]).length === 0) {
// локаль не поддерживается полноценно
}
Этот механизм особенно важен при работе с редкими или региональными локалями, где реализация ICU может быть ограниченной.
Важно учитывать, что возвращаемое значение не всегда означает полное отсутствие поддержки — иногда возвращается ближайшая доступная локаль, что может маскировать необходимость полифила.
Более точная диагностика возможна через метод
resolvedOptions(), который показывает фактические параметры
форматирования:
const formatter = new Intl.DateTimeFormat("ru-RU");
const options = formatter.resolvedOptions();
console.log(options.locale);
console.log(options.calendar);
console.log(options.numberingSystem);
Если результирующая локаль отличается от ожидаемой, это сигнал о неполной поддержке ICU-данных в среде.
Также этот метод позволяет выявить отсутствие определённых режимов форматирования, например:
timeZonecalendarСовременные расширения Intl требуют отдельного внимания,
поскольку именно они чаще всего отсутствуют в старых средах.
const hasRTF = typeof Intl.RelativeTimeFormat === "function";
Отсутствие этого API характерно для старых браузеров и требует полифила при отображении «3 дня назад», «через 2 часа» и аналогичных конструкций.
const hasPluralRules = typeof Intl.PluralRules === "function";
Критически важно для корректного склонения числительных в разных языках. Без него невозможно корректно реализовать языковые правила множественного числа.
const hasListFormat = typeof Intl.ListFormat === "function";
Используется для форматирования списков вида «яблоко, банан и груша» с учётом локали.
const hasSegmenter = typeof Intl.Segmenter === "function";
Отвечает за корректное разбиение текста на слова и предложения с учётом языка, включая сложные сценарии (например, китайский или японский текст).
const hasDisplayNames = typeof Intl.DisplayNames === "function";
Используется для отображения локализованных названий стран, валют и языков.
Определение полифилов может выполняться на разных этапах жизненного цикла приложения.
Наиболее распространённый подход — проверка во время выполнения:
function needsIntlPolyfill() {
return (
typeof Intl === "undefined" ||
typeof Intl.RelativeTimeFormat !== "function" ||
typeof Intl.PluralRules !== "function"
);
}
Этот подход обеспечивает максимальную точность, но увеличивает начальный объём логики.
Более гранулярный вариант:
export const intlCapabilities = {
dateTimeFormat: typeof Intl.DateTimeFormat === "function",
numberFormat: typeof Intl.NumberFormat === "function",
relativeTimeFormat: typeof Intl.RelativeTimeFormat === "function",
listFormat: typeof Intl.ListFormat === "function"
};
Такой подход позволяет подключать полифилы точечно.
В современных сборщиках (Webpack, Vite, Rollup) возможно определять поддержку через targets (browserslist), но это не всегда отражает реальную среду выполнения, особенно в embedded WebView.
Поэтому build-time стратегия часто комбинируется с runtime-проверками.
Полифилы для Intl обычно подключаются динамически:
async function ensureIntlSupport() {
if (typeof Intl.RelativeTimeFormat === "undefined") {
await import("@formatjs/intl-relativetimeformat/polyfill");
}
if (typeof Intl.PluralRules === "undefined") {
await import("@formatjs/intl-pluralrules/polyfill");
}
}
Подход с динамическим импортом позволяет избежать загрузки лишнего кода в современных браузерах.
В некоторых случаях используется единый пакет:
@formatjs/intlintl (устаревший, но всё ещё встречающийся)Node.js долгое время использовал собственную сборку ICU, что влияло на доступность локалей и API.
Проверка версии среды:
process.versions.node
Однако более надёжным остаётся проверка конкретных API, поскольку Node может быть собран с минимальным ICU (small-icu), где часть локалей отсутствует.
Проверка только Intl без детализации
if (!Intl) { /* плохо */ }
Такой подход игнорирует частичную поддержку.
Игнорирование supportedLocalesOf
Отсутствие проверки локалей приводит к неожиданным fallback-результатам.
Предположение полной поддержки ICU
Наличие конструктора не означает наличие всех локалей и правил форматирования.
Жёсткое подключение полифилов без проверки
Подключение полифила без feature detection увеличивает bundle и может конфликтовать с нативной реализацией.
На практике используется агрегированная модель:
const intlSupport = {
base: typeof Intl !== "undefined",
dateTimeFormat: typeof Intl.DateTimeFormat === "function",
numberFormat: typeof Intl.NumberFormat === "function",
rtf: typeof Intl.RelativeTimeFormat === "function",
pluralRules: typeof Intl.PluralRules === "function",
listFormat: typeof Intl.ListFormat === "function"
};
const needsPolyfills =
!intlSupport.base ||
!intlSupport.rtf ||
!intlSupport.pluralRules;
Такой подход позволяет разделять критические и некритические зависимости.
Корректное определение необходимости полифилов напрямую влияет на:
Чем точнее определены возможности окружения, тем меньше избыточных зависимостей и выше стабильность интернационализированных интерфейсов.