Минимизация размера бандла

Библиотека Globalize в современной JavaScript-экосистеме построена вокруг модульного подхода и использования данных CLDR (Common Locale Data Repository). Основной фактор, влияющий на размер итогового бандла, — раздельная загрузка функциональности и локализационных данных.

Базовая особенность заключается в том, что сама библиотека не содержит языковых данных. Все локали, форматы чисел, дат, валют и правила склонений подключаются отдельно. Это делает невозможным «случайное» раздувание бандла, но требует осознанной сборки.

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


Модульная архитектура Globalize

Globalize разбит на набор независимых модулей:

  • форматирование чисел
  • форматирование дат и времени
  • форматирование валют
  • парсинг чисел и дат
  • сообщения (message formatter)
  • plural rules
  • относительное время

Каждый модуль подключается отдельно:

import Globalize from "globalize";
import "globalize/number";
import "globalize/date";

Такой подход позволяет исключить неиспользуемые части библиотеки на этапе сборки через tree-shaking.


Tree-shaking и ESM-сборка

Современные сборщики (Webpack, Rollup, Vite) позволяют удалять неиспользуемый код, если используется ES Modules.

Для эффективной минимизации:

  • используется импорт из ESM-версии Globalize
  • избегаются wildcard-импорты
  • не подключаются barrel-файлы без необходимости

Пример корректного импорта:

import Globalize from "globalize/dist/globalize.runtime.esm";
import "globalize/number";

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


Разделение CLDR-данных

CLDR — основной источник локализационной информации. Его объём значительно превышает размер самого Globalize, поэтому стратегия загрузки данных критична.

Подключение выполняется явно:

import likelySubtags from "cldr-core/supplemental/likelySubtags.json";
import timeData from "cldr-core/supplemental/timeData.json";

import numbers from "cldr-numbers-full/main/ru/numbers.json";
import currencies from "cldr-numbers-full/main/ru/currencies.json";

Globalize.load(likelySubtags, timeData, numbers, currencies);

Минимизация достигается за счёт:

  • загрузки только одной или нескольких локалей
  • исключения неиспользуемых категорий данных (например, calendars, если не нужны даты)
  • использования cldr-*-minimal пакетов вместо full, когда это возможно

Ленивая загрузка локалей

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

Основной бандл содержит только базовую локаль (например, en), остальные загружаются по требованию:

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

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


Разделение функциональности по модулям

Globalize позволяет подключать только необходимые категории форматирования.

Форматирование чисел

import "globalize/number";

Если в приложении используется только форматирование валют, но не обычных чисел, подключение number может быть избыточным, хотя на практике часто используется вместе.

Валюты

import "globalize/currency";

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

Даты и время

import "globalize/date";

Даты являются одним из самых «тяжёлых» модулей из-за зависимости от календарных данных CLDR. При минимизации важно избегать загрузки календарей для всех локалей.


Оптимизация CLDR-пакетов

CLDR распространяется в нескольких вариантах:

  • cldr-core
  • cldr-numbers-full
  • cldr-dates-full
  • cldr-misc-full

Для уменьшения размера предпочтение отдаётся:

  • частичной загрузке (main/<locale>)
  • supplemental-данным только при необходимости
  • отказу от full-наборов, если функциональность ограничена

Пример минимального набора для чисел:

import numbers from "cldr-numbers-full/main/ru/numbers.json";

Исключение ненужных категорий (например, percent patterns или scientific format) уменьшает итоговый размер JSON-данных.


Code splitting на уровне маршрутов

В SPA-приложениях Globalize часто подключается на уровне маршрутов:

  • базовый бандл: интерфейс + en locale
  • дополнительные языки: отдельные чанки
  • тяжёлые форматы (даты, сообщения): ленивые модули

Пример динамического импорта:

route.onEnter(async () => {
  const { default: Globalize } = await import("globalize");
  await import("globalize/date");

  const cldr = await import("cldr-dates-full/main/ru/ca-gregorian.json");
  Globalize.load(cldr);
});

Минимизация message formatter

Модуль сообщений часто воспринимается как лёгкий, однако при использовании ICU-синтаксиса и plural rules он тянет дополнительные зависимости.

Для уменьшения размера:

  • исключается поддержка сложных plural rules, если используется одна локаль
  • ограничивается количество шаблонов
  • избегаются вложенные message-переводы
import "globalize/message";
import "globalize/plural";

Если plural logic не используется, соответствующий модуль исключается полностью.


Удаление неиспользуемых locale-данных

Даже внутри одной локали CLDR содержит множество секций:

  • date fields
  • era names
  • time zones
  • currency display names
  • number patterns

Минимизация достигается ручным отбором JSON:

  • удаление временных зон при отсутствии необходимости
  • исключение альтернативных форматов дат
  • сокращение currency display names до ISO-кодов

Влияние сборщика на итоговый размер

Разные инструменты сборки по-разному обрабатывают Globalize:

Webpack

  • требует корректной настройки sideEffects: false
  • эффективен при ESM-импортах
  • поддерживает dynamic import для локалей

Rollup

  • обеспечивает наиболее агрессивный tree-shaking
  • предпочтителен для библиотечных сборок

Vite

  • использует Rollup под капотом
  • автоматически разделяет чанки по динамическим импортам

Типичные причины раздувания бандла

  • подключение cldr-full вместо частичных данных
  • импорт всей библиотеки через globalize/dist/globalize.js
  • отсутствие tree-shaking из-за CommonJS-импорта
  • загрузка всех локалей одновременно
  • включение date + currency + message без реальной необходимости

Практика минимального продакшн-набора

Сценарий минимальной конфигурации для одного языка и числового форматирования:

import Globalize from "globalize/dist/globalize.runtime.esm";
import "globalize/number";

import likelySubtags from "cldr-core/supplemental/likelySubtags.json";
import numbers from "cldr-numbers-full/main/ru/numbers.json";

Globalize.load(likelySubtags, numbers);
Globalize.locale("ru");

Такой набор исключает:

  • даты
  • валюты
  • сообщения
  • plural rules

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


Стратегии масштабирования без увеличения initial bundle

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

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

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