При построении диаграмм числовые значения отображаются в разных элементах интерфейса:
По умолчанию JavaScript использует стандартное строковое представление чисел, которое не учитывает региональные особенности. Например:
1234567.89
Для русской локали чаще используется формат:
1 234 567,89
Для английской:
1,234,567.89
Корректное форматирование особенно важно:
Основной инструмент локализации чисел в JavaScript — объект
Intl.NumberFormat.
Пример:
const formatter = new Intl.NumberFormat('ru-RU');
console.log(formatter.format(1234567.89));
Результат:
1 234 567,89
Для английской локали:
const formatter = new Intl.NumberFormat('en-US');
console.log(formatter.format(1234567.89));
Результат:
1,234,567.89
Наиболее частая задача — локализация подписей осей.
<canvas id="chart"></canvas>
const ctx = document.getElementById('chart');
const formatter = new Intl.NumberFormat('ru-RU');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Январь', 'Февраль', 'Март'],
datasets: [{
label: 'Продажи',
data: [12500, 568000, 1450000]
}]
},
options: {
scales: {
y: {
ticks: {
callback(value) {
return formatter.format(value);
}
}
}
}
}
});
Результат:
12 500
568 000
1 450 000
Начиная с современных версий Chart.js появилась поддержка свойства
locale.
options: {
locale: 'ru-RU'
}
Полный пример:
new Chart(ctx, {
type: 'line',
data: {
labels: ['Q1', 'Q2', 'Q3'],
datasets: [{
data: [1200, 3500, 8100]
}]
},
options: {
locale: 'ru-RU'
}
});
Chart.js начинает использовать локализованное форматирование там, где это поддерживается внутренними механизмами библиотеки.
Однако callback-функции всё равно остаются наиболее гибким способом настройки.
const rubFormatter = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB'
});
Пример:
ticks: {
callback(value) {
return rubFormatter.format(value);
}
}
Результат:
1 250,00 ₽
25 000,00 ₽
const usdFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
Результат:
$1,500.00
$25,000.00
const percentFormatter = new Intl.NumberFormat('ru-RU', {
style: 'percent',
minimumFractionDigits: 1
});
console.log(percentFormatter.format(0.256));
Результат:
25,6 %
const percentFormatter = new Intl.NumberFormat('ru-RU', {
style: 'percent',
minimumFractionDigits: 2
});
new Chart(ctx, {
type: 'pie',
data: {
labels: ['Desktop', 'Mobile'],
datasets: [{
data: [0.73, 0.27]
}]
},
options: {
plugins: {
tooltip: {
callbacks: {
label(context) {
return percentFormatter.format(context.raw);
}
}
}
}
}
});
Часто требуется сокращённое отображение:
1K
15M
2B
Для этого используется параметр notation.
const compactFormatter = new Intl.NumberFormat('en-US', {
notation: 'compact'
});
Пример:
console.log(compactFormatter.format(1200));
console.log(compactFormatter.format(2500000));
Результат:
1.2K
2.5M
const formatter = new Intl.NumberFormat('ru-RU', {
notation: 'compact'
});
Результат:
1,2 тыс.
2,5 млн
Tooltip — один из главных элементов визуализации данных.
const formatter = new Intl.NumberFormat('ru-RU');
new Chart(ctx, {
type: 'line',
data: {
labels: ['Пн', 'Вт', 'Ср'],
datasets: [{
label: 'Посещения',
data: [12500, 48200, 91800]
}]
},
options: {
plugins: {
tooltip: {
callbacks: {
label(context) {
const label = context.dataset.label;
const value = formatter.format(context.raw);
return `${label}: ${value}`;
}
}
}
}
}
});
const rubFormatter = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB'
});
const percentFormatter = new Intl.NumberFormat('ru-RU', {
style: 'percent'
});
tooltip: {
callbacks: {
label(context) {
if (context.dataset.label === 'Доход') {
return rubFormatter.format(context.raw);
}
if (context.dataset.label === 'Конверсия') {
return percentFormatter.format(context.raw);
}
return context.raw;
}
}
}
Плагин:
chartjs-plugin-datalabels
Пример:
plugins: {
datalabels: {
formatter(value) {
return new Intl.NumberFormat('ru-RU').format(value);
}
}
}
const formatter = new Intl.NumberFormat('ru-RU', {
minimumFractionDigits: 2
});
Результат:
1 250,00
const formatter = new Intl.NumberFormat('ru-RU', {
maximumFractionDigits: 1
});
Результат:
1250,5
const formatter = new Intl.NumberFormat('ru-RU', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
Фиксированный вывод:
1250,00
const formatter = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB'
});
console.log(formatter.format(-12500));
Результат:
-12 500,00 ₽
Для инженерных и научных графиков применяется scientific notation.
const formatter = new Intl.NumberFormat('en-US', {
notation: 'scientific'
});
Пример:
console.log(formatter.format(1230000));
Результат:
1.23E6
ticks: {
callback: value =>
new Intl.NumberFormat('ru-RU').format(value)
}
Нежелательно создавать Intl.NumberFormat внутри callback
при каждом вызове.
Плохой вариант:
callback(value) {
return new Intl.NumberFormat('ru-RU').format(value);
}
Правильный подход:
const formatter = new Intl.NumberFormat('ru-RU');
callback(value) {
return formatter.format(value);
}
Причины:
function createFormatter(locale) {
return new Intl.NumberFormat(locale);
}
let currentLocale = 'ru-RU';
let formatter = createFormatter(currentLocale);
function changeLocale(locale) {
formatter = createFormatter(locale);
chart.options.scales.y.ticks.callback = value =>
formatter.format(value);
chart.update();
}
const locale = navigator.language;
Пример:
const formatter = new Intl.NumberFormat(navigator.language);
const formatter = new Intl.NumberFormat([
'ru-RU',
'en-US'
]);
JavaScript последовательно проверяет доступные локали.
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Q1', 'Q2'],
datasets: [
{
label: 'Доход',
data: [250000, 320000]
},
{
label: 'Расход',
data: [180000, 210000]
}
]
},
options: {
scales: {
x: {
stacked: true
},
y: {
stacked: true,
ticks: {
callback(value) {
return new Intl.NumberFormat('ru-RU', {
notation: 'compact'
}).format(value);
}
}
}
}
}
});
В финансовых и аналитических графиках часто используются даты и локализованные значения.
tooltip: {
callbacks: {
title(items) {
return new Date(items[0].parsed.x)
.toLocaleDateString('ru-RU');
},
label(context) {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB'
}).format(context.raw.y);
}
}
}
function formatNumber(value, locale = 'ru-RU') {
return new Intl.NumberFormat(locale, {
maximumFractionDigits: 2
}).format(value);
}
Использование:
ticks: {
callback: value => formatNumber(value)
}
Иногда стандартного compact notation недостаточно.
function abbreviateNumber(value) {
if (value >= 1_000_000_000) {
return (value / 1_000_000_000).toFixed(1) + 'B';
}
if (value >= 1_000_000) {
return (value / 1_000_000).toFixed(1) + 'M';
}
if (value >= 1_000) {
return (value / 1_000).toFixed(1) + 'K';
}
return value.toString();
}
new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Chrome', 'Firefox', 'Safari'],
datasets: [{
data: [65.2, 18.5, 11.7]
}]
},
options: {
plugins: {
tooltip: {
callbacks: {
label(context) {
return new Intl.NumberFormat('ru-RU', {
minimumFractionDigits: 1,
maximumFractionDigits: 1
}).format(context.raw) + '%';
}
}
}
}
}
});
new Chart(ctx, {
type: 'radar',
data: {
labels: ['CPU', 'RAM', 'Disk'],
datasets: [{
data: [12500, 8300, 15400]
}]
},
options: {
scales: {
r: {
ticks: {
callback(value) {
return new Intl.NumberFormat('ru-RU')
.format(value);
}
}
}
}
}
});
Проблема:
callback(value) {
return new Intl.NumberFormat('ru-RU').format(value);
}
Недостатки:
Ошибка:
0.25%
Правильный подход:
new Intl.NumberFormat('ru-RU', {
style: 'percent'
}).format(0.25);
Результат:
25 %
Плохо:
return value + ' ₽';
Лучше:
return formatter.format(value);
Преимущества:
const currencyFormatter = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
maximumFractionDigits: 0
});
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: [
'Янв',
'Фев',
'Мар',
'Апр'
],
datasets: [{
label: 'Прибыль',
data: [
1250000,
1850000,
2640000,
3120000
]
}]
},
options: {
locale: 'ru-RU',
scales: {
y: {
ticks: {
callback(value) {
return currencyFormatter
.format(value);
}
}
}
},
plugins: {
tooltip: {
callbacks: {
label(context) {
return currencyFormatter
.format(context.raw);
}
}
}
}
}
});