Поведение интернационализации в JavaScript через Intl API зависит от множества факторов, которые выходят за пределы самой спецификации. В тестировании это приводит к нестабильным результатам, которые меняются между окружениями, версиями движков и установленными ICU-данными.
Основная сложность тестирования заключается в том, что форматирование дат, чисел и строк определяется не только кодом, но и внешними данными:
Даже одинаковый код в Node.js и в браузере может давать различный результат.
ICU определяет:
Разные сборки ICU:
Результат: одна и та же локаль может отсутствовать или иметь упрощённые правила.
В Node.js возможны три режима ICU:
Это напрямую влияет на:
Разные браузеры используют разные реализации ICU:
Различия проявляются в:
V8 определяет:
Даже в пределах одного браузера поведение может меняться при обновлении движка.
new Intl.DateTimeFormat('ru-RU').format(new Date())
Результат зависит от:
Разные окружения:
Это приводит к различным форматам времени.
new Intl.NumberFormat('de-DE').format(1234567.89)
Ожидаемый результат:
1.234.567,89Но может отличаться, если:
['ä', 'a', 'z'].sort(new Intl.Collator('de').compare)
Различия:
Особенно нестабильны тесты при:
Snapshot-тесты с Intl часто ломаются из-за:
Типичный пример проблемы:
expect(formatDate(date)).toMatchSnapshot();
Результат может меняться без изменения кода.
Для стабилизации тестов используются:
TZ=UTC
или
process.env.TZ = 'UTC';
jest.useFakeTimers();
jest.setSystemTime(new Date('2020-01-01T00:00:00Z'));
new Intl.DateTimeFormat('en-US', { timeZone: 'UTC' })
Метод formatToParts снижает хрупкость тестов:
new Intl.DateTimeFormat('ru-RU').formatToParts(new Date());
Вместо строки:
01.01.2020
возвращается структура:
[
{ type: 'day', value: '01' },
{ type: 'literal', value: '.' },
{ type: 'month', value: '01' },
{ type: 'literal', value: '.' },
{ type: 'year', value: '2020' }
]
Это позволяет:
CI-системы часто используют:
Последствия:
Даже в Docker:
Для стабилизации:
LANGLC_ALLIntl использует цепочку fallback:
de-CH → de → en → system default
Это влияет на:
Тесты должны учитывать, что результат может быть “упрощённым”.
Разные окружения могут отличаться:
'a\u030a' !== 'å'
Intl может возвращать разные формы, что ломает строгие сравнения.
Позволяет подменять реализацию:
export const formatDate = (date) =>
new Intl.DateTimeFormat('en-US', { timeZone: 'UTC' }).format(date);
function createFormatter(locale) {
return new Intl.NumberFormat(locale);
}
В тестовой среде иногда подменяется:
Intl.DateTimeFormatIntl.NumberFormatIntl.CollatorUTC устраняет:
Некоторые расширения Intl усиливают нестабильность:
Intl.RelativeTimeFormatIntl.PluralRulesIntl.ListFormatIntl.SegmenterПричина:
ECMAScript периодически расширяет Intl API, и это приводит к:
В Node.js можно контролировать ICU:
--icu-data-dirfull-icu пакетаЭто критично для CI, где поведение может отличаться от локальной машины.
Стабильное поведение достигается комбинацией:
Такое окружение позволяет минимизировать расхождения между системами и делает поведение Intl API предсказуемым при автоматизированном тестировании.