В реальных интерфейсах часто требуется отображать не одну единицу измерения, а комбинацию нескольких величин: расстояние вместе с дробными частями, время в формате «часы и минуты», скорость, выраженную через базовую единицу расстояния и времени, бытовые форматы вроде «килограммы и граммы». Такие значения относятся к категории составных (или смешанных) единиц.
В контексте интернационализации задача усложняется тем, что каждая локаль по-своему определяет порядок следования компонентов, правила округления и допустимые сокращения. Библиотека Globalize опирается на CLDR (Unicode Common Locale Data Repository), где для единиц измерения описаны не только базовые символы, но и правила комбинирования и форматирования.
CLDR классифицирует единицы измерения как самостоятельные сущности с набором правил:
Для смешанных единиц важна не только сама единица, но и шаблоны их комбинирования.
Например:
1 hour 30 minutes1 kilometer 250 meters2 kilograms 300 gramsВ разных локалях порядок и разделители могут отличаться.
Globalize предоставляет механизм форматирования единиц через данные CLDR и соответствующие API форматирования.
Базовая идея заключается в том, что единица — это объект с типом и значением, а форматтер преобразует его в строку с учётом локали.
Пример единичного форматирования:
Globalize.locale("en");
const numberFormatter = Globalize.numberFormatter();
const unitFormatter = Globalize.unitFormatter("kilometer");
unitFormatter(5);
// "5 kilometers"
При переходе к смешанным единицам требуется расширение этой модели: несколько значений должны быть преобразованы в согласованную строку.
Смешанные единицы не являются отдельным типом данных в JavaScript-логике Globalize. Они формируются на уровне приложения, но используют единый набор правил локализации.
Основная задача — разложить значение на компоненты и отформатировать каждый из них в соответствии с локалью.
Пример разложения времени:
Это требует двух операций:
Нормализация — это приведение значения к набору базовых единиц.
Для времени:
Для массы:
Для расстояния:
Алгоритм нормализации:
Пример:
function normalizeSeconds(totalSeconds) {
const hours = Math.floor(totalSeconds / 3600);
totalSeconds %= 3600;
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return { hours, minutes, seconds };
}
После разбиения на компоненты каждый элемент форматируется отдельно через Globalize.
Globalize.locale("en");
const nf = Globalize.numberFormatter();
const unitHour = Globalize.unitFormatter("hour");
const unitMinute = Globalize.unitFormatter("minute");
const time = normalizeSeconds(3725);
const result =
unitHour(time.hours) + " " +
unitMinute(time.minutes);
Результат будет зависеть от локали:
1 hour 2 minutes1 heure 2 minutes1 Stunde 2 MinutenРазличия касаются не только слов, но и грамматических форм.
CLDR содержит таблицы plural rules, которые определяют форму слова в зависимости от числа.
Например:
Globalize использует эти правила автоматически при вызове unitFormatter.
Это критично для смешанных единиц, так как каждый компонент может иметь собственную форму:
const unitMinute = Globalize.unitFormatter("minute");
unitMinute(1); // "1 minute"
unitMinute(2); // "2 minutes"
В разных языках порядок компонентов может меняться.
Примеры:
1 hour 30 minutes1時間30分1 Stunde 30 MinutenВ Globalize порядок задаётся не вручную, а через CLDR-шаблоны, где определены правила соединения единиц.
Приложение лишь формирует набор значений, а локаль определяет способ их склейки.
Смешанные единицы часто используются для компактных интервалов:
Пример стратегии компактного отображения:
Globalize не навязывает формат «часы:минуты», но предоставляет инструменты для единиц, на основе которых такие форматы строятся.
Некоторые значения требуют трёх и более уровней детализации.
Пример: длительность 1 день 3 часа 12 минут
function normalizeDuration(seconds) {
const days = Math.floor(seconds / 86400);
seconds %= 86400;
const hours = Math.floor(seconds / 3600);
seconds %= 3600;
const minutes = Math.floor(seconds / 60);
return { days, hours, minutes };
}
Далее каждый компонент форматируется через соответствующий unitFormatter.
При работе с несколькими единицами важно соблюдать единый стиль:
Пример некорректного подхода:
result = time.hours + "h " + time.minutes + " min";
Корректный подход:
const hour = Globalize.unitFormatter("hour")(time.hours);
const minute = Globalize.unitFormatter("minute")(time.minutes);
result = hour + " " + minute;
При разложении на смешанные единицы неизбежно возникает округление.
Пример:
1.75 часа
Алгоритм должен учитывать:
Обобщённый форматтер смешанных единиц обычно состоит из трёх слоёв:
слой нормализации
слой декомпозиции
слой локализации
function formatMixedTime(seconds) {
const t = normalizeSeconds(seconds);
const parts = [];
if (t.hours) {
parts.push(Globalize.unitFormatter("hour")(t.hours));
}
if (t.minutes) {
parts.push(Globalize.unitFormatter("minute")(t.minutes));
}
if (t.seconds) {
parts.push(Globalize.unitFormatter("second")(t.seconds));
}
return parts.join(" ");
}
Локаль может менять не только слова, но и:
Например:
1 hour 0 minutes1 h1時間Это означает, что универсальный алгоритм не может быть полностью статическим и должен учитывать правила CLDR для каждой локали отдельно.