ldml-number
Version:
Formats numbers using the Unicode formatting standard, LDML.
335 lines (274 loc) • 14.9 kB
JavaScript
const tape = require( 'tape' );
const numfmt = require( '../src/ldmlnum' );
function format ( num, str, locale ) {
if ( arguments.length === 2 ) {
locale = str;
str = '';
}
return numfmt( str, locale )( num );
}
tape( 'basic funtionality', t => {
const f = numfmt();
t.equal( typeof numfmt, 'function', 'is a function' );
t.equal( typeof f, 'function', 'returns a function' );
t.equal( typeof numfmt.round, 'function', 'has a round function' );
t.equal( numfmt.round( 1.5 ), 2, 'round function works' );
t.equal( typeof numfmt.locale, 'function', 'has a locale function' );
const dummyLocale = {
thousands_separator: 'a',
decimal_separator: 'b',
positive_sign: 'c',
negative_sign: 'd',
exponent_symbol: 'e',
infinity_symbol: 'f',
nan_symbol: 'g'
};
t.deepEqual( numfmt.locale( 'a', 'b', 'c', 'd', 'e', 'f', 'g' ), dummyLocale, 'locale function works' );
t.equal( typeof numfmt.locale.en, 'object', 'english defaults are there' );
t.equal( f( 1 ), '1', 'calling a formatter works' );
t.end();
});
tape( 'invalid patterns', t => {
function compile ( patt ) {
return function () { numfmt( patt ); };
}
t.throws( compile( '@@##.#' ), '@@##.#' );
t.throws( compile( '@@##0' ), '@@##0' );
t.equal( typeof compile( '@@##5' ), 'function', '@@##5' );
t.throws( compile( '@@#50' ), '@@#50' );
t.throws( compile( '@00' ), '@00' );
t.throws( compile( '@.###' ), '@.###' );
t.throws( compile( '#,##0E0' ), '@.###' );
t.throws( compile( '0#' ), '0#' );
t.throws( compile( '.#0' ), '.#0' );
t.throws( compile( '.@' ), '.@' );
t.end();
});
// "escaped ;"
// ';#,##0.##;';(#)
tape( 'l10n', t => {
numfmt.locale.xx = numfmt.locale( ' ', '.' );
numfmt.locale.xx_XX = numfmt.locale( '~', '/' );
t.equal( format( 1234.567, '#,##0.0#;-#,##0.0#', 'xx' ), '1 234.57', 'xx locale' );
t.equal( format( 1234.567, '#,##0.0#;-#,##0.0#', 'xx_XX' ), '1~234/57', 'xx locale' );
t.equal( format( 1234.567, '#,##0.0#;-#,##0.0#', 'xx_YY' ), '1 234.57', 'xx locale' );
t.end();
});
tape( 'more', t => {
// t.equal( format(1234.567, '#,##0.##', 'en_US'), '1 234,57' );
// t.equal( format(1234.567, '#,##0.###', 'en_US'), '1 234,567' );
// t.equal( format(1234.567, '###0.#####', 'en_US'), '1234,567' );
// t.equal( format(1234.567, '###0.0000#', 'en_US'), '1234,5670' );
// t.equal( format(1234.567, '00000.0000', 'en_US'), '01234,5670' );
// t.equal( format(1234.567, '#,##0.00 ¤', 'sv'), '1 234,57 €' ); // EUR
// t.equal( format(1234.567, '#,##0.00 ¤', 'sv'), '1 234,57 ¥' ); // JPY
// should yield the same result
t.equal( format( 1234.567, '#,##0.0#;(#)', 'en_US' ), '1,234.57', '#,##0.0#;(#)' );
t.equal( format( -1234.567, '#,##0.0#;(#)', 'en_US' ), '(1,234.57)', '#,##0.0#;(#)' );
t.equal( format( 1234.567, '#,##0.0#;(#,##0.0#)', 'en_US' ), '1,234.57', '#,##0.0#;(#,##0.0#)' );
t.equal( format( -1234.567, '#,##0.0#;(#,##0.0#)', 'en_US' ), '(1,234.57)', '#,##0.0#;(#,##0.0#)' );
// padding
// equal( format(1997, '00', 'en_US'), '97' ,'00' ); // how does "maximum integer digits" work ??
t.equal( format( 1997, '00000', 'en_US' ), '01997', '00000' );
// Make sure that 10 chars min get grouped: 12 + "00,000" = 00,012
// 0.###E0" formats the number 1234 as "1.234E3"
t.end();
});
tape( 'parsed attributes', t => {
function examine ( patt, res, title ) {
const f = numfmt( patt, 'en_US' );
t.deepEqual( [ f.int_max, f.int_min, f.frac_max, f.frac_min ], res, patt || title );
}
examine( '', [ Infinity, 1, 3, 0 ], 'default pattern' );
examine( '0.00E0', [ 1, 1, 2, 2 ] );
examine( '#,##0.0#', [ Infinity, 1, 2, 1 ] );
examine( '#.', [ Infinity, 1, 0, 0 ] );
examine( '#.#', [ Infinity, 1, 1, 0 ] );
examine( '#0000000000000,00000.###', [ Infinity, 18, 3, 0 ] );
examine( '0E0', [ 1, 1, 0, 0 ] );
examine( '0.###E0', [ 1, 1, 3, 0 ] );
examine( '.00', [ Infinity, 0, 2, 2 ] );
// "#,#@#" indicates a minimum of one significant digits, a maximum of two significant digits, and a grouping size of three.
t.end();
});
tape( 'ported', t => {
t.equal( format( -1234.56, '0.##;-0.##', 'en_US' ), '-1234.56', '0.##;-0.##' );
t.equal( format( 1234.56, '0.##;-0.##', 'en_US' ), '1234.56', '0.##;-0.##' );
t.equal( format( -1234.56, '0.#', 'en_US' ), '-1234.6', '0.#' );
t.equal( format( 1234.56, '0.#', 'en_US' ), '1234.6', '0.#' );
t.equal( format( -1234.56, '#,##0.##;-#', 'en_US' ), '-1,234.56', '#,##0.##;-#' );
t.equal( format( 1234.56, '#,##0.##;-#', 'en_US' ), '1,234.56', '#,##0.##;-#' );
t.equal( format( 80, '#,##0.###', 'en_US' ), '80', '#,##0.###' );
t.equal( format( -1234.56, '00,000.000;-00,000.000', 'en_US' ), '-01,234.560', '00,000.000;-00,000.000' );
t.equal( format( 1234.56, '00,000.000;-00,000.000', 'en_US' ), '01,234.560', '00,000.000;-00,000.000' );
// var f = numfmt( "##,###,####.", 'en_US' );
// f.force_decimalpoint = true; // setDecimalSeparatorAlwaysShown
// t.equal(f(-1234.56), "-1235.");
// t.equal(f(1234.56), "1235.");
// t.equal(f(-1234567.890), "-123,4568.");
t.equal( format( -1234567.890, '#,###,###', 'en_US' ), '-1,234,568', '#,###,###' );
t.equal( format( -1234.56, '0', 'en_US' ), '-1235', '0' );
t.equal( format( 1234.56, '0', 'en_US' ), '1235', '0' );
t.equal( format( 0, '#', 'en_US' ), '0', '#' );
t.equal( format( -1234.56, '###0.#;(###0.#)', 'en_US' ), '(1234.6)', '###0.#;(###0.#)' );
t.equal( format( 1234.56, '###0.#;(###0.#)', 'en_US' ), '1234.6', '###0.#;(###0.#)' );
t.equal( format( -1234.56, '###0.#;###0.#-', 'en_US' ), '1234.6-', '###0.#;###0.#-' );
t.equal( format( 1234.56, '###0.#;###0.#-', 'en_US' ), '1234.6', '###0.#;###0.#-' );
t.equal( format( -1234.56, '#,##0%;-#,##0%', 'en_US' ), '-123,456%', '#,##0%;-#,##0%' );
t.equal( format( 1234.56, '#,##0%;-#,##0%', 'en_US' ), '123,456%', '#,##0%;-#,##0%' );
t.equal( format( 0.2, '#.#', 'en_US' ), '0.2', '#.#' );
t.equal( format( -1234.567, '000000', 'en_US' ), '-001235', '000000' );
t.equal( format( -1234.567, '##', 'en_US' ), '-1235', '##' );
t.equal( format( 0, '##', 'en_US' ), '0', '##' );
t.equal( format( 0, '##00', 'en_US' ), '00', '##00' );
t.equal( format( -0.567, '.00', 'en_US' ), '-.57', '.00' );
t.equal( format( -0.567, '#.00', 'en_US' ), '-0.57', '#.00' );
t.equal( format( -0.567, '0.00', 'en_US' ), '-0.57', '0.00' );
t.equal( format( -1234.567, '.######', 'en_US' ), '-1234.567', '.######' );
t.equal( format( -1234.567, '#.000000', 'en_US' ), '-1234.567000', '#.000000' );
// t.equal( format(10000000.1234d, "#,##0%", 'en_US'), "1,000,000,012%" ,"#,##0%" );
// t.equal( format(10000, "\u00A4#,##0.00;(\u00A4#,##0.00)", 'en_US'), "$10,000.00" ,"\u00A4#,##0.00;(\u00A4#,##0.00)" );
t.equal( format( 10000, '$#,##0.00;($#,##0.00)', 'en_US' ), '$10,000.00', '$#,##0.00;($#,##0.00)' );
t.end();
});
tape( 'quoting', t => {
t.equal( format( 123, "'#'#", 'en_US' ), '#123', "'#'#" );
t.equal( format( 3, "# o''clock", 'en_US' ), "3 o'clock", "# o''clock" );
t.equal( format( 30, "'#'#.#", 'en_US' ), '#30', "'#'#.#" );
t.equal( format( -1234.567, "'#'#", 'en_US' ), '-#1235', "'#'#" );
t.equal( format( -1234.567, "'abc'#", 'en_US' ), '-abc1235', "'abc'#" );
t.equal( format( -1234.567, "'positive'#;'negative' -#", 'en_US' ), 'negative -1235', "'positive'#;'negative' -#" );
t.equal( format( 1234.567, "'positive'#;'negative' -#", 'en_US' ), 'positive1235', "'positive'#;'negative' -#" );
t.equal( format( -1234.567, "# 'is positive';-# 'is negative'", 'en_US' ), '-1235 is negative', "# 'is positive';-# 'is negative'" );
t.equal( format( 1234.567, "# 'is positive';-# 'is negative'", 'en_US' ), '1235 is positive', "# 'is positive';-# 'is negative'" );
t.end();
});
tape( 'multiple grouping separators', t => {
// The pattern "#,##,##0", and the number 123456789 is formatted as "12,34,56,789"
t.equal( format( 123456789, '#,##,##0', 'en_US' ), '12,34,56,789', '#,##,##0' );
// for multiple grouping separators, use only the interval between the last two
t.equal( format( 123456789, '#,##,###,###0', 'en_US' ), '12,345,6789', '#,##,###,###0' );
t.equal( format( 123456789, '###,###,###0', 'en_US' ), '12,345,6789', '###,###,###0' );
t.equal( format( 123456789, '##,#,###,###0', 'en_US' ), '12,345,6789', '##,#,###,###0' );
// leading hash
t.equal( format( 98621786.6711, '#,###.###', 'en' ), '98,621,786.671', '#,###.###' );
t.end();
});
tape( 'non-finite numbers', t => {
t.equal( format( NaN, '#,##0.##', 'en_US' ), '☹', '#,##0.##' );
t.equal( format( Infinity, '#,##0.##', 'en_US' ), '∞', '#,##0.##' );
t.equal( format( -Infinity, '#,##0.##', 'en_US' ), '-∞', '#,##0.##' );
t.equal( format( -Infinity, '#,##0.##;(#)', 'en_US' ), '(∞)', '#,##0.##;(#)' );
t.end();
});
tape( 'patterns', t => {
t.equal( format( 12345, '##0', 'en_US' ), '12345', '##0' );
t.equal( format( 6.5, '0.00', 'sv' ), '6,50', '0.00' );
t.equal( format( Math.pow( 10, 20 ), '#.00', 'en_US' ), '100000000000000000000.00', '#.00' );
t.end();
});
tape( 'subpatterns', t => {
t.equal( format( -12345, '#,##0.##;-#', 'en_US' ), '-12,345', '#,##0.##;-#' );
t.equal( format( -12345, '#,##0.##;(#)', 'en_US' ), '(12,345)', '#,##0.##;(#)' );
t.end();
});
tape( 'default rounding', t => {
t.equal( format( 5.5, '0', 'sv' ), '6', '0' );
t.equal( format( -5.5, '0', 'sv' ), '-6', '0' );
t.equal( format( 6.5, '0', 'sv' ), '7', '0' );
t.equal( format( -6.5, '0', 'sv' ), '-7', '0' );
t.equal( format( 1.2325, 'sv' ), '1,232', '' );
t.equal( format( -1.2325, 'sv' ), '-1,232', '' );
t.equal( format( 1.2335, 'sv' ), '1,234', '' );
t.equal( format( -1.2335, 'sv' ), '-1,234', '' );
t.end();
});
tape( 'significant digits', t => {
t.equal( format( 123004, '@@', 'en_US' ), '120000', '@@' );
t.equal( format( -123004, '@@', 'en_US' ), '-120000', '@@' );
t.equal( format( 1.12, '@', 'sv' ), '1', '@' );
t.equal( format( 1.1, '@@', 'sv' ), '1,1', '@@' );
t.equal( format( 1.1, '@@@@@##', 'sv' ), '1,1000', '@@@@@##' );
t.equal( format( 0.0001, '@@@', 'sv' ), '0,000100', '@@@' );
t.equal( format( 0.0001234, '@@@', 'sv' ), '0,000123', '@@@' );
t.equal( format( 0.0001234, '@@@#', 'sv' ), '0,0001234', '@@@#' );
t.equal( format( 0.0001234, '@@@#', 'sv' ), '0,0001234', '@@@#' );
t.equal( format( 0.001234, '@@@#', 'sv' ), '0,001234', '@@@#' );
t.equal( format( 0.01234, '@@@#', 'sv' ), '0,01234', '@@@#' );
t.equal( format( 12345, '@@@', 'sv' ), '12300', '@@@' );
t.equal( format( 0.12345, '@@@', 'sv' ), '0,123', '@@@' );
t.equal( format( 3.14159, '@@##', 'sv' ), '3,142', '@@##' );
t.equal( format( 1.23004, '@@##', 'sv' ), '1,23', '@@##' );
t.equal( format( 1230.04, '@@,@@', 'en_US' ), '12,30', '@@,@@' );
t.equal( format( 123.41, '@@##', 'en_US' ), '123.4', '@@##' );
t.equal( format( 1, '@@', 'en_US' ), '1.0', '@@' );
t.equal( format( 0, '@', 'en_US' ), '0', '@' );
t.equal( format( 0.1, '@', 'en_US' ), '0.1', '@' );
t.equal( format( 0.1, '@#', 'en_US' ), '0.1', '@#' );
t.equal( format( 0.1, '@@', 'en_US' ), '0.10', '@@' );
t.end();
});
tape( 'scientific notation', t => {
// from Babel
t.equal( format( 0.1, '#E0', 'en_US' ), '1E-1', '#E0' );
t.equal( format( 0.01, '#E0', 'en_US' ), '1E-2', '#E0' );
t.equal( format( 10, '#E0', 'en_US' ), '1E1', '#E0' );
t.equal( format( 1234, '0.###E0', 'en_US' ), '1.234E3', '0.###E0' );
t.equal( format( 1234, '0.#E0', 'en_US' ), '1.2E3', '0.#E0' );
// Exponent grouping
t.equal( format( 12345, '##0.####E0', 'en_US' ), '12.345E3', '##0.####E0' );
// Minimum number of int digits
t.equal( format( 12345, '00.###E0', 'en_US' ), '12.345E3', '00.###E0' );
t.equal( format( -12345.6, '00.###E0', 'en_US' ), '-12.346E3', '00.###E0' );
t.equal( format( -0.01234, '00.###E0', 'en_US' ), '-12.34E-3', '00.###E0' );
// Custom pattern suffic
t.equal( format( 123.45, '#.##E0 m/s', 'en_US' ), '1.23E2 m/s', '#.##E0 m/s' );
// Exponent patterns
t.equal( format( 123.45, '#.##E00 m/s', 'en_US' ), '1.23E02 m/s', '#.##E00 m/s' );
t.equal( format( 0.012345, '#.##E00 m/s', 'en_US' ), '1.23E-02 m/s', '#.##E00 m/s' );
t.equal( format( 12345, '#.##E+00 m/s', 'en_US' ), '1.23E+04 m/s', '#.##E+00 m/s' );
// 0 (see ticket #99)
t.equal( format( 0, '#E0', 'en_US' ), '0E0', '#E0' );
// from Mauve
t.equal( format( 200000, '0.0000E0', 'en_US' ), '2.0000E5', '0.0000E0' );
t.equal( format( 200000, '00.00E00', 'en_US' ), '20.00E04', '00.00E00' );
// t.equal( format(12345, "##0.####E0", 'en_US'), "12.345E3" ,"##0.####E0" );
t.equal( format( 12345, '##.###E0', 'en_US' ), '1.2345E4', '##.###E0' );
t.equal( format( 12346, '##.###E0', 'en_US' ), '1.2346E4', '##.###E0' );
t.equal( format( 12345, '00.###E0', 'en_US' ), '12.345E3', '00.###E0' );
t.equal( format( 1234, '00.###E0', 'en_US' ), '12.34E2', '00.###E0' );
t.equal( format( 0.00123, '00.###E0', 'en_US' ), '12.3E-4', '00.###E0' );
t.equal( format( -1234.567, '0E0', 'en_US' ), '-1E3', '0E0' );
t.equal( format( -1234.567, '00E00', 'en_US' ), '-12E02', '00E00' );
t.equal( format( -1234.567, '000E00', 'en_US' ), '-123E01', '000E00' );
t.equal( format( -1234.567, '0000000000E0', 'en_US' ), '-1234567000E-6', '0000000000E0' );
t.equal( format( -1234.567, '0.0E0', 'en_US' ), '-1.2E3', '0.0E0' );
t.equal( format( -1234.567, '00.00E0', 'en_US' ), '-12.35E2', '00.00E0' );
t.equal( format( -0.1234567, '00.00E0', 'en_US' ), '-12.35E-2', '00.00E0' );
// this is missing more tests for significance in exponential notation: "@@E0"
t.equal( format( 12345, '0.0###E0', 'en_US' ), '1.2345E4', '0.0###E0' );
t.equal( format( 12345, '@@###E0', 'en_US' ), '1.2345E4', '@@###E0' );
t.end();
});
// FIXME: missing tests for % parameter
// "?" is equivalent to "#" except a space is left instead of blank
// when no number is written. So 123.45 using "##0.???" would output
// the string "123.45 ". This is as used by Microsoft Excel.
// 1000 formatted with "###0 ^^" would output "1 k".
/*
tape( 'SI prefix', t => {
t.equal( format(999, "###0.## ^^", 'en_US'), "999", "999" );
t.equal( format(1000, "###0.## ^^", 'en_US'), "1000", "1000" );
t.equal( format(1001, "###0.## ^^", 'en_US'), "1 k", "1 k" );
t.equal( format(1234, "###0.## ^^", 'en_US'), "1.23 k", "1.23 k" );
t.equal( format(1500, "###0.## ^^", 'en_US'), "1.5 k", "1.5 k" );
t.equal( format(1234567, "###0.## ^^", 'en_US'), "1.23 M", "1.23 M" );
t.equal( format(1234567000, "###0.## ^^", 'en_US'), "1.23 G", "1.23 G" );
t.equal( format(1234567000000, "###0.## ^^", 'en_US'), "1.23 T", "1.23 T" );
t.equal( format(10000000000000000000, "###0.## ^^", 'en_US'), "10 E", "10 E" );
t.equal( format(0.000000001, "###0.## ^^", 'en_US'), "1 n", "1 n" );
t.equal( format(0.000001, "###0.## ^^", 'en_US'), "1 μ", "1 μ" );
t.equal( format(0.001, "###0.## ^^", 'en_US'), "1 m", "1 m" );
t.end();
});
*/