Аудит существующего кода

Код, работающий с датами, числами и строками без использования возможностей Intl API, часто содержит скрытые ошибки, которые проявляются только при смене локали, часового пояса или региона выполнения. На этапе аудита такие участки выявляются как источники нестабильного поведения и несоответствия ожиданиям пользователей в разных окружениях.

Наиболее распространённые проблемы:

  • использование toLocaleString() без явного указания локали и параметров
  • ручная конкатенация строк для дат и чисел
  • форматирование дат через Date.getMonth(), Date.getDate(), Date.getFullYear()
  • предположение фиксированного порядка (например, DD.MM.YYYY)
  • отсутствие учёта временных зон
  • различия в форматировании чисел (разделители тысяч, десятичные знаки)

Пример фрагмента, часто встречающегося в устаревшем коде:

const date = new Date();
const formatted = date.getDate() + "." + (date.getMonth() + 1) + "." + date.getFullYear();

Такой подход игнорирует локаль, порядок компонентов и особенности календарных систем.


Выявление мест, требующих Intl.NumberFormat

Числовые значения в интерфейсе часто формируются вручную, что приводит к несогласованности отображения. Основные признаки кода, требующего замены:

  • использование number.toFixed() для отображения валюты
  • ручная вставка пробелов или запятых как разделителей
  • различия между сервером и клиентом в форматировании

Фрагмент с потенциальной проблемой:

const price = 1234567.89;
const formatted = price.toFixed(2).replace(".", ",");

В разных регионах такой код приводит к конфликту ожиданий пользователей.

Правильная точка аудита — проверка возможности замены на Intl.NumberFormat:

const formatter = new Intl.NumberFormat("ru-RU", {
  style: "currency",
  currency: "RUB"
});

formatter.format(1234567.89);

Анализ работы с датами через Intl.DateTimeFormat

Наиболее критичная зона аудита — обработка дат. Код без Intl.DateTimeFormat обычно:

  • зависит от локального времени среды выполнения
  • использует Date.toString() или toISOString() для отображения пользователю
  • вручную собирает строки дат

Проблемный пример:

const d = new Date();
const output = d.getDate() + "/" + d.getMonth() + "/" + d.getFullYear();

Аудит выявляет такие участки как нарушающие принцип локализации.

Использование форматтера:

const formatter = new Intl.DateTimeFormat("en-GB", {
  year: "numeric",
  month: "2-digit",
  day: "2-digit"
});

formatter.format(new Date());

При проверке кода важно учитывать также наличие временных зон:

new Intl.DateTimeFormat("ru-RU", {
  timeZone: "Europe/Moscow",
  dateStyle: "long",
  timeStyle: "short"
});

Проверка корректности строкового сравнения и сортировки

Код, использующий Array.sort() без локализованного сравнения, часто приводит к некорректному порядку в языках с диакритическими знаками и различной фонетикой.

Проблемный участок:

names.sort();

Аудит выявляет отсутствие Intl.Collator, который обеспечивает языковую корректность:

const collator = new Intl.Collator("ru", {
  sensitivity: "base"
});

names.sort(collator.compare);

Особое внимание уделяется:

  • регистронезависимой сортировке
  • игнорированию диакритики
  • различиям в алфавитах

Переход от ручной плюрализации к Intl.PluralRules

В коде без Intl API часто встречаются условные конструкции:

function format(count) {
  if (count === 1) return "1 элемент";
  if (count > 1 && count < 5) return count + " элемента";
  return count + " элементов";
}

Аудит таких фрагментов выявляет жестко закодированную языковую логику.

Замена на Intl.PluralRules отделяет правила языка от бизнес-логики:

const rules = new Intl.PluralRules("ru");

const form = rules.select(5);

Анализ относительных временных выражений

Часто встречается ручная логика вычисления разницы во времени:

const diff = Date.now() - createdAt;
const hours = Math.floor(diff / 3600000);
return hours + " часов назад";

Такие конструкции плохо масштабируются и не учитывают языковые особенности.

Использование Intl.RelativeTimeFormat устраняет необходимость ручных вычислений строк:

const rtf = new Intl.RelativeTimeFormat("ru", {
  numeric: "auto"
});

rtf.format(-5, "minute");

Проверка локализации списков

Формирование строк через конкатенацию остаётся распространённой проблемой:

const result = items.join(", ");

Аудит выявляет отсутствие поддержки языковых правил перечислений.

Использование Intl.ListFormat:

const lf = new Intl.ListFormat("ru", {
  style: "long",
  type: "conjunction"
});

lf.format(["яблоки", "груши", "сливы"]);

Обнаружение инлайн-локализации и дублирования логики

В крупных кодовых базах встречается дублирование форматирования:

  • разные функции для дат в разных модулях
  • локализация чисел внутри компонентов UI
  • отсутствие централизованных форматтеров

Типичный антипример:

function formatDateRu(date) { ... }
function formatDateEn(date) { ... }
function formatDateDe(date) { ... }

Аудит таких конструкций направлен на выявление точек консолидации через Intl.* API вместо множества специализированных функций.


Проверка производительности и повторного создания форматтеров

Частая проблема — создание новых экземпляров Intl-объектов в каждом вызове функции:

function formatPrice(value) {
  return new Intl.NumberFormat("ru-RU", {
    style: "currency",
    currency: "RUB"
  }).format(value);
}

При массовых вызовах это приводит к избыточным затратам.

Аудит фиксирует необходимость переиспользования:

const formatter = new Intl.NumberFormat("ru-RU", {
  style: "currency",
  currency: "RUB"
});

Анализ серверного и клиентского расхождения

В SSR-приложениях часто выявляется несоответствие между серверной и клиентской локализацией:

  • сервер использует системную локаль
  • клиент — браузерную
  • результат: hydration mismatch

Типичный источник:

new Date().toLocaleString();

Аудит требует фиксации локали и параметров через Intl.DateTimeFormat, чтобы устранить недетерминированность.


Проверка fallback-логики и поддержки окружений

Некоторые среды выполнения могут не поддерживать расширенные возможности Intl без ICU данных. В коде часто встречаются:

  • ручные полифилы
  • fallback на toString()
  • условные ветки без стандартизированного поведения

Признак проблемного участка:

if (!Intl.RelativeTimeFormat) {
  // кастомная реализация
}

Аудит таких мест направлен на унификацию поведения и устранение расхождений между окружениями.


Выявление смешивания форматов в одном модуле

Часто один модуль одновременно содержит:

  • форматирование дат
  • форматирование чисел
  • конкатенацию строк
  • бизнес-логику

Такое смешение затрудняет локализацию и усложняет тестирование. Использование Intl API позволяет выделить слой форматирования и сделать его изолированным, снижая связность кода и упрощая дальнейшую миграцию между локалями.