Библиотека Globalize использует модульный подход к работе с локализацией и интернационализацией. Основная особенность архитектуры заключается в разделении функциональности на отдельные части: форматирование чисел, дат, валют, относительного времени, сообщений и других компонентов подключаются независимо друг от друга. Такой подход позволяет существенно уменьшить размер итогового JavaScript-бандла и оптимизировать загрузку данных локализации.
В процессе разработки приложений возникает необходимость выбирать между полной загрузкой всех локализационных данных и частичной загрузкой только необходимых модулей и CLDR-данных. От выбранной стратегии зависит производительность, объём передаваемых данных и сложность поддержки проекта.
Работа библиотеки строится на двух ключевых элементах:
CLDR (Common Locale Data Repository) содержит локализационные данные для разных языков и регионов.
Структура подключения выглядит следующим образом:
const Globalize = require("globalize");
Globalize.load(
require("cldr-data/main/en/numbers.json"),
require("cldr-data/supplemental/likelySubtags.json")
);
const en = new Globalize("en");
console.log(en.formatNumber(12345.67));
В данном примере подключаются:
en.Полная загрузка подразумевает подключение:
При полном подключении приложение получает доступ ко всей функциональности библиотеки:
require("globalize");
require("globalize/number");
require("globalize/date");
require("globalize/currency");
require("globalize/message");
require("globalize/relative-time");
require("globalize/plural");
require("globalize/unit");
Загрузка CLDR может выглядеть следующим образом:
Globalize.load(
require("cldr-data/main/en/ca-gregorian.json"),
require("cldr-data/main/en/numbers.json"),
require("cldr-data/main/en/currencies.json"),
require("cldr-data/main/en/dateFields.json"),
require("cldr-data/main/en/units.json"),
require("cldr-data/supplemental/likelySubtags.json"),
require("cldr-data/supplemental/timeData.json"),
require("cldr-data/supplemental/weekData.json"),
require("cldr-data/supplemental/currencyData.json"),
require("cldr-data/supplemental/plurals.json")
);
Не требуется отслеживать зависимости между модулями и CLDR-файлами.
Все необходимые данные уже находятся в памяти приложения.
Форматирование валют, сообщений или единиц измерения можно использовать без дополнительной настройки.
CLDR содержит большой объём данных:
При подключении множества локалей размер приложения может увеличиться на несколько мегабайт.
Все локализационные данные хранятся в памяти независимо от фактического использования.
Особенно заметно в браузерных SPA-приложениях.
Частичная загрузка предполагает подключение только:
Такой подход считается предпочтительным для production-приложений.
Если приложению требуется только форматирование чисел, подключается исключительно соответствующий модуль.
const Globalize = require("globalize");
require("globalize/number");
Теперь доступны только методы работы с числами:
const formatter = Globalize("en").numberFormatter();
console.log(formatter(12345.67));
Методы форматирования дат или сообщений будут отсутствовать.
Каждый модуль Globalize требует строго определённый набор CLDR-файлов.
Globalize.load(
require("cldr-data/main/en/numbers.json"),
require("cldr-data/supplemental/likelySubtags.json")
);
Globalize.load(
require("cldr-data/main/en/currencies.json"),
require("cldr-data/supplemental/currencyData.json")
);
Globalize.load(
require("cldr-data/main/en/ca-gregorian.json"),
require("cldr-data/main/en/timeZoneNames.json"),
require("cldr-data/supplemental/timeData.json"),
require("cldr-data/supplemental/weekData.json")
);
Каждый модуль имеет собственные требования.
| Модуль | Основные CLDR-файлы |
|---|---|
| number | numbers.json |
| currency | currencies.json, currencyData.json |
| date | ca-gregorian.json, timeData.json |
| plural | plurals.json |
| relative-time | dateFields.json |
| unit | units.json |
Отсутствие обязательного файла приводит к ошибкам выполнения.
E_MISSING_CLDR: Missing required CLDR content
Часто возникает при отсутствии:
require("globalize/date");
Но отсутствует:
ca-gregorian.json
В результате:
Globalize("en").formatDate(new Date());
вызовет исключение.
Современные приложения часто загружают локали по требованию.
async function loadLocale(locale) {
const messages = await import(
`cldr-data/main/${locale}/numbers.json`
);
Globalize.load(messages.default);
return new Globalize(locale);
}
Такой подход позволяет:
В сборщиках вроде Webpack или Vite локали обычно разделяются на отдельные чанки.
function loadLocale(locale) {
return import(
/* webpackChunkName: "i18n-[request]" */
`./locales/${locale}.js`
);
}
В результате:
i18n-en.jsi18n-fr.jsi18n-de.jsзагружаются независимо.
Пакет globalize-compiler позволяет компилировать форматтеры заранее.
Вместо хранения полного рантайма приложение использует заранее сгенерированные функции.
Исходный код:
const formatter = Globalize("en").numberFormatter();
После компиляции превращается в обычную JavaScript-функцию.
В production часто удаётся сократить объём библиотеки в несколько раз.
Не требуется строить форматтеры в рантайме.
Многие данные становятся встроенными в скомпилированный код.
На практике используются два основных режима.
Полная библиотека работает в браузере:
Globalize.formatNumber(1000);
Форматтер создаётся заранее:
compiledFormatter(1000);
Compiled-режим особенно эффективен для:
Модульная архитектура Globalize хорошо сочетается с tree shaking.
import Globalize from "globalize";
import "globalize/number";
Если не импортировать:
import "globalize/date";
сборщик исключит соответствующий код.
Некоторые версии CommonJS-модулей плохо оптимизируются сборщиками.
Особенно это касается:
Для production обычно придерживаются следующей схемы:
const Globalize = require("globalize");
require("globalize/number");
require("globalize/currency");
Globalize.load(
require("cldr-data/main/en/numbers.json"),
require("cldr-data/main/en/currencies.json"),
require("cldr-data/supplemental/likelySubtags.json"),
require("cldr-data/supplemental/currencyData.json")
);
const en = new Globalize("en");
console.log(
en.formatCurrency(100, "USD")
);
Globalize.load(
require("cldr-data/main/en/numbers.json"),
require("cldr-data/main/ru/numbers.json"),
require("cldr-data/main/de/numbers.json")
);
Создание экземпляров:
const en = new Globalize("en");
const ru = new Globalize("ru");
const de = new Globalize("de");
Иногда форматтеры создаются только при первом использовании.
let formatter = null;
function formatPrice(value) {
if (!formatter) {
formatter = Globalize("en")
.currencyFormatter("USD");
}
return formatter(value);
}
Такой подход уменьшает время первоначального запуска приложения.
Создание форматтеров — дорогостоящая операция.
function format(value) {
return Globalize("en")
.numberFormatter()(value);
}
Форматтер создаётся при каждом вызове.
const formatter =
Globalize("en").numberFormatter();
function format(value) {
return formatter(value);
}
В SSR-приложениях локализационные данные могут загружаться на сервере.
const fs = require("fs");
const numbers = JSON.parse(
fs.readFileSync(
"./cldr/main/en/numbers.json"
)
);
Globalize.load(numbers);
Для популярных языков применяется preload.
<link
rel="preload"
href="/locales/en.js"
as="script"
/>
Это уменьшает задержку при переключении языка интерфейса.
Крупные приложения часто используют гибридный подход:
| Компонент | Стратегия |
|---|---|
| Основная локаль | preload |
| Редкие локали | lazy loading |
| Форматтеры | compile-time |
| CLDR | частичная загрузка |
Полный пакет cldr-data содержит огромный объём
информации.
На практике приложение редко использует:
Поэтому часто создаются кастомные сборки CLDR.
cldr/
├── en/
│ ├── numbers.json
│ └── currencies.json
└── supplemental/
├── likelySubtags.json
└── currencyData.json
Такой набор подходит для:
| Характеристика | Полная | Частичная |
|---|---|---|
| Простота настройки | высокая | средняя |
| Размер бандла | большой | минимальный |
| Скорость запуска | ниже | выше |
| Гибкость | низкая | высокая |
| Контроль зависимостей | минимальный | полный |
| Подходит для production | редко | да |
Современное приложение обычно использует:
Core bundle
├── Globalize number
├── Основная локаль
└── Compiled formatters
Async chunks
├── Дополнительные языки
├── Date formatting
└── Message translations
Такая архитектура обеспечивает: