Repository: GoogleChrome/lighthouse
Pull request #5716 (2018-07-24 15:46:59)

core(i18n): extract performance strings to UIStrings
this should be all of the audit messages necessary for performance
![image](https://user-images.githubusercontent.com/2301202/42970989-aeb83e4e-8b5f-11e8-8fcb-187a6f37210b.png)


what's left after this
* renderer strings (#5713) 
* audit table cell values
* something else I'm forgetting?

**Related Issues/PRs**
~~blocked on  #5655~~
carried over from #5692 


patrickhulce: @brendankenny what did you think of #5720, would that work for you?

Patch:
diff --git a/docs/architecture.md b/docs/architecture.md
index 2c8a68e7e..b907290a9 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -11,7 +11,7 @@ _Some incomplete notes_
   * **Artifacts** - output of a gatherer
 * **Audit** - Tests for a single feature/optimization/metric. Using the Artifacts as input, an audit evaluates a test and resolves to a numeric score. See [Understanding Results](./understanding-results.md) for details of the LHR (Lighthouse Result object).
   * **Computed Artifacts** - Generated on-demand from artifacts, these add additional meaning, and are often shared amongst multiple audits.
-* **Report** - The report UI, created client-side from the LHR. See [HTML Report Generation Overview](./report.md) for details.
+* **Report** - The report UI, created client-side from the LHR. See [HTML Report Generation Overview](../lighthouse-core/report/html/readme.md) for details.
 
 ### Audit/Report terminology
 * **Category** - Roll-up collection of audits and audit groups into a user-facing section of the report (eg. `Best Practices`). Applies weighting and overall scoring to the section. Examples: PWA, Accessibility, Best Practices.
diff --git a/lighthouse-core/audits/bootup-time.js b/lighthouse-core/audits/bootup-time.js
index 51d80344b..b1fe4d88a 100644
--- a/lighthouse-core/audits/bootup-time.js
+++ b/lighthouse-core/audits/bootup-time.js
@@ -6,9 +6,24 @@
 'use strict';
 
 const Audit = require('./audit');
-const Util = require('../report/html/renderer/util');
 const NetworkRequest = require('../lib/network-request');
 const {taskGroups} = require('../lib/task-groups');
+const i18n = require('../lib/i18n');
+
+const UIStrings = {
+  title: 'JavaScript boot-up time',
+  failureTitle: 'JavaScript boot-up time is too high',
+  description: 'Consider reducing the time spent parsing, compiling, and executing JS. ' +
+    'You may find delivering smaller JS payloads helps with this. [Learn ' +
+    'more](https://developers.google.com/web/tools/lighthouse/audits/bootup).',
+  columnTotal: 'Total',
+  columnScriptEval: 'Script Evaluation',
+  columnScriptParse: 'Script Parse',
+  chromeExtensionsWarning: 'Chrome extensions negatively affected this page\'s load' +
+    ' performance. Try auditing the page in incognito mode or from a clean Chrome profile.',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class BootupTime extends Audit {
   /**
@@ -17,12 +32,10 @@ class BootupTime extends Audit {
   static get meta() {
     return {
       id: 'bootup-time',
-      title: 'JavaScript boot-up time',
-      failureTitle: 'JavaScript boot-up time is too high',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
-      description: 'Consider reducing the time spent parsing, compiling, and executing JS. ' +
-        'You may find delivering smaller JS payloads helps with this. [Learn ' +
-        'more](https://developers.google.com/web/tools/lighthouse/audits/bootup).',
       requiredArtifacts: ['traces'],
     };
   }
@@ -132,18 +145,17 @@ class BootupTime extends Audit {
 
     // TODO: consider moving this to core gathering so you don't need to run the audit for warning
     if (hadExcessiveChromeExtension) {
-      context.LighthouseRunWarnings.push('Chrome extensions negatively affected this page\'s load' +
-        ' performance. Try auditing the page in incognito mode or from a clean Chrome profile.');
+      context.LighthouseRunWarnings.push(str_(UIStrings.chromeExtensionsWarning));
     }
 
     const summary = {wastedMs: totalBootupTime};
 
     const headings = [
-      {key: 'url', itemType: 'url', text: 'URL'},
-      {key: 'total', granularity: 1, itemType: 'ms', text: 'Total'},
-      {key: 'scripting', granularity: 1, itemType: 'ms', text: taskGroups.scriptEvaluation.label},
+      {key: 'url', itemType: 'url', text: str_(i18n.UIStrings.columnURL)},
+      {key: 'total', granularity: 1, itemType: 'ms', text: str_(UIStrings.columnTotal)},
+      {key: 'scripting', granularity: 1, itemType: 'ms', text: str_(UIStrings.columnScriptEval)},
       {key: 'scriptParseCompile', granularity: 1, itemType: 'ms',
-        text: taskGroups.scriptParseCompile.label},
+        text: str_(UIStrings.columnScriptParse)},
     ];
 
     const details = BootupTime.makeTableDetails(headings, results, summary);
@@ -157,10 +169,11 @@ class BootupTime extends Audit {
     return {
       score,
       rawValue: totalBootupTime,
-      displayValue: [Util.MS_DISPLAY_VALUE, totalBootupTime],
+      displayValue: totalBootupTime > 0 ? str_(i18n.UIStrings.ms, {timeInMs: totalBootupTime}) : '',
       details,
     };
   }
 }
 
 module.exports = BootupTime;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js
index eca744e85..49e167517 100644
--- a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js
+++ b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js
@@ -8,6 +8,9 @@
 const Audit = require('../audit');
 const linearInterpolation = require('../../lib/statistics').linearInterpolation;
 const Interactive = require('../../gather/computed/metrics/lantern-interactive');
+const i18n = require('../../lib/i18n');
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, {});
 
 /** @typedef {import('../../lib/dependency-graph/simulator/simulator')} Simulator */
 /** @typedef {import('../../lib/dependency-graph/base-node.js').Node} Node */
@@ -193,8 +196,8 @@ class UnusedBytes extends Audit {
 
     /** @type {LH.Audit.DisplayValue} */
     let displayValue = result.displayValue || '';
-    if (typeof result.displayValue === 'undefined' && wastedKb) {
-      displayValue = ['Potential savings of %d\xa0KB', wastedKb];
+    if (typeof result.displayValue === 'undefined' && wastedBytes) {
+      displayValue = str_(i18n.UIStrings.displayValueByteSavings, {wastedBytes});
     }
 
     const details = Audit.makeOpportunityDetails(result.headings, results, wastedMs, wastedBytes);
diff --git a/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js b/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js
index 525e98d66..09dc7d674 100644
--- a/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js
+++ b/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js
@@ -10,6 +10,16 @@
 
 const NetworkRequest = require('../../lib/network-request');
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Use video formats for animated content',
+  description: 'Large GIFs are inefficient for delivering animated content. Consider using ' +
+    'MPEG4/WebM videos for animations and PNG/WebP for static images instead of GIF to save ' +
+    'network bytes. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/replace-animated-gifs-with-video/)',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 // If GIFs are above this size, we'll flag them
 // See https://github.com/GoogleChrome/lighthouse/pull/4885#discussion_r178406623 and https://github.com/GoogleChrome/lighthouse/issues/4696#issuecomment-370979920
@@ -22,11 +32,9 @@ class EfficientAnimatedContent extends ByteEfficiencyAudit {
   static get meta() {
     return {
       id: 'efficient-animated-content',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-      title: 'Use video formats for animated content',
-      description: 'Large GIFs are inefficient for delivering animated content. Consider using ' +
-        'MPEG4/WebM videos for animations and PNG/WebP for static images instead of GIF to save ' +
-        'network bytes. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/replace-animated-gifs-with-video/)',
       requiredArtifacts: ['devtoolsLogs'],
     };
   }
@@ -66,9 +74,9 @@ class EfficientAnimatedContent extends ByteEfficiencyAudit {
 
     /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
     const headings = [
-      {key: 'url', valueType: 'url', label: 'URL'},
-      {key: 'totalBytes', valueType: 'bytes', label: 'Transfer Size'},
-      {key: 'wastedBytes', valueType: 'bytes', label: 'Byte Savings'},
+      {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+      {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnSize)},
+      {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
     ];
 
     return {
@@ -79,3 +87,4 @@ class EfficientAnimatedContent extends ByteEfficiencyAudit {
 }
 
 module.exports = EfficientAnimatedContent;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/offscreen-images.js b/lighthouse-core/audits/byte-efficiency/offscreen-images.js
index 401f1735a..49b3f1dc3 100644
--- a/lighthouse-core/audits/byte-efficiency/offscreen-images.js
+++ b/lighthouse-core/audits/byte-efficiency/offscreen-images.js
@@ -12,6 +12,17 @@
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const Sentry = require('../../lib/sentry');
 const URL = require('../../lib/url-shim');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Defer offscreen images',
+  description:
+    'Consider lazy-loading offscreen and hidden images after all critical resources have ' +
+    'finished loading to lower time to interactive. ' +
+    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/offscreen-images).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 const ALLOWABLE_OFFSCREEN_X = 100;
 const ALLOWABLE_OFFSCREEN_Y = 200;
@@ -29,12 +40,9 @@ class OffscreenImages extends ByteEfficiencyAudit {
   static get meta() {
     return {
       id: 'offscreen-images',
-      title: 'Defer offscreen images',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-      description:
-        'Consider lazy-loading offscreen and hidden images after all critical resources have ' +
-        'finished loading to lower time to interactive. ' +
-        '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/offscreen-images).',
       requiredArtifacts: ['ImageUsage', 'ViewportDimensions', 'traces', 'devtoolsLogs'],
     };
   }
@@ -198,9 +206,9 @@ class OffscreenImages extends ByteEfficiencyAudit {
       /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
       const headings = [
         {key: 'url', valueType: 'thumbnail', label: ''},
-        {key: 'url', valueType: 'url', label: 'URL'},
-        {key: 'totalBytes', valueType: 'bytes', label: 'Original'},
-        {key: 'wastedBytes', valueType: 'bytes', label: 'Potential Savings'},
+        {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+        {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnSize)},
+        {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
       ];
 
       return {
@@ -213,3 +221,4 @@ class OffscreenImages extends ByteEfficiencyAudit {
 }
 
 module.exports = OffscreenImages;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js b/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js
index 4892c7049..0124b7c28 100644
--- a/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js
+++ b/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js
@@ -206,7 +206,7 @@ class RenderBlockingResources extends Audit {
 
     let displayValue = '';
     if (results.length > 0) {
-      displayValue = str_(i18n.UIStrings.displayValueWastedMs, {wastedMs});
+      displayValue = str_(i18n.UIStrings.displayValueMsSavings, {wastedMs});
     }
 
     /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
diff --git a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js
index 0e58329d6..44910fd81 100644
--- a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js
+++ b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js
@@ -6,6 +6,19 @@
 'use strict';
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Avoids enormous network payloads',
+  failureTitle: 'Has enormous network payloads',
+  description:
+  'Large network payloads cost users real money and are highly correlated with ' +
+  'long load times. [Learn ' +
+  'more](https://developers.google.com/web/tools/lighthouse/audits/network-payloads).',
+  displayValue: 'Total size was {totalBytes, number, bytes}\xa0KB',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class TotalByteWeight extends ByteEfficiencyAudit {
   /**
@@ -14,13 +27,10 @@ class TotalByteWeight extends ByteEfficiencyAudit {
   static get meta() {
     return {
       id: 'total-byte-weight',
-      title: 'Avoids enormous network payloads',
-      failureTitle: 'Has enormous network payloads',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-      description:
-        'Large network payloads cost users real money and are highly correlated with ' +
-        'long load times. [Learn ' +
-        'more](https://developers.google.com/web/tools/lighthouse/audits/network-payloads).',
       requiredArtifacts: ['devtoolsLogs'],
     };
   }
@@ -76,15 +86,8 @@ class TotalByteWeight extends ByteEfficiencyAudit {
     );
 
     const headings = [
-      {key: 'url', itemType: 'url', text: 'URL'},
-      {
-        key: 'totalBytes',
-        itemType: 'bytes',
-        displayUnit: 'kb',
-        granularity: 1,
-        text: 'Total Size',
-      },
-      {key: 'totalMs', itemType: 'ms', text: 'Transfer Time'},
+      {key: 'url', itemType: 'url', text: str_(i18n.UIStrings.columnURL)},
+      {key: 'totalBytes', itemType: 'bytes', text: str_(i18n.UIStrings.columnSize)},
     ];
 
     const tableDetails = ByteEfficiencyAudit.makeTableDetails(headings, results);
@@ -92,10 +95,7 @@ class TotalByteWeight extends ByteEfficiencyAudit {
     return {
       score,
       rawValue: totalBytes,
-      displayValue: [
-        'Total size was %d\xa0KB',
-        totalBytes / 1024,
-      ],
+      displayValue: str_(UIStrings.displayValue, {totalBytes}),
       extendedInfo: {
         value: {
           results,
@@ -108,3 +108,4 @@ class TotalByteWeight extends ByteEfficiencyAudit {
 }
 
 module.exports = TotalByteWeight;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/unminified-css.js b/lighthouse-core/audits/byte-efficiency/unminified-css.js
index 61c29d7d6..724094561 100644
--- a/lighthouse-core/audits/byte-efficiency/unminified-css.js
+++ b/lighthouse-core/audits/byte-efficiency/unminified-css.js
@@ -7,6 +7,15 @@
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const UnusedCSSRules = require('./unused-css-rules');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Minify CSS',
+  description: 'Minifying CSS files can reduce network payload sizes. ' +
+  '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/minify-css).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 const IGNORE_THRESHOLD_IN_PERCENT = 5;
 const IGNORE_THRESHOLD_IN_BYTES = 2048;
@@ -21,10 +30,9 @@ class UnminifiedCSS extends ByteEfficiencyAudit {
   static get meta() {
     return {
       id: 'unminified-css',
-      title: 'Minify CSS',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-      description: 'Minifying CSS files can reduce network payload sizes. ' +
-        '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/minify-css).',
       requiredArtifacts: ['CSSUsage', 'devtoolsLogs'],
     };
   }
@@ -142,15 +150,16 @@ class UnminifiedCSS extends ByteEfficiencyAudit {
       items.push(result);
     }
 
-    return {
-      items,
-      headings: [
-        {key: 'url', valueType: 'url', label: 'URL'},
-        {key: 'totalBytes', valueType: 'bytes', label: 'Original'},
-        {key: 'wastedBytes', valueType: 'bytes', label: 'Potential Savings'},
-      ],
-    };
+    /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
+    const headings = [
+      {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+      {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnSize)},
+      {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
+    ];
+
+    return {items, headings};
   }
 }
 
 module.exports = UnminifiedCSS;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/unminified-javascript.js b/lighthouse-core/audits/byte-efficiency/unminified-javascript.js
index d19d8fd5c..c8c3ba2de 100644
--- a/lighthouse-core/audits/byte-efficiency/unminified-javascript.js
+++ b/lighthouse-core/audits/byte-efficiency/unminified-javascript.js
@@ -8,6 +8,15 @@
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 // @ts-ignore - TODO: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/25410
 const esprima = require('esprima');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Minify JavaScript',
+  description: 'Minifying JavaScript files can reduce payload sizes and script parse time. ' +
+    '[Learn more](https://developers.google.com/speed/docs/insights/MinifyResources).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 const IGNORE_THRESHOLD_IN_PERCENT = 10;
 const IGNORE_THRESHOLD_IN_BYTES = 2048;
@@ -29,11 +38,9 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit {
   static get meta() {
     return {
       id: 'unminified-javascript',
-      title: 'Minify JavaScript',
-
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-      description: 'Minifying JavaScript files can reduce payload sizes and script parse time. ' +
-        '[Learn more](https://developers.google.com/speed/docs/insights/MinifyResources).',
       requiredArtifacts: ['Scripts', 'devtoolsLogs'],
     };
   }
@@ -96,16 +103,20 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit {
       }
     }
 
+    /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
+    const headings = [
+      {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+      {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnSize)},
+      {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
+    ];
+
     return {
       items,
       warnings,
-      headings: [
-        {key: 'url', valueType: 'url', label: 'URL'},
-        {key: 'totalBytes', valueType: 'bytes', label: 'Original'},
-        {key: 'wastedBytes', valueType: 'bytes', label: 'Potential Savings'},
-      ],
+      headings,
     };
   }
 }
 
 module.exports = UnminifiedJavaScript;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/unused-css-rules.js b/lighthouse-core/audits/byte-efficiency/unused-css-rules.js
index 347af4d40..0bc18aaae 100644
--- a/lighthouse-core/audits/byte-efficiency/unused-css-rules.js
+++ b/lighthouse-core/audits/byte-efficiency/unused-css-rules.js
@@ -6,6 +6,16 @@
 'use strict';
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Defer unused CSS',
+  description: 'Remove unused rules from stylesheets to reduce unnecessary ' +
+    'bytes consumed by network activity. ' +
+    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/unused-css).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 const IGNORE_THRESHOLD_IN_BYTES = 2048;
 const PREVIEW_LENGTH = 100;
@@ -19,11 +29,9 @@ class UnusedCSSRules extends ByteEfficiencyAudit {
   static get meta() {
     return {
       id: 'unused-css-rules',
-      title: 'Defer unused CSS',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-      description: 'Remove unused rules from stylesheets to reduce unnecessary ' +
-          'bytes consumed by network activity. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/unused-css).',
       requiredArtifacts: ['CSSUsage', 'URL', 'devtoolsLogs'],
     };
   }
@@ -165,9 +173,9 @@ class UnusedCSSRules extends ByteEfficiencyAudit {
 
       /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
       const headings = [
-        {key: 'url', valueType: 'url', label: 'URL'},
-        {key: 'totalBytes', valueType: 'bytes', label: 'Original'},
-        {key: 'wastedBytes', valueType: 'bytes', label: 'Potential Savings'},
+        {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+        {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnSize)},
+        {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
       ];
 
       return {
@@ -179,3 +187,4 @@ class UnusedCSSRules extends ByteEfficiencyAudit {
 }
 
 module.exports = UnusedCSSRules;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/unused-javascript.js b/lighthouse-core/audits/byte-efficiency/unused-javascript.js
index 44e7c409d..c4cb18b81 100644
--- a/lighthouse-core/audits/byte-efficiency/unused-javascript.js
+++ b/lighthouse-core/audits/byte-efficiency/unused-javascript.js
@@ -6,6 +6,14 @@
 'use strict';
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Unused JavaScript',
+  description: 'Remove unused JavaScript to reduce bytes consumed by network activity.',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 const IGNORE_THRESHOLD_IN_BYTES = 2048;
 
@@ -16,9 +24,9 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
   static get meta() {
     return {
       id: 'unused-javascript',
-      title: 'Unused JavaScript',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-      description: 'Remove unused JavaScript to reduce bytes consumed by network activity.',
       requiredArtifacts: ['JsUsage', 'devtoolsLogs'],
     };
   }
@@ -112,12 +120,13 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
     return {
       items,
       headings: [
-        {key: 'url', valueType: 'url', label: 'URL'},
-        {key: 'totalBytes', valueType: 'bytes', label: 'Original'},
-        {key: 'wastedBytes', valueType: 'bytes', label: 'Potential Savings'},
+        {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+        {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnSize)},
+        {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
       ],
     };
   }
 }
 
 module.exports = UnusedJavaScript;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js b/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js
index 05ed4cec8..db53a2476 100644
--- a/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js
+++ b/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js
@@ -12,6 +12,21 @@ const Audit = require('../audit');
 const NetworkRequest = require('../../lib/network-request');
 const URL = require('../../lib/url-shim');
 const linearInterpolation = require('../../lib/statistics').linearInterpolation;
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Uses efficient cache policy on static assets',
+  failureTitle: 'Uses inefficient cache policy on static assets',
+  description:
+    'A long cache lifetime can speed up repeat visits to your page. ' +
+    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/cache-policy).',
+  displayValue: `{itemCount, plural,
+    =1 {1 resource found}
+    other {# resources found}
+    }`,
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 // Ignore assets that have very high likelihood of cache hit
 const IGNORE_THRESHOLD_IN_PERCENT = 0.925;
@@ -23,11 +38,9 @@ class CacheHeaders extends Audit {
   static get meta() {
     return {
       id: 'uses-long-cache-ttl',
-      title: 'Uses efficient cache policy on static assets',
-      failureTitle: 'Uses inefficient cache policy on static assets',
-      description:
-        'A long cache lifetime can speed up repeat visits to your page. ' +
-        '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/cache-policy).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
       requiredArtifacts: ['devtoolsLogs'],
     };
@@ -212,10 +225,12 @@ class CacheHeaders extends Audit {
       );
 
       const headings = [
-        {key: 'url', itemType: 'url', text: 'URL'},
-        {key: 'cacheLifetimeMs', itemType: 'ms', text: 'Cache TTL', displayUnit: 'duration'},
-        {key: 'totalBytes', itemType: 'bytes', text: 'Size (KB)', displayUnit: 'kb',
-          granularity: 1},
+        {key: 'url', itemType: 'url', text: str_(i18n.UIStrings.columnURL)},
+        // TODO(i18n): pre-compute localized duration
+        {key: 'cacheLifetimeMs', itemType: 'ms', text: str_(i18n.UIStrings.columnCacheTTL),
+          displayUnit: 'duration'},
+        {key: 'totalBytes', itemType: 'bytes', text: str_(i18n.UIStrings.columnSize),
+          displayUnit: 'kb', granularity: 1},
       ];
 
       const summary = {wastedBytes: totalWastedBytes};
@@ -224,7 +239,7 @@ class CacheHeaders extends Audit {
       return {
         score,
         rawValue: totalWastedBytes,
-        displayValue: `${results.length} asset${results.length !== 1 ? 's' : ''} found`,
+        displayValue: str_(UIStrings.displayValue, {itemCount: results.length}),
         extendedInfo: {
           value: {
             results,
@@ -238,3 +253,4 @@ class CacheHeaders extends Audit {
 }
 
 module.exports = CacheHeaders;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js
index 8311f448e..46b0020fc 100644
--- a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js
+++ b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js
@@ -11,6 +11,15 @@
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const URL = require('../../lib/url-shim');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Efficiently encode images',
+  description: 'Optimized images load faster and consume less cellular data. ' +
+  '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/optimize-images).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 const IGNORE_THRESHOLD_IN_BYTES = 4096;
 
@@ -21,10 +30,9 @@ class UsesOptimizedImages extends ByteEfficiencyAudit {
   static get meta() {
     return {
       id: 'uses-optimized-images',
-      title: 'Efficiently encode images',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-      description: 'Optimized images load faster and consume less cellular data. ' +
-        '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/optimize-images).',
       requiredArtifacts: ['OptimizedImages', 'devtoolsLogs'],
     };
   }
@@ -73,9 +81,9 @@ class UsesOptimizedImages extends ByteEfficiencyAudit {
     /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
     const headings = [
       {key: 'url', valueType: 'thumbnail', label: ''},
-      {key: 'url', valueType: 'url', label: 'URL'},
-      {key: 'totalBytes', valueType: 'bytes', label: 'Original'},
-      {key: 'wastedBytes', valueType: 'bytes', label: 'Potential Savings'},
+      {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+      {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnSize)},
+      {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
     ];
 
     return {
@@ -87,3 +95,4 @@ class UsesOptimizedImages extends ByteEfficiencyAudit {
 }
 
 module.exports = UsesOptimizedImages;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js
index add01ca94..c91cfee70 100644
--- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js
+++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js
@@ -16,6 +16,17 @@
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const Sentry = require('../../lib/sentry');
 const URL = require('../../lib/url-shim');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Properly size images',
+  description:
+  'Serve images that are appropriately-sized to save cellular data ' +
+  'and improve load time. ' +
+  '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/oversized-images).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 const IGNORE_THRESHOLD_IN_BYTES = 2048;
 
@@ -26,12 +37,9 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
   static get meta() {
     return {
       id: 'uses-responsive-images',
-      title: 'Properly size images',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-      description:
-        'Serve images that are appropriately-sized to save cellular data ' +
-        'and improve load time. ' +
-        '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/oversized-images).',
       requiredArtifacts: ['ImageUsage', 'ViewportDimensions', 'devtoolsLogs'],
     };
   }
@@ -113,9 +121,9 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
     /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
     const headings = [
       {key: 'url', valueType: 'thumbnail', label: ''},
-      {key: 'url', valueType: 'url', label: 'URL'},
-      {key: 'totalBytes', valueType: 'bytes', label: 'Original'},
-      {key: 'wastedBytes', valueType: 'bytes', label: 'Potential Savings'},
+      {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+      {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnSize)},
+      {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
     ];
 
     return {
@@ -127,3 +135,4 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
 }
 
 module.exports = UsesResponsiveImages;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/uses-text-compression.js b/lighthouse-core/audits/byte-efficiency/uses-text-compression.js
index 1b95ae977..c40697f5d 100644
--- a/lighthouse-core/audits/byte-efficiency/uses-text-compression.js
+++ b/lighthouse-core/audits/byte-efficiency/uses-text-compression.js
@@ -11,6 +11,16 @@
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const URL = require('../../lib/url-shim');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Enable text compression',
+  description: 'Text-based responses should be served with compression (gzip, deflate or' +
+    ' brotli) to minimize total network bytes.' +
+    ' [Learn more](https://developers.google.com/web/tools/lighthouse/audits/text-compression).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 const IGNORE_THRESHOLD_IN_BYTES = 1400;
 const IGNORE_THRESHOLD_IN_PERCENT = 0.1;
@@ -22,11 +32,9 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit {
   static get meta() {
     return {
       id: 'uses-text-compression',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-      title: 'Enable text compression',
-      description: 'Text-based responses should be served with compression (gzip, deflate or' +
-        ' brotli) to minimize total network bytes.' +
-        ' [Learn more](https://developers.google.com/web/tools/lighthouse/audits/text-compression).',
       requiredArtifacts: ['ResponseCompression', 'devtoolsLogs'],
     };
   }
@@ -74,9 +82,9 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit {
 
     /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
     const headings = [
-      {key: 'url', valueType: 'url', label: 'Uncompressed resource URL'},
-      {key: 'totalBytes', valueType: 'bytes', label: 'Original'},
-      {key: 'wastedBytes', valueType: 'bytes', label: 'GZIP Savings'},
+      {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+      {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnSize)},
+      {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
     ];
 
     return {
@@ -87,3 +95,4 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit {
 }
 
 module.exports = ResponsesAreCompressed;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/byte-efficiency/uses-webp-images.js b/lighthouse-core/audits/byte-efficiency/uses-webp-images.js
index 1b9a290b9..94bb043ad 100644
--- a/lighthouse-core/audits/byte-efficiency/uses-webp-images.js
+++ b/lighthouse-core/audits/byte-efficiency/uses-webp-images.js
@@ -10,6 +10,16 @@
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const URL = require('../../lib/url-shim');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Serve images in next-gen formats',
+  description: 'Image formats like JPEG 2000, JPEG XR, and WebP often provide better ' +
+    'compression than PNG or JPEG, which means faster downloads and less data consumption. ' +
+    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/webp).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 const IGNORE_THRESHOLD_IN_BYTES = 8192;
 
@@ -20,11 +30,9 @@ class UsesWebPImages extends ByteEfficiencyAudit {
   static get meta() {
     return {
       id: 'uses-webp-images',
-      title: 'Serve images in next-gen formats',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
-      description: 'Image formats like JPEG 2000, JPEG XR, and WebP often provide better ' +
-        'compression than PNG or JPEG, which means faster downloads and less data consumption. ' +
-        '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/webp).',
       requiredArtifacts: ['OptimizedImages', 'devtoolsLogs'],
     };
   }
@@ -72,9 +80,9 @@ class UsesWebPImages extends ByteEfficiencyAudit {
     /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
     const headings = [
       {key: 'url', valueType: 'thumbnail', label: ''},
-      {key: 'url', valueType: 'url', label: 'URL'},
-      {key: 'totalBytes', valueType: 'bytes', label: 'Original'},
-      {key: 'wastedBytes', valueType: 'bytes', label: 'Potential Savings'},
+      {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+      {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnSize)},
+      {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
     ];
 
     return {
@@ -86,3 +94,4 @@ class UsesWebPImages extends ByteEfficiencyAudit {
 }
 
 module.exports = UsesWebPImages;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/critical-request-chains.js b/lighthouse-core/audits/critical-request-chains.js
index 37c44c4e2..dd60bd7f0 100644
--- a/lighthouse-core/audits/critical-request-chains.js
+++ b/lighthouse-core/audits/critical-request-chains.js
@@ -6,7 +6,22 @@
 'use strict';
 
 const Audit = require('./audit');
-const Util = require('../report/html/renderer/util');
+const i18n = require('../lib/i18n');
+
+const UIStrings = {
+  title: 'Critical Request Chains',
+  description: 'The Critical Request Chains below show you what resources are ' +
+      'issued with a high priority. Consider reducing ' +
+      'the length of chains, reducing the download size of resources, or ' +
+      'deferring the download of unnecessary resources to improve page load. ' +
+      '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/critical-request-chains).',
+  displayValue: `{itemCount, plural,
+    =1 {1 chain found}
+    other {# chains found}
+    }`,
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class CriticalRequestChains extends Audit {
   /**
@@ -15,13 +30,9 @@ class CriticalRequestChains extends Audit {
   static get meta() {
     return {
       id: 'critical-request-chains',
-      title: 'Critical Request Chains',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
-      description: 'The Critical Request Chains below show you what resources are ' +
-          'issued with a high priority. Consider reducing ' +
-          'the length of chains, reducing the download size of resources, or ' +
-          'deferring the download of unnecessary resources to improve page load. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/critical-request-chains).',
       requiredArtifacts: ['devtoolsLogs', 'URL'],
     };
   }
@@ -184,7 +195,7 @@ class CriticalRequestChains extends Audit {
       return {
         rawValue: chainCount === 0,
         notApplicable: chainCount === 0,
-        displayValue: chainCount ? `${Util.formatNumber(chainCount)} chains found`: '',
+        displayValue: chainCount ? str_(UIStrings.displayValue, {itemCount: chainCount}) : '',
         extendedInfo: {
           value: {
             chains: flattenedChains,
@@ -193,7 +204,6 @@ class CriticalRequestChains extends Audit {
         },
         details: {
           type: 'criticalrequestchain',
-          header: {type: 'text', text: 'View critical network waterfall:'},
           chains: flattenedChains,
           longestChain,
         },
@@ -203,3 +213,4 @@ class CriticalRequestChains extends Audit {
 }
 
 module.exports = CriticalRequestChains;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/dobetterweb/dom-size.js b/lighthouse-core/audits/dobetterweb/dom-size.js
index d358a4628..95493d4cc 100644
--- a/lighthouse-core/audits/dobetterweb/dom-size.js
+++ b/lighthouse-core/audits/dobetterweb/dom-size.js
@@ -14,11 +14,33 @@
 
 const Audit = require('../audit');
 const Util = require('../../report/html/renderer/util.js');
+const i18n = require('../../lib/i18n');
 
 const MAX_DOM_NODES = 1500;
 const MAX_DOM_TREE_WIDTH = 60;
 const MAX_DOM_TREE_DEPTH = 32;
 
+const UIStrings = {
+  title: 'Avoids an excessive DOM size',
+  failureTitle: 'Uses an excessive DOM size',
+  description: 'Browser engineers recommend pages contain fewer than ' +
+    `~${MAX_DOM_NODES.toLocaleString()} DOM nodes. The sweet spot is a tree ` +
+    `depth < ${MAX_DOM_TREE_DEPTH} elements and fewer than ${MAX_DOM_TREE_WIDTH} ` +
+    'children/parent element. A large DOM can increase memory usage, cause longer ' +
+    '[style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), ' +
+    'and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://developers.google.com/web/tools/lighthouse/audits/dom-size).',
+  columnDOMNodes: 'Total DOM Nodes',
+  columnDOMDepth: 'Maximum DOM Depth',
+  columnDOMWidth: 'Maximum Children',
+  displayValue: `{itemCount, plural,
+    =1 {1 node}
+    other {# nodes}
+    }`,
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
+
+
 class DOMSize extends Audit {
   static get MAX_DOM_NODES() {
     return MAX_DOM_NODES;
@@ -30,14 +52,9 @@ class DOMSize extends Audit {
   static get meta() {
     return {
       id: 'dom-size',
-      title: 'Avoids an excessive DOM size',
-      failureTitle: 'Uses an excessive DOM size',
-      description: 'Browser engineers recommend pages contain fewer than ' +
-        `~${Util.formatNumber(DOMSize.MAX_DOM_NODES)} DOM nodes. The sweet spot is a tree ` +
-        `depth < ${MAX_DOM_TREE_DEPTH} elements and fewer than ${MAX_DOM_TREE_WIDTH} ` +
-        'children/parent element. A large DOM can increase memory usage, cause longer ' +
-        '[style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), ' +
-        'and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://developers.google.com/web/tools/lighthouse/audits/dom-size).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
       requiredArtifacts: ['DOMStats'],
     };
@@ -72,9 +89,9 @@ class DOMSize extends Audit {
     );
 
     const headings = [
-      {key: 'totalNodes', itemType: 'text', text: 'Total DOM Nodes'},
-      {key: 'depth', itemType: 'text', text: 'Maximum DOM Depth'},
-      {key: 'width', itemType: 'text', text: 'Maximum Children'},
+      {key: 'totalNodes', itemType: 'text', text: str_(UIStrings.columnDOMNodes)},
+      {key: 'depth', itemType: 'text', text: str_(UIStrings.columnDOMDepth)},
+      {key: 'width', itemType: 'text', text: str_(UIStrings.columnDOMWidth)},
     ];
 
     const items = [
@@ -99,7 +116,7 @@ class DOMSize extends Audit {
     return {
       score,
       rawValue: stats.totalDOMNodes,
-      displayValue: ['%d nodes', stats.totalDOMNodes],
+      displayValue: str_(UIStrings.displayValue, {itemCount: stats.totalDOMNodes}),
       extendedInfo: {
         value: items,
       },
@@ -109,3 +126,4 @@ class DOMSize extends Audit {
 }
 
 module.exports = DOMSize;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/font-display.js b/lighthouse-core/audits/font-display.js
index 5b557476e..0eec7e8ba 100644
--- a/lighthouse-core/audits/font-display.js
+++ b/lighthouse-core/audits/font-display.js
@@ -8,6 +8,17 @@
 const Audit = require('./audit');
 const NetworkRequest = require('../lib/network-request');
 const allowedFontFaceDisplays = ['block', 'fallback', 'optional', 'swap'];
+const i18n = require('../lib/i18n');
+
+const UIStrings = {
+  title: 'All text remains visible during webfont loads',
+  failureTitle: 'Text is invisible while webfonts are loading',
+  description: 'Leverage the font-display CSS feature to ensure text is user-visible while ' +
+    'webfonts are loading. ' +
+    '[Learn more](https://developers.google.com/web/updates/2016/02/font-display).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class FontDisplay extends Audit {
   /**
@@ -16,11 +27,9 @@ class FontDisplay extends Audit {
   static get meta() {
     return {
       id: 'font-display',
-      title: 'All text remains visible during webfont loads',
-      failureTitle: 'Text is invisible while webfonts are loading',
-      description: 'Leverage the font-display CSS feature to ensure text is user-visible while ' +
-        'webfonts are loading. ' +
-        '[Learn more](https://developers.google.com/web/updates/2016/02/font-display).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['devtoolsLogs', 'Fonts'],
     };
   }
@@ -54,17 +63,17 @@ class FontDisplay extends Audit {
         .map(record => {
           // In reality the end time should be calculated with paint time included
           // all browsers wait 3000ms to block text so we make sure 3000 is our max wasted time
-          const wastedTime = Math.min((record.endTime - record.startTime) * 1000, 3000);
+          const wastedMs = Math.min((record.endTime - record.startTime) * 1000, 3000);
 
           return {
             url: record.url,
-            wastedTime,
+            wastedMs,
           };
         });
 
       const headings = [
-        {key: 'url', itemType: 'url', text: 'Font URL'},
-        {key: 'wastedTime', itemType: 'ms', granularity: 1, text: 'Font download time'},
+        {key: 'url', itemType: 'url', text: str_(i18n.UIStrings.columnURL)},
+        {key: 'wastedMs', itemType: 'ms', text: str_(i18n.UIStrings.columnWastedMs)},
       ];
       const details = Audit.makeTableDetails(headings, results);
 
@@ -78,3 +87,4 @@ class FontDisplay extends Audit {
 }
 
 module.exports = FontDisplay;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/mainthread-work-breakdown.js b/lighthouse-core/audits/mainthread-work-breakdown.js
index b9503f2dd..c6ece16f6 100644
--- a/lighthouse-core/audits/mainthread-work-breakdown.js
+++ b/lighthouse-core/audits/mainthread-work-breakdown.js
@@ -11,8 +11,18 @@
 'use strict';
 
 const Audit = require('./audit');
-const Util = require('../report/html/renderer/util');
 const {taskGroups} = require('../lib/task-groups');
+const i18n = require('../lib/i18n');
+
+const UIStrings = {
+  title: 'Minimizes main thread work',
+  failureTitle: 'Has significant main thread work',
+  description: 'Consider reducing the time spent parsing, compiling and executing JS. ' +
+    'You may find delivering smaller JS payloads helps with this.',
+  columnCategory: 'Category',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class MainThreadWorkBreakdown extends Audit {
   /**
@@ -21,11 +31,10 @@ class MainThreadWorkBreakdown extends Audit {
   static get meta() {
     return {
       id: 'mainthread-work-breakdown',
-      title: 'Minimizes main thread work',
-      failureTitle: 'Has significant main thread work',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
-      description: 'Consider reducing the time spent parsing, compiling and executing JS. ' +
-        'You may find delivering smaller JS payloads helps with this.',
       requiredArtifacts: ['traces'],
     };
   }
@@ -89,8 +98,8 @@ class MainThreadWorkBreakdown extends Audit {
     });
 
     const headings = [
-      {key: 'groupLabel', itemType: 'text', text: 'Category'},
-      {key: 'duration', itemType: 'ms', granularity: 1, text: 'Time Spent'},
+      {key: 'groupLabel', itemType: 'text', text: str_(UIStrings.columnCategory)},
+      {key: 'duration', itemType: 'ms', granularity: 1, text: str_(i18n.UIStrings.columnTimeSpent)},
     ];
 
     results.sort((a, b) => categoryTotals[b.group] - categoryTotals[a.group]);
@@ -105,10 +114,11 @@ class MainThreadWorkBreakdown extends Audit {
     return {
       score,
       rawValue: totalExecutionTime,
-      displayValue: [Util.MS_DISPLAY_VALUE, totalExecutionTime],
+      displayValue: str_(i18n.UIStrings.ms, {timeInMs: totalExecutionTime}),
       details: tableDetails,
     };
   }
 }
 
 module.exports = MainThreadWorkBreakdown;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/metrics/estimated-input-latency.js b/lighthouse-core/audits/metrics/estimated-input-latency.js
index 09a564647..88b096c55 100644
--- a/lighthouse-core/audits/metrics/estimated-input-latency.js
+++ b/lighthouse-core/audits/metrics/estimated-input-latency.js
@@ -6,6 +6,17 @@
 'use strict';
 
 const Audit = require('../audit');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Estimated Input Latency',
+  description: 'The score above is an estimate of how long your app takes to respond to user ' +
+      'input, in milliseconds, during the busiest 5s window of page load. If your ' +
+      'latency is higher than 50 ms, users may perceive your app as laggy. ' +
+      '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class EstimatedInputLatency extends Audit {
   /**
@@ -14,11 +25,8 @@ class EstimatedInputLatency extends Audit {
   static get meta() {
     return {
       id: 'estimated-input-latency',
-      title: 'Estimated Input Latency',
-      description: 'The score above is an estimate of how long your app takes to respond to user ' +
-          'input, in milliseconds, during the busiest 5s window of page load. If your ' +
-          'latency is higher than 50 ms, users may perceive your app as laggy. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency).',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
       requiredArtifacts: ['traces'],
     };
@@ -56,9 +64,10 @@ class EstimatedInputLatency extends Audit {
         context.options.scoreMedian
       ),
       rawValue: metricResult.timing,
-      displayValue: ['%d\xa0ms', metricResult.timing],
+      displayValue: str_(i18n.UIStrings.ms, {timeInMs: metricResult.timing}),
     };
   }
 }
 
 module.exports = EstimatedInputLatency;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/metrics/first-contentful-paint.js b/lighthouse-core/audits/metrics/first-contentful-paint.js
index b8ff28582..b3772148d 100644
--- a/lighthouse-core/audits/metrics/first-contentful-paint.js
+++ b/lighthouse-core/audits/metrics/first-contentful-paint.js
@@ -6,7 +6,15 @@
 'use strict';
 
 const Audit = require('../audit');
-const Util = require('../../report/html/renderer/util.js');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'First Contentful Paint',
+  description: 'First contentful paint marks the time at which the first text/image is ' +
+      `painted. [Learn more](https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#first_paint_and_first_contentful_paint).`,
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class FirstContentfulPaint extends Audit {
   /**
@@ -15,9 +23,8 @@ class FirstContentfulPaint extends Audit {
   static get meta() {
     return {
       id: 'first-contentful-paint',
-      title: 'First Contentful Paint',
-      description: 'First contentful paint marks the time at which the first text/image is ' +
-          `painted. [Learn more](https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#first_paint_and_first_contentful_paint).`,
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
       requiredArtifacts: ['traces', 'devtoolsLogs'],
     };
@@ -54,9 +61,10 @@ class FirstContentfulPaint extends Audit {
         context.options.scoreMedian
       ),
       rawValue: metricResult.timing,
-      displayValue: [Util.MS_DISPLAY_VALUE, metricResult.timing],
+      displayValue: str_(i18n.UIStrings.ms, {timeInMs: metricResult.timing}),
     };
   }
 }
 
 module.exports = FirstContentfulPaint;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/metrics/first-cpu-idle.js b/lighthouse-core/audits/metrics/first-cpu-idle.js
index 62e9f1236..96a70636b 100644
--- a/lighthouse-core/audits/metrics/first-cpu-idle.js
+++ b/lighthouse-core/audits/metrics/first-cpu-idle.js
@@ -6,7 +6,16 @@
 'use strict';
 
 const Audit = require('../audit');
-const Util = require('../../report/html/renderer/util.js');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'First CPU Idle',
+  description: 'First CPU Idle marks the first time at which the page\'s main thread is ' +
+    'quiet enough to handle input. ' +
+    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-interactive).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class FirstCPUIdle extends Audit {
   /**
@@ -15,10 +24,8 @@ class FirstCPUIdle extends Audit {
   static get meta() {
     return {
       id: 'first-cpu-idle',
-      title: 'First CPU Idle',
-      description: 'First CPU Idle marks the first time at which the page\'s main thread is ' +
-          'quiet enough to handle input. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-interactive).',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
       requiredArtifacts: ['traces'],
     };
@@ -58,9 +65,10 @@ class FirstCPUIdle extends Audit {
         context.options.scoreMedian
       ),
       rawValue: metricResult.timing,
-      displayValue: [Util.MS_DISPLAY_VALUE, metricResult.timing],
+      displayValue: str_(i18n.UIStrings.ms, {timeInMs: metricResult.timing}),
     };
   }
 }
 
 module.exports = FirstCPUIdle;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/metrics/first-meaningful-paint.js b/lighthouse-core/audits/metrics/first-meaningful-paint.js
index 5a2380725..116503edf 100644
--- a/lighthouse-core/audits/metrics/first-meaningful-paint.js
+++ b/lighthouse-core/audits/metrics/first-meaningful-paint.js
@@ -6,7 +6,15 @@
 'use strict';
 
 const Audit = require('../audit');
-const Util = require('../../report/html/renderer/util');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'First Meaningful Paint',
+  description: 'First Meaningful Paint measures when the primary content of a page is ' +
+      'visible. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class FirstMeaningfulPaint extends Audit {
   /**
@@ -15,9 +23,8 @@ class FirstMeaningfulPaint extends Audit {
   static get meta() {
     return {
       id: 'first-meaningful-paint',
-      title: 'First Meaningful Paint',
-      description: 'First Meaningful Paint measures when the primary content of a page is ' +
-          'visible. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint).',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
       requiredArtifacts: ['traces'],
     };
@@ -57,9 +64,10 @@ class FirstMeaningfulPaint extends Audit {
         context.options.scoreMedian
       ),
       rawValue: metricResult.timing,
-      displayValue: [Util.MS_DISPLAY_VALUE, metricResult.timing],
+      displayValue: str_(i18n.UIStrings.ms, {timeInMs: metricResult.timing}),
     };
   }
 }
 
 module.exports = FirstMeaningfulPaint;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/metrics/speed-index.js b/lighthouse-core/audits/metrics/speed-index.js
index b64c8a40e..ed1119295 100644
--- a/lighthouse-core/audits/metrics/speed-index.js
+++ b/lighthouse-core/audits/metrics/speed-index.js
@@ -6,7 +6,15 @@
 'use strict';
 
 const Audit = require('../audit');
-const Util = require('../../report/html/renderer/util');
+const i18n = require('../../lib/i18n');
+
+const UIStrings = {
+  title: 'Speed Index',
+  description: 'Speed Index shows how quickly the contents of a page are visibly populated. ' +
+      '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/speed-index).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class SpeedIndex extends Audit {
   /**
@@ -15,9 +23,8 @@ class SpeedIndex extends Audit {
   static get meta() {
     return {
       id: 'speed-index',
-      title: 'Speed Index',
-      description: 'Speed Index shows how quickly the contents of a page are visibly populated. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/speed-index).',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
       requiredArtifacts: ['traces', 'devtoolsLogs'],
     };
@@ -56,9 +63,10 @@ class SpeedIndex extends Audit {
         context.options.scoreMedian
       ),
       rawValue: metricResult.timing,
-      displayValue: [Util.MS_DISPLAY_VALUE, metricResult.timing],
+      displayValue: str_(i18n.UIStrings.ms, {timeInMs: metricResult.timing}),
     };
   }
 }
 
 module.exports = SpeedIndex;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/redirects.js b/lighthouse-core/audits/redirects.js
index 891625822..80b7bfbd0 100644
--- a/lighthouse-core/audits/redirects.js
+++ b/lighthouse-core/audits/redirects.js
@@ -7,6 +7,14 @@
 
 const Audit = require('./audit');
 const UnusedBytes = require('./byte-efficiency/byte-efficiency-audit');
+const i18n = require('../lib/i18n');
+
+const UIStrings = {
+  title: 'Avoid multiple page redirects',
+  description: 'Redirects introduce additional delays before the page can be loaded. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/redirects).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class Redirects extends Audit {
   /**
@@ -15,9 +23,9 @@ class Redirects extends Audit {
   static get meta() {
     return {
       id: 'redirects',
-      title: 'Avoid multiple page redirects',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
-      description: 'Redirects introduce additional delays before the page can be loaded. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/redirects).',
       requiredArtifacts: ['URL', 'devtoolsLogs', 'traces'],
     };
   }
@@ -85,8 +93,8 @@ class Redirects extends Audit {
     }
 
     const headings = [
-      {key: 'url', itemType: 'text', text: 'Redirected URL'},
-      {key: 'wastedMs', itemType: 'ms', text: 'Time for Redirect'},
+      {key: 'url', itemType: 'text', text: str_(i18n.UIStrings.columnURL)},
+      {key: 'wastedMs', itemType: 'ms', text: str_(i18n.UIStrings.columnTimeSpent)},
     ];
     const summary = {wastedMs: totalWastedMs};
     const details = Audit.makeTableDetails(headings, pageRedirects, summary);
@@ -95,7 +103,9 @@ class Redirects extends Audit {
       // We award a passing grade if you only have 1 redirect
       score: redirectRequests.length <= 2 ? 1 : UnusedBytes.scoreForWastedMs(totalWastedMs),
       rawValue: totalWastedMs,
-      displayValue: ['%d\xa0ms', totalWastedMs],
+      displayValue: totalWastedMs ?
+        str_(i18n.UIStrings.displayValueMsSavings, {wastedMs: totalWastedMs}) :
+        '',
       extendedInfo: {
         value: {
           wastedMs: totalWastedMs,
@@ -107,3 +117,4 @@ class Redirects extends Audit {
 }
 
 module.exports = Redirects;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/time-to-first-byte.js b/lighthouse-core/audits/time-to-first-byte.js
index 3bbebbc0c..3a21d4854 100644
--- a/lighthouse-core/audits/time-to-first-byte.js
+++ b/lighthouse-core/audits/time-to-first-byte.js
@@ -6,6 +6,16 @@
 'use strict';
 
 const Audit = require('./audit');
+const i18n = require('../lib/i18n');
+
+const UIStrings = {
+  title: 'Keep server response times low (TTFB)',
+  description: 'Time To First Byte identifies the time at which your server sends a response.' +
+    ' [Learn more](https://developers.google.com/web/tools/lighthouse/audits/ttfb).',
+  displayValue: `Root document took {timeInMs, number, milliseconds}\xa0ms`,
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 const TTFB_THRESHOLD = 600;
 
@@ -16,9 +26,8 @@ class TTFBMetric extends Audit {
   static get meta() {
     return {
       id: 'time-to-first-byte',
-      title: 'Keep server response times low (TTFB)',
-      description: 'Time To First Byte identifies the time at which your server sends a response.' +
-        ' [Learn more](https://developers.google.com/web/tools/lighthouse/audits/ttfb).',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['devtoolsLogs', 'URL'],
     };
   }
@@ -41,6 +50,7 @@ class TTFBMetric extends Audit {
 
     const ttfb = TTFBMetric.caclulateTTFB(mainResource);
     const passed = ttfb < TTFB_THRESHOLD;
+    const displayValue = str_(UIStrings.displayValue, {timeInMs: ttfb});
 
     /** @type {LH.Result.Audit.OpportunityDetails} */
     const details = {
@@ -53,7 +63,7 @@ class TTFBMetric extends Audit {
     return {
       rawValue: ttfb,
       score: Number(passed),
-      displayValue: passed ? '' : ['Root document took %10d', ttfb],
+      displayValue,
       details,
       extendedInfo: {
         value: {
@@ -65,3 +75,4 @@ class TTFBMetric extends Audit {
 }
 
 module.exports = TTFBMetric;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/user-timings.js b/lighthouse-core/audits/user-timings.js
index f54f48321..68a732653 100644
--- a/lighthouse-core/audits/user-timings.js
+++ b/lighthouse-core/audits/user-timings.js
@@ -6,6 +6,20 @@
 'use strict';
 
 const Audit = require('./audit');
+const i18n = require('../lib/i18n');
+
+const UIStrings = {
+  title: 'User Timing marks and measures',
+  description: 'Consider instrumenting your app with the User Timing API to create custom, ' +
+      'real-world measurements of key user experiences. ' +
+      '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/user-timing).',
+  displayValue: `{itemCount, plural,
+    =1 {1 user timing}
+    other {# user timings}
+    }`,
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 /** @typedef {{name: string, isMark: true, args: LH.TraceEvent['args'], startTime: number}} MarkEvent */
 /** @typedef {{name: string, isMark: false, args: LH.TraceEvent['args'], startTime: number, endTime: number, duration: number}} MeasureEvent */
@@ -17,11 +31,9 @@ class UserTimings extends Audit {
   static get meta() {
     return {
       id: 'user-timings',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
-      title: 'User Timing marks and measures',
-      description: 'Consider instrumenting your app with the User Timing API to create custom, ' +
-          'real-world measurements of key user experiences. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/user-timing).',
       requiredArtifacts: ['traces'],
     };
   }
@@ -145,10 +157,7 @@ class UserTimings extends Audit {
       /** @type {LH.Audit.Product['displayValue']} */
       let displayValue;
       if (userTimings.length) {
-        displayValue = [
-          userTimings.length === 1 ? '%d user timing' : '%d user timings',
-          userTimings.length,
-        ];
+        displayValue = str_(UIStrings.displayValue, {itemCount: userTimings.length});
       }
 
       return {
@@ -166,3 +175,4 @@ class UserTimings extends Audit {
 }
 
 module.exports = UserTimings;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/uses-rel-preconnect.js b/lighthouse-core/audits/uses-rel-preconnect.js
index ff10ef3fc..859e2e05b 100644
--- a/lighthouse-core/audits/uses-rel-preconnect.js
+++ b/lighthouse-core/audits/uses-rel-preconnect.js
@@ -8,6 +8,8 @@
 
 const Audit = require('./audit');
 const UnusedBytes = require('./byte-efficiency/byte-efficiency-audit');
+const i18n = require('../lib/i18n');
+
 // Preconnect establishes a "clean" socket. Chrome's socket manager will keep an unused socket
 // around for 10s. Meaning, the time delta between processing preconnect a request should be <10s,
 // otherwise it's wasted. We add a 5s margin so we are sure to capture all key requests.
@@ -16,6 +18,15 @@ const PRECONNECT_SOCKET_MAX_IDLE = 15;
 
 const IGNORE_THRESHOLD_IN_MS = 50;
 
+const UIStrings = {
+  title: 'Avoid multiple, costly round trips to any origin',
+  description:
+    'Consider adding preconnect or dns-prefetch resource hints to establish early ' +
+    `connections to important third-party origins. [Learn more](https://developers.google.com/web/fundamentals/performance/resource-prioritization#preconnect).`,
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
+
 class UsesRelPreconnectAudit extends Audit {
   /**
    * @return {LH.Audit.Meta}
@@ -23,10 +34,8 @@ class UsesRelPreconnectAudit extends Audit {
   static get meta() {
     return {
       id: 'uses-rel-preconnect',
-      title: 'Avoid multiple, costly round trips to any origin',
-      description:
-        'Consider adding preconnect or dns-prefetch resource hints to establish early ' +
-        `connections to important third-party origins. [Learn more](https://developers.google.com/web/fundamentals/performance/resource-prioritization#preconnect).`,
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['devtoolsLogs', 'URL'],
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
     };
@@ -152,15 +161,16 @@ class UsesRelPreconnectAudit extends Audit {
 
     /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
     const headings = [
-      {key: 'url', valueType: 'url', label: 'Origin'},
-      {key: 'wastedMs', valueType: 'timespanMs', label: 'Potential Savings'},
+      {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+      {key: 'wastedMs', valueType: 'timespanMs', label: str_(i18n.UIStrings.columnWastedMs)},
     ];
+
     const details = Audit.makeOpportunityDetails(headings, results, maxWasted);
 
     return {
       score: UnusedBytes.scoreForWastedMs(maxWasted),
       rawValue: maxWasted,
-      displayValue: ['Potential savings of %10d\xa0ms', maxWasted],
+      displayValue: str_(i18n.UIStrings.displayValueMsSavings, {wastedMs: maxWasted}),
       extendedInfo: {
         value: results,
       },
@@ -170,3 +180,4 @@ class UsesRelPreconnectAudit extends Audit {
 }
 
 module.exports = UsesRelPreconnectAudit;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/uses-rel-preload.js b/lighthouse-core/audits/uses-rel-preload.js
index 8622629d7..c12d70680 100644
--- a/lighthouse-core/audits/uses-rel-preload.js
+++ b/lighthouse-core/audits/uses-rel-preload.js
@@ -8,6 +8,16 @@
 const URL = require('../lib/url-shim');
 const Audit = require('./audit');
 const UnusedBytes = require('./byte-efficiency/byte-efficiency-audit');
+const i18n = require('../lib/i18n');
+
+const UIStrings = {
+  title: 'Preload key requests',
+  description: 'Consider using <link rel=preload> to prioritize fetching late-discovered ' +
+    'resources sooner. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/preload).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
+
 const THRESHOLD_IN_MS = 100;
 
 class UsesRelPreloadAudit extends Audit {
@@ -17,9 +27,8 @@ class UsesRelPreloadAudit extends Audit {
   static get meta() {
     return {
       id: 'uses-rel-preload',
-      title: 'Preload key requests',
-      description: 'Consider using <link rel=preload> to prioritize fetching late-discovered ' +
-        'resources sooner. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/preload).',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['devtoolsLogs', 'traces', 'URL'],
       scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
     };
@@ -185,15 +194,15 @@ class UsesRelPreloadAudit extends Audit {
 
     /** @type {LH.Result.Audit.OpportunityDetails['headings']} */
     const headings = [
-      {key: 'url', valueType: 'url', label: 'URL'},
-      {key: 'wastedMs', valueType: 'timespanMs', label: 'Potential Savings'},
+      {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
+      {key: 'wastedMs', valueType: 'timespanMs', label: str_(i18n.UIStrings.columnWastedMs)},
     ];
     const details = Audit.makeOpportunityDetails(headings, results, wastedMs);
 
     return {
       score: UnusedBytes.scoreForWastedMs(wastedMs),
       rawValue: wastedMs,
-      displayValue: ['Potential savings of %10d\xa0ms', wastedMs],
+      displayValue: str_(i18n.UIStrings.displayValueMsSavings, {wastedMs}),
       extendedInfo: {
         value: results,
       },
@@ -203,3 +212,4 @@ class UsesRelPreloadAudit extends Audit {
 }
 
 module.exports = UsesRelPreloadAudit;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/lib/i18n.js b/lighthouse-core/lib/i18n.js
index bb18a8814..a666b8de8 100644
--- a/lighthouse-core/lib/i18n.js
+++ b/lighthouse-core/lib/i18n.js
@@ -35,14 +35,21 @@ const LH_ROOT = path.join(__dirname, '../../');
 
 const UIStrings = {
   ms: '{timeInMs, number, milliseconds}\xa0ms',
+  displayValueByteSavings: 'Potential savings of {wastedBytes, number, bytes}\xa0KB',
+  displayValueMsSavings: 'Potential savings of {wastedMs, number, milliseconds}\xa0ms',
   columnURL: 'URL',
   columnSize: 'Size (KB)',
+  columnCacheTTL: 'Cache TTL',
+  columnWastedBytes: 'Potential Savings (KB)',
   columnWastedMs: 'Potential Savings (ms)',
-  displayValueWastedMs: 'Potential savings of {wastedMs, number, milliseconds}\xa0ms',
+  columnTimeSpent: 'Time Spent',
 };
 
 const formats = {
   number: {
+    bytes: {
+      maximumFractionDigits: 0,
+    },
     milliseconds: {
       maximumFractionDigits: 0,
     },
@@ -58,14 +65,25 @@ function _preprocessMessageValues(icuMessage, values) {
 
   const clonedValues = JSON.parse(JSON.stringify(values));
   const parsed = MessageParser.parse(icuMessage);
+  // Throw an error if a message's value isn't provided
+  parsed.elements
+    .filter(el => el.type === 'argumentElement')
+    .forEach(el => {
+      if (el.id && (el.id in values) === false) {
+        throw new Error('ICU Message contains a value reference that wasn\'t provided');
+      }
+    });
+
   // Round all milliseconds to the nearest 10
   parsed.elements
     .filter(el => el.format && el.format.style === 'milliseconds')
+    // @ts-ignore - el.id is always defined when el.format is defined
     .forEach(el => (clonedValues[el.id] = Math.round(clonedValues[el.id] / 10) * 10));
 
   // Replace all the bytes with KB
   parsed.elements
     .filter(el => el.format && el.format.style === 'bytes')
+    // @ts-ignore - el.id is always defined when el.format is defined
     .forEach(el => (clonedValues[el.id] = clonedValues[el.id] / 1024));
 
   return clonedValues;
diff --git a/lighthouse-core/lib/locales/en-US.json b/lighthouse-core/lib/locales/en-US.json
index 49ce49628..4aa3f75c2 100644
--- a/lighthouse-core/lib/locales/en-US.json
+++ b/lighthouse-core/lib/locales/en-US.json
@@ -1,16 +1,238 @@
 {
+  "lighthouse-core/audits/bootup-time.js | title": {
+    "message": "JavaScript boot-up time"
+  },
+  "lighthouse-core/audits/bootup-time.js | failureTitle": {
+    "message": "JavaScript boot-up time is too high"
+  },
+  "lighthouse-core/audits/bootup-time.js | description": {
+    "message": "Consider reducing the time spent parsing, compiling, and executing JS. You may find delivering smaller JS payloads helps with this. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/bootup)."
+  },
+  "lighthouse-core/audits/bootup-time.js | columnTotal": {
+    "message": "Total"
+  },
+  "lighthouse-core/audits/bootup-time.js | columnScriptEval": {
+    "message": "Script Evaluation"
+  },
+  "lighthouse-core/audits/bootup-time.js | columnScriptParse": {
+    "message": "Script Parse"
+  },
+  "lighthouse-core/audits/bootup-time.js | chromeExtensionsWarning": {
+    "message": "Chrome extensions negatively affected this page's load performance. Try auditing the page in incognito mode or from a clean Chrome profile."
+  },
+  "lighthouse-core/audits/byte-efficiency/efficient-animated-content.js | title": {
+    "message": "Use video formats for animated content"
+  },
+  "lighthouse-core/audits/byte-efficiency/efficient-animated-content.js | description": {
+    "message": "Large GIFs are inefficient for delivering animated content. Consider using MPEG4/WebM videos for animations and PNG/WebP for static images instead of GIF to save network bytes. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/replace-animated-gifs-with-video/)"
+  },
+  "lighthouse-core/audits/byte-efficiency/offscreen-images.js | title": {
+    "message": "Defer offscreen images"
+  },
+  "lighthouse-core/audits/byte-efficiency/offscreen-images.js | description": {
+    "message": "Consider lazy-loading offscreen and hidden images after all critical resources have finished loading to lower time to interactive. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/offscreen-images)."
+  },
   "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | title": {
     "message": "Eliminate render-blocking resources"
   },
   "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | description": {
     "message": "Resources are blocking the first paint of your page. Consider delivering critical JS/CSS inline and deferring all non-critical JS/styles. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources)."
   },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | title": {
+    "message": "Avoids enormous network payloads"
+  },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | failureTitle": {
+    "message": "Has enormous network payloads"
+  },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | description": {
+    "message": "Large network payloads cost users real money and are highly correlated with long load times. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/network-payloads)."
+  },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | displayValue": {
+    "message": "Total size was {totalBytes, number, bytes} KB"
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-css.js | title": {
+    "message": "Minify CSS"
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-css.js | description": {
+    "message": "Minifying CSS files can reduce network payload sizes. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/minify-css)."
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-javascript.js | title": {
+    "message": "Minify JavaScript"
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-javascript.js | description": {
+    "message": "Minifying JavaScript files can reduce payload sizes and script parse time. [Learn more](https://developers.google.com/speed/docs/insights/MinifyResources)."
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-css-rules.js | title": {
+    "message": "Defer unused CSS"
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-css-rules.js | description": {
+    "message": "Remove unused rules from stylesheets to reduce unnecessary bytes consumed by network activity. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/unused-css)."
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-javascript.js | title": {
+    "message": "Unused JavaScript"
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-javascript.js | description": {
+    "message": "Remove unused JavaScript to reduce bytes consumed by network activity."
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | title": {
+    "message": "Uses efficient cache policy on static assets"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | failureTitle": {
+    "message": "Uses inefficient cache policy on static assets"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | description": {
+    "message": "A long cache lifetime can speed up repeat visits to your page. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/cache-policy)."
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | displayValue": {
+    "message": "{itemCount, plural,\n    =1 {1 resource found}\n    other {# resources found}\n    }"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | title": {
+    "message": "Efficiently encode images"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | description": {
+    "message": "Optimized images load faster and consume less cellular data. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/optimize-images)."
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | title": {
+    "message": "Properly size images"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | description": {
+    "message": "Serve images that are appropriately-sized to save cellular data and improve load time. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/oversized-images)."
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-text-compression.js | title": {
+    "message": "Enable text compression"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-text-compression.js | description": {
+    "message": "Text-based responses should be served with compression (gzip, deflate or brotli) to minimize total network bytes. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/text-compression)."
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-webp-images.js | title": {
+    "message": "Serve images in next-gen formats"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-webp-images.js | description": {
+    "message": "Image formats like JPEG 2000, JPEG XR, and WebP often provide better compression than PNG or JPEG, which means faster downloads and less data consumption. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/webp)."
+  },
+  "lighthouse-core/audits/critical-request-chains.js | title": {
+    "message": "Critical Request Chains"
+  },
+  "lighthouse-core/audits/critical-request-chains.js | description": {
+    "message": "The Critical Request Chains below show you what resources are issued with a high priority. Consider reducing the length of chains, reducing the download size of resources, or deferring the download of unnecessary resources to improve page load. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/critical-request-chains)."
+  },
+  "lighthouse-core/audits/critical-request-chains.js | displayValue": {
+    "message": "{itemCount, plural,\n    =1 {1 chain found}\n    other {# chains found}\n    }"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | title": {
+    "message": "Avoids an excessive DOM size"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | failureTitle": {
+    "message": "Uses an excessive DOM size"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | description": {
+    "message": "Browser engineers recommend pages contain fewer than ~1,500 DOM nodes. The sweet spot is a tree depth < 32 elements and fewer than 60 children/parent element. A large DOM can increase memory usage, cause longer [style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://developers.google.com/web/tools/lighthouse/audits/dom-size)."
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | columnDOMNodes": {
+    "message": "Total DOM Nodes"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | columnDOMDepth": {
+    "message": "Maximum DOM Depth"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | columnDOMWidth": {
+    "message": "Maximum Children"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | displayValue": {
+    "message": "{itemCount, plural,\n    =1 {1 node}\n    other {# nodes}\n    }"
+  },
+  "lighthouse-core/audits/font-display.js | title": {
+    "message": "All text remains visible during webfont loads"
+  },
+  "lighthouse-core/audits/font-display.js | failureTitle": {
+    "message": "Text is invisible while webfonts are loading"
+  },
+  "lighthouse-core/audits/font-display.js | description": {
+    "message": "Leverage the font-display CSS feature to ensure text is user-visible while webfonts are loading. [Learn more](https://developers.google.com/web/updates/2016/02/font-display)."
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | title": {
+    "message": "Minimizes main thread work"
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | failureTitle": {
+    "message": "Has significant main thread work"
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | description": {
+    "message": "Consider reducing the time spent parsing, compiling and executing JS. You may find delivering smaller JS payloads helps with this."
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | columnCategory": {
+    "message": "Category"
+  },
+  "lighthouse-core/audits/metrics/estimated-input-latency.js | title": {
+    "message": "Estimated Input Latency"
+  },
+  "lighthouse-core/audits/metrics/estimated-input-latency.js | description": {
+    "message": "The score above is an estimate of how long your app takes to respond to user input, in milliseconds, during the busiest 5s window of page load. If your latency is higher than 50 ms, users may perceive your app as laggy. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency)."
+  },
+  "lighthouse-core/audits/metrics/first-contentful-paint.js | title": {
+    "message": "First Contentful Paint"
+  },
+  "lighthouse-core/audits/metrics/first-contentful-paint.js | description": {
+    "message": "First contentful paint marks the time at which the first text/image is painted. [Learn more](https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#first_paint_and_first_contentful_paint)."
+  },
+  "lighthouse-core/audits/metrics/first-cpu-idle.js | title": {
+    "message": "First CPU Idle"
+  },
+  "lighthouse-core/audits/metrics/first-cpu-idle.js | description": {
+    "message": "First CPU Idle marks the first time at which the page's main thread is quiet enough to handle input. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-interactive)."
+  },
+  "lighthouse-core/audits/metrics/first-meaningful-paint.js | title": {
+    "message": "First Meaningful Paint"
+  },
+  "lighthouse-core/audits/metrics/first-meaningful-paint.js | description": {
+    "message": "First Meaningful Paint measures when the primary content of a page is visible. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint)."
+  },
   "lighthouse-core/audits/metrics/interactive.js | title": {
     "message": "Time to Interactive"
   },
   "lighthouse-core/audits/metrics/interactive.js | description": {
     "message": "Interactive marks the time at which the page is fully interactive. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive)."
   },
+  "lighthouse-core/audits/metrics/speed-index.js | title": {
+    "message": "Speed Index"
+  },
+  "lighthouse-core/audits/metrics/speed-index.js | description": {
+    "message": "Speed Index shows how quickly the contents of a page are visibly populated. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/speed-index)."
+  },
+  "lighthouse-core/audits/redirects.js | title": {
+    "message": "Avoid multiple page redirects"
+  },
+  "lighthouse-core/audits/redirects.js | description": {
+    "message": "Redirects introduce additional delays before the page can be loaded. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/redirects)."
+  },
+  "lighthouse-core/audits/time-to-first-byte.js | title": {
+    "message": "Keep server response times low (TTFB)"
+  },
+  "lighthouse-core/audits/time-to-first-byte.js | description": {
+    "message": "Time To First Byte identifies the time at which your server sends a response. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/ttfb)."
+  },
+  "lighthouse-core/audits/time-to-first-byte.js | displayValue": {
+    "message": "Root document took {timeInMs, number, milliseconds} ms"
+  },
+  "lighthouse-core/audits/user-timings.js | title": {
+    "message": "User Timing marks and measures"
+  },
+  "lighthouse-core/audits/user-timings.js | description": {
+    "message": "Consider instrumenting your app with the User Timing API to create custom, real-world measurements of key user experiences. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/user-timing)."
+  },
+  "lighthouse-core/audits/user-timings.js | displayValue": {
+    "message": "{itemCount, plural,\n    =1 {1 user timing}\n    other {# user timings}\n    }"
+  },
+  "lighthouse-core/audits/uses-rel-preconnect.js | title": {
+    "message": "Avoid multiple, costly round trips to any origin"
+  },
+  "lighthouse-core/audits/uses-rel-preconnect.js | description": {
+    "message": "Consider adding preconnect or dns-prefetch resource hints to establish early connections to important third-party origins. [Learn more](https://developers.google.com/web/fundamentals/performance/resource-prioritization#preconnect)."
+  },
+  "lighthouse-core/audits/uses-rel-preload.js | title": {
+    "message": "Preload key requests"
+  },
+  "lighthouse-core/audits/uses-rel-preload.js | description": {
+    "message": "Consider using <link rel=preload> to prioritize fetching late-discovered resources sooner. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/preload)."
+  },
   "lighthouse-core/config/default-config.js | performanceCategoryTitle": {
     "message": "Performance"
   },
@@ -32,17 +254,29 @@
   "lighthouse-core/lib/i18n.js | ms": {
     "message": "{timeInMs, number, milliseconds} ms"
   },
+  "lighthouse-core/lib/i18n.js | displayValueByteSavings": {
+    "message": "Potential savings of {wastedBytes, number, bytes} KB"
+  },
+  "lighthouse-core/lib/i18n.js | displayValueMsSavings": {
+    "message": "Potential savings of {wastedMs, number, milliseconds} ms"
+  },
   "lighthouse-core/lib/i18n.js | columnURL": {
     "message": "URL"
   },
   "lighthouse-core/lib/i18n.js | columnSize": {
     "message": "Size (KB)"
   },
+  "lighthouse-core/lib/i18n.js | columnCacheTTL": {
+    "message": "Cache TTL"
+  },
+  "lighthouse-core/lib/i18n.js | columnWastedBytes": {
+    "message": "Potential Savings (KB)"
+  },
   "lighthouse-core/lib/i18n.js | columnWastedMs": {
     "message": "Potential Savings (ms)"
   },
-  "lighthouse-core/lib/i18n.js | displayValueWastedMs": {
-    "message": "Potential savings of {wastedMs, number, milliseconds} ms"
+  "lighthouse-core/lib/i18n.js | columnTimeSpent": {
+    "message": "Time Spent"
   },
   "lighthouse-core/report/html/renderer/util.js | varianceDisclaimer": {
     "message": "Values are estimated and may vary."
diff --git a/lighthouse-core/lib/locales/en-XA.json b/lighthouse-core/lib/locales/en-XA.json
index 3693b8ba1..7bdde7b4a 100644
--- a/lighthouse-core/lib/locales/en-XA.json
+++ b/lighthouse-core/lib/locales/en-XA.json
@@ -1,16 +1,238 @@
 {
+  "lighthouse-core/audits/bootup-time.js | title": {
+    "message": "Ĵáv̂áŜćr̂íp̂t́ b̂óôt́-ûṕ t̂ím̂é"
+  },
+  "lighthouse-core/audits/bootup-time.js | failureTitle": {
+    "message": "Ĵáv̂áŜćr̂íp̂t́ b̂óôt́-ûṕ t̂ím̂é îś t̂óô h́îǵĥ"
+  },
+  "lighthouse-core/audits/bootup-time.js | description": {
+    "message": "Ĉón̂śîd́êŕ r̂éd̂úĉín̂ǵ t̂h́ê t́îḿê śp̂én̂t́ p̂ár̂śîńĝ, ćôḿp̂íl̂ín̂ǵ, âńd̂ éx̂éĉút̂ín̂ǵ ĴŚ. Ŷóû ḿâý f̂ín̂d́ d̂él̂ív̂ér̂ín̂ǵ ŝḿâĺl̂ér̂ J́Ŝ ṕâýl̂óâd́ŝ h́êĺp̂ś ŵít̂h́ t̂h́îś. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/t̂óôĺŝ/ĺîǵĥt́ĥóûśê/áûd́ît́ŝ/b́ôót̂úp̂)."
+  },
+  "lighthouse-core/audits/bootup-time.js | columnTotal": {
+    "message": "T̂ót̂ál̂"
+  },
+  "lighthouse-core/audits/bootup-time.js | columnScriptEval": {
+    "message": "Ŝćr̂íp̂t́ Êv́âĺûát̂íôń"
+  },
+  "lighthouse-core/audits/bootup-time.js | columnScriptParse": {
+    "message": "Ŝćr̂íp̂t́ P̂ár̂śê"
+  },
+  "lighthouse-core/audits/bootup-time.js | chromeExtensionsWarning": {
+    "message": "Ĉh́r̂óm̂é êx́t̂én̂śîón̂ś n̂éĝát̂ív̂él̂ý âf́f̂éĉt́êd́ t̂h́îś p̂áĝé'ŝ ĺôád̂ ṕêŕf̂ór̂ḿâńĉé. T̂ŕŷ áûd́ît́îńĝ t́ĥé p̂áĝé îń îńĉóĝńît́ô ḿôd́ê ór̂ f́r̂óm̂ á ĉĺêán̂ Ćĥŕôḿê ṕr̂óf̂íl̂é."
+  },
+  "lighthouse-core/audits/byte-efficiency/efficient-animated-content.js | title": {
+    "message": "Ûśê v́îd́êó f̂ór̂ḿât́ŝ f́ôŕ âńîḿât́êd́ ĉón̂t́êńt̂"
+  },
+  "lighthouse-core/audits/byte-efficiency/efficient-animated-content.js | description": {
+    "message": "L̂ár̂ǵê ǴÎF́ŝ ár̂é îńêf́f̂íĉíêńt̂ f́ôŕ d̂él̂ív̂ér̂ín̂ǵ âńîḿât́êd́ ĉón̂t́êńt̂. Ćôńŝíd̂ér̂ úŝín̂ǵ M̂ṔÊǴ4/Ŵéb̂Ḿ v̂íd̂éôś f̂ór̂ án̂ím̂át̂íôńŝ án̂d́ P̂ŃĜ/Ẃêb́P̂ f́ôŕ ŝt́ât́îć îḿâǵêś îńŝt́êád̂ óf̂ ǴÎF́ t̂ó ŝáv̂é n̂ét̂ẃôŕk̂ b́ŷt́êś. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/f̂ún̂d́âḿêńt̂ál̂ś/p̂ér̂f́ôŕm̂án̂ćê/óp̂t́îḿîźîńĝ-ćôńt̂én̂t́-êf́f̂íĉíêńĉý/r̂ép̂ĺâćê-án̂ím̂át̂éd̂-ǵîf́ŝ-ẃît́ĥ-v́îd́êó/)"
+  },
+  "lighthouse-core/audits/byte-efficiency/offscreen-images.js | title": {
+    "message": "D̂éf̂ér̂ óf̂f́ŝćr̂éêń îḿâǵêś"
+  },
+  "lighthouse-core/audits/byte-efficiency/offscreen-images.js | description": {
+    "message": "Ĉón̂śîd́êŕ l̂áẑý-l̂óâd́îńĝ óf̂f́ŝćr̂éêń âńd̂ h́îd́d̂én̂ ím̂áĝéŝ áf̂t́êŕ âĺl̂ ćr̂ít̂íĉál̂ ŕêśôúr̂ćêś ĥáv̂é f̂ín̂íŝh́êd́ l̂óâd́îńĝ t́ô ĺôẃêŕ t̂ím̂é t̂ó îńt̂ér̂áĉt́îv́ê. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/ôf́f̂śĉŕêén̂-ím̂áĝéŝ)."
+  },
   "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | title": {
     "message": "Êĺîḿîńât́ê ŕêńd̂ér̂-b́l̂óĉḱîńĝ ŕêśôúr̂ćêś"
   },
   "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | description": {
     "message": "R̂éŝóûŕĉéŝ ár̂é b̂ĺôćk̂ín̂ǵ t̂h́ê f́îŕŝt́ p̂áîńt̂ óf̂ ýôúr̂ ṕâǵê. Ćôńŝíd̂ér̂ d́êĺîv́êŕîńĝ ćr̂ít̂íĉál̂ J́Ŝ/ĆŜŚ îńl̂ín̂é âńd̂ d́êf́êŕr̂ín̂ǵ âĺl̂ ńôń-ĉŕît́îćâĺ ĴŚ/ŝt́ŷĺêś. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/t̂óôĺŝ/ĺîǵĥt́ĥóûśê/áûd́ît́ŝ/b́l̂óĉḱîńĝ-ŕêśôúr̂ćêś)."
   },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | title": {
+    "message": "Âv́ôíd̂ś êńôŕm̂óûś n̂ét̂ẃôŕk̂ ṕâýl̂óâd́ŝ"
+  },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | failureTitle": {
+    "message": "Ĥáŝ én̂ór̂ḿôúŝ ńêt́ŵór̂ḱ p̂áŷĺôád̂ś"
+  },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | description": {
+    "message": "L̂ár̂ǵê ńêt́ŵór̂ḱ p̂áŷĺôád̂ś ĉóŝt́ ûśêŕŝ ŕêál̂ ḿôńêý âńd̂ ár̂é ĥíĝh́l̂ý ĉór̂ŕêĺât́êd́ ŵít̂h́ l̂ón̂ǵ l̂óâd́ t̂ím̂éŝ. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/n̂ét̂ẃôŕk̂-ṕâýl̂óâd́ŝ)."
+  },
+  "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | displayValue": {
+    "message": "T̂ót̂ál̂ śîźê ẃâś {totalBytes, number, bytes} K̂B́"
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-css.js | title": {
+    "message": "M̂ín̂íf̂ý ĈŚŜ"
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-css.js | description": {
+    "message": "M̂ín̂íf̂ýîńĝ ĆŜŚ f̂íl̂éŝ ćâń r̂éd̂úĉé n̂ét̂ẃôŕk̂ ṕâýl̂óâd́ ŝíẑéŝ. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/m̂ín̂íf̂ý-ĉśŝ)."
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-javascript.js | title": {
+    "message": "M̂ín̂íf̂ý Ĵáv̂áŜćr̂íp̂t́"
+  },
+  "lighthouse-core/audits/byte-efficiency/unminified-javascript.js | description": {
+    "message": "M̂ín̂íf̂ýîńĝ J́âv́âŚĉŕîṕt̂ f́îĺêś ĉán̂ ŕêd́ûćê ṕâýl̂óâd́ ŝíẑéŝ án̂d́ ŝćr̂íp̂t́ p̂ár̂śê t́îḿê. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŝṕêéd̂/d́ôćŝ/ín̂śîǵĥt́ŝ/Ḿîńîf́ŷŔêśôúr̂ćêś)."
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-css-rules.js | title": {
+    "message": "D̂éf̂ér̂ ún̂úŝéd̂ ĆŜŚ"
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-css-rules.js | description": {
+    "message": "R̂ém̂óv̂é ûńûśêd́ r̂úl̂éŝ f́r̂óm̂ śt̂ýl̂éŝh́êét̂ś t̂ó r̂éd̂úĉé ûńn̂éĉéŝśâŕŷ b́ŷt́êś ĉón̂śûḿêd́ b̂ý n̂ét̂ẃôŕk̂ áĉt́îv́ît́ŷ. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/ûńûśêd́-ĉśŝ)."
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-javascript.js | title": {
+    "message": "Ûńûśêd́ Ĵáv̂áŜćr̂íp̂t́"
+  },
+  "lighthouse-core/audits/byte-efficiency/unused-javascript.js | description": {
+    "message": "R̂ém̂óv̂é ûńûśêd́ Ĵáv̂áŜćr̂íp̂t́ t̂ó r̂éd̂úĉé b̂ýt̂éŝ ćôńŝúm̂éd̂ b́ŷ ńêt́ŵór̂ḱ âćt̂ív̂ít̂ý."
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | title": {
+    "message": "Ûśêś êf́f̂íĉíêńt̂ ćâćĥé p̂ól̂íĉý ôń ŝt́ât́îć âśŝét̂ś"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | failureTitle": {
+    "message": "Ûśêś îńêf́f̂íĉíêńt̂ ćâćĥé p̂ól̂íĉý ôń ŝt́ât́îć âśŝét̂ś"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | description": {
+    "message": "Â ĺôńĝ ćâćĥé l̂íf̂ét̂ím̂é ĉán̂ śp̂éêd́ ûṕ r̂ép̂éât́ v̂íŝít̂ś t̂ó ŷóûŕ p̂áĝé. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/t̂óôĺŝ/ĺîǵĥt́ĥóûśê/áûd́ît́ŝ/ćâćĥé-p̂ól̂íĉý)."
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | displayValue": {
+    "message": "{itemCount, plural,\n    =1 {1 resource found}\n    other {# resources found}\n    }"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | title": {
+    "message": "Êf́f̂íĉíêńt̂ĺŷ én̂ćôd́ê ím̂áĝéŝ"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | description": {
+    "message": "Ôṕt̂ím̂íẑéd̂ ím̂áĝéŝ ĺôád̂ f́âśt̂ér̂ án̂d́ ĉón̂śûḿê ĺêśŝ ćêĺl̂úl̂ár̂ d́ât́â. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/ôṕt̂ím̂íẑé-îḿâǵêś)."
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | title": {
+    "message": "P̂ŕôṕêŕl̂ý ŝíẑé îḿâǵêś"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | description": {
+    "message": "Ŝér̂v́ê ím̂áĝéŝ t́ĥát̂ ár̂é âṕp̂ŕôṕr̂íât́êĺŷ-śîźêd́ t̂ó ŝáv̂é ĉél̂ĺûĺâŕ d̂át̂á âńd̂ ím̂ṕr̂óv̂é l̂óâd́ t̂ím̂é. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/t̂óôĺŝ/ĺîǵĥt́ĥóûśê/áûd́ît́ŝ/óv̂ér̂śîźêd́-îḿâǵêś)."
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-text-compression.js | title": {
+    "message": "Êńâb́l̂é t̂éx̂t́ ĉóm̂ṕr̂éŝśîón̂"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-text-compression.js | description": {
+    "message": "T̂éx̂t́-b̂áŝéd̂ ŕêśp̂ón̂śêś ŝh́ôúl̂d́ b̂é ŝér̂v́êd́ ŵít̂h́ ĉóm̂ṕr̂éŝśîón̂ (ǵẑíp̂, d́êf́l̂át̂é ôŕ b̂ŕôt́l̂í) t̂ó m̂ín̂ím̂íẑé t̂ót̂ál̂ ńêt́ŵór̂ḱ b̂ýt̂éŝ. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/t̂éx̂t́-ĉóm̂ṕr̂éŝśîón̂)."
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-webp-images.js | title": {
+    "message": "Ŝér̂v́ê ím̂áĝéŝ ín̂ ńêx́t̂-ǵêń f̂ór̂ḿât́ŝ"
+  },
+  "lighthouse-core/audits/byte-efficiency/uses-webp-images.js | description": {
+    "message": "Îḿâǵê f́ôŕm̂át̂ś l̂ík̂é ĴṔÊǴ 2000, ĴṔÊǴ X̂Ŕ, âńd̂ Ẃêb́P̂ óf̂t́êń p̂ŕôv́îd́ê b́êt́t̂ér̂ ćôḿp̂ŕêśŝíôń t̂h́âń P̂ŃĜ ór̂ J́P̂ÉĜ, ẃĥíĉh́ m̂éâńŝ f́âśt̂ér̂ d́ôẃn̂ĺôád̂ś âńd̂ ĺêśŝ d́ât́â ćôńŝúm̂ṕt̂íôń. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/t̂óôĺŝ/ĺîǵĥt́ĥóûśê/áûd́ît́ŝ/ẃêb́p̂)."
+  },
+  "lighthouse-core/audits/critical-request-chains.js | title": {
+    "message": "Ĉŕît́îćâĺ R̂éq̂úêśt̂ Ćĥáîńŝ"
+  },
+  "lighthouse-core/audits/critical-request-chains.js | description": {
+    "message": "T̂h́ê Ćr̂ít̂íĉál̂ Ŕêq́ûéŝt́ Ĉh́âín̂ś b̂él̂óŵ śĥóŵ ýôú ŵh́ât́ r̂éŝóûŕĉéŝ ár̂é îśŝúêd́ ŵít̂h́ â h́îǵĥ ṕr̂íôŕît́ŷ. Ćôńŝíd̂ér̂ ŕêd́ûćîńĝ t́ĥé l̂én̂ǵt̂h́ ôf́ ĉh́âín̂ś, r̂éd̂úĉín̂ǵ t̂h́ê d́ôẃn̂ĺôád̂ śîźê óf̂ ŕêśôúr̂ćêś, ôŕ d̂éf̂ér̂ŕîńĝ t́ĥé d̂óŵńl̂óâd́ ôf́ ûńn̂éĉéŝśâŕŷ ŕêśôúr̂ćêś t̂ó îḿp̂ŕôv́ê ṕâǵê ĺôád̂. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/ĉŕît́îćâĺ-r̂éq̂úêśt̂-ćĥáîńŝ)."
+  },
+  "lighthouse-core/audits/critical-request-chains.js | displayValue": {
+    "message": "{itemCount, plural,\n    =1 {1 chain found}\n    other {# chains found}\n    }"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | title": {
+    "message": "Âv́ôíd̂ś âń êx́ĉéŝśîv́ê D́ÔḾ ŝíẑé"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | failureTitle": {
+    "message": "Ûśêś âń êx́ĉéŝśîv́ê D́ÔḾ ŝíẑé"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | description": {
+    "message": "B̂ŕôẃŝér̂ én̂ǵîńêér̂ś r̂éĉóm̂ḿêńd̂ ṕâǵêś ĉón̂t́âín̂ f́êẃêŕ t̂h́âń ~1,500 D̂ÓM̂ ńôd́êś. T̂h́ê śŵéêt́ ŝṕôt́ îś â t́r̂éê d́êṕt̂h́ < 32 êĺêḿêńt̂ś âńd̂ f́êẃêŕ t̂h́âń 60 ĉh́îĺd̂ŕêń/p̂ár̂én̂t́ êĺêḿêńt̂. Á l̂ár̂ǵê D́ÔḾ ĉán̂ ín̂ćr̂éâśê ḿêḿôŕŷ úŝáĝé, ĉáûśê ĺôńĝér̂ [śt̂ýl̂é ĉál̂ćûĺât́îón̂ś](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/f́ûńd̂ám̂én̂t́âĺŝ/ṕêŕf̂ór̂ḿâńĉé/r̂én̂d́êŕîńĝ/ŕêd́ûćê-t́ĥé-ŝćôṕê-án̂d́-ĉóm̂ṕl̂éx̂ít̂ý-ôf́-ŝt́ŷĺê-ćâĺĉúl̂át̂íôńŝ), án̂d́ p̂ŕôd́ûćê ćôśt̂ĺŷ [ĺâýôút̂ ŕêf́l̂óŵś](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŝṕêéd̂/ár̂t́îćl̂éŝ/ŕêf́l̂óŵ). [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/d̂óm̂-śîźê)."
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | columnDOMNodes": {
+    "message": "T̂ót̂ál̂ D́ÔḾ N̂ód̂éŝ"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | columnDOMDepth": {
+    "message": "M̂áx̂ím̂úm̂ D́ÔḾ D̂ép̂t́ĥ"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | columnDOMWidth": {
+    "message": "M̂áx̂ím̂úm̂ Ćĥíl̂d́r̂én̂"
+  },
+  "lighthouse-core/audits/dobetterweb/dom-size.js | displayValue": {
+    "message": "{itemCount, plural,\n    =1 {1 node}\n    other {# nodes}\n    }"
+  },
+  "lighthouse-core/audits/font-display.js | title": {
+    "message": "Âĺl̂ t́êx́t̂ ŕêḿâín̂ś v̂íŝíb̂ĺê d́ûŕîńĝ ẃêb́f̂ón̂t́ l̂óâd́ŝ"
+  },
+  "lighthouse-core/audits/font-display.js | failureTitle": {
+    "message": "T̂éx̂t́ îś îńv̂íŝíb̂ĺê ẃĥíl̂é ŵéb̂f́ôńt̂ś âŕê ĺôád̂ín̂ǵ"
+  },
+  "lighthouse-core/audits/font-display.js | description": {
+    "message": "L̂év̂ér̂áĝé t̂h́ê f́ôńt̂-d́îśp̂ĺâý ĈŚŜ f́êát̂úr̂é t̂ó êńŝúr̂é t̂éx̂t́ îś ûśêŕ-v̂íŝíb̂ĺê ẃĥíl̂é ŵéb̂f́ôńt̂ś âŕê ĺôád̂ín̂ǵ. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/ûṕd̂át̂éŝ/2016/02/f́ôńt̂-d́îśp̂ĺâý)."
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | title": {
+    "message": "M̂ín̂ím̂íẑéŝ ḿâín̂ t́ĥŕêád̂ ẃôŕk̂"
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | failureTitle": {
+    "message": "Ĥáŝ śîǵn̂íf̂íĉán̂t́ m̂áîń t̂h́r̂éâd́ ŵór̂ḱ"
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | description": {
+    "message": "Ĉón̂śîd́êŕ r̂éd̂úĉín̂ǵ t̂h́ê t́îḿê śp̂én̂t́ p̂ár̂śîńĝ, ćôḿp̂íl̂ín̂ǵ âńd̂ éx̂éĉút̂ín̂ǵ ĴŚ. Ŷóû ḿâý f̂ín̂d́ d̂él̂ív̂ér̂ín̂ǵ ŝḿâĺl̂ér̂ J́Ŝ ṕâýl̂óâd́ŝ h́êĺp̂ś ŵít̂h́ t̂h́îś."
+  },
+  "lighthouse-core/audits/mainthread-work-breakdown.js | columnCategory": {
+    "message": "Ĉát̂éĝór̂ý"
+  },
+  "lighthouse-core/audits/metrics/estimated-input-latency.js | title": {
+    "message": "Êśt̂ím̂át̂éd̂ Ín̂ṕût́ L̂át̂én̂ćŷ"
+  },
+  "lighthouse-core/audits/metrics/estimated-input-latency.js | description": {
+    "message": "T̂h́ê śĉór̂é âb́ôv́ê íŝ án̂ éŝt́îḿât́ê óf̂ h́ôẃ l̂ón̂ǵ ŷóûŕ âṕp̂ t́âḱêś t̂ó r̂éŝṕôńd̂ t́ô úŝér̂ ín̂ṕût́, îń m̂íl̂ĺîśêćôńd̂ś, d̂úr̂ín̂ǵ t̂h́ê b́ûśîéŝt́ 5ŝ ẃîńd̂óŵ óf̂ ṕâǵê ĺôád̂. Íf̂ ýôúr̂ ĺât́êńĉý îś ĥíĝh́êŕ t̂h́âń 50 m̂ś, ûśêŕŝ ḿâý p̂ér̂ćêív̂é ŷóûŕ âṕp̂ áŝ ĺâǵĝý. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/t̂óôĺŝ/ĺîǵĥt́ĥóûśê/áûd́ît́ŝ/éŝt́îḿât́êd́-îńp̂út̂-ĺât́êńĉý)."
+  },
+  "lighthouse-core/audits/metrics/first-contentful-paint.js | title": {
+    "message": "F̂ír̂śt̂ Ćôńt̂én̂t́f̂úl̂ Ṕâín̂t́"
+  },
+  "lighthouse-core/audits/metrics/first-contentful-paint.js | description": {
+    "message": "F̂ír̂śt̂ ćôńt̂én̂t́f̂úl̂ ṕâín̂t́ m̂ár̂ḱŝ t́ĥé t̂ím̂é ât́ ŵh́îćĥ t́ĥé f̂ír̂śt̂ t́êx́t̂/ím̂áĝé îś p̂áîńt̂éd̂. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/f́ûńd̂ám̂én̂t́âĺŝ/ṕêŕf̂ór̂ḿâńĉé/ûśêŕ-ĉén̂t́r̂íĉ-ṕêŕf̂ór̂ḿâńĉé-m̂ét̂ŕîćŝ#f́îŕŝt́_p̂áîńt̂_án̂d́_f̂ír̂śt̂_ćôńt̂én̂t́f̂úl̂_ṕâín̂t́)."
+  },
+  "lighthouse-core/audits/metrics/first-cpu-idle.js | title": {
+    "message": "F̂ír̂śt̂ ĆP̂Ú Îd́l̂é"
+  },
+  "lighthouse-core/audits/metrics/first-cpu-idle.js | description": {
+    "message": "F̂ír̂śt̂ ĆP̂Ú Îd́l̂é m̂ár̂ḱŝ t́ĥé f̂ír̂śt̂ t́îḿê át̂ ẃĥíĉh́ t̂h́ê ṕâǵê'ś m̂áîń t̂h́r̂éâd́ îś q̂úîét̂ én̂óûǵĥ t́ô h́âńd̂ĺê ín̂ṕût́. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/t̂óôĺŝ/ĺîǵĥt́ĥóûśê/áûd́ît́ŝ/f́îŕŝt́-îńt̂ér̂áĉt́îv́ê)."
+  },
+  "lighthouse-core/audits/metrics/first-meaningful-paint.js | title": {
+    "message": "F̂ír̂śt̂ Ḿêán̂ín̂ǵf̂úl̂ Ṕâín̂t́"
+  },
+  "lighthouse-core/audits/metrics/first-meaningful-paint.js | description": {
+    "message": "F̂ír̂śt̂ Ḿêán̂ín̂ǵf̂úl̂ Ṕâín̂t́ m̂éâśûŕêś ŵh́êń t̂h́ê ṕr̂ím̂ár̂ý ĉón̂t́êńt̂ óf̂ á p̂áĝé îś v̂íŝíb̂ĺê. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/f̂ír̂śt̂-ḿêán̂ín̂ǵf̂úl̂-ṕâín̂t́)."
+  },
   "lighthouse-core/audits/metrics/interactive.js | title": {
     "message": "T̂ím̂é t̂ó Îńt̂ér̂áĉt́îv́ê"
   },
   "lighthouse-core/audits/metrics/interactive.js | description": {
     "message": "Îńt̂ér̂áĉt́îv́ê ḿâŕk̂ś t̂h́ê t́îḿê át̂ ẃĥíĉh́ t̂h́ê ṕâǵê íŝ f́ûĺl̂ý îńt̂ér̂áĉt́îv́ê. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/ĉón̂śîśt̂én̂t́l̂ý-îńt̂ér̂áĉt́îv́ê)."
   },
+  "lighthouse-core/audits/metrics/speed-index.js | title": {
+    "message": "Ŝṕêéd̂ Ín̂d́êx́"
+  },
+  "lighthouse-core/audits/metrics/speed-index.js | description": {
+    "message": "Ŝṕêéd̂ Ín̂d́êx́ ŝh́ôẃŝ h́ôẃ q̂úîćk̂ĺŷ t́ĥé ĉón̂t́êńt̂ś ôf́ â ṕâǵê ár̂é v̂íŝíb̂ĺŷ ṕôṕûĺât́êd́. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/t̂óôĺŝ/ĺîǵĥt́ĥóûśê/áûd́ît́ŝ/śp̂éêd́-îńd̂éx̂)."
+  },
+  "lighthouse-core/audits/redirects.js | title": {
+    "message": "Âv́ôíd̂ ḿûĺt̂íp̂ĺê ṕâǵê ŕêd́îŕêćt̂ś"
+  },
+  "lighthouse-core/audits/redirects.js | description": {
+    "message": "R̂éd̂ír̂éĉt́ŝ ín̂t́r̂ód̂úĉé âd́d̂ít̂íôńâĺ d̂él̂áŷś b̂éf̂ór̂é t̂h́ê ṕâǵê ćâń b̂é l̂óâd́êd́. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/t̂óôĺŝ/ĺîǵĥt́ĥóûśê/áûd́ît́ŝ/ŕêd́îŕêćt̂ś)."
+  },
+  "lighthouse-core/audits/time-to-first-byte.js | title": {
+    "message": "K̂éêṕ ŝér̂v́êŕ r̂éŝṕôńŝé t̂ím̂éŝ ĺôẃ (T̂T́F̂B́)"
+  },
+  "lighthouse-core/audits/time-to-first-byte.js | description": {
+    "message": "T̂ím̂é T̂ó F̂ír̂śt̂ B́ŷt́ê íd̂én̂t́îf́îéŝ t́ĥé t̂ím̂é ât́ ŵh́îćĥ ýôúr̂ śêŕv̂ér̂ śêńd̂ś â ŕêśp̂ón̂śê. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/t̂t́f̂b́)."
+  },
+  "lighthouse-core/audits/time-to-first-byte.js | displayValue": {
+    "message": "R̂óôt́ d̂óĉúm̂én̂t́ t̂óôḱ {timeInMs, number, milliseconds} m̂ś"
+  },
+  "lighthouse-core/audits/user-timings.js | title": {
+    "message": "Ûśêŕ T̂ím̂ín̂ǵ m̂ár̂ḱŝ án̂d́ m̂éâśûŕêś"
+  },
+  "lighthouse-core/audits/user-timings.js | description": {
+    "message": "Ĉón̂śîd́êŕ îńŝt́r̂úm̂én̂t́îńĝ ýôúr̂ áp̂ṕ ŵít̂h́ t̂h́ê Úŝér̂ T́îḿîńĝ ÁP̂Í t̂ó ĉŕêát̂é ĉúŝt́ôḿ, r̂éâĺ-ŵór̂ĺd̂ ḿêáŝúr̂ém̂én̂t́ŝ óf̂ ḱêý ûśêŕ êx́p̂ér̂íêńĉéŝ. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/t́ôól̂ś/l̂íĝh́t̂h́ôúŝé/âúd̂ít̂ś/ûśêŕ-t̂ím̂ín̂ǵ)."
+  },
+  "lighthouse-core/audits/user-timings.js | displayValue": {
+    "message": "{itemCount, plural,\n    =1 {1 user timing}\n    other {# user timings}\n    }"
+  },
+  "lighthouse-core/audits/uses-rel-preconnect.js | title": {
+    "message": "Âv́ôíd̂ ḿûĺt̂íp̂ĺê, ćôśt̂ĺŷ ŕôún̂d́ t̂ŕîṕŝ t́ô án̂ý ôŕîǵîń"
+  },
+  "lighthouse-core/audits/uses-rel-preconnect.js | description": {
+    "message": "Ĉón̂śîd́êŕ âd́d̂ín̂ǵ p̂ŕêćôńn̂éĉt́ ôŕ d̂ńŝ-ṕr̂éf̂ét̂ćĥ ŕêśôúr̂ćê h́îńt̂ś t̂ó êśt̂áb̂ĺîśĥ éâŕl̂ý ĉón̂ńêćt̂íôńŝ t́ô ím̂ṕôŕt̂án̂t́ t̂h́îŕd̂-ṕâŕt̂ý ôŕîǵîńŝ. [Ĺêár̂ń m̂ór̂é](ĥt́t̂ṕŝ://d́êv́êĺôṕêŕŝ.ǵôóĝĺê.ćôḿ/ŵéb̂/f́ûńd̂ám̂én̂t́âĺŝ/ṕêŕf̂ór̂ḿâńĉé/r̂éŝóûŕĉé-p̂ŕîór̂ít̂íẑát̂íôń#p̂ŕêćôńn̂éĉt́)."
+  },
+  "lighthouse-core/audits/uses-rel-preload.js | title": {
+    "message": "P̂ŕêĺôád̂ ḱêý r̂éq̂úêśt̂ś"
+  },
+  "lighthouse-core/audits/uses-rel-preload.js | description": {
+    "message": "Ĉón̂śîd́êŕ ûśîńĝ <ĺîńk̂ ŕêĺ=p̂ŕêĺôád̂> t́ô ṕr̂íôŕît́îźê f́êt́ĉh́îńĝ ĺât́ê-d́îśĉóv̂ér̂éd̂ ŕêśôúr̂ćêś ŝóôńêŕ. [L̂éâŕn̂ ḿôŕê](h́t̂t́p̂ś://d̂év̂él̂óp̂ér̂ś.ĝóôǵl̂é.ĉóm̂/ẃêb́/t̂óôĺŝ/ĺîǵĥt́ĥóûśê/áûd́ît́ŝ/ṕr̂él̂óâd́)."
+  },
   "lighthouse-core/config/default-config.js | performanceCategoryTitle": {
     "message": "P̂ér̂f́ôŕm̂án̂ćê"
   },
@@ -32,17 +254,29 @@
   "lighthouse-core/lib/i18n.js | ms": {
     "message": "{timeInMs, number, milliseconds} m̂ś"
   },
+  "lighthouse-core/lib/i18n.js | displayValueByteSavings": {
+    "message": "P̂ót̂én̂t́îál̂ śâv́îńĝś ôf́ {wastedBytes, number, bytes} K̂B́"
+  },
+  "lighthouse-core/lib/i18n.js | displayValueMsSavings": {
+    "message": "P̂ót̂én̂t́îál̂ śâv́îńĝś ôf́ {wastedMs, number, milliseconds} m̂ś"
+  },
   "lighthouse-core/lib/i18n.js | columnURL": {
     "message": "ÛŔL̂"
   },
   "lighthouse-core/lib/i18n.js | columnSize": {
     "message": "Ŝíẑé (K̂B́)"
   },
+  "lighthouse-core/lib/i18n.js | columnCacheTTL": {
+    "message": "Ĉáĉh́ê T́T̂Ĺ"
+  },
+  "lighthouse-core/lib/i18n.js | columnWastedBytes": {
+    "message": "P̂ót̂én̂t́îál̂ Śâv́îńĝś (K̂B́)"
+  },
   "lighthouse-core/lib/i18n.js | columnWastedMs": {
     "message": "P̂ót̂én̂t́îál̂ Śâv́îńĝś (m̂ś)"
   },
-  "lighthouse-core/lib/i18n.js | displayValueWastedMs": {
-    "message": "P̂ót̂én̂t́îál̂ śâv́îńĝś ôf́ {wastedMs, number, milliseconds} m̂ś"
+  "lighthouse-core/lib/i18n.js | columnTimeSpent": {
+    "message": "T̂ím̂é Ŝṕêńt̂"
   },
   "lighthouse-core/report/html/renderer/util.js | varianceDisclaimer": {
     "message": "V̂ál̂úêś âŕê éŝt́îḿât́êd́ âńd̂ ḿâý v̂ár̂ý."
diff --git a/docs/report.md b/lighthouse-core/report/html/readme.md
similarity index 90%
rename from docs/report.md
rename to lighthouse-core/report/html/readme.md
index 35132e5bc..c9e9ebc41 100644
--- a/docs/report.md
+++ b/lighthouse-core/report/html/readme.md
@@ -1,8 +1,13 @@
-## HTML Report Generation Overview
+# Lighthouse HTML Report Renderer
+
+[Source on Github](https://github.com/GoogleChrome/lighthouse/tree/master/lighthouse-core/report/html)
+
+
+## Overview
 
 Lighthouse has an indepedent report renderer that takes the **LHR** (Lighthouse Result object) and creates a DOM tree of the report. It's all done client-side.
 
-Example standalone HTML report, delivered by the CLI: [`dbwtest-3.0.0-alpha.html`](https://googlechrome.github.io/lighthouse/reports/dbwtest-3.0.0-alpha.html) _(View the source! 📖)_
+Example standalone HTML report, delivered by the CLI: [`dbwtest-3.0.3.html`](https://googlechrome.github.io/lighthouse/reports/dbwtest-3.0.3.html) _(View the source! 📖)_
 
 ### Report Renderer components
 
@@ -10,7 +15,7 @@ Example standalone HTML report, delivered by the CLI: [`dbwtest-3.0.0-alpha.html
    - It runs natively in Node.js but can run in the browser after a compile step is applied during our bundling pipeline. That compile step uses a browserify transform that takes any `fs.readFileSync()` calls and replaces them with the stringified file content.
 1. [`lighthouse-core/report/html/`](https://github.com/GoogleChrome/lighthouse/tree/master/lighthouse-core/report/html) has everything required to create the HTML report.
 1. [`lighthouse-core/report/html/renderer`](https://github.com/GoogleChrome/lighthouse/tree/master/lighthouse-core/report/html/renderer) are all client-side JS files. They transform an LHR object into a report DOM tree. They are all executed within the browser.
-1. [`lighthouse-core/report/html/report-template.html`](https://github.com/GoogleChrome/lighthouse/blob/master/lighthouse-core/report/html/report-template.html) is where the client-side build of the DOM report is typically kicked off ([with these four lines](https://github.com/GoogleChrome/lighthouse/blob/63c999789dc08b9a3b56b22f25f478f13050da29/lighthouse-core/report/html/report-template.html#L27-L31)) However, see _Current Uses.._ below for more possibilities.
+1. [`lighthouse-core/report/html/report-template.html`](https://github.com/GoogleChrome/lighthouse/blob/master/lighthouse-core/report/html/report-template.html) is where the client-side build of the DOM report is typically kicked off ([with these four lines](https://github.com/GoogleChrome/lighthouse/blob/eda3a3e2e271249f261655f9504fd542d6acf0f8/lighthouse-core/report/html/report-template.html#L29-L33)) However, see _Current Uses.._ below for more possibilities.
 
 
 ### Data Hydration
diff --git a/typings/intl-messageformat-parser/index.d.ts b/typings/intl-messageformat-parser/index.d.ts
index 0db5d1fed..8f0f2b380 100644
--- a/typings/intl-messageformat-parser/index.d.ts
+++ b/typings/intl-messageformat-parser/index.d.ts
@@ -1,7 +1,7 @@
 declare module 'intl-messageformat-parser' {
   interface Element {
     type: 'messageTextElement'|'argumentElement';
-    id: string
+    id?: string
     value?: string
     format?: null | {type: string; style?: string};
   }


Test patch:
diff --git a/lighthouse-cli/test/smokehouse/byte-efficiency/expectations.js b/lighthouse-cli/test/smokehouse/byte-efficiency/expectations.js
index 885bc0472..0822e727c 100644
--- a/lighthouse-cli/test/smokehouse/byte-efficiency/expectations.js
+++ b/lighthouse-cli/test/smokehouse/byte-efficiency/expectations.js
@@ -94,10 +94,7 @@ module.exports = [
         },
       },
       'uses-responsive-images': {
-        displayValue: [
-          'Potential savings of %d\xa0KB',
-          75,
-        ],
+        displayValue: 'Potential savings of 75\xa0KB',
         details: {
           overallSavingsBytes: '>75000',
           items: [
diff --git a/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js b/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js
index 598eee70c..b383e2d2e 100644
--- a/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js
+++ b/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js
@@ -176,7 +176,7 @@ describe('Byte efficiency base audit', () => {
       ],
     }, graph, simulator);
 
-    assert.ok(result.displayValue.includes(2), 'contains correct KB');
+    assert.ok(result.displayValue);
   });
 
   it('should work on real graphs', async () => {
diff --git a/lighthouse-core/test/audits/byte-efficiency/uses-long-cache-ttl-test.js b/lighthouse-core/test/audits/byte-efficiency/uses-long-cache-ttl-test.js
index 56d5af02f..845c1a10b 100644
--- a/lighthouse-core/test/audits/byte-efficiency/uses-long-cache-ttl-test.js
+++ b/lighthouse-core/test/audits/byte-efficiency/uses-long-cache-ttl-test.js
@@ -46,7 +46,7 @@ describe('Cache headers audit', () => {
       assert.equal(items.length, 1);
       assert.equal(items[0].cacheLifetimeMs, 0);
       assert.equal(items[0].wastedBytes, 10000);
-      assert.equal(result.displayValue, '1 asset found');
+      assert.ok(result.displayValue);
     });
   });
 
@@ -68,7 +68,7 @@ describe('Cache headers audit', () => {
       assert.equal(Math.round(items[1].wastedBytes), 8000);
       assert.equal(items[2].cacheLifetimeMs, 86400 * 1000);
       assert.equal(Math.round(items[2].wastedBytes), 4000);
-      assert.equal(result.displayValue, '3 assets found');
+      assert.ok(result.displayValue);
     });
   });
 
diff --git a/lighthouse-core/test/audits/critical-request-chains-test.js b/lighthouse-core/test/audits/critical-request-chains-test.js
index c9d415c5b..0006a803e 100644
--- a/lighthouse-core/test/audits/critical-request-chains-test.js
+++ b/lighthouse-core/test/audits/critical-request-chains-test.js
@@ -86,7 +86,7 @@ const mockArtifacts = (mockChain) => {
 describe('Performance: critical-request-chains audit', () => {
   it('calculates the correct chain result for failing example', () => {
     return CriticalRequestChains.audit(mockArtifacts(FAILING_REQUEST_CHAIN)).then(output => {
-      assert.equal(output.displayValue, '2 chains found');
+      assert.ok(output.displayValue);
       assert.equal(output.rawValue, false);
       assert.ok(output.details);
     });
diff --git a/lighthouse-core/test/audits/dobetterweb/dom-size-test.js b/lighthouse-core/test/audits/dobetterweb/dom-size-test.js
index 7df904020..ee1ee0aed 100644
--- a/lighthouse-core/test/audits/dobetterweb/dom-size-test.js
+++ b/lighthouse-core/test/audits/dobetterweb/dom-size-test.js
@@ -25,7 +25,7 @@ describe('Num DOM nodes audit', () => {
     const auditResult = DOMSize.audit(artifact, {options});
     assert.equal(auditResult.score, 0.43);
     assert.equal(auditResult.rawValue, numNodes);
-    assert.deepEqual(auditResult.displayValue, ['%d nodes', numNodes]);
+    assert.ok(auditResult.displayValue);
     assert.equal(auditResult.details.items[0].totalNodes, numNodes.toLocaleString());
     assert.equal(auditResult.details.items[0].depth, '1');
     assert.equal(auditResult.details.items[0].width, '2');
diff --git a/lighthouse-core/test/audits/font-display-test.js b/lighthouse-core/test/audits/font-display-test.js
index 99f98a9ae..1a836ba4c 100644
--- a/lighthouse-core/test/audits/font-display-test.js
+++ b/lighthouse-core/test/audits/font-display-test.js
@@ -61,7 +61,7 @@ describe('Performance: Font Display audit', () => {
     ], webFonts)).then(result => {
       const items = [{
         url: openSansFontBold.src[0],
-        wastedTime: 2000,
+        wastedMs: 2000,
       }];
       assert.strictEqual(result.rawValue, false);
       assert.deepEqual(result.details.items, items);
diff --git a/lighthouse-core/test/audits/metrics/__snapshots__/first-meaningful-paint-test.js.snap b/lighthouse-core/test/audits/metrics/__snapshots__/first-meaningful-paint-test.js.snap
index 779d6b85f..9b02f95fd 100644
--- a/lighthouse-core/test/audits/metrics/__snapshots__/first-meaningful-paint-test.js.snap
+++ b/lighthouse-core/test/audits/metrics/__snapshots__/first-meaningful-paint-test.js.snap
@@ -2,10 +2,6 @@
 
 exports[`Performance: first-meaningful-paint audit computes FMP correctly for simulated 1`] = `
 Object {
-  "displayValue": Array [
-    "%10d ms",
-    1354.506000038469,
-  ],
   "rawValue": 1354.506000038469,
   "score": 1,
 }
diff --git a/lighthouse-core/test/audits/metrics/estimated-input-latency-test.js b/lighthouse-core/test/audits/metrics/estimated-input-latency-test.js
index 69c9966c6..5b4fc9c44 100644
--- a/lighthouse-core/test/audits/metrics/estimated-input-latency-test.js
+++ b/lighthouse-core/test/audits/metrics/estimated-input-latency-test.js
@@ -7,7 +7,6 @@
 
 const Audit = require('../../../audits/metrics/estimated-input-latency.js');
 const Runner = require('../../../runner');
-const Util = require('../../../report/html/renderer/util');
 const assert = require('assert');
 const options = Audit.defaultOptions;
 
@@ -29,8 +28,8 @@ describe('Performance: estimated-input-latency audit', () => {
     const settings = {throttlingMethod: 'provided'};
     return Audit.audit(artifacts, {options, settings}).then(output => {
       assert.equal(Math.round(output.rawValue * 10) / 10, 17.1);
-      assert.equal(Util.formatDisplayValue(output.displayValue), '17\xa0ms');
       assert.equal(output.score, 1);
+      assert.ok(output.displayValue);
     });
   });
 });
diff --git a/lighthouse-core/test/audits/metrics/first-meaningful-paint-test.js b/lighthouse-core/test/audits/metrics/first-meaningful-paint-test.js
index 9aeee36c2..2936b2217 100644
--- a/lighthouse-core/test/audits/metrics/first-meaningful-paint-test.js
+++ b/lighthouse-core/test/audits/metrics/first-meaningful-paint-test.js
@@ -7,7 +7,6 @@
 
 const FMPAudit = require('../../../audits/metrics/first-meaningful-paint.js');
 const Audit = require('../../../audits/audit.js');
-const Util = require('../../../report/html/renderer/util');
 const assert = require('assert');
 const options = FMPAudit.defaultOptions;
 const trace = require('../../fixtures/traces/progressive-app-m60.json');
@@ -27,8 +26,8 @@ describe('Performance: first-meaningful-paint audit', () => {
     const fmpResult = await FMPAudit.audit(artifacts, context);
 
     assert.equal(fmpResult.score, 1);
-    assert.equal(Util.formatDisplayValue(fmpResult.displayValue), '780\xa0ms');
     assert.equal(fmpResult.rawValue, 783.328);
+    assert.ok(fmpResult.displayValue);
   });
 
   it('computes FMP correctly for simulated', async () => {
@@ -42,7 +41,6 @@ describe('Performance: first-meaningful-paint audit', () => {
     expect({
       score: fmpResult.score,
       rawValue: fmpResult.rawValue,
-      displayValue: fmpResult.displayValue,
     }).toMatchSnapshot();
   });
 });
diff --git a/lighthouse-core/test/audits/user-timing-test.js b/lighthouse-core/test/audits/user-timing-test.js
index 1ebef20f0..8cd9d8015 100644
--- a/lighthouse-core/test/audits/user-timing-test.js
+++ b/lighthouse-core/test/audits/user-timing-test.js
@@ -30,7 +30,7 @@ describe('Performance: user-timings audit', () => {
       assert.equal(blackListedUTs.length, 0, 'Blacklisted usertimings included in results');
 
       assert.equal(auditResult.rawValue, false);
-      assert.deepStrictEqual(auditResult.displayValue, ['%d user timings', 2]);
+      assert.ok(auditResult.displayValue);
 
       assert.equal(auditResult.details.items[0].name, 'measure_test');
       assert.equal(auditResult.details.items[0].timingType, 'Measure');
diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json
index 7dc2528d2..fe260a3b9 100644
--- a/lighthouse-core/test/results/sample_v2.json
+++ b/lighthouse-core/test/results/sample_v2.json
@@ -79,10 +79,7 @@
       "score": 0.51,
       "scoreDisplayMode": "numeric",
       "rawValue": 3969.135,
-      "displayValue": [
-        "%10d ms",
-        3969.135
-      ]
+      "displayValue": "3,970 ms"
     },
     "first-meaningful-paint": {
       "id": "first-meaningful-paint",
@@ -91,10 +88,7 @@
       "score": 0.51,
       "scoreDisplayMode": "numeric",
       "rawValue": 3969.136,
-      "displayValue": [
-        "%10d ms",
-        3969.136
-      ]
+      "displayValue": "3,970 ms"
     },
     "load-fast-enough-for-pwa": {
       "id": "load-fast-enough-for-pwa",
@@ -111,10 +105,7 @@
       "score": 0.74,
       "scoreDisplayMode": "numeric",
       "rawValue": 4417,
-      "displayValue": [
-        "%10d ms",
-        4417
-      ]
+      "displayValue": "4,420 ms"
     },
     "screenshot-thumbnails": {
       "id": "screenshot-thumbnails",
@@ -187,10 +178,7 @@
       "score": 1,
       "scoreDisplayMode": "numeric",
       "rawValue": 16,
-      "displayValue": [
-        "%d ms",
-        16
-      ]
+      "displayValue": "20 ms"
     },
     "errors-in-console": {
       "id": "errors-in-console",
@@ -249,7 +237,7 @@
       "score": 1,
       "scoreDisplayMode": "binary",
       "rawValue": 570.5630000000001,
-      "displayValue": "",
+      "displayValue": "Root document took 570 ms",
       "details": {
         "type": "opportunity",
         "overallSavingsMs": -29.436999999999898,
@@ -264,10 +252,7 @@
       "score": 0.72,
       "scoreDisplayMode": "numeric",
       "rawValue": 4927.278,
-      "displayValue": [
-        "%10d ms",
-        4927.278
-      ]
+      "displayValue": "4,930 ms"
     },
     "interactive": {
       "id": "interactive",
@@ -301,10 +286,6 @@
       "displayValue": "12 chains found",
       "details": {
         "type": "criticalrequestchain",
-        "header": {
-          "type": "text",
-          "text": "View critical network waterfall:"
-        },
         "chains": {
           "F3B687683512E0F003DD41EB23E2091A": {
             "request": {
@@ -462,10 +443,7 @@
       "score": 1,
       "scoreDisplayMode": "numeric",
       "rawValue": 0,
-      "displayValue": [
-        "%d ms",
-        0
-      ],
+      "displayValue": "",
       "details": {
         "type": "table",
         "headings": [],
@@ -652,10 +630,7 @@
       "score": 0.96,
       "scoreDisplayMode": "numeric",
       "rawValue": 1548.5690000000002,
-      "displayValue": [
-        "%10d ms",
-        1548.5690000000002
-      ],
+      "displayValue": "1,550 ms",
       "details": {
         "type": "table",
         "headings": [
@@ -717,10 +692,7 @@
       "score": 0.92,
       "scoreDisplayMode": "numeric",
       "rawValue": 1150.932,
-      "displayValue": [
-        "%10d ms",
-        1150.932
-      ],
+      "displayValue": "1,150 ms",
       "details": {
         "type": "table",
         "headings": [
@@ -745,7 +717,7 @@
             "key": "scriptParseCompile",
             "granularity": 1,
             "itemType": "ms",
-            "text": "Script Parsing & Compilation"
+            "text": "Script Parse"
           }
         ],
         "items": [
@@ -780,10 +752,7 @@
       "score": 1,
       "scoreDisplayMode": "numeric",
       "rawValue": 0,
-      "displayValue": [
-        "Potential savings of %10d ms",
-        0
-      ],
+      "displayValue": "Potential savings of 0 ms",
       "details": {
         "type": "opportunity",
         "headings": [],
@@ -798,10 +767,7 @@
       "score": 1,
       "scoreDisplayMode": "numeric",
       "rawValue": 0,
-      "displayValue": [
-        "Potential savings of %10d ms",
-        0
-      ],
+      "displayValue": "Potential savings of 0 ms",
       "details": {
         "type": "opportunity",
         "headings": [],
@@ -1692,7 +1658,7 @@
       "score": 0.58,
       "scoreDisplayMode": "numeric",
       "rawValue": 103455,
-      "displayValue": "11 assets found",
+      "displayValue": "11 resources found",
       "details": {
         "type": "table",
         "headings": [
@@ -1817,10 +1783,7 @@
       "score": 1,
       "scoreDisplayMode": "numeric",
       "rawValue": 160738,
-      "displayValue": [
-        "Total size was %d KB",
-        156.970703125
-      ],
+      "displayValue": "Total size was 157 KB",
       "details": {
         "type": "table",
         "headings": [
@@ -1832,14 +1795,7 @@
           {
             "key": "totalBytes",
             "itemType": "bytes",
-            "displayUnit": "kb",
-            "granularity": 1,
-            "text": "Total Size"
-          },
-          {
-            "key": "totalMs",
-            "itemType": "ms",
-            "text": "Transfer Time"
+            "text": "Size (KB)"
           }
         ],
         "items": [
@@ -1993,10 +1949,7 @@
       "score": 1,
       "scoreDisplayMode": "numeric",
       "rawValue": 0,
-      "displayValue": [
-        "Potential savings of %d KB",
-        30
-      ],
+      "displayValue": "Potential savings of 30 KB",
       "warnings": [],
       "details": {
         "type": "opportunity",
@@ -2009,12 +1962,12 @@
           {
             "key": "totalBytes",
             "valueType": "bytes",
-            "label": "Original"
+            "label": "Size (KB)"
           },
           {
             "key": "wastedBytes",
             "valueType": "bytes",
-            "label": "Potential Savings"
+            "label": "Potential Savings (KB)"
           }
         ],
         "items": [
@@ -2032,7 +1985,7 @@
     "unused-css-rules": {
       "id": "unused-css-rules",
       "title": "Defer unused CSS",
-      "description": "Remove unused rules from stylesheets to reduce unnecessary bytes consumed by network activity. [Learn more](https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery).",
+      "description": "Remove unused rules from stylesheets to reduce unnecessary bytes consumed by network activity. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/unused-css).",
       "score": 1,
       "scoreDisplayMode": "numeric",
       "rawValue": 0,
@@ -2052,10 +2005,7 @@
       "score": 1,
       "scoreDisplayMode": "numeric",
       "rawValue": 0,
-      "displayValue": [
-        "Potential savings of %d KB",
-        8
-      ],
+      "displayValue": "Potential savings of 8 KB",
       "warnings": [],
       "details": {
         "type": "opportunity",
@@ -2073,12 +2023,12 @@
           {
             "key": "totalBytes",
             "valueType": "bytes",
-            "label": "Original"
+            "label": "Size (KB)"
           },
           {
             "key": "wastedBytes",
             "valueType": "bytes",
-            "label": "Potential Savings"
+            "label": "Potential Savings (KB)"
           }
         ],
         "items": [
@@ -2118,27 +2068,24 @@
       "score": 0.88,
       "scoreDisplayMode": "numeric",
       "rawValue": 150,
-      "displayValue": [
-        "Potential savings of %d KB",
-        63
-      ],
+      "displayValue": "Potential savings of 63 KB",
       "details": {
         "type": "opportunity",
         "headings": [
           {
             "key": "url",
             "valueType": "url",
-            "label": "Uncompressed resource URL"
+            "label": "URL"
           },
           {
             "key": "totalBytes",
             "valueType": "bytes",
-            "label": "Original"
+            "label": "Size (KB)"
           },
           {
             "key": "wastedBytes",
             "valueType": "bytes",
-            "label": "GZIP Savings"
+            "label": "Potential Savings (KB)"
           }
         ],
         "items": [
@@ -2214,10 +2161,7 @@
       "score": 1,
       "scoreDisplayMode": "numeric",
       "rawValue": 53,
-      "displayValue": [
-        "%d nodes",
-        53
-      ],
+      "displayValue": "53 nodes",
       "details": {
         "type": "table",
         "headings": [
@@ -3445,27 +3389,187 @@
       "scorescaleLabel": "Score scale:"
     },
     "icuMessagePaths": {
+      "lighthouse-core/audits/metrics/first-contentful-paint.js | title": [
+        "audits[first-contentful-paint].title"
+      ],
+      "lighthouse-core/audits/metrics/first-contentful-paint.js | description": [
+        "audits[first-contentful-paint].description"
+      ],
+      "lighthouse-core/lib/i18n.js | ms": [
+        {
+          "values": {
+            "timeInMs": 3969.135
+          },
+          "path": "audits[first-contentful-paint].displayValue"
+        },
+        {
+          "values": {
+            "timeInMs": 3969.136
+          },
+          "path": "audits[first-meaningful-paint].displayValue"
+        },
+        {
+          "values": {
+            "timeInMs": 4417
+          },
+          "path": "audits[speed-index].displayValue"
+        },
+        {
+          "values": {
+            "timeInMs": 16
+          },
+          "path": "audits[estimated-input-latency].displayValue"
+        },
+        {
+          "values": {
+            "timeInMs": 4927.278
+          },
+          "path": "audits[first-cpu-idle].displayValue"
+        },
+        {
+          "values": {
+            "timeInMs": 4927.278
+          },
+          "path": "audits.interactive.displayValue"
+        },
+        {
+          "values": {
+            "timeInMs": 1548.5690000000002
+          },
+          "path": "audits[mainthread-work-breakdown].displayValue"
+        },
+        {
+          "values": {
+            "timeInMs": 1150.932
+          },
+          "path": "audits[bootup-time].displayValue"
+        }
+      ],
+      "lighthouse-core/audits/metrics/first-meaningful-paint.js | title": [
+        "audits[first-meaningful-paint].title"
+      ],
+      "lighthouse-core/audits/metrics/first-meaningful-paint.js | description": [
+        "audits[first-meaningful-paint].description"
+      ],
+      "lighthouse-core/audits/metrics/speed-index.js | title": [
+        "audits[speed-index].title"
+      ],
+      "lighthouse-core/audits/metrics/speed-index.js | description": [
+        "audits[speed-index].description"
+      ],
+      "lighthouse-core/audits/metrics/estimated-input-latency.js | title": [
+        "audits[estimated-input-latency].title"
+      ],
+      "lighthouse-core/audits/metrics/estimated-input-latency.js | description": [
+        "audits[estimated-input-latency].description"
+      ],
+      "lighthouse-core/audits/time-to-first-byte.js | title": [
+        "audits[time-to-first-byte].title"
+      ],
+      "lighthouse-core/audits/time-to-first-byte.js | description": [
+        "audits[time-to-first-byte].description"
+      ],
+      "lighthouse-core/audits/time-to-first-byte.js | displayValue": [
+        {
+          "values": {
+            "timeInMs": 570.5630000000001
+          },
+          "path": "audits[time-to-first-byte].displayValue"
+        }
+      ],
+      "lighthouse-core/audits/metrics/first-cpu-idle.js | title": [
+        "audits[first-cpu-idle].title"
+      ],
+      "lighthouse-core/audits/metrics/first-cpu-idle.js | description": [
+        "audits[first-cpu-idle].description"
+      ],
       "lighthouse-core/audits/metrics/interactive.js | title": [
         "audits.interactive.title"
       ],
       "lighthouse-core/audits/metrics/interactive.js | description": [
         "audits.interactive.description"
       ],
-      "lighthouse-core/lib/i18n.js | ms": [
+      "lighthouse-core/audits/user-timings.js | title": [
+        "audits[user-timings].title"
+      ],
+      "lighthouse-core/audits/user-timings.js | description": [
+        "audits[user-timings].description"
+      ],
+      "lighthouse-core/audits/critical-request-chains.js | title": [
+        "audits[critical-request-chains].title"
+      ],
+      "lighthouse-core/audits/critical-request-chains.js | description": [
+        "audits[critical-request-chains].description"
+      ],
+      "lighthouse-core/audits/critical-request-chains.js | displayValue": [
         {
           "values": {
-            "timeInMs": 4927.278
+            "itemCount": 12
           },
-          "path": "audits.interactive.displayValue"
+          "path": "audits[critical-request-chains].displayValue"
         }
       ],
-      "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | title": [
-        "audits[render-blocking-resources].title"
+      "lighthouse-core/audits/redirects.js | title": [
+        "audits.redirects.title"
       ],
-      "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | description": [
-        "audits[render-blocking-resources].description"
+      "lighthouse-core/audits/redirects.js | description": [
+        "audits.redirects.description"
+      ],
+      "lighthouse-core/audits/mainthread-work-breakdown.js | title": [
+        "audits[mainthread-work-breakdown].title"
+      ],
+      "lighthouse-core/audits/mainthread-work-breakdown.js | description": [
+        "audits[mainthread-work-breakdown].description"
+      ],
+      "lighthouse-core/audits/mainthread-work-breakdown.js | columnCategory": [
+        "audits[mainthread-work-breakdown].details.headings[0].text"
+      ],
+      "lighthouse-core/lib/i18n.js | columnTimeSpent": [
+        "audits[mainthread-work-breakdown].details.headings[1].text"
       ],
-      "lighthouse-core/lib/i18n.js | displayValueWastedMs": [
+      "lighthouse-core/audits/bootup-time.js | title": [
+        "audits[bootup-time].title"
+      ],
+      "lighthouse-core/audits/bootup-time.js | description": [
+        "audits[bootup-time].description"
+      ],
+      "lighthouse-core/lib/i18n.js | columnURL": [
+        "audits[bootup-time].details.headings[0].text",
+        "audits[uses-long-cache-ttl].details.headings[0].text",
+        "audits[total-byte-weight].details.headings[0].text",
+        "audits[render-blocking-resources].details.headings[0].label",
+        "audits[unminified-javascript].details.headings[0].label",
+        "audits[uses-webp-images].details.headings[1].label",
+        "audits[uses-text-compression].details.headings[0].label"
+      ],
+      "lighthouse-core/audits/bootup-time.js | columnTotal": [
+        "audits[bootup-time].details.headings[1].text"
+      ],
+      "lighthouse-core/audits/bootup-time.js | columnScriptEval": [
+        "audits[bootup-time].details.headings[2].text"
+      ],
+      "lighthouse-core/audits/bootup-time.js | columnScriptParse": [
+        "audits[bootup-time].details.headings[3].text"
+      ],
+      "lighthouse-core/audits/uses-rel-preload.js | title": [
+        "audits[uses-rel-preload].title"
+      ],
+      "lighthouse-core/audits/uses-rel-preload.js | description": [
+        "audits[uses-rel-preload].description"
+      ],
+      "lighthouse-core/lib/i18n.js | displayValueMsSavings": [
+        {
+          "values": {
+            "wastedMs": 0
+          },
+          "path": "audits[uses-rel-preload].displayValue"
+        },
+        {
+          "values": {
+            "wastedMs": 0
+          },
+          "path": "audits[uses-rel-preconnect].displayValue"
+        },
         {
           "values": {
             "wastedMs": 1129
@@ -3473,15 +3577,168 @@
           "path": "audits[render-blocking-resources].displayValue"
         }
       ],
-      "lighthouse-core/lib/i18n.js | columnURL": [
-        "audits[render-blocking-resources].details.headings[0].label"
+      "lighthouse-core/audits/uses-rel-preconnect.js | title": [
+        "audits[uses-rel-preconnect].title"
+      ],
+      "lighthouse-core/audits/uses-rel-preconnect.js | description": [
+        "audits[uses-rel-preconnect].description"
+      ],
+      "lighthouse-core/audits/font-display.js | title": [
+        "audits[font-display].title"
+      ],
+      "lighthouse-core/audits/font-display.js | description": [
+        "audits[font-display].description"
+      ],
+      "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | failureTitle": [
+        "audits[uses-long-cache-ttl].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | description": [
+        "audits[uses-long-cache-ttl].description"
+      ],
+      "lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | displayValue": [
+        {
+          "values": {
+            "itemCount": 11
+          },
+          "path": "audits[uses-long-cache-ttl].displayValue"
+        }
+      ],
+      "lighthouse-core/lib/i18n.js | columnCacheTTL": [
+        "audits[uses-long-cache-ttl].details.headings[1].text"
       ],
       "lighthouse-core/lib/i18n.js | columnSize": [
-        "audits[render-blocking-resources].details.headings[1].label"
+        "audits[uses-long-cache-ttl].details.headings[2].text",
+        "audits[total-byte-weight].details.headings[1].text",
+        "audits[render-blocking-resources].details.headings[1].label",
+        "audits[unminified-javascript].details.headings[1].label",
+        "audits[uses-webp-images].details.headings[2].label",
+        "audits[uses-text-compression].details.headings[1].label"
+      ],
+      "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | title": [
+        "audits[total-byte-weight].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | description": [
+        "audits[total-byte-weight].description"
+      ],
+      "lighthouse-core/audits/byte-efficiency/total-byte-weight.js | displayValue": [
+        {
+          "values": {
+            "totalBytes": 160738
+          },
+          "path": "audits[total-byte-weight].displayValue"
+        }
+      ],
+      "lighthouse-core/audits/byte-efficiency/offscreen-images.js | title": [
+        "audits[offscreen-images].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/offscreen-images.js | description": [
+        "audits[offscreen-images].description"
+      ],
+      "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | title": [
+        "audits[render-blocking-resources].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | description": [
+        "audits[render-blocking-resources].description"
       ],
       "lighthouse-core/lib/i18n.js | columnWastedMs": [
         "audits[render-blocking-resources].details.headings[2].label"
       ],
+      "lighthouse-core/audits/byte-efficiency/unminified-css.js | title": [
+        "audits[unminified-css].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/unminified-css.js | description": [
+        "audits[unminified-css].description"
+      ],
+      "lighthouse-core/audits/byte-efficiency/unminified-javascript.js | title": [
+        "audits[unminified-javascript].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/unminified-javascript.js | description": [
+        "audits[unminified-javascript].description"
+      ],
+      "lighthouse-core/lib/i18n.js | displayValueByteSavings": [
+        {
+          "values": {
+            "wastedBytes": 30470
+          },
+          "path": "audits[unminified-javascript].displayValue"
+        },
+        {
+          "values": {
+            "wastedBytes": 8526
+          },
+          "path": "audits[uses-webp-images].displayValue"
+        },
+        {
+          "values": {
+            "wastedBytes": 64646
+          },
+          "path": "audits[uses-text-compression].displayValue"
+        }
+      ],
+      "lighthouse-core/lib/i18n.js | columnWastedBytes": [
+        "audits[unminified-javascript].details.headings[2].label",
+        "audits[uses-webp-images].details.headings[3].label",
+        "audits[uses-text-compression].details.headings[2].label"
+      ],
+      "lighthouse-core/audits/byte-efficiency/unused-css-rules.js | title": [
+        "audits[unused-css-rules].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/unused-css-rules.js | description": [
+        "audits[unused-css-rules].description"
+      ],
+      "lighthouse-core/audits/byte-efficiency/uses-webp-images.js | title": [
+        "audits[uses-webp-images].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/uses-webp-images.js | description": [
+        "audits[uses-webp-images].description"
+      ],
+      "lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | title": [
+        "audits[uses-optimized-images].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | description": [
+        "audits[uses-optimized-images].description"
+      ],
+      "lighthouse-core/audits/byte-efficiency/uses-text-compression.js | title": [
+        "audits[uses-text-compression].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/uses-text-compression.js | description": [
+        "audits[uses-text-compression].description"
+      ],
+      "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | title": [
+        "audits[uses-responsive-images].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | description": [
+        "audits[uses-responsive-images].description"
+      ],
+      "lighthouse-core/audits/byte-efficiency/efficient-animated-content.js | title": [
+        "audits[efficient-animated-content].title"
+      ],
+      "lighthouse-core/audits/byte-efficiency/efficient-animated-content.js | description": [
+        "audits[efficient-animated-content].description"
+      ],
+      "lighthouse-core/audits/dobetterweb/dom-size.js | title": [
+        "audits[dom-size].title"
+      ],
+      "lighthouse-core/audits/dobetterweb/dom-size.js | description": [
+        "audits[dom-size].description"
+      ],
+      "lighthouse-core/audits/dobetterweb/dom-size.js | displayValue": [
+        {
+          "values": {
+            "itemCount": 53
+          },
+          "path": "audits[dom-size].displayValue"
+        }
+      ],
+      "lighthouse-core/audits/dobetterweb/dom-size.js | columnDOMNodes": [
+        "audits[dom-size].details.headings[0].text"
+      ],
+      "lighthouse-core/audits/dobetterweb/dom-size.js | columnDOMDepth": [
+        "audits[dom-size].details.headings[1].text"
+      ],
+      "lighthouse-core/audits/dobetterweb/dom-size.js | columnDOMWidth": [
+        "audits[dom-size].details.headings[2].text"
+      ],
       "lighthouse-core/config/default-config.js | performanceCategoryTitle": [
         "categories.performance.title"
       ],
diff --git a/lighthouse-core/test/runner-test.js b/lighthouse-core/test/runner-test.js
index f266ed7f3..d29195a10 100644
--- a/lighthouse-core/test/runner-test.js
+++ b/lighthouse-core/test/runner-test.js
@@ -216,7 +216,7 @@ describe('Runner', () => {
 
     return Runner.run({}, {config}).then(results => {
       const audits = results.lhr.audits;
-      assert.equal(audits['user-timings'].displayValue[1], 2);
+      assert.equal(audits['user-timings'].displayValue, '2 user timings');
       assert.equal(audits['user-timings'].rawValue, false);
     });
   });