UNPKG

monetochka

Version:

Simple, fast and zero-dependency currency formatting library

287 lines (279 loc) 8.45 kB
'use strict'; const symbols = { BTC: '₿', LTC: null, ETH: null, USDT: 'USD₮', JPYT: 'JPY₮', EURT: 'EUR₮', BCH: null, DASH: 'Dash', AED: 'د.إ.‏', ARS: '$', AUD: 'A$', BRL: 'R$', CAD: 'CA$', CHF: 'CHF', CLP: '$', CNY: '¥', COP: '$', CZK: 'Kč', DKK: 'kr', DOP: 'RD$', EGP: '£', EUR: '€', GBP: '£', HKD: 'HK$', HRK: 'kn', HUF: 'Ft', IDR: 'Rp', INR: '₹', IRR: '﷼', JPY: '¥', KES: 'KSh', KRW: '₩', KZT: '₸', MAD: '.د.م', MXN: 'MX$', MYR: 'RM', NGN: '₦', NOK: 'kr', NZD: 'NZ$', PEN: 'S/.', PHP: '₱', PKR: '₨', PLN: 'zł', RON: 'lei', RUB: '₽', SAR: '.ر.س', SEK: 'kr', SGD: 'S$', THB: '฿', TRY: '₺', TZS: 'TSh', UAH: '₴', USD: '$', VEF: 'Bs', VND: '₫', ZAR: 'R', }; const defaultLocales = { BTC: 'crypto', LTC: 'crypto', ETH: 'crypto', USDT: 'crypto', JPYT: 'crypto', EURT: 'crypto', BCH: 'crypto', DASH: 'crypto', AED: 'ar-AE', ARS: 'es-AR', AUD: 'en', BRL: 'pt-BR', CAD: 'en-CA', CHF: 'de-CH', CLP: 'es-CL', CNY: 'zh-Hans-CN', COP: 'es-CO', CZK: 'cs', DKK: 'da-DK', DOP: 'es-DO', EGP: 'ar-EG', EUR: 'en', GBP: 'en', HKD: 'zh-Hans-HK', HRK: 'hr-HR', HUF: 'hu-HU', IDR: 'id', INR: 'en-IN', IRR: 'fa-IR', JPY: 'ja', KES: 'en-KE', KRW: 'ko-KR', KZT: 'kk', MAD: 'ar', MXN: 'es-MX', MYR: 'ms-MY', NGN: 'en-NG', NOK: 'nb-NO', NZD: 'en-NZ', PEN: 'es-PE', PHP: 'fil', PKR: 'ur-PK', PLN: 'pl', RON: 'ro', RUB: 'ru', SAR: 'ar-SA', SEK: 'sv-SE', SGD: 'en-SG', THB: 'th', TRY: 'tr-TR', TZS: 'sw-TZ', UAH: 'uk', USD: 'en-US', VEF: 'es-VE', VND: 'vi', ZAR: 'zu', }; const locales = { 'crypto': { g: ',', d: '.', fMax: 8, t: '@ !' }, 'ar': { g: ',', d: '.', f: 2, t: '! @' }, 'ar-AE': { h: 'ar' }, 'ar-EG': { h: 'ar' }, 'ar-SA': { h: 'ar' }, 'cs': { g: ' ', d: ',', f: 2, t: '@ !' }, 'da': { g: '.', d: ',', f: 2, t: '@ !' }, 'da-DK': { h: 'da' }, 'de-CH': { g: '’', d: '.', f: 2, t: '! @' }, 'en': { g: ',', d: '.', f: 2, t: '!@' }, 'en-CA': { h: 'en' }, 'en-IN': { g: ',', d: '.', f: 2, t: '! @' }, 'en-KE': { h: 'en' }, 'en-NG': { h: 'en' }, 'en-NZ': { h: 'en' }, 'en-SG': { h: 'en' }, 'en-US': { h: 'en' }, 'es': { g: '.', d: ',', f: 2, t: '@ !' }, 'es-AR': { g: '.', d: ',', f: 2, t: '! @' }, 'es-CL': { g: '.', d: ',', f: 0, t: '!@' }, 'es-CO': { g: '.', d: ',', f: 2, t: '! @' }, 'es-DO': { h: 'es' }, 'es-MX': { h: 'es' }, 'es-PE': { h: 'es' }, 'es-VE': { g: '.', d: ',', f: 2, t: '!@' }, 'fa': { g: ',', d: '.', f: 2, t: '!@' }, 'fa-IR': { h: 'fa' }, 'fil': { g: ',', d: '.', f: 2, t: '!@' }, 'hr': { g: '.', d: ',', f: 2, t: '@ !' }, 'hr-HR': { h: 'hr' }, 'hu': { g: ' ', d: ',', f: 2, t: '@ !' }, 'hu-HU': { h: 'hu' }, 'id': { g: '.', d: ',', f: 2, t: '!@' }, 'ja': { g: ',', d: '.', f: 0, t: '!@' }, 'kk': { g: ' ', d: ',', f: 2, t: '@ !' }, 'ko': { g: ',', d: '.', f: 0, t: '!@' }, 'ko-KR': { h: 'ko' }, 'ms': { g: ',', d: '.', f: 2, t: '!@' }, 'ms-MY': { h: 'ms' }, 'nb': { g: ' ', d: ',', f: 2, t: '! @' }, 'nb-NO': { h: 'nb' }, 'pl': { g: ' ', d: ',', f: 2, t: '@ !' }, 'pt': { g: '.', d: ',', f: 2, t: '!@' }, 'pt-BR': { h: 'pt' }, 'ro': { g: '.', d: ',', f: 2, t: '@ !' }, 'ru': { g: ' ', d: ',', f: 2, t: '@ !' }, 'sv': { g: ' ', d: ',', f: 2, t: '@ !' }, 'sv-SE': { h: 'sv' }, 'sw': { g: ',', d: '.', f: 2, t: '!@' }, 'sw-TZ': { h: 'sw' }, 'th': { g: ',', d: '.', f: 2, t: '!@' }, 'tr': { g: '.', d: ',', f: 2, t: '@ !' }, 'tr-TR': { h: 'tr' }, 'uk': { g: ' ', d: ',', f: 2, t: '@ !' }, 'ur': { g: ',', d: '.', f: 2, t: '! @' }, 'ur-PK': { h: 'ur' }, 'vi': { g: '.', d: ',', f: 0, t: '@ !' }, 'zh': { g: ',', d: '.', f: 2, t: '!@' }, 'zh-Hans-CN': { h: 'zh' }, 'zh-Hans-HK': { h: 'zh' }, 'zu': { g: ',', d: '.', f: 2, t: '!@' }, }; const siRound = (value, options) => { if (value < 1000 || !options.siRounding) return [value, '']; const units = options.siUnits; let decimal; for (let i = units.length - 1; i >= 0; i--) { decimal = Math.pow(1000, i + 1); if (value <= -decimal || value >= decimal) { return [value / decimal, units[i]]; } } }; const numberFormat = (siAmount, options, localeSettings) => { const [amount, siPostfix] = siAmount; let minimumFractionDigits = typeof localeSettings.f !== 'undefined' ? localeSettings.f : 0; if (typeof options.fractionDigits !== 'undefined' || typeof options.maximumFractionDigits !== 'undefined') { minimumFractionDigits = typeof options.fractionDigits === 'undefined' ? options.maximumFractionDigits : options.fractionDigits; } let maximumFractionDigits = typeof localeSettings.fMax !== 'undefined' ? localeSettings.fMax : typeof localeSettings.f !== 'undefined' ? localeSettings.f : options.fractionDigits; if (options.siRounding && amount < 1000) { minimumFractionDigits = options.siPrecision; maximumFractionDigits = options.siPrecision; } const localeString = amount.toLocaleString(`${options.locale}-u-nu-latn`, { minimumFractionDigits, maximumFractionDigits, }); return `${localeString}${siPostfix}`; }; const defaultSiUnits = ['K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; const defaultOptions = { isCrypto: false, siPrecision: 0, siRounding: false, siUnits: defaultSiUnits, throwOnError: false, errorValue: '—', }; const throwErr = (message, options, e) => { if (options.throwOnError) { throw (e || new Error(message)); } return options.errorValue; }; const format = (amount, currency, options = {}) => { const mergedOptions = Object.assign({}, defaultOptions, options); if (Number.isNaN(amount) || !Number.isFinite(amount)) { return throwErr('The amount is not countable (NaN or Infinity)', mergedOptions); } if (typeof amount !== 'number') { return throwErr(`Wrong amount type (${typeof amount})`, mergedOptions); } if (typeof currency !== 'string') { return throwErr(`Wrong currency type (${typeof amount})`, mergedOptions); } const currencyLocale = defaultLocales[currency] ? defaultLocales[currency] : 'en'; if (!mergedOptions.locale) mergedOptions.locale = currencyLocale; const currencySymbol = symbols[currency] && currencyLocale !== 'crypto' ? symbols[currency] : currency; const formatRules = locales[currencyLocale].h ? locales[locales[currencyLocale].h] : locales[currencyLocale]; let amountWithSI; let formattedAmount; try { amountWithSI = siRound(amount, mergedOptions); formattedAmount = numberFormat(amountWithSI, mergedOptions, formatRules); } catch (e) { return throwErr(`Can't parse a value`, mergedOptions, e); } return formatRules.t .replace('!', currencySymbol) .replace('@', formattedAmount); }; const parse = (string, currency) => { const currencyLocale = defaultLocales[currency] ? defaultLocales[currency] : 'en'; const formatRules = locales[currencyLocale].h ? locales[locales[currencyLocale].h] : locales[currencyLocale]; let cleanedString = string; if (symbols[currency]) cleanedString = cleanedString.replace(symbols[currency], ''); cleanedString = cleanedString.replace(currency, ''); cleanedString = cleanedString.trim(); cleanedString = cleanedString.split(formatRules.g).join(''); cleanedString = cleanedString.replace(formatRules.d, '.'); return parseFloat(cleanedString); }; var index = { format, parse }; module.exports = index;