Rollup настройка

При использовании Globalize в приложениях с модульной сборкой ключевую роль играет корректная интеграция с Rollup, поскольку библиотека опирается на внешние CLDR-данные, имеет модульную структуру и требует аккуратной настройки tree-shaking и загрузки культурных файлов.

Globalize построен как набор независимых модулей, которые подключаются по мере необходимости: форматирование чисел, дат, сообщений, работа с CLDR и выбор локалей. Это означает, что при сборке Rollup не должен объединять всё в единый тяжёлый бандл без контроля, иначе теряется смысл модульности.

Основная сложность заключается в двух аспектах:

  • необходимость явной загрузки CLDR-данных;
  • отсутствие автоматической инициализации локалей;
  • важность корректного tree-shaking для уменьшения размера итогового бандла.

Rollup в связке с Globalize требует строгого контроля импортов: любые «широкие» импорты вида import * as Globalize ухудшают оптимизацию и увеличивают размер сборки.

Установка зависимостей

Базовый набор включает сам Globalize, Rollup и плагины для работы с node_modules:

npm install globalize
npm install rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs

Дополнительно для обработки JSON (CLDR-данные поставляются в JSON-формате):

npm install @rollup/plugin-json

Если используется TypeScript:

npm install typescript @rollup/plugin-typescript

Базовая структура проекта

Типичная структура при работе с Globalize:

src/
  index.js
  i18n/
    globalize.js
    cldr-data.js
node_modules/
rollup.config.js

Отдельное выделение i18n-слоя позволяет изолировать всю работу с локализацией и не смешивать её с бизнес-логикой.

Базовая конфигурация Rollup

Минимальная конфигурация для сборки приложения с Globalize:

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm',
    sourcemap: true
  },
  plugins: [
    json(),
    resolve({
      browser: true
    }),
    commonjs()
  ]
};

Ключевой момент — подключение @rollup/plugin-json, без которого CLDR-данные не будут корректно импортироваться.

Подключение Globalize и CLDR

Globalize не включает данные локалей по умолчанию. Они должны быть импортированы вручную.

Пример загрузки минимального набора данных:

import Globalize from 'globalize';

import likelySubtags from 'cldr-data/supplemental/likelySubtags.json';
import currencyData from 'cldr-data/supplemental/currencyData.json';
import timeData from 'cldr-data/supplemental/timeData.json';

import numbers from 'cldr-data/main/ru/numbers.json';
import currencies from 'cldr-data/main/ru/currencies.json';
import caGregorian from 'cldr-data/main/ru/ca-gregorian.json';
import dateFields from 'cldr-data/main/ru/dateFields.json';

Globalize.load(
  likelySubtags,
  currencyData,
  timeData,
  numbers,
  currencies,
  caGregorian,
  dateFields
);

Globalize.locale('ru');

Такая схема позволяет точно контролировать объём данных и исключает попадание лишних локалей в финальный бандл.

Разделение инициализации локализации

Чтобы Rollup корректно оптимизировал код, инициализация Globalize должна быть вынесена в отдельный модуль:

// src/i18n/globalize.js
import Globalize from 'globalize';

export function initLocale(locale = 'ru') {
  Globalize.locale(locale);
  return Globalize;
}

export default Globalize;

Это позволяет избежать повторной инициализации и упрощает тестирование.

Tree-shaking и влияние структуры импортов

Rollup эффективно удаляет неиспользуемый код только при условии, что импортируется конкретная функциональность:

Правильный вариант:

import formatDate from 'globalize/date';
import formatNumber from 'globalize/number';

Нежелательный вариант:

import Globalize from 'globalize';

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

Оптимизация размера бандла

Globalize сильно зависит от CLDR, поэтому основной вес приложения формируется именно данными локализации.

Стратегии оптимизации:

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

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

export async function loadRussianLocale() {
  const [
    likelySubtags,
    numbers,
    currencies,
    caGregorian,
    dateFields
  ] = await Promise.all([
    import('cldr-data/supplemental/likelySubtags.json'),
    import('cldr-data/main/ru/numbers.json'),
    import('cldr-data/main/ru/currencies.json'),
    import('cldr-data/main/ru/ca-gregorian.json'),
    import('cldr-data/main/ru/dateFields.json')
  ]);

  const Globalize = (await import('globalize')).default;

  Globalize.load(
    likelySubtags.default,
    numbers.default,
    currencies.default,
    caGregorian.default,
    dateFields.default
  );

  Globalize.locale('ru');

  return Globalize;
}

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

Разделение бандлов по локалям

Rollup позволяет создавать несколько выходных файлов, каждый из которых содержит свою локаль:

export default [
  {
    input: 'src/locales/ru.js',
    output: {
      file: 'dist/ru.js',
      format: 'esm'
    },
    plugins: [resolve(), commonjs(), json()]
  },
  {
    input: 'src/locales/en.js',
    output: {
      file: 'dist/en.js',
      format: 'esm'
    },
    plugins: [resolve(), commonjs(), json()]
  }
];

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

Работа с плагинами и конфликтами модулей

При использовании CommonJS-версий зависимостей Globalize иногда возникают проблемы интероперабельности. Решаются они настройкой commonjs():

commonjs({
  include: 'node_modules/**',
  transformMixedEsModules: true
})

Важно следить за порядком плагинов: json() должен идти до commonjs(), иначе JSON-модули могут быть обработаны некорректно.

Использование virtual modules для CLDR

При масштабных проектах удобно инкапсулировать CLDR-данные в виртуальные модули:

// rollup plugin example
export function cldrVirtualPlugin() {
  return {
    name: 'cldr-virtual',
    resolveId(id) {
      if (id === 'virtual:cldr-ru') return id;
    },
    load(id) {
      if (id === 'virtual:cldr-ru') {
        return `
          import numbers from 'cldr-data/main/ru/numbers.json';
          export default { numbers };
        `;
      }
    }
  };
}

Это позволяет централизовать управление локалями и упрощает переиспользование.

Типичные проблемы при сборке

Некорректная настройка Rollup и Globalize часто приводит к следующим проблемам:

  • отсутствие данных локали при запуске;
  • ошибка Globalize is not defined locale data при вызове форматирования;
  • дублирование CLDR в нескольких чанках;
  • чрезмерный размер бандла из-за отсутствия tree-shaking;
  • конфликт CommonJS и ES модулей.

Большинство этих проблем связано не с самой библиотекой, а с тем, как Rollup обрабатывает зависимости.

Производственная конфигурация

Для production-сборки добавляются дополнительные оптимизации:

import terser from '@rollup/plugin-terser';

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/app.min.js',
    format: 'esm',
    sourcemap: false
  },
  plugins: [
    json(),
    resolve({
      browser: true,
      preferBuiltins: false
    }),
    commonjs({
      transformMixedEsModules: true
    }),
    terser()
  ]
};

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

Архитектурные принципы интеграции

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

  • изоляция i18n-слоя от бизнес-кода;
  • явная декларация всех используемых локалей;
  • минимизация глобального состояния;
  • разделение runtime и build-time загрузки данных;
  • контроль размера CLDR как отдельной метрики качества сборки.

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