Компактная форма записи

Форматирование больших чисел в сокращённом виде — одна из наиболее востребованных задач локализации интерфейсов. В англоязычных приложениях часто используются записи вида:

  • 1K
  • 25M
  • 7.3B

В русской локали аналогичная запись может выглядеть иначе:

  • 1 тыс.
  • 25 млн
  • 7,3 млрд

Библиотека Globalize предоставляет встроенный механизм компактного форматирования чисел с учётом правил конкретной локали.


Назначение компактного форматирования

Компактная форма применяется в ситуациях, где пространство интерфейса ограничено:

  • статистические панели;
  • аналитические дашборды;
  • графики;
  • карточки товаров;
  • мобильные интерфейсы;
  • рейтинги и счётчики;
  • финансовые приложения.

Основная задача — уменьшить визуальный объём числа без потери смысла.


Подключение CLDR-данных

Для работы компактного форматирования необходимы данные CLDR.

const Globalize = require("globalize");

Globalize.load(
  require("cldr-data/main/en/numbers.json"),
  require("cldr-data/main/en/ca-gregorian.json"),
  require("cldr-data/supplemental/likelySubtags.json"),
  require("cldr-data/supplemental/numberingSystems.json"),
  require("cldr-data/supplemental/plurals.json")
);

Globalize.locale("en");

Для русской локали:

Globalize.load(
  require("cldr-data/main/ru/numbers.json")
);

Globalize.locale("ru");

Метод numberFormatter

Компактное форматирование активируется через параметр compact.

const formatter = Globalize.numberFormatter({
  compact: "short"
});

Поддерживаются два режима:

Режим Описание
short Короткая запись
long Полная текстовая форма

Короткая компактная форма

Английская локаль

const formatter = Globalize.numberFormatter({
  compact: "short"
});

console.log(formatter(1200));
console.log(formatter(550000));
console.log(formatter(7200000));

Результат:

1.2K
550K
7.2M

Длинная компактная форма

const formatter = Globalize.numberFormatter({
  compact: "long"
});

console.log(formatter(1200));
console.log(formatter(550000));
console.log(formatter(7200000));

Результат:

1.2 thousand
550 thousand
7.2 million

Компактное форматирование в русской локали

Globalize.locale("ru");

const formatter = Globalize.numberFormatter({
  compact: "short"
});

console.log(formatter(1200));
console.log(formatter(2500000));
console.log(formatter(7300000000));

Результат:

1,2 тыс.
2,5 млн
7,3 млрд

Globalize автоматически:

  • выбирает нужные сокращения;
  • применяет локальные разделители;
  • использует корректные формы единиц;
  • учитывает правила CLDR.

Режим long для русского языка

const formatter = Globalize.numberFormatter({
  compact: "long"
});

console.log(formatter(1200));
console.log(formatter(2500000));

Результат:

1,2 тысячи
2,5 миллиона

Автоматический выбор масштаба

Globalize самостоятельно определяет, какое сокращение использовать.

const formatter = Globalize.numberFormatter({
  compact: "short"
});

console.log(formatter(999));
console.log(formatter(1000));
console.log(formatter(1500000));
console.log(formatter(9800000000));

Результат:

999
1K
1.5M
9.8B

Масштаб выбирается автоматически:

Диапазон Суффикс
Тысячи K / тыс.
Миллионы M / млн
Миллиарды B / млрд
Триллионы T / трлн

Управление количеством знаков после запятой

Параметр maximumFractionDigits

const formatter = Globalize.numberFormatter({
  compact: "short",
  maximumFractionDigits: 1
});

console.log(formatter(1250));
console.log(formatter(9876543));

Результат:

1.3K
9.9M

Полное округление

const formatter = Globalize.numberFormatter({
  compact: "short",
  maximumFractionDigits: 0
});

console.log(formatter(1250));
console.log(formatter(9876543));

Результат:

1K
10M

Минимальное количество дробных знаков

const formatter = Globalize.numberFormatter({
  compact: "short",
  minimumFractionDigits: 2
});

console.log(formatter(1200));

Результат:

1.20K

Работа с отрицательными числами

const formatter = Globalize.numberFormatter({
  compact: "short"
});

console.log(formatter(-1200));
console.log(formatter(-7800000));

Результат:

-1.2K
-7.8M

Форматирование нуля

console.log(formatter(0));

Результат:

0

Компактная запись для нуля не применяется.


Очень маленькие числа

console.log(formatter(12));
console.log(formatter(999));

Результат:

12
999

Сокращение используется только начиная с определённых порогов.


Использование вместе с процентами

const formatter = Globalize.numberFormatter({
  style: "percent",
  compact: "short"
});

console.log(formatter(1200));

Подобные комбинации обычно не используются на практике. Некоторые локали могут поддерживать их некорректно.


Использование вместе с валютой

const formatter = Globalize.numberFormatter({
  style: "currency",
  currency: "USD",
  compact: "short"
});

console.log(formatter(1250000));

Возможный результат:

$1.3M

Для русской локали:

Globalize.locale("ru");

const formatter = Globalize.numberFormatter({
  style: "currency",
  currency: "RUB",
  compact: "short"
});

console.log(formatter(1500000));

Результат:

1,5 млн ₽

Использование в аналитических интерфейсах

