Angular подключение

В Angular-проектах библиотека Globalize подключается через npm как обычная зависимость и требует дополнительной настройки CLDR-данных для корректной локализации чисел, дат и валют.

Установка выполняется стандартной командой:

npm install globalize cldr-data

Дополнительно часто требуется установка вспомогательных пакетов для работы с CLDR:

npm install cldrjs

После установки формируется базовая инфраструктура, в которой Globalize не работает «из коробки» — ему требуется загрузка локализационных данных и регистрация форматов.


Подготовка CLDR-данных

Globalize опирается на Unicode CLDR (Common Locale Data Repository). Без загрузки этих данных форматирование остаётся неполным.

В Angular-проекте данные обычно подключаются вручную в одном из bootstrap-файлов (например, main.ts или отдельном i18n.ts).

Пример подключения базового набора:

import * as Globalize from "globalize";

import likelySubtags from "cldr-data/supplemental/likelySubtags";
import numberingSystems from "cldr-data/supplemental/numberingSystems";

import enNumbers from "cldr-data/main/en/numbers";
import enCurrencies from "cldr-data/main/en/currencies";
import enCaGregorian from "cldr-data/main/en/ca-gregorian";
import enTimeZoneNames from "cldr-data/main/en/timeZoneNames";

Globalize.load(
  likelySubtags,
  numberingSystems,
  enNumbers,
  enCurrencies,
  enCaGregorian,
  enTimeZoneNames
);

Globalize.locale("en");

При увеличении числа поддерживаемых локалей структура расширяется: добавляются соответствующие наборы main/<locale>.


Интеграция Globalize в Angular через сервис

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

Создание сервиса:

import { Injectable } from "@angular/core";
import * as Globalize from "globalize";

@Injectable({
  providedIn: "root"
})
export class I18nService {
  private globalize = Globalize;

  setLocale(locale: string): void {
    this.globalize.locale(locale);
  }

  formatNumber(value: number): string {
    return this.globalize.numberFormatter()(value);
  }

  formatCurrency(value: number, currency: string): string {
    return this.globalize.currencyFormatter(currency)(value);
  }

  formatDate(value: Date): string {
    return this.globalize.dateFormatter()(value);
  }
}

Такой слой изолирует библиотеку от компонентов Angular и упрощает замену реализации при необходимости.


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

Компоненты получают доступ к форматированию через DI:

import { Component } from "@angular/core";
import { I18nService } from "./i18n.service";

@Component({
  selector: "app-dashboard",
  template: `
    <div>{{ price }}</div>
    <div>{{ date }}</div>
  `
})
export class DashboardComponent {
  price: string;
  date: string;

  constructor(private i18n: I18nService) {
    this.price = this.i18n.formatCurrency(1250, "USD");
    this.date = this.i18n.formatDate(new Date());
  }
}

Форматирование выполняется на уровне бизнес-логики, а не шаблона, что снижает нагрузку на Angular change detection при сложных выражениях.


Создание Angular Pipe для Globalize

Для декларативного использования в шаблонах создаётся pipe-обёртка.

Pipe для чисел

import { Pipe, PipeTransform } from "@angular/core";
import * as Globalize from "globalize";

@Pipe({
  name: "gNumber",
  pure: true
})
export class NumberPipe implements PipeTransform {
  transform(value: number): string {
    return Globalize.numberFormatter()(value);
  }
}

Pipe для валют

import { Pipe, PipeTransform } from "@angular/core";
import * as Globalize from "globalize";

@Pipe({
  name: "gCurrency",
  pure: true
})
export class CurrencyPipe implements PipeTransform {
  transform(value: number, currency: string): string {
    return Globalize.currencyFormatter(currency)(value);
  }
}

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

<div>{{ 1500 | gNumber }}</div>
<div>{{ 1500 | gCurrency:'EUR' }}</div>

Pipe обеспечивает лаконичность шаблонов, но требует осторожности при массовом использовании из-за возможной повторной инициализации форматтеров.


Управление локалью в Angular приложении

Динамическое переключение локали требует обновления состояния Globalize и повторного пересчёта отображаемых значений.

Расширение сервиса:

setLocale(locale: string): void {
  this.globalize.locale(locale);
}

Дополнительно применяется реактивный подход через BehaviorSubject:

import { BehaviorSubject } from "rxjs";

private locale$ = new BehaviorSubject<string>("en");

setLocale(locale: string): void {
  this.globalize.locale(locale);
  this.locale$.next(locale);
}

getLocale() {
  return this.locale$.asObservable();
}

Компоненты подписываются на изменения и обновляют данные при смене языка интерфейса.


Lazy loading CLDR данных в Angular

При большом количестве локалей загрузка всех CLDR данных в bundle приводит к увеличению размера сборки. Используется динамическая подгрузка через import().

async loadLocale(locale: string): Promise<void> {
  const [
    numbers,
    currencies,
    caGregorian,
    timeZoneNames
  ] = await Promise.all([
    import(`cldr-data/main/${locale}/numbers`),
    import(`cldr-data/main/${locale}/currencies`),
    import(`cldr-data/main/${locale}/ca-gregorian`),
    import(`cldr-data/main/${locale}/timeZoneNames`)
  ]);

  Globalize.load(numbers, currencies, caGregorian, timeZoneNames);
  Globalize.locale(locale);
}

Такой подход уменьшает initial bundle size и переносит нагрузку на runtime.


Обработка дат с учётом Angular DatePipe и Globalize

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

Пример форматирования даты через Globalize:

const formatter = Globalize.dateFormatter({
  datetime: "medium"
});

formatter(new Date());

Сопоставление форматов:

  • short — минимальное отображение
  • medium — стандартное
  • long — расширенное
  • full — максимально подробное

Использование Globalize позволяет унифицировать форматирование вне зависимости от Angular шаблонов.


Работа с валютами и финансовыми данными

Форматирование валют в Globalize учитывает правила конкретной локали:

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

usdFormatter(1234.56);

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

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

Для Angular-приложений финансового профиля целесообразно централизовать форматтеры и избегать их пересоздания.


Оптимизация производительности

Создание форматтеров Globalize является относительно дорогой операцией. В Angular-приложениях применяется кэширование:

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

format(value: number): string {
  return this.numberFormatter(value);
}

Также учитывается:

  • избегание создания форматтеров в шаблонах
  • использование pure pipes
  • минимизация повторной загрузки CLDR
  • инициализация Globalize до bootstrap Angular приложения

Инициализация Globalize до запуска Angular

Для корректной работы локализации Globalize должен быть настроен до вызова bootstrapApplication или platformBrowserDynamic.

Пример структуры main.ts:

import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { AppModule } from "./app/app.module";

import "./i18n/globalize-init";

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch(err => console.error(err));

Файл globalize-init.ts содержит всю загрузку CLDR и установку дефолтной локали.


Интеграция с реактивными формами

При работе с ReactiveFormsModule форматирование часто применяется на уровне отображения, а не хранения значения.

Пример преобразования отображаемого значения:

this.form.get("price")?.valueChanges.subscribe(value => {
  const formatted = this.i18n.formatCurrency(value, "USD");
});

Хранение остаётся в числовом виде, что упрощает валидацию и серверную обработку.


Обработка ошибок и fallback-локали

При отсутствии данных CLDR для выбранной локали используется fallback:

Globalize.locale(["fr", "en"]);

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

Такой механизм повышает устойчивость приложения в многоязычных средах и снижает вероятность runtime-ошибок форматирования.