import { TransformStream } from 'node:stream/web';
-// User locale's decimal separator
-const DECIMAL_SEPARATOR = Intl.NumberFormat().formatToParts(.1).find(p => p.type === 'decimal').value;
+/**
+ * Get the decimal separator character to parse numbers formatted in different locales.
+ * @param {string|Intl.Locale} [locale] A `string` Unicode locale identifier, or an `Intl.Locale` object specifying
+ * the locale that is applicable. If no value is provided, the current execution environment's locale is used.
+ * @returns {string} The locale's decimal separator as a `string` (e.g., "." for the "en-US" locale).
+ */
+export function getDecimalSeparator(locale) {
+ return Intl.NumberFormat(locale).formatToParts(.1).find(p => p.type === 'decimal').value;
+}
/**
* Convert a string to camelCase.
}
const e = Number(exponent);
let expanded = null;
- if (mantissa.indexOf(DECIMAL_SEPARATOR) === -1) {
+ if (mantissa.indexOf(getDecimalSeparator()) === -1) {
expanded = mantissa + '0'.repeat(e);
}
else {
// Excel added extra characters from setting the format to "Accounting" or other custom formats
// Delete everything that's not a number or the decimal separator
- const replaced = original.replaceAll(new RegExp(`[^0-9${DECIMAL_SEPARATOR}]`, 'g'), '');
+ const replaced = original.replaceAll(new RegExp(`[^0-9${getDecimalSeparator()}]`, 'g'), '');
const num2 = Number(replaced);
if (!Number.isNaN(num2) && replaced !== '') {
return num2;
// Excel added extra characters from setting the format to "Accounting" or other custom formats
// Delete everything that's not a number or the decimal separator
try {
- const str = original.replaceAll(new RegExp(`[^0-9${DECIMAL_SEPARATOR}]`, 'g'), '');
+ const decimalSeparator = getDecimalSeparator();
+ const str = original.replaceAll(new RegExp(`[^0-9${decimalSeparator}]`, 'g'), '');
if (str === '') { throw new Error(); }
- return BigInt(str.split(DECIMAL_SEPARATOR)[0]).toString();
+ return BigInt(str.split(decimalSeparator)[0]).toString();
} catch { }
// Otherwise, pass through as string
import { suite, test } from 'node:test';
-import { deepStrictEqual, throws } from 'node:assert/strict';
+import { ok, deepStrictEqual, throws } from 'node:assert/strict';
import { csvStreamEqualWritable, createCSVMockStream, assertConsole } from './utils.js';
import { CSVReader } from '../src/core.js';
-import { toCamelCase, expandScientificNotation, CSVNormalizer, CSVDenormalizer } from '../src/normalization.js';
+import { getDecimalSeparator, toCamelCase, expandScientificNotation, CSVNormalizer, CSVDenormalizer } from '../src/normalization.js';
+
+suite('getDecimalSeparator', { concurrency: true }, () => {
+ test('returns a value if no locale is specified', { concurrency: true }, () => {
+ const s = getDecimalSeparator();
+ ok(typeof s === 'string');
+ ok(s.length === 1);
+ });
+ test('returns "." for locale identifier "en-US"', { concurrency: true }, () => {
+ const s = getDecimalSeparator('en-US');
+ deepStrictEqual(s, '.');
+ });
+ test('returns "," for locale identifier "de-DE"', { concurrency: true }, () => {
+ const s = getDecimalSeparator('de-DE');
+ deepStrictEqual(s, ',');
+ });
+ test('returns "." for `Intl.Locale` object set to "en-US"', { concurrency: true }, () => {
+ const s = getDecimalSeparator(new Intl.Locale('en-US'));
+ deepStrictEqual(s, '.');
+ });
+ test('returns "," for `Intl.Locale` object set to "de-DE"', { concurrency: true }, () => {
+ const s = getDecimalSeparator(new Intl.Locale('de-DE'));
+ deepStrictEqual(s, ',');
+ });
+});
suite('toCamelCase', { concurrency: true }, () => {
const vectors = [