Финансовые панели

const formatter = Globalize.numberFormatter({
  compact: "short",
  maximumFractionDigits: 1
});

const revenue = 25400000;

console.log(formatter(revenue));

Результат:

25.4M

Социальные сети

console.log(formatter(1200));
console.log(formatter(58200));
console.log(formatter(990000));

Результат:

1.2K
58.2K
990K

Игровая статистика

console.log(formatter(150000000));

Результат:

150M

Использование с formatMessage

Компактные числа могут вставляться в локализованные сообщения.

const numberFormatter = Globalize.numberFormatter({
  compact: "short"
});

const users = numberFormatter(2500000);

console.log(`Users online: ${users}`);

Результат:

Users online: 2.5M

Кэширование форматтеров

Создание форматтера — относительно затратная операция.

Неправильно:

function format(value) {
  return Globalize.numberFormatter({
    compact: "short"
  })(value);
}

Правильно:

const formatter = Globalize.numberFormatter({
  compact: "short"
});

function format(value) {
  return formatter(value);
}

Компиляция форматтеров

В production-среде рекомендуется использовать предварительную компиляцию через:

  • globalize-compiler
  • globalize-webpack-plugin

Это позволяет:

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

Различия между локалями

Компактные обозначения отличаются в разных языках.

Английский

1.2K
5M
7B

Русский

1,2 тыс.
5 млн
7 млрд

Японский

120万

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


Ограничения компактного форматирования

Потеря точности

1254300

После форматирования:

1.3M

Точное значение теряется.


Неподходящий формат для бухгалтерии

Компактная форма плохо подходит для:

  • банковских выписок;
  • налоговой отчётности;
  • финансовых документов;
  • точных расчётов.

Возможные различия между браузерами

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


Сравнение с Intl.NumberFormat

Intl

const formatter = new Intl.NumberFormat("en", {
  notation: "compact",
  compactDisplay: "short"
});

console.log(formatter.format(1200000));

Globalize

const formatter = Globalize.numberFormatter({
  compact: "short"
});

console.log(formatter(1200000));

Преимущества Globalize

  • единый API для локализации;
  • поддержка CLDR;
  • гибкая настройка;
  • совместимость со старыми окружениями;
  • предсказуемое поведение;
  • поддержка компиляции форматтеров;
  • интеграция с другими механизмами интернационализации.

Практический пример панели статистики

Globalize.locale("ru");

const compact = Globalize.numberFormatter({
  compact: "short",
  maximumFractionDigits: 1
});

const stats = {
  users: 12500,
  downloads: 728000,
  revenue: 15400000
};

console.log({
  users: compact(stats.users),
  downloads: compact(stats.downloads),
  revenue: compact(stats.revenue)
});

Результат:

{
  users: "12,5 тыс.",
  downloads: "728 тыс.",
  revenue: "15,4 млн"
}

Комбинирование параметров

const formatter = Globalize.numberFormatter({
  compact: "short",
  minimumFractionDigits: 1,
  maximumFractionDigits: 2,
  useGrouping: false
});

console.log(formatter(1234567));

Результат:

1.23M

Проверка поддержки локали

Globalize.locale("fr");

const formatter = Globalize.numberFormatter({
  compact: "short"
});

console.log(formatter(1500000));

Если необходимые CLDR-данные не подключены, Globalize выбросит исключение.


Типичные ошибки

Отсутствие plurals.json

Компактное форматирование зависит от правил множественного числа.

Нужно подключать:

require("cldr-data/supplemental/plurals.json")

Отсутствие данных локали

require("cldr-data/main/ru/numbers.json")

Без этих данных форматирование работать не будет.


Создание форматтера внутри цикла

Плохо:

items.forEach(item => {
  const formatter = Globalize.numberFormatter({
    compact: "short"
  });

  console.log(formatter(item));
});

Хорошо:

const formatter = Globalize.numberFormatter({
  compact: "short"
});

items.forEach(item => {
  console.log(formatter(item));
});

Поддержка пользовательских интерфейсов

Компактное форматирование особенно полезно в:

  • React;
  • Vue;
  • Angular;
  • Electron;
  • мобильных WebView;
  • аналитических SPA-приложениях.

Использование в React

const compact = Globalize.numberFormatter({
  compact: "short"
});

function Stats({ value }) {
  return (
    <span>{compact(value)}</span>
  );
}

Использование в Vue

const compact = Globalize.numberFormatter({
  compact: "short"
});

app.config.globalProperties.$compact = compact;

Компонент:

<span>{{ $compact(2500000) }}</span>

Использование в Angular

const compact = Globalize.numberFormatter({
  compact: "short"
});

console.log(compact(9800000));

Производительность

Компактное форматирование требует:

  • анализа локали;
  • выбора шаблона;
  • определения масштаба;
  • округления;
  • применения правил множественного числа.

Поэтому рекомендуется:

  • кэшировать форматтеры;
  • компилировать форматирование заранее;
  • минимизировать создание новых экземпляров.

Поддерживаемые единицы масштаба

Globalize обычно поддерживает:

Значение Английский Русский
1 000 K тыс.
1 000 000 M млн
1 000 000 000 B млрд
1 000 000 000 000 T трлн

Набор сокращений определяется данными CLDR конкретной локали.