| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- import { getNumberFormatLocales } from './numberformat/locales.js';
- import { getNumberFormatModifier, getNumberFormatModifierSource } from './numberformat/modifier.js';
- import { getNumberFormatOptions } from './numberformat/options.js';
- import { parseNumberPattern } from './parse-pattern.js';
- import { parseNumberSkeleton } from './parse-skeleton.js';
- /**
- * Returns a number formatter function for the given locales and number skeleton
- *
- * @remarks
- * Uses `Intl.NumberFormat` (ES2020) internally.
- *
- * @public
- * @param locales - One or more valid BCP 47 language tags, e.g. `fr` or `en-CA`
- * @param skeleton - An ICU NumberFormatter pattern or `::`-prefixed skeleton
- * string, or a parsed `Skeleton` structure
- * @param currency - If `skeleton` is a pattern string that includes ¤ tokens,
- * their skeleton representation requires a three-letter currency code.
- * @param onError - If defined, will be called separately for each encountered
- * parsing error and unsupported feature.
- * @example
- * ```js
- * import { getNumberFormatter } from '@messageformat/number-skeleton'
- *
- * let src = ':: currency/CAD unit-width-narrow'
- * let fmt = getNumberFormatter('en-CA', src, console.error)
- * fmt(42) // '$42.00'
- *
- * src = '::percent scale/100'
- * fmt = getNumberFormatter('en', src, console.error)
- * fmt(0.3) // '30%'
- * ```
- */
- export function getNumberFormatter(locales, skeleton, currency, onError) {
- if (typeof skeleton === 'string') {
- skeleton =
- skeleton.indexOf('::') === 0
- ? parseNumberSkeleton(skeleton.slice(2), onError)
- : parseNumberPattern(skeleton, currency, onError);
- }
- const lc = getNumberFormatLocales(locales, skeleton);
- const opt = getNumberFormatOptions(skeleton, onError);
- const mod = getNumberFormatModifier(skeleton);
- const nf = new Intl.NumberFormat(lc, opt);
- if (skeleton.affix) {
- const [p0, p1] = skeleton.affix.pos;
- const [n0, n1] = skeleton.affix.neg || ['', ''];
- return (value) => {
- const n = nf.format(mod(value));
- return value < 0 ? `${n0}${n}${n1}` : `${p0}${n}${p1}`;
- };
- }
- return (value) => nf.format(mod(value));
- }
- /**
- * Returns a string of JavaScript source that evaluates to a number formatter
- * function with the same `(value: number) => string` signature as the function
- * returned by {@link getNumberFormatter}.
- *
- * @remarks
- * The returned function will memoize an `Intl.NumberFormat` instance.
- *
- * @public
- * @param locales - One or more valid BCP 47 language tags, e.g. `fr` or `en-CA`
- * @param skeleton - An ICU NumberFormatter pattern or `::`-prefixed skeleton
- * string, or a parsed `Skeleton` structure
- * @param currency - If `skeleton` is a pattern string that includes ¤ tokens,
- * their skeleton representation requires a three-letter currency code.
- * @param onError - If defined, will be called separately for each encountered
- * parsing error and unsupported feature.
- * @example
- * ```js
- * import { getNumberFormatterSource } from '@messageformat/number-skeleton'
- *
- * getNumberFormatterSource('en', '::percent', console.error)
- * // '(function() {\n' +
- * // ' var opt = {"style":"percent"};\n' +
- * // ' var nf = new Intl.NumberFormat(["en"], opt);\n' +
- * // ' var mod = function(n) { return n * 0.01; };\n' +
- * // ' return function(value) { return nf.format(mod(value)); }\n' +
- * // '})()'
- *
- * const src = getNumberFormatterSource('en-CA', ':: currency/CAD unit-width-narrow', console.error)
- * // '(function() {\n' +
- * // ' var opt = {"style":"currency","currency":"CAD","currencyDisplay":"narrowSymbol","unitDisplay":"narrow"};\n' +
- * // ' var nf = new Intl.NumberFormat(["en-CA"], opt);\n'
- * // ' return function(value) { return nf.format(value); }\n' +
- * // '})()'
- * const fmt = new Function(`return ${src}`)()
- * fmt(42) // '$42.00'
- * ```
- */
- export function getNumberFormatterSource(locales, skeleton, currency, onError) {
- if (typeof skeleton === 'string') {
- skeleton =
- skeleton.indexOf('::') === 0
- ? parseNumberSkeleton(skeleton.slice(2), onError)
- : parseNumberPattern(skeleton, currency, onError);
- }
- const lc = getNumberFormatLocales(locales, skeleton);
- const opt = getNumberFormatOptions(skeleton, onError);
- const modSrc = getNumberFormatModifierSource(skeleton);
- const lines = [
- `(function() {`,
- `var opt = ${JSON.stringify(opt)};`,
- `var nf = new Intl.NumberFormat(${JSON.stringify(lc)}, opt);`
- ];
- let res = 'nf.format(value)';
- if (modSrc) {
- lines.push(`var mod = ${modSrc};`);
- res = 'nf.format(mod(value))';
- }
- if (skeleton.affix) {
- const [p0, p1] = skeleton.affix.pos.map(s => JSON.stringify(s));
- if (skeleton.affix.neg) {
- const [n0, n1] = skeleton.affix.neg.map(s => JSON.stringify(s));
- res = `value < 0 ? ${n0} + ${res} + ${n1} : ${p0} + ${res} + ${p1}`;
- }
- else {
- res = `${p0} + ${res} + ${p1}`;
- }
- }
- lines.push(`return function(value) { return ${res}; }`);
- return lines.join('\n ') + '\n})()';
- }
|