Библиотека Globalize использует данные CLDR (Common Locale Data Repository) для локализации чисел, валют, дат, единиц измерения и сообщений. Стандартные данные покрывают большинство языков и регионов, однако в реальных проектах часто требуется изменить или дополнить поведение библиотеки:
Кастомизация в Globalize строится вокруг модификации и расширения CLDR-структур.
Globalize не хранит локализационные данные внутри себя. Все данные поставляются отдельно через библиотеку CLDR и модуль Cldr.js.
Основная схема выглядит так:
const Globalize = require("globalize");
const Cldr = require("cldrjs");
Cldr.load(...cldrData);
Globalize.locale("ru");
После загрузки данные попадают в глобальное хранилище CLDR, откуда Globalize извлекает информацию о:
Большинство данных организовано в древовидной JSON-структуре.
Пример фрагмента локали:
{
"main": {
"ru": {
"numbers": {
"symbols-numberSystem-latn": {
"decimal": ",",
"group": " "
}
}
}
}
}
Основные секции:
| Раздел | Назначение |
|---|---|
numbers |
Форматирование чисел |
currencies |
Валюты |
ca-gregorian |
Календарь |
dates |
Форматы дат |
units |
Единицы измерения |
messages |
Сообщения |
supplemental |
Глобальные правила |
Globalize позволяет загружать произвольные структуры через
Cldr.load().
Cldr.load({
customData: {
projectName: "Enterprise CRM",
timezoneLabel: "МСК"
}
});
Получение данных:
const cldr = new Cldr("ru");
console.log(
cldr.get("customData/projectName")
);
Результат:
Enterprise CRM
Можно заменить разделители чисел.
Исходное значение:
1 234,56
Кастомизация:
Cldr.load({
main: {
ru: {
numbers: {
"symbols-numberSystem-latn": {
decimal: ".",
group: "_"
}
}
}
}
});
Использование:
Globalize.locale("ru");
const formatter =
Globalize.numberFormatter();
console.log(formatter(1234.56));
Результат:
1_234.56
Стандартные шаблоны CLDR можно переопределять.
Cldr.load({
main: {
ru: {
dates: {
calendars: {
gregorian: {
dateFormats: {
short: "dd.MM.yyyy",
medium: "d MMM yyyy",
long: "d MMMM yyyy 'года'"
}
}
}
}
}
}
});
Форматирование:
const formatter =
Globalize.dateFormatter({
datetime: "long"
});
console.log(
formatter(new Date(2026, 4, 12))
);
CLDR хранит названия валют в секции currencies.
Cldr.load({
main: {
ru: {
numbers: {
currencies: {
USD: {
displayName: "доллар США",
symbol: "US$"
}
}
}
}
}
});
Пример:
const formatter =
Globalize.currencyFormatter("USD");
console.log(formatter(150));
Результат:
US$150.00
Иногда используются внутренние токены, бонусные баллы или игровые валюты.
Cldr.load({
main: {
ru: {
numbers: {
currencies: {
COIN: {
displayName: "Coin",
symbol: "ⓒ"
}
}
}
}
}
});
Форматирование:
const formatter =
Globalize.currencyFormatter("COIN");
console.log(formatter(500));
Cldr.load({
main: {
ru: {
units: {
long: {
"digital-packet": {
displayName: "пакет",
unitPattern_count_one: "{0} пакет",
unitPattern_count_few: "{0} пакета",
unitPattern_count_many: "{0} пакетов"
}
}
}
}
}
});
Получение данных:
const cldr = new Cldr("ru");
console.log(
cldr.get(
"main/ru/units/long/digital-packet"
)
);
Plural-правила используются при склонении слов.
Globalize опирается на CLDR plural categories:
zeroonetwofewmanyotherДля русского языка:
| Число | Категория |
|---|---|
| 1 | one |
| 2 | few |
| 5 | many |
Проверка:
const pluralGenerator =
Globalize.pluralGenerator();
console.log(pluralGenerator(1));
console.log(pluralGenerator(2));
console.log(pluralGenerator(5));
CLDR позволяет внедрять собственные правила.
Cldr.load({
supplemental: {
"plurals-type-cardinal": {
ru: {
pluralRuleCountOne:
"i = 1 and v = 0"
}
}
}
});
Подобная кастомизация применяется редко, поскольку может нарушить корректность локализации.
Globalize поддерживает ICU MessageFormat.
const formatter =
Globalize.messageFormatter(
"{count, plural, " +
"one {# файл} " +
"few {# файла} " +
"many {# файлов} " +
"other {# файла}}"
);
console.log(formatter({ count: 5 }));
Сообщения можно хранить централизованно.
Globalize.loadMessages({
ru: {
greetings: {
morning: "Доброе утро",
evening: "Добрый вечер"
}
}
});
Использование:
const formatter =
Globalize.messageFormatter(
"greetings/morning"
);
console.log(formatter());
Cldr.load({
main: {
ru: {
dates: {
calendars: {
gregorian: {
months: {
format: {
wide: {
1: "Январь",
2: "Февраль"
}
}
}
}
}
}
}
}
});
CLDR содержит данные о time zone names.
Cldr.load({
main: {
ru: {
dates: {
timeZoneNames: {
hourFormat: "+HH:mm;-HH:mm",
gmtFormat: "GMT{0}",
gmtZeroFormat: "GMT"
}
}
}
}
});
Например:
Globalize.locale("ru-CORP");
Загрузка:
Cldr.load({
main: {
"ru-CORP": {
identity: {
language: "ru",
territory: "CORP"
},
numbers: {
defaultNumberingSystem: "latn"
}
}
}
});
Теперь локаль становится полноценной частью системы.
CLDR поддерживает fallback-механизм.
Пример:
ru-CORP → ru → root
Если значение отсутствует в ru-CORP, оно берётся из
ru.
Раздел supplemental содержит общие правила:
Пример:
Cldr.load({
supplemental: {
weekData: {
firstDay: {
ru: "mon"
}
}
}
});
Полный CLDR может занимать несколько мегабайт.
Практика production-приложений:
Cldr.load(
require(
"cldr-data/main/ru/numbers.json"
),
require(
"cldr-data/main/ru/ca-gregorian.json"
),
require(
"cldr-data/supplemental/plurals.json"
)
);
Загружаются только необходимые части.
Часто используется build-этап.
Пример:
node scripts/build-cldr.js
Скрипт:
const fs = require("fs");
const result = {
main: {
ru: {
numbers: {
defaultNumberingSystem: "latn"
}
}
}
};
fs.writeFileSync(
"./dist/cldr.json",
JSON.stringify(result)
);
async function loadLocale(locale) {
const data =
await fetch(`/cldr/${locale}.json`)
.then(r => r.json());
Cldr.load(data);
Globalize.locale(locale);
}
CLDR-данные можно изменять напрямую.
const cldr = new Cldr("ru");
cldr.attributes.maxLanguageId = "ru";
Однако подобный подход считается опасным, поскольку внутренние структуры библиотеки могут изменяться.
Форматтеры Globalize создаются дорого.
Рекомендуется:
const cache = {};
function getFormatter(locale) {
if (!cache[locale]) {
cache[locale] =
new Globalize(locale)
.numberFormatter();
}
return cache[locale];
}
Globalize поддерживает precompilation.
const formatter =
Globalize.compileNumberFormatter();
Это уменьшает runtime-издержки и ускоряет работу приложения.
Крупные приложения разделяют данные:
cldr/
├── base/
├── currencies/
├── calendars/
├── enterprise/
Такой подход:
Типичная ошибка:
{
number: {}
}
Вместо:
{
numbers: {}
}
Для проверки используется:
const cldr = new Cldr("ru");
console.log(
cldr.get("main/ru/numbers")
);
Полезный инструмент:
cldr.main([
"numbers",
"currencies",
"USD"
]);
Если загрузить одинаковые секции несколько раз:
Cldr.load(dataA);
Cldr.load(dataB);
последняя загрузка перезапишет предыдущую.
Это важно при:
Практика enterprise-приложений:
{
enterprise: {
branding: {},
units: {},
labels: {}
}
}
Вместо внедрения данных в системные секции.
Для объединения конфигураций:
const deepmerge =
require("deepmerge");
const result =
deepmerge(baseData, customData);
При обновлении CLDR структура может меняться.
Рекомендуется хранить:
/locales/v1/
/locales/v2/
или:
{
"version": "44.0"
}
Ошибка:
numbers: {}
Без необходимых полей.
В результате Globalize теряет доступ к стандартным форматам.
Для pluralization и formatting нужны:
supplemental/plurals.json
supplemental/likelySubtags.json
Версии:
должны быть совместимы между собой.
Пример production-структуры:
src/
├── i18n/
│ ├── cldr/
│ ├── locales/
│ ├── messages/
│ ├── custom/
│ └── loaders/
Изменяется только конкретная локаль.
ru-RU
Подходит для региональных проектов.
Модифицируются supplemental rules.
Используется при:
Большие JSON-структуры:
Практики оптимизации:
Пример динамической загрузки:
async function setLocale(locale) {
const data =
await import(
`./cldr/${locale}.json`
);
Cldr.load(data.default);
Globalize.locale(locale);
}
const locales =
import.meta.glob("./cldr/*.json");
async function load(locale) {
const loader =
locales[`./cldr/${locale}.json`];
const module = await loader();
Cldr.load(module.default);
}
Серверная локализация:
const Globalize =
require("globalize");
Globalize.locale("ru");
module.exports = {
money(value) {
return Globalize
.currencyFormatter("RUB")(value);
}
};
В крупных системах кастомизация обычно включает:
Наиболее устойчивый подход: