Vue и локализация

API Intl предоставляет встроенные средства интернационализации в JavaScript-движках и опирается на ICU (International Components for Unicode). Основная задача — корректное форматирование чисел, дат, валют, единиц измерения и строк с учётом локали пользователя без необходимости ручной реализации региональных правил.

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


Механизм выбора локали и языковая среда

Любая функция Intl опирается на параметр локали, который задаётся строкой BCP 47:

  • ru-RU — русский (Россия)
  • en-US — английский (США)
  • kk-KZ — казахский (Казахстан)
  • tr-TR — турецкий (Турция)

При отсутствии явного указания используется локаль среды выполнения:

const locale = navigator.language;

Если локаль недоступна, применяется fallback-механизм, который выбирает наиболее близкую доступную конфигурацию.


Intl.NumberFormat и форматирование чисел

Intl.NumberFormat обеспечивает форматирование чисел с учётом правил конкретной локали.

Основные сценарии:

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

Базовое форматирование чисел

const formatter = new Intl.NumberFormat('ru-RU');

formatter.format(1234567.89);
// "1 234 567,89"

Форматирование валют

const currencyFormatter = new Intl.NumberFormat('ru-RU', {
  style: 'currency',
  currency: 'KZT'
});

currencyFormatter.format(12345.67);
// "12 345,67 ₸"

Особенность валютного форматирования заключается в автоматическом учёте:

  • положения символа валюты
  • правил округления
  • локальных пробелов и разделителей

Проценты

const percentFormatter = new Intl.NumberFormat('en-US', {
  style: 'percent'
});

percentFormatter.format(0.42);
// "42%"

Intl.DateTimeFormat и работа с датами

Форматирование дат в разных регионах существенно отличается. Intl.DateTimeFormat решает эту задачу без необходимости ручной обработки.

Базовое форматирование

const dateFormatter = new Intl.DateTimeFormat('ru-RU');

dateFormatter.format(new Date());
// "26.05.2026"

Полный контроль над выводом

const detailed = new Intl.DateTimeFormat('en-GB', {
  dateStyle: 'full',
  timeStyle: 'long'
});

detailed.format(new Date());
// "Tuesday, 26 May 2026 at 14:35:12 GMT+6"

Разбор частей даты

const parts = new Intl.DateTimeFormat('ru-RU', {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
}).formatToParts(new Date());

Метод formatToParts позволяет разбирать результат на структурные элементы, что важно для UI-компонентов с кастомной версткой.


Intl.Collator и сортировка строк

Сортировка строк в разных языках отличается из-за особенностей алфавитов и правил сравнения.

const collator = new Intl.Collator('ru-RU');

['яблоко', 'ёж', 'арбуз'].sort(collator.compare);

Intl.Collator учитывает:

  • диакритические знаки
  • регистр
  • локальные правила алфавита

Дополнительные опции:

new Intl.Collator('en', {
  sensitivity: 'base',
  numeric: true
});

Интеграция Intl API в Vue

Vue не предоставляет встроенную систему интернационализации уровня ICU, поэтому Intl API часто используется как базовый слой форматирования в приложениях.

В Vue 3 архитектура локализации обычно строится вокруг реактивного состояния текущей локали и набора форматирующих функций.


Реактивная локаль и переключение языка

В Composition API локаль часто хранится как реактивная переменная:

import { ref, computed } from 'vue';

const locale = ref('ru-RU');

Форматтеры зависят от текущей локали, поэтому их необходимо пересоздавать при изменении языка.


Обёртка над Intl в виде composable

Типовой подход — создание универсального слоя форматирования.

Формат чисел

import { computed } from 'vue';

export function useNumberFormat(locale) {
  const formatNumber = (value) => {
    return new Intl.NumberFormat(locale.value).format(value);
  };

  return { formatNumber };
}

Формат валют

export function useCurrency(locale, currency) {
  const format = (value) => {
    return new Intl.NumberFormat(locale.value, {
      style: 'currency',
      currency
    }).format(value);
  };

  return { format };
}

Использование в компонентах Vue

const { formatNumber } = useNumberFormat(locale);

const price = computed(() => formatNumber(1234567));

Реактивность обеспечивает автоматическое обновление UI при смене локали.


Хранение локали и синхронизация состояния

Локаль часто сохраняется в localStorage:

localStorage.setItem('locale', 'ru-RU');

При инициализации приложения:

const locale = ref(localStorage.getItem('locale') || navigator.language);

Изменение локали сопровождается синхронизацией:

watch(locale, (newLocale) => {
  localStorage.setItem('locale', newLocale);
});

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

Объекты Intl.* не являются полностью дешёвыми в создании, поэтому часто применяются стратегии кеширования.

const cache = new Map();

function getNumberFormatter(locale) {
  if (!cache.has(locale)) {
    cache.set(locale, new Intl.NumberFormat(locale));
  }
  return cache.get(locale);
}

В Vue это может быть вынесено в отдельный сервис, чтобы избежать лишних пересозданий при реактивных обновлениях.


Работа с форматированием дат в реактивной среде

function formatDate(locale, date) {
  return new Intl.DateTimeFormat(locale.value, {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }).format(date);
}

При изменении локали вычисляемые свойства автоматически пересчитываются:

const formattedDate = computed(() =>
  formatDate(locale, new Date())
);

SSR и гидратация локалей

В серверном рендеринге (например, Vue SSR) важно синхронизировать локаль между сервером и клиентом.

Проблемы возникают при различии:

  • navigator.language (клиент)
  • заголовка Accept-Language (сервер)

Типовой подход:

  • сервер определяет локаль и передаёт в initial state
  • клиент использует то же значение при гидратации

Несоответствие приводит к расхождению HTML и предупреждениям гидратации.


Форматирование единиц измерения

Intl.NumberFormat поддерживает форматирование физических единиц:

const speed = new Intl.NumberFormat('ru-RU', {
  style: 'unit',
  unit: 'kilometer-per-hour'
});

speed.format(120);
// "120 км/ч"

Поддерживаются единицы:

  • длины
  • массы
  • скорости
  • объёма
  • температуры

Сравнение с внешними библиотеками локализации

В экосистеме Vue часто используются библиотеки уровня i18n, однако Intl API остаётся базовым слоем:

  • Intl — форматирование
  • i18n-библиотеки — переводы строк и управление словарями

Типичная архитектура разделяет:

  • перевод текста (ключ → строка)
  • форматирование (числа, даты, валюты)

Частые ошибки при использовании Intl в Vue

Игнорирование реактивности локали

Форматтер создаётся один раз и не обновляется при смене языка, что приводит к несоответствию UI.

Дублирование экземпляров форматтеров

Создание Intl.NumberFormat внутри шаблонов вызывает лишние вычисления.

Смешивание перевода и форматирования

Форматирование чисел через строки переводов приводит к ошибкам локализации.


Систематизация использования Intl в компонентной архитектуре

В Vue-приложениях Intl API чаще всего становится частью сервисного слоя:

  • formatService
  • localeService
  • dateService

Каждый сервис инкапсулирует создание форматтеров и их кеширование, обеспечивая предсказуемое поведение интерфейса при смене локали.