diff --git a/lighthouse-core/report/html/renderer/report-renderer.js b/lighthouse-core/report/html/renderer/report-renderer.js index 053cafff03a5..c6011e3e23f9 100644 --- a/lighthouse-core/report/html/renderer/report-renderer.js +++ b/lighthouse-core/report/html/renderer/report-renderer.js @@ -47,7 +47,12 @@ class ReportRenderer { // If any mutations happen to the report within the renderers, we want the original object untouched const clone = /** @type {LH.ReportResult} */ (JSON.parse(JSON.stringify(report))); // Mutate the UIStrings if necessary (while saving originals) - const clonedStrings = JSON.parse(JSON.stringify(Util.UIStrings)); + const originalUIStrings = JSON.parse(JSON.stringify(Util.UIStrings)); + // If LHR is older (≤3.0.3), it has no locale setting. Set default. + if (!clone.configSettings.locale) { + clone.configSettings.locale = 'en-US'; + } + Util.setNumberDateLocale(clone.configSettings.locale); if (clone.i18n && clone.i18n.rendererFormattedStrings) { ReportRenderer.updateAllUIStrings(clone.i18n.rendererFormattedStrings); } @@ -61,7 +66,7 @@ class ReportRenderer { container.appendChild(this._renderReport(clone)); // put the UIStrings back into original state - ReportRenderer.updateAllUIStrings(clonedStrings); + ReportRenderer.updateAllUIStrings(originalUIStrings); return /** @type {Element} **/ (container); } diff --git a/lighthouse-core/report/html/renderer/util.js b/lighthouse-core/report/html/renderer/util.js index 0429eeb59e46..16f450d1a146 100644 --- a/lighthouse-core/report/html/renderer/util.js +++ b/lighthouse-core/report/html/renderer/util.js @@ -137,7 +137,7 @@ class Util { */ static formatNumber(number, granularity = 0.1) { const coarseValue = Math.round(number / granularity) * granularity; - return coarseValue.toLocaleString(); + return coarseValue.toLocaleString(Util.numberDateLocale); } /** @@ -146,7 +146,8 @@ class Util { * @return {string} */ static formatBytesToKB(size, granularity = 0.1) { - const kbs = (Math.round(size / 1024 / granularity) * granularity).toLocaleString(); + const kbs = (Math.round(size / 1024 / granularity) * granularity) + .toLocaleString(Util.numberDateLocale); return `${kbs}${NBSP}KB`; } @@ -157,7 +158,7 @@ class Util { */ static formatMilliseconds(ms, granularity = 10) { const coarseTime = Math.round(ms / granularity) * granularity; - return `${coarseTime.toLocaleString()}${NBSP}ms`; + return `${coarseTime.toLocaleString(Util.numberDateLocale)}${NBSP}ms`; } /** @@ -167,7 +168,7 @@ class Util { */ static formatSeconds(ms, granularity = 0.1) { const coarseTime = Math.round(ms / 1000 / granularity) * granularity; - return `${coarseTime.toLocaleString()}${NBSP}s`; + return `${coarseTime.toLocaleString(Util.numberDateLocale)}${NBSP}s`; } /** @@ -180,14 +181,14 @@ class Util { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', timeZoneName: 'short', }; - let formatter = new Intl.DateTimeFormat('en-US', options); + let formatter = new Intl.DateTimeFormat(Util.numberDateLocale, options); // Force UTC if runtime timezone could not be detected. // See https://github.com/GoogleChrome/lighthouse/issues/1056 const tz = formatter.resolvedOptions().timeZone; if (!tz || tz.toLowerCase() === 'etc/unknown') { options.timeZone = 'UTC'; - formatter = new Intl.DateTimeFormat('en-US', options); + formatter = new Intl.DateTimeFormat(Util.numberDateLocale, options); } return formatter.format(new Date(date)); } @@ -399,8 +400,26 @@ class Util { summary: `${deviceEmulation}, ${summary}`, }; } + + /** + * Set the locale to be used for Util's number and date formatting functions. + * @param {LH.Locale} locale + */ + static setNumberDateLocale(locale) { + Util.numberDateLocale = locale; + + // When testing, use a locale with more exciting numeric formatting + // @ts-ignore - TODO: until `de-DE` is in LH.Locale + if (Util.numberDateLocale === 'en-XA') Util.numberDateLocale = 'de-DE'; + } } +/** + * This value is updated on each run to the locale of the report + * @type {LH.Locale} + */ +Util.numberDateLocale = 'en-US'; + Util.UIStrings = { /** Disclaimer shown to users below the metric values (First Contentful Paint, Time to Interactive, etc) to warn them that the numbers they see will likely change slightly the next time they run Lighthouse. */ varianceDisclaimer: 'Values are estimated and may vary.', diff --git a/lighthouse-core/test/report/html/renderer/util-test.js b/lighthouse-core/test/report/html/renderer/util-test.js index 60be7bd8c1a6..2937c2771722 100644 --- a/lighthouse-core/test/report/html/renderer/util-test.js +++ b/lighthouse-core/test/report/html/renderer/util-test.js @@ -62,6 +62,27 @@ describe('util helpers', () => { assert.equal(Util.formatDuration(28 * 60 * 60 * 1000 + 5000), `1${NBSP}d 4${NBSP}h 5${NBSP}s`); }); + // TODO: need ICU support in node on Travis/Appveyor + it.skip('formats based on locale', () => { + const number = 12346.858558; + + const originalLocale = Util.numberDateLocale; + Util.setNumberDateLocale('de-DE'); + assert.strictEqual(Util.formatNumber(number), '12.346,9'); + Util.setNumberDateLocale(originalLocale); // reset + assert.strictEqual(Util.formatNumber(number), '12,346.9'); + }); + + it.skip('uses decimal comma with en-XA test locale', () => { + const number = 12346.858558; + + const originalLocale = Util.numberDateLocale; + Util.setNumberDateLocale('en-XA'); + assert.strictEqual(Util.formatNumber(number), '12.346,9'); + Util.setNumberDateLocale(originalLocale); // reset + assert.strictEqual(Util.formatNumber(number), '12,346.9'); + }); + it('calculates a score ratings', () => { assert.equal(Util.calculateRating(0.0), 'fail'); assert.equal(Util.calculateRating(0.10), 'fail');