Интернационализация денежных значений — одна из ключевых задач при разработке веб-приложений, работающих с несколькими языками и регионами. Формат отображения валют отличается в зависимости от локали:
Библиотека Globalize предоставляет механизм локализованного форматирования денежных значений на основе данных CLDR.
Для работы с валютами необходимы:
const Globalize = require("globalize");
require("cldr-data").entireSupplemental();
require("cldr-data").entireMainFor("en", "ru", "de", "fr");
Globalize.load(
require("cldr-data/supplemental/likelySubtags.json"),
require("cldr-data/supplemental/numberingSystems.json"),
require("cldr-data/supplemental/plurals.json"),
require("cldr-data/supplemental/currencyData.json"),
require("cldr-data/main/en/numbers.json"),
require("cldr-data/main/en/currencies.json"),
require("cldr-data/main/ru/numbers.json"),
require("cldr-data/main/ru/currencies.json"),
require("cldr-data/main/de/numbers.json"),
require("cldr-data/main/de/currencies.json"),
require("cldr-data/main/fr/numbers.json"),
require("cldr-data/main/fr/currencies.json")
);
const en = new Globalize("en");
const ru = new Globalize("ru");
const de = new Globalize("de");
Каждый экземпляр использует собственные правила форматирования.
Метод currencyFormatter() создаёт функцию
форматирования.
const formatter = en.currencyFormatter("USD");
console.log(formatter(1500.5));
Результат:
$1,500.50
Одинаковое значение отображается по-разному.
const value = 1500.5;
console.log(en.currencyFormatter("USD")(value));
console.log(ru.currencyFormatter("USD")(value));
console.log(de.currencyFormatter("USD")(value));
console.log(fr.currencyFormatter("USD")(value));
Результаты:
$1,500.50
1 500,50 $
1.500,50 $
1 500,50 $US
const formatter = ru.currencyFormatter("RUB");
console.log(formatter(78500.75));
Результат:
78 500,75 ₽
const euroFormatter = de.currencyFormatter("EUR");
console.log(euroFormatter(9999.99));
Результат:
9.999,99 €
Некоторые валюты не используют дробную часть.
const yenFormatter = en.currencyFormatter("JPY");
console.log(yenFormatter(1500.75));
Результат:
¥1,501
Globalize автоматически использует правила CLDR для конкретной валюты.
Метод currencyFormatter() принимает объект настроек.
const formatter = en.currencyFormatter("USD", {
minimumFractionDigits: 4
});
console.log(formatter(10));
Результат:
$10.0000
const formatter = en.currencyFormatter("USD", {
maximumFractionDigits: 1
});
console.log(formatter(15.789));
Результат:
$15.8
const formatter = en.currencyFormatter("USD", {
minimumIntegerDigits: 5
});
console.log(formatter(25));
Результат:
$00,025.00
По умолчанию используется стандартный символ.
const formatter = en.currencyFormatter("EUR");
console.log(formatter(100));
Результат:
€100.00
Опция style: "code" выводит ISO-код валюты.
const formatter = en.currencyFormatter("EUR", {
style: "code"
});
console.log(formatter(100));
Результат:
EUR 100.00
const formatter = en.currencyFormatter("EUR", {
style: "name"
});
console.log(formatter(100));
Результат:
100.00 euros
Для русской локали:
const formatter = ru.currencyFormatter("EUR", {
style: "name"
});
console.log(formatter(100));
Результат:
100,00 евро
const formatter = en.currencyFormatter("USD");
console.log(formatter(-3500));
Результат:
-$3,500.00
В некоторых локалях формат отличается.
const formatter = de.currencyFormatter("EUR");
console.log(formatter(-3500));
Результат:
-3.500,00 €
const formatter = ru.currencyFormatter("RUB");
console.log(formatter(0));
Результат:
0,00 ₽
Создание форматтера — относительно дорогая операция. В производительных приложениях рекомендуется переиспользование.
Неправильно:
function formatPrice(value) {
return new Globalize("en")
.currencyFormatter("USD")(value);
}
Правильно:
const formatter = new Globalize("en")
.currencyFormatter("USD");
function formatPrice(value) {
return formatter(value);
}
function createCurrencyFormatter(locale, currency) {
const globalize = new Globalize(locale);
return globalize.currencyFormatter(currency);
}
const usd = createCurrencyFormatter("en", "USD");
const rub = createCurrencyFormatter("ru", "RUB");
console.log(usd(1000));
console.log(rub(1000));
function formatMoney(locale, currency, value) {
const globalize = new Globalize(locale);
return globalize.currencyFormatter(currency)(value);
}
console.log(formatMoney("en", "USD", 500));
console.log(formatMoney("ru", "RUB", 500));
console.log(formatMoney("de", "EUR", 500));
const formatter = ru.currencyFormatter("USD", {
minimumFractionDigits: 3,
maximumFractionDigits: 3
});
console.log(formatter(15));
Результат:
15,000 $
const formatter = en.currencyFormatter("USD");
console.log(formatter(999999999.99));
Результат:
$999,999,999.99
JavaScript использует тип Number, который может
создавать проблемы точности.
console.log(0.1 + 0.2);
Результат:
0.30000000000000004
При форматировании:
const formatter = en.currencyFormatter("USD");
console.log(formatter(0.1 + 0.2));
Результат:
$0.30
Globalize корректно форматирует число, но ошибка вычисления остаётся. Для финансовых расчётов рекомендуется:
function formatCents(cents) {
const formatter = ru.currencyFormatter("RUB");
return formatter(cents / 100);
}
console.log(formatCents(105099));
Результат:
1 050,99 ₽
const globalize = new Globalize("en");
const usd = globalize.currencyFormatter("USD");
const eur = globalize.currencyFormatter("EUR");
const gbp = globalize.currencyFormatter("GBP");
console.log(usd(100));
console.log(eur(100));
console.log(gbp(100));
Результат:
$100.00
€100.00
£100.00
const formatter = ru.currencyFormatter("RUB");
const products = [
{ name: "Ноутбук", price: 120000 },
{ name: "Мышь", price: 2500 },
{ name: "Клавиатура", price: 7000 }
];
products.forEach(product => {
console.log(
`${product.name}: ${formatter(product.price)}`
);
});
import Globalize from "globalize";
const formatter = new Globalize("ru")
.currencyFormatter("RUB");
function Price({ value }) {
return (
<span>{formatter(value)}</span>
);
}
import Globalize from "globalize";
const formatter = new Globalize("ru")
.currencyFormatter("RUB");
export default {
props: ["price"],
computed: {
formattedPrice() {
return formatter(this.price);
}
}
};
import Globalize from "globalize";
const formatter = new Globalize("ru")
.currencyFormatter("RUB");
export class ProductComponent {
format(value) {
return formatter(value);
}
}
class CurrencyService {
constructor(locale) {
this.globalize = new Globalize(locale);
this.cache = {};
}
formatter(currency) {
if (!this.cache[currency]) {
this.cache[currency] =
this.globalize.currencyFormatter(currency);
}
return this.cache[currency];
}
format(currency, value) {
return this.formatter(currency)(value);
}
}
const service = new CurrencyService("ru");
console.log(service.format("RUB", 1000));
console.log(service.format("USD", 1000));
Если CLDR-данные не загружены, возникнет ошибка.
Error: E_MISSING_CLDR
Наиболее частые причины:
currencies.json;currencyData.json;function safeCurrencyFormatter(locale, currency) {
try {
return new Globalize(locale)
.currencyFormatter(currency);
} catch (e) {
console.error("Валюта не поддерживается");
return null;
}
}
Современный JavaScript содержит встроенный API:
new Intl.NumberFormat("ru", {
style: "currency",
currency: "RUB"
}).format(1000);
Результат:
1 000,00 ₽
Globalize отличается:
class ShopFormatter {
constructor(locale) {
this.globalize = new Globalize(locale);
}
price(value, currency) {
const formatter =
this.globalize.currencyFormatter(currency);
return formatter(value);
}
}
const shop = new ShopFormatter("ru");
const items = [
{ name: "Телефон", price: 59990, currency: "RUB" },
{ name: "Наушники", price: 199.99, currency: "USD" },
{ name: "Камера", price: 899.5, currency: "EUR" }
];
items.forEach(item => {
console.log(
`${item.name}: ${shop.price(
item.price,
item.currency
)}`
);
});
class MultiCurrency {
constructor(locale) {
this.globalize = new Globalize(locale);
this.formatters = {};
}
getFormatter(currency) {
if (!this.formatters[currency]) {
this.formatters[currency] =
this.globalize.currencyFormatter(currency);
}
return this.formatters[currency];
}
format(value, currency) {
return this.getFormatter(currency)(value);
}
}
const mc = new MultiCurrency("en");
console.log(mc.format(1200, "USD"));
console.log(mc.format(1200, "EUR"));
console.log(mc.format(1200, "JPY"));