From fcb565fc8ef5e32ff9cb03cbeb79a23899533ddb Mon Sep 17 00:00:00 2001 From: Yarchik Date: Mon, 22 Jun 2026 19:46:39 +0100 Subject: [PATCH] fix: stop dropping digits when pad is combined with a locale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When `pad` is used together with `locale`, the padding step inferred the decimal separator by taking the last `.`/`,` in the already-localized string. For a value the locale renders with grouping separators and no decimal part, that last separator is a grouping separator, so splitting on it dropped digit groups and skipped padding: filesize(1234500e24, {locale: 'en', pad: true, round: 2}) // '1,234 YB' (lost '500', no padding) — expected '1,234,500.00 YB' Let the locale formatter emit the fixed number of fraction digits via `minimumFractionDigits`/`maximumFractionDigits`, so the localized string is never re-parsed. The manual string padding now runs only for the non-locale paths (plain / custom separator), where the string has a single decimal separator. Full suite passes (171); added a regression test. --- src/helpers.js | 23 ++++++++++++++--------- tests/unit/filesize.test.js | 11 +++++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/helpers.js b/src/helpers.js index 7001825..a9e4611 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -225,26 +225,31 @@ export function applyPrecisionHandling( export function applyNumberFormatting(value, locale, localeOptions, separator, pad, round) { let result = value; + // When padding alongside a locale, let the locale formatter emit the fixed + // number of fraction digits. The manual string padding below cannot tell a + // locale-inserted grouping separator from the decimal separator, so it + // dropped digits (e.g. "1,234,500" became "1,234"). + const localePad = + pad && round > 0 ? { minimumFractionDigits: round, maximumFractionDigits: round } : undefined; + // Apply locale formatting if (locale === true) { - result = result.toLocaleString(); + result = result.toLocaleString(undefined, localePad); } else if (locale.length > 0) { - result = result.toLocaleString(locale, localeOptions); + result = result.toLocaleString(locale, { ...localeOptions, ...localePad }); } else if (separator.length > 0) { result = result.toString().replace(PERIOD, separator); } - // Apply padding - if (pad && round > 0) { + // Apply padding for the non-locale paths, where the string has a single + // decimal separator and no grouping is inserted. + if (pad && round > 0 && locale !== true && locale.length === 0) { const resultStr = result.toString(); - const x = separator || (resultStr.slice(1).match(/[.,]/g) || []).pop() || PERIOD; + const x = separator || PERIOD; const tmp = resultStr.split(x); const s = tmp[1] || EMPTY; - const l = s.length; - const n = round - l; - - result = `${tmp[0]}${x}${s.padEnd(l + n, ZERO)}`; + result = `${tmp[0]}${x}${s.padEnd(round, ZERO)}`; } return result; diff --git a/tests/unit/filesize.test.js b/tests/unit/filesize.test.js index 751764b..0665462 100644 --- a/tests/unit/filesize.test.js +++ b/tests/unit/filesize.test.js @@ -178,6 +178,17 @@ describe("filesize", () => { assert.strictEqual(filesize(1234567, { locale: "en-US", pad: true, round: 2 }), "1.23 MB"); assert.strictEqual(filesize(1234567890, { locale: "en-US", pad: true, round: 2 }), "1.23 GB"); }); + + it("should keep every digit group and pad when an integer value has grouping separators", () => { + // en: comma grouping with no decimal in the raw value. Previously the + // trailing grouping comma was mistaken for the decimal separator, which + // dropped digit groups and skipped padding. + assert.strictEqual( + filesize(1234500e24, { locale: "en", pad: true, round: 2 }), + "1,234,500.00 YB", + ); + assert.strictEqual(filesize(1000e24, { locale: "en", pad: true, round: 2 }), "1,000.00 YB"); + }); }); describe("Custom separators and spacers", () => {