-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
report: remove Util.UIStrings mutation, add I18n renderer class #10153
Changes from 3 commits
b97ea5e
c9af0c5
4ede333
cdf85ac
e4f2476
ff99eae
7f5ad9e
2a3b7a7
e545bf1
b4b8eed
73e92b6
bc081dd
93e6794
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,10 +45,10 @@ class CategoryRenderer { | |
*/ | ||
get _clumpTitles() { | ||
return { | ||
warning: Util.UIStrings.warningAuditsGroupTitle, | ||
manual: Util.UIStrings.manualAuditsGroupTitle, | ||
passed: Util.UIStrings.passedAuditsGroupTitle, | ||
notApplicable: Util.UIStrings.notApplicableAuditsGroupTitle, | ||
warning: Util.i18n.strings.warningAuditsGroupTitle, | ||
manual: Util.i18n.strings.manualAuditsGroupTitle, | ||
passed: Util.i18n.strings.passedAuditsGroupTitle, | ||
notApplicable: Util.i18n.strings.notApplicableAuditsGroupTitle, | ||
}; | ||
} | ||
|
||
|
@@ -68,6 +68,7 @@ class CategoryRenderer { | |
* @return {Element} | ||
*/ | ||
populateAuditValues(audit, tmpl) { | ||
const strings = Util.i18n.strings; | ||
const auditEl = this.dom.find('.lh-audit', tmpl); | ||
auditEl.id = audit.result.id; | ||
const scoreDisplayMode = audit.result.scoreDisplayMode; | ||
|
@@ -115,10 +116,11 @@ class CategoryRenderer { | |
if (audit.result.scoreDisplayMode === 'error') { | ||
auditEl.classList.add(`lh-audit--error`); | ||
const textEl = this.dom.find('.lh-audit__display-text', auditEl); | ||
textEl.textContent = Util.UIStrings.errorLabel; | ||
textEl.textContent = strings.errorLabel; | ||
textEl.classList.add('tooltip-boundary'); | ||
const tooltip = this.dom.createChildOf(textEl, 'div', 'tooltip tooltip--error'); | ||
tooltip.textContent = audit.result.errorMessage || Util.UIStrings.errorMissingAuditInfo; | ||
tooltip.textContent = | ||
audit.result.errorMessage || strings.errorMissingAuditInfo; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. didn't this line get shorter? must be a leftover newline from earlier version :) |
||
} else if (audit.result.explanation) { | ||
const explEl = this.dom.createChildOf(titleEl, 'div', 'lh-audit-explanation'); | ||
explEl.textContent = audit.result.explanation; | ||
|
@@ -128,7 +130,7 @@ class CategoryRenderer { | |
|
||
// Add list of warnings or singular warning | ||
const warningsEl = this.dom.createChildOf(titleEl, 'div', 'lh-warnings'); | ||
this.dom.createChildOf(warningsEl, 'span').textContent = Util.UIStrings.warningHeader; | ||
this.dom.createChildOf(warningsEl, 'span').textContent = strings.warningHeader; | ||
if (warnings.length === 1) { | ||
warningsEl.appendChild(this.dom.document().createTextNode(warnings.join(''))); | ||
} else { | ||
|
@@ -287,7 +289,7 @@ class CategoryRenderer { | |
|
||
const summaryInnerEl = this.dom.find('.lh-audit-group__summary', clumpElement); | ||
const chevronEl = summaryInnerEl.appendChild(this._createChevron()); | ||
chevronEl.title = Util.UIStrings.auditGroupExpandTooltip; | ||
chevronEl.title = Util.i18n.strings.auditGroupExpandTooltip; | ||
|
||
const headerEl = this.dom.find('.lh-audit-group__header', clumpElement); | ||
const title = this._clumpTitles[clumpId]; | ||
|
@@ -345,7 +347,7 @@ class CategoryRenderer { | |
percentageEl.textContent = scoreOutOf100.toString(); | ||
if (category.score === null) { | ||
percentageEl.textContent = '?'; | ||
percentageEl.title = Util.UIStrings.errorLabel; | ||
percentageEl.title = Util.i18n.strings.errorLabel; | ||
} | ||
|
||
this.dom.find('.lh-gauge__label', tmpl).textContent = category.title; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
/** | ||
* @license | ||
* Copyright 2019 Google Inc. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS-IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
'use strict'; | ||
|
||
/* globals self, URL */ | ||
|
||
// Not named `NBSP` because that creates a duplicate identifier (util.js). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😬, on of these days the grimaces will add up to a build step for renderer :) |
||
const NBSP2 = '\xa0'; | ||
|
||
class I18n { | ||
/** | ||
* @param {LH.Locale} locale | ||
*/ | ||
constructor(locale) { | ||
this.setNumberDateLocale(locale); | ||
this._numberDateLocale = locale; | ||
this._numberFormatter = new Intl.NumberFormat(locale); | ||
} | ||
|
||
/** | ||
* @param {LH.I18NRendererStrings} strings | ||
*/ | ||
setStrings(strings) { | ||
connorjclark marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this._strings = strings; | ||
} | ||
|
||
get strings() { | ||
return this._strings; | ||
} | ||
|
||
/** | ||
* Format number. | ||
* @param {number} number | ||
* @param {number=} granularity Number of decimal places to include. Defaults to 0.1. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: remove granularity in most of these functions. We never send a granularity, and sending it as a decimal, instead of the number of digits you want to be significant seems like a bad API. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't want to change functionality in this pr. this change would at least require changing many tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. None of the unit-tests use granularity except 1. I'm fine if it's a follow up/not done, just seems like old YAGNI. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, i assumed.
|
||
* @return {string} | ||
*/ | ||
formatNumber(number, granularity = 0.1) { | ||
const coarseValue = Math.round(number / granularity) * granularity; | ||
return this._numberFormatter.format(coarseValue); | ||
} | ||
|
||
/** | ||
* @param {number} size | ||
* @param {number=} granularity Controls how coarse the displayed value is, defaults to .01 | ||
connorjclark marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @return {string} | ||
*/ | ||
formatBytesToKB(size, granularity = 0.1) { | ||
const kbs = this._numberFormatter.format(Math.round(size / 1024 / granularity) * granularity); | ||
return `${kbs}${NBSP2}KB`; | ||
} | ||
|
||
/** | ||
* @param {number} ms | ||
* @param {number=} granularity Controls how coarse the displayed value is, defaults to 10 | ||
* @return {string} | ||
*/ | ||
formatMilliseconds(ms, granularity = 10) { | ||
const coarseTime = Math.round(ms / granularity) * granularity; | ||
return `${this._numberFormatter.format(coarseTime)}${NBSP2}ms`; | ||
} | ||
|
||
/** | ||
* @param {number} ms | ||
* @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1 | ||
* @return {string} | ||
*/ | ||
formatSeconds(ms, granularity = 0.1) { | ||
const coarseTime = Math.round(ms / 1000 / granularity) * granularity; | ||
return `${this._numberFormatter.format(coarseTime)}${NBSP2}s`; | ||
} | ||
|
||
/** | ||
* Format time. | ||
* @param {string} date | ||
* @return {string} | ||
*/ | ||
formatDateTime(date) { | ||
/** @type {Intl.DateTimeFormatOptions} */ | ||
const options = { | ||
month: 'short', day: 'numeric', year: 'numeric', | ||
hour: 'numeric', minute: 'numeric', timeZoneName: 'short', | ||
}; | ||
let formatter = new Intl.DateTimeFormat(this._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(this._numberDateLocale, options); | ||
} | ||
return formatter.format(new Date(date)); | ||
} | ||
/** | ||
* Converts a time in milliseconds into a duration string, i.e. `1d 2h 13m 52s` | ||
* @param {number} timeInMilliseconds | ||
* @return {string} | ||
*/ | ||
formatDuration(timeInMilliseconds) { | ||
let timeInSeconds = timeInMilliseconds / 1000; | ||
if (Math.round(timeInSeconds) === 0) { | ||
return 'None'; | ||
} | ||
|
||
/** @type {Array<string>} */ | ||
const parts = []; | ||
const unitLabels = /** @type {Object<string, number>} */ ({ | ||
d: 60 * 60 * 24, | ||
h: 60 * 60, | ||
m: 60, | ||
s: 1, | ||
}); | ||
|
||
Object.keys(unitLabels).forEach(label => { | ||
const unit = unitLabels[label]; | ||
const numberOfUnits = Math.floor(timeInSeconds / unit); | ||
if (numberOfUnits > 0) { | ||
timeInSeconds -= numberOfUnits * unit; | ||
parts.push(`${numberOfUnits}\xa0${label}`); | ||
} | ||
}); | ||
|
||
return parts.join(' '); | ||
} | ||
|
||
/** | ||
* Set the locale to be used for I18n's number and date formatting functions. | ||
* @param {LH.Locale} locale | ||
*/ | ||
setNumberDateLocale(locale) { | ||
connorjclark marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// When testing, use a locale with more exciting numeric formatting | ||
if (locale === 'en-XA') locale = 'de'; | ||
|
||
this._numberDateLocale = locale; | ||
this._numberFormatter = new Intl.NumberFormat(locale); | ||
} | ||
} | ||
|
||
if (typeof module !== 'undefined' && module.exports) { | ||
module.exports = I18n; | ||
} else { | ||
self.I18n = I18n; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's a bummer these won't get into the localeLog, maybe we file a separate issue to track that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no idea what this means :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh sorry I meant icuMessagePaths
when we create i18n'd strings using
str
we get to track it in the lhr so we can useswap-locale
later, it'd be great if these usages could take advantage of thatThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah right. an additional challenge - besides figuring out how to sink these paths to a central place - the swap locale script only works for UIStrings, not for value formatting.
Instead, I think we shouldn't format in the audit at all. These values should be raw numbers - and all the
numeric
detail types should be formatted by the report renderer (odd that this isn't the case now ...). That way there's nothing to swap.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does that make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh sorry! yes!
Agreed I think there was even a todo I put in one of my PRs during review on this topic that I've totally forgotten to merge...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool.. so I'll leave the changes in the audits as they are now. It's still an improvement. We can follow up on formatting in just the renderer later.
I'll fix those tests now..