Vue.js интеграция

Интеграция библиотеки Globalize в приложения на Vue.js строится вокруг единого принципа: локализация должна быть реактивной, централизованной и отделённой от бизнес-логики компонентов. Globalize предоставляет набор инструментов для форматирования чисел, дат, валют, сообщений и единиц измерения, а Vue — реактивную систему, которая позволяет динамически переключать локаль без перезагрузки приложения.

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


Установка и базовая подготовка

Globalize требует наличия CLDR-данных (Unicode Common Locale Data Repository). Это принципиально отличает его от более лёгких решений: библиотека не содержит локали «из коробки» и работает только с внешними данными.

Установка:

npm install globalize cldr-data

Дополнительно часто используются загрузчики CLDR:

npm install cldrjs

Подключение CLDR и инициализация Globalize

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

Пример базовой инициализации:

import Globalize from "globalize";
import Cldr from "cldrjs";

// загрузка CLDR JSON (пример условный)
import likelySubtags from "cldr-data/supplemental/likelySubtags.json";
import numbers from "cldr-data/main/ru/numbers.json";
import currencies from "cldr-data/main/ru/currencies.json";
import timeZoneNames from "cldr-data/main/ru/timeZoneNames.json";
import dateFields from "cldr-data/main/ru/dateFields.json";

// инициализация CLDR
Cldr.load(likelySubtags);
Cldr.load(numbers);
Cldr.load(currencies);
Cldr.load(timeZoneNames);
Cldr.load(dateFields);

// установка глобальной локали
Globalize.locale("ru");

Важно: Globalize не работает без предварительной загрузки данных. Ошибки на этом этапе — наиболее частая причина «пустого» форматирования.


Организация локализации в Vue-проекте

В Vue-архитектуре Globalize обычно не используется напрямую в компонентах. Вместо этого создаётся сервис локализации, который инкапсулирует:

  • текущую локаль
  • экземпляры Globalize
  • методы форматирования
  • переключение языка

Базовый локализационный сервис

// i18n/globalizeService.js
import Globalize from "globalize";

class GlobalizeService {
  constructor() {
    this.locale = "ru";
    this.instance = Globalize;
  }

  setLocale(locale) {
    this.locale = locale;
    this.instance.locale(locale);
  }

  formatNumber(value) {
    return this.instance.numberFormatter()(value);
  }

  formatCurrency(value, currency) {
    return this.instance.currencyFormatter(currency)(value);
  }

  formatDate(value) {
    return this.instance.dateFormatter()(value);
  }

  formatMessage(message, params) {
    return this.instance.messageFormatter(message)(params);
  }
}

export const globalizeService = new GlobalizeService();

Реактивная интеграция с Vue

Чтобы локаль автоматически обновляла интерфейс, используется реактивное состояние.

Pinia или Vue reactivity слой

Пример через reactive:

import { reactive } from "vue";
import { globalizeService } from "./globalizeService";

export const i18nState = reactive({
  locale: "ru",
});

Переключение локали:

export function setLocale(locale) {
  i18nState.locale = locale;
  globalizeService.setLocale(locale);
}

Создание Vue composable для Globalize

Composable позволяет использовать форматирование в любом компоненте.

import { computed } from "vue";
import { i18nState } from "./state";
import { globalizeService } from "./globalizeService";

export function useGlobalize() {
  const locale = computed(() => i18nState.locale);

  const formatNumber = (value) => {
    locale.value; // зависимость для реактивности
    return globalizeService.formatNumber(value);
  };

  const formatCurrency = (value, currency) => {
    locale.value;
    return globalizeService.formatCurrency(value, currency);
  };

  const formatDate = (value) => {
    locale.value;
    return globalizeService.formatDate(value);
  };

  return {
    locale,
    formatNumber,
    formatCurrency,
    formatDate,
  };
}

Особенность подхода: любое использование locale.value внутри функций заставляет Vue пересчитывать зависимости при смене языка.


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

<script setup>
import { useGlobalize } from "@/i18n/useGlobalize";

const { formatNumber, formatCurrency } = useGlobalize();

const price = 12999.5;
</script>

<template>
  <div>
    <p>Цена: {{ formatCurrency(price, "RUB") }}</p>
    <p>Количество: {{ formatNumber(10234.56) }}</p>
  </div>
</template>

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

Переключение локали должно обновлять весь интерфейс без перезагрузки.

import { setLocale } from "@/i18n/state";

function switchToEnglish() {
  setLocale("en");
}

function switchToRussian() {
  setLocale("ru");
}

После изменения локали Vue автоматически перерисует компоненты, где используются функции форматирования.


Форматирование дат с учётом локали

Globalize использует CLDR-правила для форматирования дат:

const date = new Date();

globalizeService.formatDate(date);

Расширенный вариант:

this.instance.dateFormatter({
  date: "long",
  time: "short",
})(date);

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

  • short
  • medium
  • long
  • full

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

globalizeService.formatCurrency(1500, "USD");

Особенность Globalize — строгая привязка к ISO-кодам валют и локали. Отображение символов и позиционирование автоматически определяется CLDR.


Форматирование сообщений (MessageFormatter)

MessageFormatter используется для интерполяции строк:

const msg = Globalize.messageFormatter("cart.items");

msg({ count: 3 });

Пример CLDR-совместимого шаблона:

{
  "cart": {
    "items": "В корзине {count} товаров"
  }
}

Поддерживается pluralization:

{
  "items": {
    "one": "{count} товар",
    "few": "{count} товара",
    "many": "{count} товаров"
  }
}

Хранение и загрузка локалей динамически

В крупных приложениях локали не загружаются сразу.

Пример ленивой загрузки:

async function loadLocale(locale) {
  const data = await import(`cldr-data/main/${locale}/numbers.json`);
  Globalize.load(data);
  Globalize.locale(locale);
}

Такой подход уменьшает начальный бандл.


Интеграция через Vue plugin

Для централизованного подключения создаётся плагин:

import { globalizeService } from "./globalizeService";

export default {
  install(app) {
    app.config.globalProperties.$g = globalizeService;
  },
};

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

this.$g.formatNumber(1000);

SSR особенности

При серверном рендеринге важно:

  • изолировать локаль на запрос
  • избегать глобального состояния Globalize
  • создавать отдельный instance на запрос

Пример:

export function createGlobalize(locale) {
  const g = Globalize;
  g.locale(locale);
  return g;
}

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

Globalize создаёт форматтеры, которые тяжёлые при частом создании. Оптимизация:

const numberFormatter = Globalize.numberFormatter();
const currencyFormatter = Globalize.currencyFormatter("USD");

Переиспользование экземпляров уменьшает нагрузку при рендере списков.


Частые ошибки интеграции

Отсутствие CLDR-данных

Без загрузки данных Globalize возвращает ошибки или пустые строки.

Создание форматтеров в шаблонах

Неправильно:

formatCurrency(value) {
  return Globalize.currencyFormatter("USD")(value);
}

При каждом вызове создаётся новый formatter.

Глобальная локаль вместо реактивной

Использование Globalize.locale() без реактивного слоя приводит к отсутствию обновлений UI.


Структурирование локализационного слоя

Рекомендуемая архитектура:

  • /i18n/cldr/ — данные локалей
  • /i18n/service.js — Globalize wrapper
  • /i18n/state.js — reactive locale
  • /i18n/useGlobalize.js — composable
  • /plugins/globalize.js — Vue plugin

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