Repository: GoogleChrome/lighthouse
Pull request #9538 (2019-08-09 02:33:58)

core(audits): Point best practices audit links to web.dev
**Summary**
- Point "Learn more" links in best practices audits to relevant web.dev guides.
- Change last sentence in **Specifying a doctype prevents the browser** audit description from "Read more on the MDN Web Docs page" to "Learn more".
- Add "Learn more" link to **Detected JS libraries** audit.
- Add "Learn more" link to **Errors logged to the console indicate unresolved problems** audit.


Patch:
diff --git a/lighthouse-core/audits/apple-touch-icon.js b/lighthouse-core/audits/apple-touch-icon.js
index 46313a29e..eb5ad8486 100644
--- a/lighthouse-core/audits/apple-touch-icon.js
+++ b/lighthouse-core/audits/apple-touch-icon.js
@@ -17,13 +17,13 @@ const UIStrings = {
   title: 'Provides a valid `apple-touch-icon`',
   /** Title of a Lighthouse audit that tells the user that their site contains a vaild touch icon. This descriptive title is shown when the page does not contain a valid iOS touch icon. "apple-touch-icon" is an HTML attribute value and should not be translated. */
   failureTitle: 'Does not provide a valid `apple-touch-icon`',
-  /** Description of a Lighthouse audit that tells the user what having a valid apple-touch-icon does. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. "apple-touch-icon" is an HTML attribute value and should not be translated. */
-  description: 'For ideal appearance on iOS when users add to the home screen, define an ' +
-  'apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG. ' +
-  '[Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).',
+  /** Description of a Lighthouse audit that tells the user that having an apple-touch-icon allows websites to include an app icon to their installed progressive web apps, similar to a native app. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. "apple-touch-icon" is an HTML attribute value and should not be translated. */
+  description: 'For ideal appearance on iOS when users add a progressive web app to the home ' +
+    'screen, define an `apple-touch-icon`. It must point to a non-transparent 192px (or 180px) ' +
+    'square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).',
   /** Warning that HTML attribute `apple-touch-icon-precomposed` should not be used in favor of `apple-touch-icon`.  "apple-touch-icon-precomposed" and "apple-touch-icon" are HTML attribute values and should not be translated. */
   precomposedWarning: '`apple-touch-icon-precomposed` is out of date; ' +
-  '`apple-touch-icon` is preferred.',
+    '`apple-touch-icon` is preferred.',
 };
 
 const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
diff --git a/lighthouse-core/audits/content-width.js b/lighthouse-core/audits/content-width.js
index 710a269f9..308fa393a 100644
--- a/lighthouse-core/audits/content-width.js
+++ b/lighthouse-core/audits/content-width.js
@@ -6,6 +6,27 @@
 'use strict';
 
 const Audit = require('./audit.js');
+const i18n = require('../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that provides detail on the content size of a web site compared to the viewport, which is the size of the screen the site is displayed on. This descriptive title is shown to users when the site's content is sized appropriately. */
+  title: 'Content is sized correctly for the viewport',
+  /** Title of a Lighthouse audit that provides detail on the content size of a web site compared to the viewport, which is the size of the screen the site is displayed on. This descriptive title is shown to users when the site's content is not sized appropriately. */
+  failureTitle: 'Content is not sized correctly for the viewport',
+  /** Description of a Lighthouse audit that tells the user why they should care that a site's content size should match its viewport size, which is the size of the screen the site is displayed on. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'If the width of your app\'s content doesn\'t match the width ' +
+    'of the viewport, your app might not be optimized for mobile screens. ' +
+    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport).',
+  /**
+   * @description Explanatory message stating that the viewport size and window size differ.
+   * @example {100} innerWidth
+   * @example {101} outerWidth
+   * */
+  explanation: 'The viewport size of {innerWidth}px does not match the window ' +
+    'size of {outerWidth}px.',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class ContentWidth extends Audit {
   /**
@@ -14,11 +35,9 @@ class ContentWidth extends Audit {
   static get meta() {
     return {
       id: 'content-width',
-      title: 'Content is sized correctly for the viewport',
-      failureTitle: 'Content is not sized correctly for the viewport',
-      description: 'If the width of your app\'s content doesn\'t match the width ' +
-          'of the viewport, your app might not be optimized for mobile screens. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['ViewportDimensions', 'TestedAsMobileDevice'],
     };
   }
@@ -33,32 +52,26 @@ class ContentWidth extends Audit {
     const windowWidth = artifacts.ViewportDimensions.outerWidth;
     const widthsMatch = viewportWidth === windowWidth;
 
-    if (IsMobile) {
-      return {
-        score: Number(widthsMatch),
-        explanation: this.createExplanation(widthsMatch, artifacts.ViewportDimensions),
-      };
-    } else {
+    if (!IsMobile) {
       return {
         score: 1,
         notApplicable: true,
       };
     }
-  }
 
-  /**
-   * @param {boolean} match
-   * @param {LH.Artifacts.ViewportDimensions} artifact
-   * @return {string}
-   */
-  static createExplanation(match, artifact) {
-    if (match) {
-      return '';
+    let explanation = '';
+    if (!widthsMatch) {
+      explanation = str_(UIStrings.explanation,
+        {innerWidth: artifacts.ViewportDimensions.innerWidth,
+          outerWidth: artifacts.ViewportDimensions.outerWidth});
     }
 
-    return 'The viewport size is ' + artifact.innerWidth + 'px, ' +
-        'whereas the window size is ' + artifact.outerWidth + 'px.';
+    return {
+      score: Number(widthsMatch),
+      explanation,
+    };
   }
 }
 
 module.exports = ContentWidth;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/deprecations.js b/lighthouse-core/audits/deprecations.js
index f7eb7b14b..4dfa35a94 100644
--- a/lighthouse-core/audits/deprecations.js
+++ b/lighthouse-core/audits/deprecations.js
@@ -21,7 +21,7 @@ const UIStrings = {
   failureTitle: 'Uses deprecated APIs',
   /** Description of a Lighthouse audit that tells the user why they should not use deprecated APIs on their page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'Deprecated APIs will eventually be removed from the browser. ' +
-      '[Learn more](https://www.chromestatus.com/features#deprecated).',
+      '[Learn more](https://web.dev/deprecations).',
   /** [ICU Syntax] Label for the audit identifying the number of warnings generated by using deprecated APIs. */
   displayValue: `{itemCount, plural,
     =1 {1 warning found}
diff --git a/lighthouse-core/audits/dobetterweb/appcache-manifest.js b/lighthouse-core/audits/dobetterweb/appcache-manifest.js
index b20ab17af..7e4f0fa40 100644
--- a/lighthouse-core/audits/dobetterweb/appcache-manifest.js
+++ b/lighthouse-core/audits/dobetterweb/appcache-manifest.js
@@ -20,7 +20,7 @@ const UIStrings = {
   failureTitle: 'Uses Application Cache',
   /** Description of a Lighthouse audit that tells the user why they should not use the Application Cache API. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'Application Cache is deprecated. ' +
-    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/appcache).',
+    '[Learn more](https://web.dev/appcache-manifest).',
   /**
    * @description Label for the audit identifying uses of the Application Cache.
    * @example {clock.appcache} AppCacheManifest
diff --git a/lighthouse-core/audits/dobetterweb/doctype.js b/lighthouse-core/audits/dobetterweb/doctype.js
index 48f38df9c..78d86189e 100644
--- a/lighthouse-core/audits/dobetterweb/doctype.js
+++ b/lighthouse-core/audits/dobetterweb/doctype.js
@@ -15,8 +15,8 @@ const UIStrings = {
   failureTitle: 'Page lacks the HTML doctype, thus triggering quirks-mode',
   /** Description of a Lighthouse audit that tells the user why they should define an HTML doctype. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'Specifying a doctype prevents the browser ' +
-    'from switching to quirks-mode. Read more on the ' +
-    '[MDN Web Docs page](https://developer.mozilla.org/en-US/docs/Glossary/Doctype)',
+    'from switching to quirks-mode. ' +
+    '[Learn more](https://web.dev/doctype).',
   /** Explanatory message stating that the document has no doctype. */
   explanationNoDoctype: 'Document must contain a doctype',
   /** Explanatory message stating that the publicId field is not empty. */
diff --git a/lighthouse-core/audits/dobetterweb/dom-size.js b/lighthouse-core/audits/dobetterweb/dom-size.js
index 8e64ca7d8..b0cfc7c09 100644
--- a/lighthouse-core/audits/dobetterweb/dom-size.js
+++ b/lighthouse-core/audits/dobetterweb/dom-size.js
@@ -31,7 +31,7 @@ const UIStrings = {
     `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).',
+    'and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://web.dev/dom-size).',
   /** Table column header for the type of statistic. These statistics describe how big the DOM is (count of DOM elements, children, depth). */
   columnStatistic: 'Statistic',
   /** Table column header for the DOM element. Each DOM element is described with its HTML representation. */
diff --git a/lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js b/lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js
index f8ccee8ff..5fc0bdd18 100644
--- a/lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js
+++ b/lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js
@@ -17,7 +17,7 @@ const UIStrings = {
   /** Description of a Lighthouse audit that tells the user why and how they should secure cross-origin links. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'Add `rel="noopener"` or `rel="noreferrer"` to any external links to improve ' +
     'performance and prevent security vulnerabilities. ' +
-    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/noopener).',
+    '[Learn more](https://web.dev/external-anchors-use-rel-noopener).',
   /**
    * @description Warning that some links' destinations cannot be determined and therefore the audit cannot evaluate the link's safety.
    * @example {<a target="_blank">} anchorHTML
diff --git a/lighthouse-core/audits/dobetterweb/geolocation-on-start.js b/lighthouse-core/audits/dobetterweb/geolocation-on-start.js
index 208040e26..abad7e916 100644
--- a/lighthouse-core/audits/dobetterweb/geolocation-on-start.js
+++ b/lighthouse-core/audits/dobetterweb/geolocation-on-start.js
@@ -22,7 +22,7 @@ const UIStrings = {
   /** Description of a Lighthouse audit that tells the user why they should not ask for geolocation permissions on load. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'Users are mistrustful of or confused by sites that request their ' +
     'location without context. Consider tying the request to a user action instead. ' +
-    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/geolocation-on-load).',
+    '[Learn more](https://web.dev/geolocation-on-start).',
 };
 
 const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
diff --git a/lighthouse-core/audits/dobetterweb/js-libraries.js b/lighthouse-core/audits/dobetterweb/js-libraries.js
index 7a30e9f8d..ac1e4d170 100644
--- a/lighthouse-core/audits/dobetterweb/js-libraries.js
+++ b/lighthouse-core/audits/dobetterweb/js-libraries.js
@@ -17,7 +17,7 @@ const UIStrings = {
   /** Title of a Lighthouse audit that provides detail on the Javascript libraries that are used on the page. */
   title: 'Detected JavaScript libraries',
   /** Description of a Lighthouse audit that tells the user what this audit is detecting. This is displayed after a user expands the section to see more. No character length limits. */
-  description: 'All front-end JavaScript libraries detected on the page.',
+  description: 'All front-end JavaScript libraries detected on the page. [Learn more](https://web.dev/js-libraries).',
   /** Label for a column in a data table; entries will be the version numbers of the detected Javascript libraries.  */
   columnVersion: 'Version',
 };
diff --git a/lighthouse-core/audits/dobetterweb/no-document-write.js b/lighthouse-core/audits/dobetterweb/no-document-write.js
index 77368e048..e470353fe 100644
--- a/lighthouse-core/audits/dobetterweb/no-document-write.js
+++ b/lighthouse-core/audits/dobetterweb/no-document-write.js
@@ -21,7 +21,7 @@ const UIStrings = {
   /** Description of a Lighthouse audit that tells the user why they should avoid `document.write`. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'For users on slow connections, external scripts dynamically injected via ' +
       '`document.write()` can delay page load by tens of seconds. ' +
-      '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/document-write).',
+      '[Learn more](https://web.dev/no-document-write).',
 };
 
 const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
diff --git a/lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js b/lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js
index ec3036e10..7e875b6a2 100644
--- a/lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js
+++ b/lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js
@@ -28,7 +28,7 @@ const UIStrings = {
   /** Description of a Lighthouse audit that tells the user why they should be concerned about the third party Javascript libraries that they use. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'Some third-party scripts may contain known security vulnerabilities ' +
     'that are easily identified and exploited by attackers. ' +
-    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/vulnerabilities).',
+    '[Learn more](https://web.dev/no-vulnerable-libraries).',
   /** [ICU Syntax] Label for the audit identifying the number of vulnerable Javascript libraries found. */
   displayValue: `{itemCount, plural,
     =1 {1 vulnerability detected}
diff --git a/lighthouse-core/audits/dobetterweb/notification-on-start.js b/lighthouse-core/audits/dobetterweb/notification-on-start.js
index 25559a96c..490a526f5 100644
--- a/lighthouse-core/audits/dobetterweb/notification-on-start.js
+++ b/lighthouse-core/audits/dobetterweb/notification-on-start.js
@@ -22,7 +22,7 @@ const UIStrings = {
   /** Description of a Lighthouse audit that tells the user why they should not ask for notification permission on load. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'Users are mistrustful of or confused by sites that request to send ' +
       'notifications without context. Consider tying the request to user gestures ' +
-      'instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/notifications-on-load).',
+      'instead. [Learn more](https://web.dev/notification-on-start).',
 };
 
 const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
diff --git a/lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js b/lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js
index b417988ca..3e90b7f06 100644
--- a/lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js
+++ b/lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js
@@ -15,7 +15,7 @@ const UIStrings = {
   failureTitle: 'Prevents users to paste into password fields',
   /** Description of a Lighthouse audit that tells the user why they should allow pasting of content into password fields. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'Preventing password pasting undermines good security policy. ' +
-      '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/password-pasting).',
+      '[Learn more](https://web.dev/password-inputs-can-be-pasted-into).',
   /** Table column header for the HTML elements that do not allow pasting of content. */
   columnFailingElem: 'Failing Elements',
 };
diff --git a/lighthouse-core/audits/dobetterweb/uses-http2.js b/lighthouse-core/audits/dobetterweb/uses-http2.js
index 73d2524fb..10d980ef0 100644
--- a/lighthouse-core/audits/dobetterweb/uses-http2.js
+++ b/lighthouse-core/audits/dobetterweb/uses-http2.js
@@ -23,7 +23,7 @@ const UIStrings = {
   failureTitle: 'Does not use HTTP/2 for all of its resources',
   /** Description of a Lighthouse audit that tells the user why they should use HTTP/2. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'HTTP/2 offers many benefits over HTTP/1.1, including binary headers, ' +
-      'multiplexing, and server push. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http2).',
+      'multiplexing, and server push. [Learn more](https://web.dev/uses-http2).',
   /** [ICU Syntax] Label identifying the number of network requests that were not served with HTTP/2. */
   displayValue: `{itemCount, plural,
     =1 {1 request not served via HTTP/2}
diff --git a/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js b/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js
index 24e476c0a..b1a638780 100644
--- a/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js
+++ b/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js
@@ -22,7 +22,7 @@ const UIStrings = {
   /** Description of a Lighthouse audit that tells the user why they should use passive event listeners on the page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'Consider marking your touch and wheel event listeners as `passive` ' +
       'to improve your page\'s scroll performance. ' +
-      '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners).',
+      '[Learn more](https://web.dev/uses-passive-event-listeners).',
 };
 
 const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
diff --git a/lighthouse-core/audits/errors-in-console.js b/lighthouse-core/audits/errors-in-console.js
index 22b6afae4..05cff456b 100644
--- a/lighthouse-core/audits/errors-in-console.js
+++ b/lighthouse-core/audits/errors-in-console.js
@@ -20,7 +20,8 @@ const UIStrings = {
   failureTitle: 'Browser errors were logged to the console',
   /** Description of a Lighthouse audit that tells the user why errors being logged to the devtools console are a cause for concern and so should be fixed. This is displayed after a user expands the section to see more. No character length limits. */
   description: 'Errors logged to the console indicate unresolved problems. ' +
-    'They can come from network request failures and other browser concerns.',
+    'They can come from network request failures and other browser concerns. ' +
+    '[Learn more](https://web.dev/errors-in-console)',
   /**  Label for a column in a data table; entries in the column will be the descriptions of logged browser errors. */
   columnDesc: 'Description',
 };
diff --git a/lighthouse-core/audits/image-aspect-ratio.js b/lighthouse-core/audits/image-aspect-ratio.js
index b736977df..3b5114c3f 100644
--- a/lighthouse-core/audits/image-aspect-ratio.js
+++ b/lighthouse-core/audits/image-aspect-ratio.js
@@ -22,7 +22,7 @@ const UIStrings = {
   failureTitle: 'Displays images with incorrect aspect ratio',
   /** Description of a Lighthouse audit that tells the user why they should maintain the correct aspect ratios for all images. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
   description: 'Image display dimensions should match natural aspect ratio. ' +
-    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/aspect-ratio).',
+    '[Learn more](https://web.dev/image-aspect-ratio).',
   /**
    * @description Warning that the size information for an image was nonsensical.
    * @example {https://image.cdn.com/} url
diff --git a/lighthouse-core/audits/installable-manifest.js b/lighthouse-core/audits/installable-manifest.js
index 280f05d24..952ab0656 100644
--- a/lighthouse-core/audits/installable-manifest.js
+++ b/lighthouse-core/audits/installable-manifest.js
@@ -7,6 +7,20 @@
 
 const MultiCheckAudit = require('./multi-check-audit.js');
 const ManifestValues = require('../computed/manifest-values.js');
+const i18n = require('../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that provides detail on if a website is installable as an application. This descriptive title is shown to users when a webapp is installable. */
+  title: 'Web app manifest meets the installability requirements',
+  /** Title of a Lighthouse audit that provides detail on if a website is installable as an application. This descriptive title is shown to users when a webapp is not installable. */
+  failureTitle: 'Web app manifest does not meet the installability requirements',
+  /** Description of a Lighthouse audit that tells the user why installability is important for webapps. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'Browsers can proactively prompt users to add your app to their homescreen, ' +
+    'which can lead to higher engagement. ' +
+    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/install-prompt).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 /**
  * @fileoverview
@@ -29,11 +43,9 @@ class InstallableManifest extends MultiCheckAudit {
   static get meta() {
     return {
       id: 'installable-manifest',
-      title: 'Web app manifest meets the installability requirements',
-      failureTitle: 'Web app manifest does not meet the installability requirements',
-      description: 'Browsers can proactively prompt users to add your app to their homescreen, ' +
-          'which can lead to higher engagement. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/install-prompt).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['URL', 'WebAppManifest'],
     };
   }
@@ -92,3 +104,4 @@ class InstallableManifest extends MultiCheckAudit {
 }
 
 module.exports = InstallableManifest;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/is-on-https.js b/lighthouse-core/audits/is-on-https.js
index 9b1d245fd..707df928f 100644
--- a/lighthouse-core/audits/is-on-https.js
+++ b/lighthouse-core/audits/is-on-https.js
@@ -20,7 +20,7 @@ const UIStrings = {
       'sensitive data. HTTPS prevents intruders from tampering with or passively listening ' +
       'in on the communications between your app and your users, and is a prerequisite for ' +
       'HTTP/2 and many new web platform APIs. ' +
-      '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/https).',
+      '[Learn more](https://web.dev/is-on-https).',
   /** [ICU Syntax] Label identifying the number of insecure network requests found by an audit of a web page. */
   displayValue: `{itemCount, plural,
     =1 {1 insecure request found}
diff --git a/lighthouse-core/audits/load-fast-enough-for-pwa.js b/lighthouse-core/audits/load-fast-enough-for-pwa.js
index 052cedcc2..68f500eb9 100644
--- a/lighthouse-core/audits/load-fast-enough-for-pwa.js
+++ b/lighthouse-core/audits/load-fast-enough-for-pwa.js
@@ -33,6 +33,10 @@ const UIStrings = {
   /** Label for the audit identifying the time it took for the page to become interactive on a mobile network. */
   displayValueTextWithOverride: 'Interactive on simulated mobile network at ' +
   '{timeInMs, number, seconds}\xa0s',
+  /** Explanatory message displayed when a web page loads too slowly to be considered quickly interactive. This references another Lighthouse auditing category, "Performance", that can give additional details on performance debugging.  */
+  explanationLoadSlow: 'Your page loads too slowly and is not interactive within 10 seconds. ' +
+    'Look at the opportunities and diagnostics in the "Performance" section to learn how to ' +
+    'improve.',
 };
 
 const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
@@ -86,9 +90,7 @@ class LoadFastEnough4Pwa extends Audit {
     let explanation;
     if (!score) {
       displayValue = str_(displayValueTemplate, {timeInMs: tti.timing});
-      explanation = 'Your page loads too slowly and is not interactive within 10 seconds. ' +
-        'Look at the opportunities and diagnostics in the "Performance" section to learn how to ' +
-        'improve.';
+      explanation = str_(UIStrings.explanationLoadSlow);
     }
 
     return {
diff --git a/lighthouse-core/audits/manual/pwa-cross-browser.js b/lighthouse-core/audits/manual/pwa-cross-browser.js
index b28bf97a5..57d073a58 100644
--- a/lighthouse-core/audits/manual/pwa-cross-browser.js
+++ b/lighthouse-core/audits/manual/pwa-cross-browser.js
@@ -7,6 +7,17 @@
 'use strict';
 
 const ManualAudit = require('./manual-audit.js');
+const i18n = require('../../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that prompts the user to manually check that their site works across different web browsers. */
+  title: 'Site works cross-browser',
+  /** Description of a Lighthouse audit that tells the user why they should make sites work across different browsers. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'To reach the most number of users, sites should work across ' +
+  'every major browser. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 /**
  * @fileoverview Manual PWA audit for cross browser support.
@@ -19,11 +30,12 @@ class PWACrossBrowser extends ManualAudit {
   static get meta() {
     return Object.assign({
       id: 'pwa-cross-browser',
-      description: 'To reach the most number of users, sites should work across ' +
-      'every major browser. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser).',
-      title: 'Site works cross-browser',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
     }, super.partialMeta);
   }
 }
 
 module.exports = PWACrossBrowser;
+module.exports.UIStrings = UIStrings;
+
diff --git a/lighthouse-core/audits/manual/pwa-each-page-has-url.js b/lighthouse-core/audits/manual/pwa-each-page-has-url.js
index 835cd8d06..6b6eec477 100644
--- a/lighthouse-core/audits/manual/pwa-each-page-has-url.js
+++ b/lighthouse-core/audits/manual/pwa-each-page-has-url.js
@@ -6,6 +6,17 @@
 'use strict';
 
 const ManualAudit = require('./manual-audit.js');
+const i18n = require('../../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that prompts the user to manually check that each page on their website uses a unique URL. */
+  title: 'Each page has a URL',
+  /** Description of a Lighthouse audit that tells the user why they should use unique URLs for each web page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'Ensure individual pages are deep linkable via URL and that URLs are ' +
+      'unique for the purpose of shareability on social media. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 /**
  * @fileoverview Manual PWA audit to ensure every page has a deep link.
@@ -18,11 +29,11 @@ class PWAEachPageHasURL extends ManualAudit {
   static get meta() {
     return Object.assign({
       id: 'pwa-each-page-has-url',
-      description: 'Ensure individual pages are deep linkable via the URLs and that URLs are ' +
-          'unique for the purpose of shareability on social media. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url).',
-      title: 'Each page has a URL',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
     }, super.partialMeta);
   }
 }
 
 module.exports = PWAEachPageHasURL;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/manual/pwa-page-transitions.js b/lighthouse-core/audits/manual/pwa-page-transitions.js
index 10cbe7bdb..5de70a73f 100644
--- a/lighthouse-core/audits/manual/pwa-page-transitions.js
+++ b/lighthouse-core/audits/manual/pwa-page-transitions.js
@@ -6,6 +6,17 @@
 'use strict';
 
 const ManualAudit = require('./manual-audit.js');
+const i18n = require('../../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that prompts the user to manually check that page transitions (navigating to other pages on a website) shouldn't feel like they are waiting for the network to load. */
+  title: 'Page transitions don\'t feel like they block on the network',
+  /** Description of a Lighthouse audit that tells the user why they should make transitions in their web app feel fast. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'Transitions should feel snappy as you tap around, even on a slow network, a ' +
+      'key to perceived performance. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 /**
  * @fileoverview Manual PWA audit for janky-free page transitions.
@@ -18,11 +29,11 @@ class PWAPageTransitions extends ManualAudit {
   static get meta() {
     return Object.assign({
       id: 'pwa-page-transitions',
-      description: 'Transitions should feel snappy as you tap around, even on a slow network, a ' +
-          'key to perceived performance. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network).',
-      title: 'Page transitions don\'t feel like they block on the network',
+      title: str_(UIStrings.title),
+      description: str_(UIStrings.description),
     }, super.partialMeta);
   }
 }
 
 module.exports = PWAPageTransitions;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/multi-check-audit.js b/lighthouse-core/audits/multi-check-audit.js
index afb099ffb..22a4def70 100644
--- a/lighthouse-core/audits/multi-check-audit.js
+++ b/lighthouse-core/audits/multi-check-audit.js
@@ -53,6 +53,7 @@ class MultiCheckAudit extends Audit {
     if (result.failures.length > 0) {
       return {
         score: 0,
+        // TODO(#7238): make this i18n-able.
         explanation: `Failures: ${result.failures.join(',\n')}.`,
         details,
       };
diff --git a/lighthouse-core/audits/offline-start-url.js b/lighthouse-core/audits/offline-start-url.js
index 72d4c2282..88b78d248 100644
--- a/lighthouse-core/audits/offline-start-url.js
+++ b/lighthouse-core/audits/offline-start-url.js
@@ -6,6 +6,25 @@
 'use strict';
 
 const Audit = require('./audit.js');
+const i18n = require('../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that provides detail on the a website's offline capabilities. "200" refers to the HTTP status code when a site responds successfully. This descriptive title is shown to users when the test page responds successfully while offline. */
+  title: '`start_url` responds with a 200 when offline',
+  /** Title of a Lighthouse audit that provides detail on the a website's offline capabilities. "200" refers to the HTTP status code when a site responds successfully. This descriptive title is shown to users when the test page does not respond successfully while offline. */
+  failureTitle: '`start_url` does not respond with a 200 when offline',
+  /** Description of a Lighthouse audit that tells the user why a website should respond to requests when offline. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'A service worker enables your web app to be reliable in unpredictable ' +
+    'network conditions. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline).',
+  /**
+   * @description Warning that the audit couldn't find the start_url and used the page's URL instead.
+   * @example {No Manifest Fetched.} manifestWarning
+   * */
+  warningCantStart: 'Lighthouse couldn\'t read the `start_url` from the manifest. As a result, ' +
+  'the `start_url` was assumed to be the document\'s URL. Error message: \'{manifestWarning}\'.',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class OfflineStartUrl extends Audit {
   /**
@@ -14,9 +33,9 @@ class OfflineStartUrl extends Audit {
   static get meta() {
     return {
       id: 'offline-start-url',
-      title: 'start_url responds with a 200 when offline',
-      failureTitle: 'start_url does not respond with a 200 when offline',
-      description: 'A service worker enables your web app to be reliable in unpredictable network conditions. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['WebAppManifest', 'StartUrl'],
     };
   }
@@ -32,8 +51,7 @@ class OfflineStartUrl extends Audit {
     const manifest = artifacts.WebAppManifest;
     if (manifest && manifest.value && manifest.value.start_url.warning) {
       const manifestWarning = manifest.value.start_url.warning;
-      warnings.push('We couldn\'t read the start_url from the manifest. As a result, the ' +
-          `start_url was assumed to be the document's URL. Error message: '${manifestWarning}'.`);
+      warnings.push(str_(UIStrings.warningCantStart, {manifestWarning}));
     }
 
     const hasOfflineStartUrl = artifacts.StartUrl.statusCode === 200;
@@ -47,3 +65,4 @@ class OfflineStartUrl extends Audit {
 }
 
 module.exports = OfflineStartUrl;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/redirects-http.js b/lighthouse-core/audits/redirects-http.js
index 9162a18b0..ecca81a09 100644
--- a/lighthouse-core/audits/redirects-http.js
+++ b/lighthouse-core/audits/redirects-http.js
@@ -6,6 +6,19 @@
 'use strict';
 
 const Audit = require('./audit.js');
+const i18n = require('../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that provides detail on HTTP to HTTPS redirects. This descriptive title is shown to users when HTTP traffic is redirected to HTTPS. */
+  title: 'Redirects HTTP traffic to HTTPS',
+  /** Title of a Lighthouse audit that provides detail on HTTP to HTTPS redirects. This descriptive title is shown to users when HTTP traffic is not redirected to HTTPS. */
+  failureTitle: 'Does not redirect HTTP traffic to HTTPS',
+  /** Description of a Lighthouse audit that tells the user why they should direct HTTP traffic to HTTPS. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'If you\'ve already set up HTTPS, make sure that you redirect all HTTP ' +
+    'traffic to HTTPS in order to enable secure web features for all your users. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class RedirectsHTTP extends Audit {
   /**
@@ -14,10 +27,9 @@ class RedirectsHTTP extends Audit {
   static get meta() {
     return {
       id: 'redirects-http',
-      title: 'Redirects HTTP traffic to HTTPS',
-      failureTitle: 'Does not redirect HTTP traffic to HTTPS',
-      description: 'If you\'ve already set up HTTPS, make sure that you redirect all HTTP ' +
-         'traffic to HTTPS. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['HTTPRedirect'],
     };
   }
@@ -34,3 +46,4 @@ class RedirectsHTTP extends Audit {
 }
 
 module.exports = RedirectsHTTP;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/service-worker.js b/lighthouse-core/audits/service-worker.js
index f5518c937..ad2e21490 100644
--- a/lighthouse-core/audits/service-worker.js
+++ b/lighthouse-core/audits/service-worker.js
@@ -7,6 +7,39 @@
 
 const URL = require('../lib/url-shim.js');
 const Audit = require('./audit.js');
+const i18n = require('../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that provides detail on a page's service worker. This descriptive title is shown to users when a service worker is registered and valid. */
+  title: 'Registers a service worker that controls page and `start_url`',
+  /** Title of a Lighthouse audit that provides detail on a page's service worker. This descriptive title is shown to users when a service worker is not present or invalid. */
+  failureTitle: 'Does not register a service worker that controls page and `start_url`',
+  /** Description of a Lighthouse audit that tells the user why they should use a service worker. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'The service worker is the technology that enables your app to use many ' +
+    'Progressive Web App features, such as offline, add to homescreen, and push ' +
+    'notifications. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker).',
+  /**
+   * @description Message explaining that the website may have service workers, but none are in scope to control the tested web page.
+   * @example {https://example.com/} pageUrl
+   * */
+  explanationOutOfScope: 'This origin has one or more service workers, however the page ' +
+    '({pageUrl}) is not in scope.',
+  /** Message explaining that the page has no manifest file so can't specify a starting url. */
+  explanationNoManifest: 'This page is controlled by a service worker, however ' +
+    'no `start_url` was found because no manifest was fetched.',
+  /** Message explaining that the page had an invalid manifest file so can't specify a starting url. */
+  explanationBadManifest: 'This page is controlled by a service worker, however ' +
+    'no `start_url` was found because manifest failed to parse as valid JSON',
+  /**
+   * @description Message explaining that the website has a service worker, but none are in scope to control the tested starting url.
+   * @example {https://example.com/} startUrl
+   * @example {https://othersite.com/} scopeUrl
+   * */
+  explanationBadStartUrl: 'This page is controlled by a service worker, however ' +
+    'the `start_url` ({startUrl}) is not in the service worker\'s scope ({scopeUrl})',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class ServiceWorker extends Audit {
   /**
@@ -15,11 +48,9 @@ class ServiceWorker extends Audit {
   static get meta() {
     return {
       id: 'service-worker',
-      title: 'Registers a service worker that controls page and start_url',
-      failureTitle: 'Does not register a service worker that controls page and start_url',
-      description: 'The service worker is the technology that enables your app to use many ' +
-         'Progressive Web App features, such as offline, add to homescreen, and push ' +
-         'notifications. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['URL', 'ServiceWorker', 'WebAppManifest'],
     };
   }
@@ -70,15 +101,15 @@ class ServiceWorker extends Audit {
    */
   static checkStartUrl(manifest, scopeUrl) {
     if (!manifest) {
-      return 'no start_url was found because no manifest was fetched';
+      return str_(UIStrings.explanationNoManifest);
     }
     if (!manifest.value) {
-      return 'no start_url was found because manifest failed to parse as valid JSON';
+      return str_(UIStrings.explanationBadManifest);
     }
 
     const startUrl = manifest.value.start_url.value;
     if (!startUrl.startsWith(scopeUrl)) {
-      return `the start_url ("${startUrl}") is not in the service worker's scope ("${scopeUrl}")`;
+      return str_(UIStrings.explanationBadStartUrl, {startUrl, scopeUrl});
     }
   }
 
@@ -103,7 +134,7 @@ class ServiceWorker extends Audit {
     if (!controllingScopeUrl) {
       return {
         score: 0,
-        explanation: `This origin has one or more service workers, however the page ("${pageUrl.href}") is not in scope.`, // eslint-disable-line max-len
+        explanation: str_(UIStrings.explanationOutOfScope, {pageUrl: pageUrl.href}),
       };
     }
 
@@ -112,7 +143,7 @@ class ServiceWorker extends Audit {
     if (startUrlFailure) {
       return {
         score: 0,
-        explanation: `This page is controlled by a service worker, however ${startUrlFailure}.`,
+        explanation: startUrlFailure,
       };
     }
 
@@ -124,3 +155,4 @@ class ServiceWorker extends Audit {
 }
 
 module.exports = ServiceWorker;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/splash-screen.js b/lighthouse-core/audits/splash-screen.js
index 7a4cc0af5..69f83a3ea 100644
--- a/lighthouse-core/audits/splash-screen.js
+++ b/lighthouse-core/audits/splash-screen.js
@@ -7,6 +7,20 @@
 
 const MultiCheckAudit = require('./multi-check-audit.js');
 const ManifestValues = require('../computed/manifest-values.js');
+const i18n = require('../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that provides detail on splash screens. This descriptive title is shown to users when the site has a custom splash screen. */
+  title: 'Configured for a custom splash screen',
+  /** Title of a Lighthouse audit that provides detail on splash screens. This descriptive title is shown to users when the site does not have a custom splash screen. */
+  failureTitle: 'Is not configured for a custom splash screen',
+  /** Description of a Lighthouse audit that tells the user why they should configure a custom splash screen. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'A themed splash screen ensures a high-quality experience when ' +
+    'users launch your app from their homescreens. [Learn ' +
+    'more](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 /**
  * @fileoverview
@@ -28,11 +42,9 @@ class SplashScreen extends MultiCheckAudit {
   static get meta() {
     return {
       id: 'splash-screen',
-      title: 'Configured for a custom splash screen',
-      failureTitle: 'Is not configured for a custom splash screen',
-      description: 'A themed splash screen ensures a high-quality experience when ' +
-          'users launch your app from their homescreens. [Learn ' +
-          'more](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['WebAppManifest'],
     };
   }
@@ -83,3 +95,4 @@ class SplashScreen extends MultiCheckAudit {
 }
 
 module.exports = SplashScreen;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/themed-omnibox.js b/lighthouse-core/audits/themed-omnibox.js
index 6d2deca8f..2295a98ff 100644
--- a/lighthouse-core/audits/themed-omnibox.js
+++ b/lighthouse-core/audits/themed-omnibox.js
@@ -8,6 +8,19 @@
 const MultiCheckAudit = require('./multi-check-audit.js');
 const ManifestValues = require('../computed/manifest-values.js');
 const cssParsers = require('cssstyle/lib/parsers');
+const i18n = require('../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that provides detail on the theme color the web page has set for the browser's address bar. This descriptive title is shown to users when an address-bar theme color has been set. */
+  title: 'Sets a theme color for the address bar.',
+  /** Title of a Lighthouse audit that provides detail on the theme color the web page has set for the browser's address bar. This descriptive title is shown to users when an address-bar theme color has not been set. */
+  failureTitle: 'Does not set a theme color for the address bar.',
+  /** Description of a Lighthouse audit that tells the user why they should set a theme color for the browser's address bar. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'The browser address bar can be themed to match your site. ' +
+    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/address-bar).',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 /**
  * @fileoverview
@@ -26,10 +39,9 @@ class ThemedOmnibox extends MultiCheckAudit {
   static get meta() {
     return {
       id: 'themed-omnibox',
-      title: 'Sets an address-bar theme color',
-      failureTitle: 'Does not set an address-bar theme color',
-      description: 'The browser address bar can be themed to match your site. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/address-bar).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['WebAppManifest', 'MetaElements'],
     };
   }
@@ -48,6 +60,7 @@ class ThemedOmnibox extends MultiCheckAudit {
    */
   static assessMetaThemecolor(themeColorMeta, failures) {
     if (!themeColorMeta) {
+      // TODO(#7238): i18n
       failures.push('No `<meta name="theme-color">` tag found');
     } else if (!ThemedOmnibox.isValidColor(themeColorMeta.content || '')) {
       failures.push('The theme-color meta tag did not contain a valid CSS color');
@@ -93,3 +106,4 @@ class ThemedOmnibox extends MultiCheckAudit {
 }
 
 module.exports = ThemedOmnibox;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/viewport.js b/lighthouse-core/audits/viewport.js
index 000e53eca..6f67a0de1 100644
--- a/lighthouse-core/audits/viewport.js
+++ b/lighthouse-core/audits/viewport.js
@@ -7,6 +7,22 @@
 
 const Audit = require('./audit.js');
 const ComputedViewportMeta = require('../computed/viewport-meta.js');
+const i18n = require('../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that provides detail on the viewport meta tag in a web page's html. This descriptive title is shown to users when a viewport tag is set and configured. */
+  title: 'Has a `<meta name="viewport">` tag with `width` or `initial-scale`',
+  /** Title of a Lighthouse audit that provides detail on the viewport meta tag in a web page's html. This descriptive title is shown to users when a viewport tag is not set or configured. */
+  failureTitle: 'Does not have a `<meta name="viewport">` tag with `width` ' +
+    'or `initial-scale`',
+  /** Description of a Lighthouse audit that tells the user why they should have a viewport meta tag in their html. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'Add a `<meta name="viewport">` tag to optimize your app for mobile screens. ' +
+    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag).',
+  /** Explanatory message stating that no viewport meta tag exists on the page. */
+  explanationNoTag: 'No `<meta name="viewport">` tag found',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class Viewport extends Audit {
   /**
@@ -15,11 +31,9 @@ class Viewport extends Audit {
   static get meta() {
     return {
       id: 'viewport',
-      title: 'Has a `<meta name="viewport">` tag with `width` or `initial-scale`',
-      failureTitle: 'Does not have a `<meta name="viewport">` tag with `width` ' +
-          'or `initial-scale`',
-      description: 'Add a viewport meta tag to optimize your app for mobile screens. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['MetaElements'],
     };
   }
@@ -35,7 +49,7 @@ class Viewport extends Audit {
     if (!viewportMeta.hasViewportTag) {
       return {
         score: 0,
-        explanation: 'No viewport meta tag found',
+        explanation: str_(UIStrings.explanationNoTag),
       };
     }
 
@@ -47,3 +61,5 @@ class Viewport extends Audit {
 }
 
 module.exports = Viewport;
+module.exports.UIStrings = UIStrings;
+
diff --git a/lighthouse-core/audits/without-javascript.js b/lighthouse-core/audits/without-javascript.js
index acbcb5169..7f93e95fa 100644
--- a/lighthouse-core/audits/without-javascript.js
+++ b/lighthouse-core/audits/without-javascript.js
@@ -6,6 +6,22 @@
 'use strict';
 
 const Audit = require('./audit.js');
+const i18n = require('../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that provides detail on the web page's ability to return content with Javascript disabled in the browser. This descriptive title is shown to users when at least some content is shown when Javascript is not available. */
+  title: 'Contains some content when JavaScript is not available',
+  /** Title of a Lighthouse audit that provides detail on the web page's ability to return content with Javascript disabled in the browser. This descriptive title is shown to users when no content is shown when Javascript is not available. */
+  failureTitle: 'Does not provide fallback content when JavaScript is not available',
+  /** Description of a Lighthouse audit that tells the user why they should return content even if Javascript is unavailable in a browser. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'Your app should display some content when JavaScript is disabled, even if ' +
+    'it\'s just a warning to the user that JavaScript is required to use the app. ' +
+    '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/no-js).',
+  /** Message explaining that a website's body should render some (any) content even if the page's JavaScript cannot be loaded. */
+  explanation: 'The page body should render some content if its scripts are not available.',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class WithoutJavaScript extends Audit {
   /**
@@ -14,11 +30,9 @@ class WithoutJavaScript extends Audit {
   static get meta() {
     return {
       id: 'without-javascript',
-      title: 'Contains some content when JavaScript is not available',
-      failureTitle: 'Does not provide fallback content when JavaScript is not available',
-      description: 'Your app should display some content when JavaScript is disabled, even if ' +
-          'it\'s just a warning to the user that JavaScript is required to use the app. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/no-js).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['HTMLWithoutJavaScript'],
     };
   }
@@ -34,7 +48,7 @@ class WithoutJavaScript extends Audit {
     if (artifact.bodyText.trim() === '' && !artifact.hasNoScript) {
       return {
         score: 0,
-        explanation: 'The page body should render some content if its scripts are not available.',
+        explanation: str_(UIStrings.explanation),
       };
     }
 
@@ -45,3 +59,4 @@ class WithoutJavaScript extends Audit {
 }
 
 module.exports = WithoutJavaScript;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/audits/works-offline.js b/lighthouse-core/audits/works-offline.js
index 6d0f4446c..187b3486b 100644
--- a/lighthouse-core/audits/works-offline.js
+++ b/lighthouse-core/audits/works-offline.js
@@ -7,6 +7,28 @@
 
 const URL = require('../lib/url-shim.js');
 const Audit = require('./audit.js');
+const i18n = require('../lib/i18n/i18n.js');
+
+const UIStrings = {
+  /** Title of a Lighthouse audit that provides detail on the offline responsiveness of a website. "200" refers to the HTTP status code when a site responds successfully. This descriptive title is shown to users when the site responds successfully even when offline. */
+  title: 'Current page responds with a 200 when offline',
+  /** Title of a Lighthouse audit that provides detail on the offline responsiveness of a website. "200" refers to the HTTP status code when a site responds successfully. This descriptive title is shown to users when the site does not respond successfully when offline. */
+  failureTitle: 'Current page does not respond with a 200 when offline',
+  /** Description of a Lighthouse audit that tells the user why a website should respond to requests when offline. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  description: 'If you\'re building a Progressive Web App, consider using a service worker ' +
+      'so that your app can work offline. ' +
+      '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline).',
+  /**
+   * @description Warning that the web page redirected during testing and that may have affected the offline load test.
+   * @example {https://example.com/requested/page} requested
+   * @example {https://example.com/final/resolved/page} final
+   * */
+  warningNoLoad: 'The page may not be loading offline because your test URL ' +
+  `({requested}) was redirected to "{final}". ` +
+  'Try testing the second URL directly.',
+};
+
+const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
 
 class WorksOffline extends Audit {
   /**
@@ -15,11 +37,9 @@ class WorksOffline extends Audit {
   static get meta() {
     return {
       id: 'works-offline',
-      title: 'Current page responds with a 200 when offline',
-      failureTitle: 'Current page does not respond with a 200 when offline',
-      description: 'If you\'re building a Progressive Web App, consider using a service worker ' +
-          'so that your app can work offline. ' +
-          '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline).',
+      title: str_(UIStrings.title),
+      failureTitle: str_(UIStrings.failureTitle),
+      description: str_(UIStrings.description),
       requiredArtifacts: ['Offline', 'URL'],
     };
   }
@@ -33,9 +53,8 @@ class WorksOffline extends Audit {
     const passed = artifacts.Offline === 200;
     if (!passed &&
         !URL.equalWithExcludedFragments(artifacts.URL.requestedUrl, artifacts.URL.finalUrl)) {
-      warnings.push('You may be not loading offline because your test URL ' +
-          `(${artifacts.URL.requestedUrl}) was redirected to "${artifacts.URL.finalUrl}". ` +
-          'Try testing the second URL directly.');
+      warnings.push(str_(UIStrings.warningNoLoad,
+        {requested: artifacts.URL.requestedUrl, final: artifacts.URL.finalUrl}));
     }
 
     return {
@@ -46,3 +65,4 @@ class WorksOffline extends Audit {
 }
 
 module.exports = WorksOffline;
+module.exports.UIStrings = UIStrings;
diff --git a/lighthouse-core/config/default-config.js b/lighthouse-core/config/default-config.js
index 0395215f3..085e1f0dd 100644
--- a/lighthouse-core/config/default-config.js
+++ b/lighthouse-core/config/default-config.js
@@ -94,6 +94,15 @@ const UIStrings = {
   seoCrawlingGroupTitle: 'Crawling and Indexing',
   /** Description of the navigation section within the Search Engine Optimization (SEO) category. Within this section are audits with descriptive titles that highlight ways to make a website accessible to search engine crawlers. */
   seoCrawlingGroupDescription: 'To appear in search results, crawlers need access to your app.',
+  /** Title of the Progressive Web Application (PWA) category of audits. This is displayed at the top of a list of audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc. Also used as a label of a score gauge. */
+  pwaCategoryTitle: 'Progressive Web App',
+  /** Description of the Progressive Web Application (PWA) category. This is displayed at the top of a list of audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc. No character length limits. 'Learn More' becomes link text to additional documentation. */
+  pwaCategoryDescription: 'These checks validate the aspects of a Progressive Web App. ' +
+  '[Learn more](https://developers.google.com/web/progressive-web-apps/checklist).',
+  /** Description of the Progressive Web Application (PWA) manual checks category, containing a list of additional validators must be run by hand in order to check all PWA best practices. This is displayed at the top of a list of manually run audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc.. No character length limits. */
+  pwaCategoryManualDescription: 'These checks are required by the baseline ' +
+  '[PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist) but are ' +
+  'not automatically checked by Lighthouse. They do not affect your score but it\'s important that you verify them manually.',
   /** Title of the Best Practices category of audits. This is displayed at the top of a list of audits focused on topics related to following web development best practices and accepted guidelines. Also used as a label of a score gauge; try to limit to 20 characters. */
   bestPracticesCategoryTitle: 'Best Practices',
   /** Title of the Fast and Reliable section of the web app category. Within this section are audits that check if the web site loaded quickly and can reliably load even if the internet connection is very slow or goes offline. */
@@ -507,11 +516,9 @@ const defaultConfig = {
       ],
     },
     'pwa': {
-      title: 'Progressive Web App',
-      description: 'These checks validate the aspects of a Progressive Web App. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist).',
-      manualDescription: 'These checks are required by the baseline ' +
-          '[PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist) but are ' +
-          'not automatically checked by Lighthouse. They do not affect your score but it\'s important that you verify them manually.',
+      title: str_(UIStrings.pwaCategoryTitle),
+      description: str_(UIStrings.pwaCategoryDescription),
+      manualDescription: str_(UIStrings.pwaCategoryManualDescription),
       auditRefs: [
         // Fast and Reliable
         {id: 'load-fast-enough-for-pwa', weight: 7, group: 'pwa-fast-reliable'},
diff --git a/lighthouse-core/lib/i18n/locales.js b/lighthouse-core/lib/i18n/locales.js
index 09a27caee..8ca744746 100644
--- a/lighthouse-core/lib/i18n/locales.js
+++ b/lighthouse-core/lib/i18n/locales.js
@@ -10,8 +10,11 @@
  *
  * Google locale inheritance rules: https://goto.google.com/ccssm
  * CLDR language aliases: https://www.unicode.org/cldr/charts/latest/supplemental/aliases.html
+ * CLDR locale inheritance: https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/parentLocales.json
  */
 
+// TODO(paulirish): Centralize locale inheritance (combining this & i18n.lookupLocale()), adopt cldr parentLocale rules.
+
 /** @typedef {Record<string, {message: string}>} LhlMessages */
 
 // The keys within this const must exactly match the LH.Locale type in externs.d.ts
diff --git a/lighthouse-core/lib/i18n/locales/en-US.json b/lighthouse-core/lib/i18n/locales/en-US.json
index 7f65c17fd..b9f13a105 100644
--- a/lighthouse-core/lib/i18n/locales/en-US.json
+++ b/lighthouse-core/lib/i18n/locales/en-US.json
@@ -318,7 +318,7 @@
     "message": "`<video>` elements contain a `<track>` element with `[kind=\"description\"]`"
   },
   "lighthouse-core/audits/apple-touch-icon.js | description": {
-    "message": "For ideal appearance on iOS when users add to the home screen, define an apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)."
+    "message": "For ideal appearance on iOS when users add a progressive web app to the home screen, define an `apple-touch-icon`. It must point to a non-transparent 192px (or 180px) square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)."
   },
   "lighthouse-core/audits/apple-touch-icon.js | failureTitle": {
     "message": "Does not provide a valid `apple-touch-icon`"
@@ -440,6 +440,18 @@
   "lighthouse-core/audits/byte-efficiency/uses-webp-images.js | title": {
     "message": "Serve images in next-gen formats"
   },
+  "lighthouse-core/audits/content-width.js | description": {
+    "message": "If the width of your app's content doesn't match the width of the viewport, your app might not be optimized for mobile screens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport)."
+  },
+  "lighthouse-core/audits/content-width.js | explanation": {
+    "message": "The viewport size of {innerWidth}px does not match the window size of {outerWidth}px."
+  },
+  "lighthouse-core/audits/content-width.js | failureTitle": {
+    "message": "Content is not sized correctly for the viewport"
+  },
+  "lighthouse-core/audits/content-width.js | title": {
+    "message": "Content is sized correctly for the viewport"
+  },
   "lighthouse-core/audits/critical-request-chains.js | description": {
     "message": "The Critical Request Chains below show you what resources are loaded 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)."
   },
@@ -456,7 +468,7 @@
     "message": "Line"
   },
   "lighthouse-core/audits/deprecations.js | description": {
-    "message": "Deprecated APIs will eventually be removed from the browser. [Learn more](https://www.chromestatus.com/features#deprecated)."
+    "message": "Deprecated APIs will eventually be removed from the browser. [Learn more](https://web.dev/deprecations)."
   },
   "lighthouse-core/audits/deprecations.js | displayValue": {
     "message": "{itemCount, plural,\n    =1 {1 warning found}\n    other {# warnings found}\n    }"
@@ -468,7 +480,7 @@
     "message": "Avoids deprecated APIs"
   },
   "lighthouse-core/audits/dobetterweb/appcache-manifest.js | description": {
-    "message": "Application Cache is deprecated. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/appcache)."
+    "message": "Application Cache is deprecated. [Learn more](https://web.dev/appcache-manifest)."
   },
   "lighthouse-core/audits/dobetterweb/appcache-manifest.js | displayValue": {
     "message": "Found \"{AppCacheManifest}\""
@@ -480,7 +492,7 @@
     "message": "Avoids Application Cache"
   },
   "lighthouse-core/audits/dobetterweb/doctype.js | description": {
-    "message": "Specifying a doctype prevents the browser from switching to quirks-mode. Read more on the [MDN Web Docs page](https://developer.mozilla.org/en-US/docs/Glossary/Doctype)"
+    "message": "Specifying a doctype prevents the browser from switching to quirks-mode. [Learn more](https://web.dev/doctype)."
   },
   "lighthouse-core/audits/dobetterweb/doctype.js | explanationBadDoctype": {
     "message": "Doctype name must be the lowercase string `html`"
@@ -510,7 +522,7 @@
     "message": "Value"
   },
   "lighthouse-core/audits/dobetterweb/dom-size.js | description": {
-    "message": "Browser engineers recommend pages contain fewer than ~1,500 DOM elements. 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)."
+    "message": "Browser engineers recommend pages contain fewer than ~1,500 DOM elements. 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://web.dev/dom-size)."
   },
   "lighthouse-core/audits/dobetterweb/dom-size.js | displayValue": {
     "message": "{itemCount, plural,\n    =1 {1 element}\n    other {# elements}\n    }"
@@ -537,7 +549,7 @@
     "message": "Target"
   },
   "lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js | description": {
-    "message": "Add `rel=\"noopener\"` or `rel=\"noreferrer\"` to any external links to improve performance and prevent security vulnerabilities. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/noopener)."
+    "message": "Add `rel=\"noopener\"` or `rel=\"noreferrer\"` to any external links to improve performance and prevent security vulnerabilities. [Learn more](https://web.dev/external-anchors-use-rel-noopener)."
   },
   "lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js | failureTitle": {
     "message": "Links to cross-origin destinations are unsafe"
@@ -549,7 +561,7 @@
     "message": "Unable to determine the destination for anchor ({anchorHTML}). If not used as a hyperlink, consider removing target=_blank."
   },
   "lighthouse-core/audits/dobetterweb/geolocation-on-start.js | description": {
-    "message": "Users are mistrustful of or confused by sites that request their location without context. Consider tying the request to a user action instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/geolocation-on-load)."
+    "message": "Users are mistrustful of or confused by sites that request their location without context. Consider tying the request to a user action instead. [Learn more](https://web.dev/geolocation-on-start)."
   },
   "lighthouse-core/audits/dobetterweb/geolocation-on-start.js | failureTitle": {
     "message": "Requests the geolocation permission on page load"
@@ -561,13 +573,13 @@
     "message": "Version"
   },
   "lighthouse-core/audits/dobetterweb/js-libraries.js | description": {
-    "message": "All front-end JavaScript libraries detected on the page."
+    "message": "All front-end JavaScript libraries detected on the page. [Learn more](https://web.dev/js-libraries)."
   },
   "lighthouse-core/audits/dobetterweb/js-libraries.js | title": {
     "message": "Detected JavaScript libraries"
   },
   "lighthouse-core/audits/dobetterweb/no-document-write.js | description": {
-    "message": "For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/document-write)."
+    "message": "For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn more](https://web.dev/no-document-write)."
   },
   "lighthouse-core/audits/dobetterweb/no-document-write.js | failureTitle": {
     "message": "Uses `document.write()`"
@@ -585,7 +597,7 @@
     "message": "Vulnerability Count"
   },
   "lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | description": {
-    "message": "Some third-party scripts may contain known security vulnerabilities that are easily identified and exploited by attackers. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/vulnerabilities)."
+    "message": "Some third-party scripts may contain known security vulnerabilities that are easily identified and exploited by attackers. [Learn more](https://web.dev/no-vulnerable-libraries)."
   },
   "lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | displayValue": {
     "message": "{itemCount, plural,\n    =1 {1 vulnerability detected}\n    other {# vulnerabilities detected}\n    }"
@@ -606,7 +618,7 @@
     "message": "Avoids front-end JavaScript libraries with known security vulnerabilities"
   },
   "lighthouse-core/audits/dobetterweb/notification-on-start.js | description": {
-    "message": "Users are mistrustful of or confused by sites that request to send notifications without context. Consider tying the request to user gestures instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/notifications-on-load)."
+    "message": "Users are mistrustful of or confused by sites that request to send notifications without context. Consider tying the request to user gestures instead. [Learn more](https://web.dev/notification-on-start)."
   },
   "lighthouse-core/audits/dobetterweb/notification-on-start.js | failureTitle": {
     "message": "Requests the notification permission on page load"
@@ -618,7 +630,7 @@
     "message": "Failing Elements"
   },
   "lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js | description": {
-    "message": "Preventing password pasting undermines good security policy. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/password-pasting)."
+    "message": "Preventing password pasting undermines good security policy. [Learn more](https://web.dev/password-inputs-can-be-pasted-into)."
   },
   "lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js | failureTitle": {
     "message": "Prevents users to paste into password fields"
@@ -630,7 +642,7 @@
     "message": "Protocol"
   },
   "lighthouse-core/audits/dobetterweb/uses-http2.js | description": {
-    "message": "HTTP/2 offers many benefits over HTTP/1.1, including binary headers, multiplexing, and server push. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http2)."
+    "message": "HTTP/2 offers many benefits over HTTP/1.1, including binary headers, multiplexing, and server push. [Learn more](https://web.dev/uses-http2)."
   },
   "lighthouse-core/audits/dobetterweb/uses-http2.js | displayValue": {
     "message": "{itemCount, plural,\n    =1 {1 request not served via HTTP/2}\n    other {# requests not served via HTTP/2}\n    }"
@@ -642,7 +654,7 @@
     "message": "Uses HTTP/2 for its own resources"
   },
   "lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js | description": {
-    "message": "Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners)."
+    "message": "Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more](https://web.dev/uses-passive-event-listeners)."
   },
   "lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js | failureTitle": {
     "message": "Does not use passive listeners to improve scrolling performance"
@@ -654,7 +666,7 @@
     "message": "Description"
   },
   "lighthouse-core/audits/errors-in-console.js | description": {
-    "message": "Errors logged to the console indicate unresolved problems. They can come from network request failures and other browser concerns."
+    "message": "Errors logged to the console indicate unresolved problems. They can come from network request failures and other browser concerns. [Learn more](https://web.dev/errors-in-console)"
   },
   "lighthouse-core/audits/errors-in-console.js | failureTitle": {
     "message": "Browser errors were logged to the console"
@@ -681,7 +693,7 @@
     "message": "Aspect Ratio (Displayed)"
   },
   "lighthouse-core/audits/image-aspect-ratio.js | description": {
-    "message": "Image display dimensions should match natural aspect ratio. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/aspect-ratio)."
+    "message": "Image display dimensions should match natural aspect ratio. [Learn more](https://web.dev/image-aspect-ratio)."
   },
   "lighthouse-core/audits/image-aspect-ratio.js | failureTitle": {
     "message": "Displays images with incorrect aspect ratio"
@@ -692,11 +704,20 @@
   "lighthouse-core/audits/image-aspect-ratio.js | warningCompute": {
     "message": "Invalid image sizing information {url}"
   },
+  "lighthouse-core/audits/installable-manifest.js | description": {
+    "message": "Browsers can proactively prompt users to add your app to their homescreen, which can lead to higher engagement. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/install-prompt)."
+  },
+  "lighthouse-core/audits/installable-manifest.js | failureTitle": {
+    "message": "Web app manifest does not meet the installability requirements"
+  },
+  "lighthouse-core/audits/installable-manifest.js | title": {
+    "message": "Web app manifest meets the installability requirements"
+  },
   "lighthouse-core/audits/is-on-https.js | columnInsecureURL": {
     "message": "Insecure URL"
   },
   "lighthouse-core/audits/is-on-https.js | description": {
-    "message": "All sites should be protected with HTTPS, even ones that don't handle sensitive data. HTTPS prevents intruders from tampering with or passively listening in on the communications between your app and your users, and is a prerequisite for HTTP/2 and many new web platform APIs. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/https)."
+    "message": "All sites should be protected with HTTPS, even ones that don't handle sensitive data. HTTPS prevents intruders from tampering with or passively listening in on the communications between your app and your users, and is a prerequisite for HTTP/2 and many new web platform APIs. [Learn more](https://web.dev/is-on-https)."
   },
   "lighthouse-core/audits/is-on-https.js | displayValue": {
     "message": "{itemCount, plural,\n    =1 {1 insecure request found}\n    other {# insecure requests found}\n    }"
@@ -716,6 +737,9 @@
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | displayValueTextWithOverride": {
     "message": "Interactive on simulated mobile network at {timeInMs, number, seconds} s"
   },
+  "lighthouse-core/audits/load-fast-enough-for-pwa.js | explanationLoadSlow": {
+    "message": "Your page loads too slowly and is not interactive within 10 seconds. Look at the opportunities and diagnostics in the \"Performance\" section to learn how to improve."
+  },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | failureTitle": {
     "message": "Page load is not fast enough on mobile networks"
   },
@@ -734,6 +758,24 @@
   "lighthouse-core/audits/mainthread-work-breakdown.js | title": {
     "message": "Minimizes main-thread work"
   },
+  "lighthouse-core/audits/manual/pwa-cross-browser.js | description": {
+    "message": "To reach the most number of users, sites should work across every major browser. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser)."
+  },
+  "lighthouse-core/audits/manual/pwa-cross-browser.js | title": {
+    "message": "Site works cross-browser"
+  },
+  "lighthouse-core/audits/manual/pwa-each-page-has-url.js | description": {
+    "message": "Ensure individual pages are deep linkable via URL and that URLs are unique for the purpose of shareability on social media. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url)."
+  },
+  "lighthouse-core/audits/manual/pwa-each-page-has-url.js | title": {
+    "message": "Each page has a URL"
+  },
+  "lighthouse-core/audits/manual/pwa-page-transitions.js | description": {
+    "message": "Transitions should feel snappy as you tap around, even on a slow network, a key to perceived performance. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network)."
+  },
+  "lighthouse-core/audits/manual/pwa-page-transitions.js | title": {
+    "message": "Page transitions don't feel like they block on the network"
+  },
   "lighthouse-core/audits/metrics/estimated-input-latency.js | description": {
     "message": "Estimated Input Latency 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)."
   },
@@ -794,6 +836,18 @@
   "lighthouse-core/audits/network-server-latency.js | title": {
     "message": "Server Backend Latencies"
   },
+  "lighthouse-core/audits/offline-start-url.js | description": {
+    "message": "A service worker enables your web app to be reliable in unpredictable network conditions. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)."
+  },
+  "lighthouse-core/audits/offline-start-url.js | failureTitle": {
+    "message": "`start_url` does not respond with a 200 when offline"
+  },
+  "lighthouse-core/audits/offline-start-url.js | title": {
+    "message": "`start_url` responds with a 200 when offline"
+  },
+  "lighthouse-core/audits/offline-start-url.js | warningCantStart": {
+    "message": "Lighthouse couldn't read the `start_url` from the manifest. As a result, the `start_url` was assumed to be the document's URL. Error message: '{manifestWarning}'."
+  },
   "lighthouse-core/audits/performance-budget.js | columnOverBudget": {
     "message": "Over Budget"
   },
@@ -806,6 +860,15 @@
   "lighthouse-core/audits/performance-budget.js | title": {
     "message": "Performance budget"
   },
+  "lighthouse-core/audits/redirects-http.js | description": {
+    "message": "If you've already set up HTTPS, make sure that you redirect all HTTP traffic to HTTPS in order to enable secure web features for all your users. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https)."
+  },
+  "lighthouse-core/audits/redirects-http.js | failureTitle": {
+    "message": "Does not redirect HTTP traffic to HTTPS"
+  },
+  "lighthouse-core/audits/redirects-http.js | title": {
+    "message": "Redirects HTTP traffic to HTTPS"
+  },
   "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)."
   },
@@ -971,6 +1034,45 @@
   "lighthouse-core/audits/seo/tap-targets.js | title": {
     "message": "Tap targets are sized appropriately"
   },
+  "lighthouse-core/audits/service-worker.js | description": {
+    "message": "The service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker)."
+  },
+  "lighthouse-core/audits/service-worker.js | explanationBadManifest": {
+    "message": "This page is controlled by a service worker, however no `start_url` was found because manifest failed to parse as valid JSON"
+  },
+  "lighthouse-core/audits/service-worker.js | explanationBadStartUrl": {
+    "message": "This page is controlled by a service worker, however the `start_url` ({startUrl}) is not in the service worker's scope ({scopeUrl})"
+  },
+  "lighthouse-core/audits/service-worker.js | explanationNoManifest": {
+    "message": "This page is controlled by a service worker, however no `start_url` was found because no manifest was fetched."
+  },
+  "lighthouse-core/audits/service-worker.js | explanationOutOfScope": {
+    "message": "This origin has one or more service workers, however the page ({pageUrl}) is not in scope."
+  },
+  "lighthouse-core/audits/service-worker.js | failureTitle": {
+    "message": "Does not register a service worker that controls page and `start_url`"
+  },
+  "lighthouse-core/audits/service-worker.js | title": {
+    "message": "Registers a service worker that controls page and `start_url`"
+  },
+  "lighthouse-core/audits/splash-screen.js | description": {
+    "message": "A themed splash screen ensures a high-quality experience when users launch your app from their homescreens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen)."
+  },
+  "lighthouse-core/audits/splash-screen.js | failureTitle": {
+    "message": "Is not configured for a custom splash screen"
+  },
+  "lighthouse-core/audits/splash-screen.js | title": {
+    "message": "Configured for a custom splash screen"
+  },
+  "lighthouse-core/audits/themed-omnibox.js | description": {
+    "message": "The browser address bar can be themed to match your site. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/address-bar)."
+  },
+  "lighthouse-core/audits/themed-omnibox.js | failureTitle": {
+    "message": "Does not set a theme color for the address bar."
+  },
+  "lighthouse-core/audits/themed-omnibox.js | title": {
+    "message": "Sets a theme color for the address bar."
+  },
   "lighthouse-core/audits/third-party-summary.js | columnMainThreadTime": {
     "message": "Main Thread Time"
   },
@@ -1034,6 +1136,42 @@
   "lighthouse-core/audits/uses-rel-preload.js | title": {
     "message": "Preload key requests"
   },
+  "lighthouse-core/audits/viewport.js | description": {
+    "message": "Add a `<meta name=\"viewport\">` tag to optimize your app for mobile screens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag)."
+  },
+  "lighthouse-core/audits/viewport.js | explanationNoTag": {
+    "message": "No `<meta name=\"viewport\">` tag found"
+  },
+  "lighthouse-core/audits/viewport.js | failureTitle": {
+    "message": "Does not have a `<meta name=\"viewport\">` tag with `width` or `initial-scale`"
+  },
+  "lighthouse-core/audits/viewport.js | title": {
+    "message": "Has a `<meta name=\"viewport\">` tag with `width` or `initial-scale`"
+  },
+  "lighthouse-core/audits/without-javascript.js | description": {
+    "message": "Your app should display some content when JavaScript is disabled, even if it's just a warning to the user that JavaScript is required to use the app. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/no-js)."
+  },
+  "lighthouse-core/audits/without-javascript.js | explanation": {
+    "message": "The page body should render some content if its scripts are not available."
+  },
+  "lighthouse-core/audits/without-javascript.js | failureTitle": {
+    "message": "Does not provide fallback content when JavaScript is not available"
+  },
+  "lighthouse-core/audits/without-javascript.js | title": {
+    "message": "Contains some content when JavaScript is not available"
+  },
+  "lighthouse-core/audits/works-offline.js | description": {
+    "message": "If you're building a Progressive Web App, consider using a service worker so that your app can work offline. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)."
+  },
+  "lighthouse-core/audits/works-offline.js | failureTitle": {
+    "message": "Current page does not respond with a 200 when offline"
+  },
+  "lighthouse-core/audits/works-offline.js | title": {
+    "message": "Current page responds with a 200 when offline"
+  },
+  "lighthouse-core/audits/works-offline.js | warningNoLoad": {
+    "message": "The page may not be loading offline because your test URL ({requested}) was redirected to \"{final}\". Try testing the second URL directly."
+  },
   "lighthouse-core/config/default-config.js | a11yAriaGroupDescription": {
     "message": "These are opportunities to improve the usage of ARIA in your application which may enhance the experience for users of assistive technology, like a screen reader."
   },
@@ -1130,6 +1268,15 @@
   "lighthouse-core/config/default-config.js | performanceCategoryTitle": {
     "message": "Performance"
   },
+  "lighthouse-core/config/default-config.js | pwaCategoryDescription": {
+    "message": "These checks validate the aspects of a Progressive Web App. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist)."
+  },
+  "lighthouse-core/config/default-config.js | pwaCategoryManualDescription": {
+    "message": "These checks are required by the baseline [PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist) but are not automatically checked by Lighthouse. They do not affect your score but it's important that you verify them manually."
+  },
+  "lighthouse-core/config/default-config.js | pwaCategoryTitle": {
+    "message": "Progressive Web App"
+  },
   "lighthouse-core/config/default-config.js | pwaFastReliableGroupTitle": {
     "message": "Fast and reliable"
   },
diff --git a/lighthouse-core/lib/i18n/locales/en-XL.json b/lighthouse-core/lib/i18n/locales/en-XL.json
index 4ff4399b0..bfbca9025 100644
--- a/lighthouse-core/lib/i18n/locales/en-XL.json
+++ b/lighthouse-core/lib/i18n/locales/en-XL.json
@@ -318,7 +318,7 @@
     "message": "`<video>` êĺêḿêńt̂ś ĉón̂t́âín̂ á `<track>` êĺêḿêńt̂ ẃît́ĥ `[kind=\"description\"]`"
   },
   "lighthouse-core/audits/apple-touch-icon.js | description": {
-    "message": "F̂ór̂ íd̂éâĺ âṕp̂éâŕâńĉé ôń îÓŜ ẃĥén̂ úŝér̂ś âd́d̂ t́ô t́ĥé ĥóm̂é ŝćr̂éêń, d̂éf̂ín̂é âń âṕp̂ĺê-t́ôúĉh́-îćôń. Ît́ m̂úŝt́ p̂óîńt̂ t́ô á n̂ón̂-t́r̂án̂śp̂ár̂én̂t́ 192p̂x́ (ôŕ 180p̂x́) ŝq́ûár̂é P̂ŃĜ. [Ĺêár̂ń M̂ór̂é](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)."
+    "message": "F̂ór̂ íd̂éâĺ âṕp̂éâŕâńĉé ôń îÓŜ ẃĥén̂ úŝér̂ś âd́d̂ á p̂ŕôǵr̂éŝśîv́ê ẃêb́ âṕp̂ t́ô t́ĥé ĥóm̂é ŝćr̂éêń, d̂éf̂ín̂é âń `apple-touch-icon`. Ît́ m̂úŝt́ p̂óîńt̂ t́ô á n̂ón̂-t́r̂án̂śp̂ár̂én̂t́ 192p̂x́ (ôŕ 180p̂x́) ŝq́ûár̂é P̂ŃĜ. [Ĺêár̂ń M̂ór̂é](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/)."
   },
   "lighthouse-core/audits/apple-touch-icon.js | failureTitle": {
     "message": "D̂óêś n̂ót̂ ṕr̂óv̂íd̂é â v́âĺîd́ `apple-touch-icon`"
@@ -440,6 +440,18 @@
   "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/content-width.js | description": {
+    "message": "Îf́ t̂h́ê ẃîd́t̂h́ ôf́ ŷóûŕ âṕp̂'ś ĉón̂t́êńt̂ d́ôéŝń't̂ ḿât́ĉh́ t̂h́ê ẃîd́t̂h́ ôf́ t̂h́ê v́îéŵṕôŕt̂, ýôúr̂ áp̂ṕ m̂íĝh́t̂ ńôt́ b̂é ôṕt̂ím̂íẑéd̂ f́ôŕ m̂ób̂íl̂é ŝćr̂éêńŝ. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport)."
+  },
+  "lighthouse-core/audits/content-width.js | explanation": {
+    "message": "T̂h́ê v́îéŵṕôŕt̂ śîźê óf̂ {innerWidth}ṕx̂ d́ôéŝ ńôt́ m̂át̂ćĥ t́ĥé ŵín̂d́ôẃ ŝíẑé ôf́ {outerWidth}p̂x́."
+  },
+  "lighthouse-core/audits/content-width.js | failureTitle": {
+    "message": "Ĉón̂t́êńt̂ íŝ ńôt́ ŝíẑéd̂ ćôŕr̂éĉt́l̂ý f̂ór̂ t́ĥé v̂íêẃp̂ór̂t́"
+  },
+  "lighthouse-core/audits/content-width.js | title": {
+    "message": "Ĉón̂t́êńt̂ íŝ śîźêd́ ĉór̂ŕêćt̂ĺŷ f́ôŕ t̂h́ê v́îéŵṕôŕt̂"
+  },
   "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́ l̂óâd́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́](https://developers.google.com/web/tools/lighthouse/audits/critical-request-chains)."
   },
@@ -456,7 +468,7 @@
     "message": "L̂ín̂é"
   },
   "lighthouse-core/audits/deprecations.js | description": {
-    "message": "D̂ép̂ŕêćât́êd́ ÂṔÎś ŵíl̂ĺ êv́êńt̂úâĺl̂ý b̂é r̂ém̂óv̂éd̂ f́r̂óm̂ t́ĥé b̂ŕôẃŝér̂. [Ĺêár̂ń m̂ór̂é](https://www.chromestatus.com/features#deprecated)."
+    "message": "D̂ép̂ŕêćât́êd́ ÂṔÎś ŵíl̂ĺ êv́êńt̂úâĺl̂ý b̂é r̂ém̂óv̂éd̂ f́r̂óm̂ t́ĥé b̂ŕôẃŝér̂. [Ĺêár̂ń m̂ór̂é](https://web.dev/deprecations)."
   },
   "lighthouse-core/audits/deprecations.js | displayValue": {
     "message": "{itemCount, plural,\n    =1 {1 ŵár̂ńîńĝ f́ôún̂d́}\n    other {# ŵár̂ńîńĝś f̂óûńd̂}\n    }"
@@ -468,7 +480,7 @@
     "message": "Âv́ôíd̂ś d̂ép̂ŕêćât́êd́ ÂṔÎś"
   },
   "lighthouse-core/audits/dobetterweb/appcache-manifest.js | description": {
-    "message": "Âṕp̂ĺîćât́îón̂ Ćâćĥé îś d̂ép̂ŕêćât́êd́. [L̂éâŕn̂ ḿôŕê](https://developers.google.com/web/tools/lighthouse/audits/appcache)."
+    "message": "Âṕp̂ĺîćât́îón̂ Ćâćĥé îś d̂ép̂ŕêćât́êd́. [L̂éâŕn̂ ḿôŕê](https://web.dev/appcache-manifest)."
   },
   "lighthouse-core/audits/dobetterweb/appcache-manifest.js | displayValue": {
     "message": "F̂óûńd̂ \"{AppCacheManifest}\""
@@ -480,7 +492,7 @@
     "message": "Âv́ôíd̂ś Âṕp̂ĺîćât́îón̂ Ćâćĥé"
   },
   "lighthouse-core/audits/dobetterweb/doctype.js | description": {
-    "message": "Ŝṕêćîf́ŷín̂ǵ â d́ôćt̂ýp̂é p̂ŕêv́êńt̂ś t̂h́ê b́r̂óŵśêŕ f̂ŕôḿ ŝẃît́ĉh́îńĝ t́ô q́ûír̂ḱŝ-ḿôd́ê. Ŕêád̂ ḿôŕê ón̂ t́ĥé [M̂D́N̂ Ẃêb́ D̂óĉś p̂áĝé](https://developer.mozilla.org/en-US/docs/Glossary/Doctype)"
+    "message": "Ŝṕêćîf́ŷín̂ǵ â d́ôćt̂ýp̂é p̂ŕêv́êńt̂ś t̂h́ê b́r̂óŵśêŕ f̂ŕôḿ ŝẃît́ĉh́îńĝ t́ô q́ûír̂ḱŝ-ḿôd́ê. [Ĺêár̂ń m̂ór̂é](https://web.dev/doctype)."
   },
   "lighthouse-core/audits/dobetterweb/doctype.js | explanationBadDoctype": {
     "message": "D̂óĉt́ŷṕê ńâḿê ḿûśt̂ b́ê t́ĥé l̂óŵér̂ćâśê śt̂ŕîńĝ `html`"
@@ -510,7 +522,7 @@
     "message": "V̂ál̂ú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̂ él̂ém̂én̂t́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̂](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), 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́](https://developers.google.com/speed/articles/reflow). [L̂éâŕn̂ ḿôŕê](https://developers.google.com/web/tools/lighthouse/audits/dom-size)."
+    "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̂ él̂ém̂én̂t́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̂](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), 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́](https://developers.google.com/speed/articles/reflow). [L̂éâŕn̂ ḿôŕê](https://web.dev/dom-size)."
   },
   "lighthouse-core/audits/dobetterweb/dom-size.js | displayValue": {
     "message": "{itemCount, plural,\n    =1 {1 êĺêḿêńt̂}\n    other {# él̂ém̂én̂t́ŝ}\n    }"
@@ -537,7 +549,7 @@
     "message": "T̂ár̂ǵêt́"
   },
   "lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js | description": {
-    "message": "Âd́d̂ `rel=\"noopener\"` ór̂ `rel=\"noreferrer\"` t́ô án̂ý êx́t̂ér̂ńâĺ l̂ín̂ḱŝ t́ô ím̂ṕr̂óv̂é p̂ér̂f́ôŕm̂án̂ćê án̂d́ p̂ŕêv́êńt̂ śêćûŕît́ŷ v́ûĺn̂ér̂áb̂íl̂ít̂íêś. [L̂éâŕn̂ ḿôŕê](https://developers.google.com/web/tools/lighthouse/audits/noopener)."
+    "message": "Âd́d̂ `rel=\"noopener\"` ór̂ `rel=\"noreferrer\"` t́ô án̂ý êx́t̂ér̂ńâĺ l̂ín̂ḱŝ t́ô ím̂ṕr̂óv̂é p̂ér̂f́ôŕm̂án̂ćê án̂d́ p̂ŕêv́êńt̂ śêćûŕît́ŷ v́ûĺn̂ér̂áb̂íl̂ít̂íêś. [L̂éâŕn̂ ḿôŕê](https://web.dev/external-anchors-use-rel-noopener)."
   },
   "lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js | failureTitle": {
     "message": "L̂ín̂ḱŝ t́ô ćr̂óŝś-ôŕîǵîń d̂éŝt́îńât́îón̂ś âŕê ún̂śâf́ê"
@@ -549,7 +561,7 @@
     "message": "Ûńâb́l̂é t̂ó d̂ét̂ér̂ḿîńê t́ĥé d̂éŝt́îńât́îón̂ f́ôŕ âńĉh́ôŕ ({anchorHTML}). Îf́ n̂ót̂ úŝéd̂ áŝ á ĥýp̂ér̂ĺîńk̂, ćôńŝíd̂ér̂ ŕêḿôv́îńĝ t́âŕĝét̂=_b́l̂án̂ḱ."
   },
   "lighthouse-core/audits/dobetterweb/geolocation-on-start.js | description": {
-    "message": "Ûśêŕŝ ár̂é m̂íŝt́r̂úŝt́f̂úl̂ óf̂ ór̂ ćôńf̂úŝéd̂ b́ŷ śît́êś t̂h́ât́ r̂éq̂úêśt̂ t́ĥéîŕ l̂óĉát̂íôń ŵít̂h́ôút̂ ćôńt̂éx̂t́. Ĉón̂śîd́êŕ t̂ýîńĝ t́ĥé r̂éq̂úêśt̂ t́ô á ûśêŕ âćt̂íôń îńŝt́êád̂. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/geolocation-on-load)."
+    "message": "Ûśêŕŝ ár̂é m̂íŝt́r̂úŝt́f̂úl̂ óf̂ ór̂ ćôńf̂úŝéd̂ b́ŷ śît́êś t̂h́ât́ r̂éq̂úêśt̂ t́ĥéîŕ l̂óĉát̂íôń ŵít̂h́ôút̂ ćôńt̂éx̂t́. Ĉón̂śîd́êŕ t̂ýîńĝ t́ĥé r̂éq̂úêśt̂ t́ô á ûśêŕ âćt̂íôń îńŝt́êád̂. [Ĺêár̂ń m̂ór̂é](https://web.dev/geolocation-on-start)."
   },
   "lighthouse-core/audits/dobetterweb/geolocation-on-start.js | failureTitle": {
     "message": "R̂éq̂úêśt̂ś t̂h́ê ǵêól̂óĉát̂íôń p̂ér̂ḿîśŝíôń ôń p̂áĝé l̂óâd́"
@@ -561,13 +573,13 @@
     "message": "V̂ér̂śîón̂"
   },
   "lighthouse-core/audits/dobetterweb/js-libraries.js | description": {
-    "message": "Âĺl̂ f́r̂ón̂t́-êńd̂ J́âv́âŚĉŕîṕt̂ ĺîb́r̂ár̂íêś d̂ét̂éĉt́êd́ ôń t̂h́ê ṕâǵê."
+    "message": "Âĺl̂ f́r̂ón̂t́-êńd̂ J́âv́âŚĉŕîṕt̂ ĺîb́r̂ár̂íêś d̂ét̂éĉt́êd́ ôń t̂h́ê ṕâǵê. [Ĺêár̂ń m̂ór̂é](https://web.dev/js-libraries)."
   },
   "lighthouse-core/audits/dobetterweb/js-libraries.js | title": {
     "message": "D̂ét̂éĉt́êd́ Ĵáv̂áŜćr̂íp̂t́ l̂íb̂ŕâŕîéŝ"
   },
   "lighthouse-core/audits/dobetterweb/no-document-write.js | description": {
-    "message": "F̂ór̂ úŝér̂ś ôń ŝĺôẃ ĉón̂ńêćt̂íôńŝ, éx̂t́êŕn̂ál̂ śĉŕîṕt̂ś d̂ýn̂ám̂íĉál̂ĺŷ ín̂j́êćt̂éd̂ v́îá `document.write()` ĉán̂ d́êĺâý p̂áĝé l̂óâd́ b̂ý t̂én̂ś ôf́ ŝéĉón̂d́ŝ. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/document-write)."
+    "message": "F̂ór̂ úŝér̂ś ôń ŝĺôẃ ĉón̂ńêćt̂íôńŝ, éx̂t́êŕn̂ál̂ śĉŕîṕt̂ś d̂ýn̂ám̂íĉál̂ĺŷ ín̂j́êćt̂éd̂ v́îá `document.write()` ĉán̂ d́êĺâý p̂áĝé l̂óâd́ b̂ý t̂én̂ś ôf́ ŝéĉón̂d́ŝ. [Ĺêár̂ń m̂ór̂é](https://web.dev/no-document-write)."
   },
   "lighthouse-core/audits/dobetterweb/no-document-write.js | failureTitle": {
     "message": "Ûśêś `document.write()`"
@@ -585,7 +597,7 @@
     "message": "V̂úl̂ńêŕâb́îĺît́ŷ Ćôún̂t́"
   },
   "lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | description": {
-    "message": "Ŝóm̂é t̂h́îŕd̂-ṕâŕt̂ý ŝćr̂íp̂t́ŝ ḿâý ĉón̂t́âín̂ ḱn̂óŵń ŝéĉúr̂ít̂ý v̂úl̂ńêŕâb́îĺît́îéŝ t́ĥát̂ ár̂é êáŝíl̂ý îd́êńt̂íf̂íêd́ âńd̂ éx̂ṕl̂óît́êd́ b̂ý ât́t̂áĉḱêŕŝ. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/vulnerabilities)."
+    "message": "Ŝóm̂é t̂h́îŕd̂-ṕâŕt̂ý ŝćr̂íp̂t́ŝ ḿâý ĉón̂t́âín̂ ḱn̂óŵń ŝéĉúr̂ít̂ý v̂úl̂ńêŕâb́îĺît́îéŝ t́ĥát̂ ár̂é êáŝíl̂ý îd́êńt̂íf̂íêd́ âńd̂ éx̂ṕl̂óît́êd́ b̂ý ât́t̂áĉḱêŕŝ. [Ĺêár̂ń m̂ór̂é](https://web.dev/no-vulnerable-libraries)."
   },
   "lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | displayValue": {
     "message": "{itemCount, plural,\n    =1 {1 v̂úl̂ńêŕâb́îĺît́ŷ d́êt́êćt̂éd̂}\n    other {# v́ûĺn̂ér̂áb̂íl̂ít̂íêś d̂ét̂éĉt́êd́}\n    }"
@@ -606,7 +618,7 @@
     "message": "Âv́ôíd̂ś f̂ŕôńt̂-én̂d́ Ĵáv̂áŜćr̂íp̂t́ l̂íb̂ŕâŕîéŝ ẃît́ĥ ḱn̂óŵń ŝéĉúr̂ít̂ý v̂úl̂ńêŕâb́îĺît́îéŝ"
   },
   "lighthouse-core/audits/dobetterweb/notification-on-start.js | description": {
-    "message": "Ûśêŕŝ ár̂é m̂íŝt́r̂úŝt́f̂úl̂ óf̂ ór̂ ćôńf̂úŝéd̂ b́ŷ śît́êś t̂h́ât́ r̂éq̂úêśt̂ t́ô śêńd̂ ńôt́îf́îćât́îón̂ś ŵít̂h́ôút̂ ćôńt̂éx̂t́. Ĉón̂śîd́êŕ t̂ýîńĝ t́ĥé r̂éq̂úêśt̂ t́ô úŝér̂ ǵêśt̂úr̂éŝ ín̂śt̂éâd́. [L̂éâŕn̂ ḿôŕê](https://developers.google.com/web/tools/lighthouse/audits/notifications-on-load)."
+    "message": "Ûśêŕŝ ár̂é m̂íŝt́r̂úŝt́f̂úl̂ óf̂ ór̂ ćôńf̂úŝéd̂ b́ŷ śît́êś t̂h́ât́ r̂éq̂úêśt̂ t́ô śêńd̂ ńôt́îf́îćât́îón̂ś ŵít̂h́ôút̂ ćôńt̂éx̂t́. Ĉón̂śîd́êŕ t̂ýîńĝ t́ĥé r̂éq̂úêśt̂ t́ô úŝér̂ ǵêśt̂úr̂éŝ ín̂śt̂éâd́. [L̂éâŕn̂ ḿôŕê](https://web.dev/notification-on-start)."
   },
   "lighthouse-core/audits/dobetterweb/notification-on-start.js | failureTitle": {
     "message": "R̂éq̂úêśt̂ś t̂h́ê ńôt́îf́îćât́îón̂ ṕêŕm̂íŝśîón̂ ón̂ ṕâǵê ĺôád̂"
@@ -618,7 +630,7 @@
     "message": "F̂áîĺîńĝ Él̂ém̂én̂t́ŝ"
   },
   "lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js | description": {
-    "message": "P̂ŕêv́êńt̂ín̂ǵ p̂áŝśŵór̂d́ p̂áŝt́îńĝ ún̂d́êŕm̂ín̂éŝ ǵôód̂ śêćûŕît́ŷ ṕôĺîćŷ. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/password-pasting)."
+    "message": "P̂ŕêv́êńt̂ín̂ǵ p̂áŝśŵór̂d́ p̂áŝt́îńĝ ún̂d́êŕm̂ín̂éŝ ǵôód̂ śêćûŕît́ŷ ṕôĺîćŷ. [Ĺêár̂ń m̂ór̂é](https://web.dev/password-inputs-can-be-pasted-into)."
   },
   "lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js | failureTitle": {
     "message": "P̂ŕêv́êńt̂ś ûśêŕŝ t́ô ṕâśt̂é îńt̂ó p̂áŝśŵór̂d́ f̂íêĺd̂ś"
@@ -630,7 +642,7 @@
     "message": "P̂ŕôt́ôćôĺ"
   },
   "lighthouse-core/audits/dobetterweb/uses-http2.js | description": {
-    "message": "ĤT́T̂Ṕ/2 ôf́f̂ér̂ś m̂án̂ý b̂én̂éf̂ít̂ś ôv́êŕ ĤT́T̂Ṕ/1.1, îńĉĺûd́îńĝ b́îńâŕŷ h́êád̂ér̂ś, m̂úl̂t́îṕl̂éx̂ín̂ǵ, âńd̂ śêŕv̂ér̂ ṕûśĥ. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/http2)."
+    "message": "ĤT́T̂Ṕ/2 ôf́f̂ér̂ś m̂án̂ý b̂én̂éf̂ít̂ś ôv́êŕ ĤT́T̂Ṕ/1.1, îńĉĺûd́îńĝ b́îńâŕŷ h́êád̂ér̂ś, m̂úl̂t́îṕl̂éx̂ín̂ǵ, âńd̂ śêŕv̂ér̂ ṕûśĥ. [Ĺêár̂ń m̂ór̂é](https://web.dev/uses-http2)."
   },
   "lighthouse-core/audits/dobetterweb/uses-http2.js | displayValue": {
     "message": "{itemCount, plural,\n    =1 {1 r̂éq̂úêśt̂ ńôt́ ŝér̂v́êd́ v̂íâ H́T̂T́P̂/2}\n    other {# ŕêq́ûéŝt́ŝ ńôt́ ŝér̂v́êd́ v̂íâ H́T̂T́P̂/2}\n    }"
@@ -642,7 +654,7 @@
     "message": "Ûśêś ĤT́T̂Ṕ/2 f̂ór̂ ít̂ś ôẃn̂ ŕêśôúr̂ćêś"
   },
   "lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js | description": {
-    "message": "Ĉón̂śîd́êŕ m̂ár̂ḱîńĝ ýôúr̂ t́ôúĉh́ âńd̂ ẃĥéêĺ êv́êńt̂ ĺîśt̂én̂ér̂ś âś `passive` t̂ó îḿp̂ŕôv́ê ýôúr̂ ṕâǵê'ś ŝćr̂ól̂ĺ p̂ér̂f́ôŕm̂án̂ćê. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners)."
+    "message": "Ĉón̂śîd́êŕ m̂ár̂ḱîńĝ ýôúr̂ t́ôúĉh́ âńd̂ ẃĥéêĺ êv́êńt̂ ĺîśt̂én̂ér̂ś âś `passive` t̂ó îḿp̂ŕôv́ê ýôúr̂ ṕâǵê'ś ŝćr̂ól̂ĺ p̂ér̂f́ôŕm̂án̂ćê. [Ĺêár̂ń m̂ór̂é](https://web.dev/uses-passive-event-listeners)."
   },
   "lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js | failureTitle": {
     "message": "D̂óêś n̂ót̂ úŝé p̂áŝśîv́ê ĺîśt̂én̂ér̂ś t̂ó îḿp̂ŕôv́ê śĉŕôĺl̂ín̂ǵ p̂ér̂f́ôŕm̂án̂ćê"
@@ -654,7 +666,7 @@
     "message": "D̂éŝćr̂íp̂t́îón̂"
   },
   "lighthouse-core/audits/errors-in-console.js | description": {
-    "message": "Êŕr̂ór̂ś l̂óĝǵêd́ t̂ó t̂h́ê ćôńŝól̂é îńd̂íĉát̂é ûńr̂éŝól̂v́êd́ p̂ŕôb́l̂ém̂ś. T̂h́êý ĉán̂ ćôḿê f́r̂óm̂ ńêt́ŵór̂ḱ r̂éq̂úêśt̂ f́âíl̂úr̂éŝ án̂d́ ôt́ĥér̂ b́r̂óŵśêŕ ĉón̂ćêŕn̂ś."
+    "message": "Êŕr̂ór̂ś l̂óĝǵêd́ t̂ó t̂h́ê ćôńŝól̂é îńd̂íĉát̂é ûńr̂éŝól̂v́êd́ p̂ŕôb́l̂ém̂ś. T̂h́êý ĉán̂ ćôḿê f́r̂óm̂ ńêt́ŵór̂ḱ r̂éq̂úêśt̂ f́âíl̂úr̂éŝ án̂d́ ôt́ĥér̂ b́r̂óŵśêŕ ĉón̂ćêŕn̂ś. [L̂éâŕn̂ ḿôŕê](https://web.dev/errors-in-console)"
   },
   "lighthouse-core/audits/errors-in-console.js | failureTitle": {
     "message": "B̂ŕôẃŝér̂ ér̂ŕôŕŝ ẃêŕê ĺôǵĝéd̂ t́ô t́ĥé ĉón̂śôĺê"
@@ -681,7 +693,7 @@
     "message": "Âśp̂éĉt́ R̂át̂íô (D́îśp̂ĺâýêd́)"
   },
   "lighthouse-core/audits/image-aspect-ratio.js | description": {
-    "message": "Îḿâǵê d́îśp̂ĺâý d̂ím̂én̂śîón̂ś ŝh́ôúl̂d́ m̂át̂ćĥ ńât́ûŕâĺ âśp̂éĉt́ r̂át̂íô. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/aspect-ratio)."
+    "message": "Îḿâǵê d́îśp̂ĺâý d̂ím̂én̂śîón̂ś ŝh́ôúl̂d́ m̂át̂ćĥ ńât́ûŕâĺ âśp̂éĉt́ r̂át̂íô. [Ĺêár̂ń m̂ór̂é](https://web.dev/image-aspect-ratio)."
   },
   "lighthouse-core/audits/image-aspect-ratio.js | failureTitle": {
     "message": "D̂íŝṕl̂áŷś îḿâǵêś ŵít̂h́ îńĉór̂ŕêćt̂ áŝṕêćt̂ ŕât́îó"
@@ -692,11 +704,20 @@
   "lighthouse-core/audits/image-aspect-ratio.js | warningCompute": {
     "message": "Îńv̂ál̂íd̂ ím̂áĝé ŝíẑín̂ǵ îńf̂ór̂ḿât́îón̂ {url}"
   },
+  "lighthouse-core/audits/installable-manifest.js | description": {
+    "message": "B̂ŕôẃŝér̂ś ĉán̂ ṕr̂óâćt̂ív̂él̂ý p̂ŕôḿp̂t́ ûśêŕŝ t́ô ád̂d́ ŷóûŕ âṕp̂ t́ô t́ĥéîŕ ĥóm̂éŝćr̂éêń, ŵh́îćĥ ćâń l̂éâd́ t̂ó ĥíĝh́êŕ êńĝáĝém̂én̂t́. [L̂éâŕn̂ ḿôŕê](https://developers.google.com/web/tools/lighthouse/audits/install-prompt)."
+  },
+  "lighthouse-core/audits/installable-manifest.js | failureTitle": {
+    "message": "Ŵéb̂ áp̂ṕ m̂án̂íf̂éŝt́ d̂óêś n̂ót̂ ḿêét̂ t́ĥé îńŝt́âĺl̂áb̂íl̂ít̂ý r̂éq̂úîŕêḿêńt̂ś"
+  },
+  "lighthouse-core/audits/installable-manifest.js | title": {
+    "message": "Ŵéb̂ áp̂ṕ m̂án̂íf̂éŝt́ m̂éêt́ŝ t́ĥé îńŝt́âĺl̂áb̂íl̂ít̂ý r̂éq̂úîŕêḿêńt̂ś"
+  },
   "lighthouse-core/audits/is-on-https.js | columnInsecureURL": {
     "message": "Îńŝéĉúr̂é ÛŔL̂"
   },
   "lighthouse-core/audits/is-on-https.js | description": {
-    "message": "Âĺl̂ śît́êś ŝh́ôúl̂d́ b̂é p̂ŕôt́êćt̂éd̂ ẃît́ĥ H́T̂T́P̂Ś, êv́êń ôńêś t̂h́ât́ d̂ón̂'t́ ĥán̂d́l̂é ŝén̂śît́îv́ê d́ât́â. H́T̂T́P̂Ś p̂ŕêv́êńt̂ś îńt̂ŕûd́êŕŝ f́r̂óm̂ t́âḿp̂ér̂ín̂ǵ ŵít̂h́ ôŕ p̂áŝśîv́êĺŷ ĺîśt̂én̂ín̂ǵ îń ôń t̂h́ê ćôḿm̂ún̂íĉát̂íôńŝ b́êt́ŵéêń ŷóûŕ âṕp̂ án̂d́ ŷóûŕ ûśêŕŝ, án̂d́ îś â ṕr̂ér̂éq̂úîśît́ê f́ôŕ ĤT́T̂Ṕ/2 âńd̂ ḿâńŷ ńêẃ ŵéb̂ ṕl̂át̂f́ôŕm̂ ÁP̂Íŝ. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/https)."
+    "message": "Âĺl̂ śît́êś ŝh́ôúl̂d́ b̂é p̂ŕôt́êćt̂éd̂ ẃît́ĥ H́T̂T́P̂Ś, êv́êń ôńêś t̂h́ât́ d̂ón̂'t́ ĥán̂d́l̂é ŝén̂śît́îv́ê d́ât́â. H́T̂T́P̂Ś p̂ŕêv́êńt̂ś îńt̂ŕûd́êŕŝ f́r̂óm̂ t́âḿp̂ér̂ín̂ǵ ŵít̂h́ ôŕ p̂áŝśîv́êĺŷ ĺîśt̂én̂ín̂ǵ îń ôń t̂h́ê ćôḿm̂ún̂íĉát̂íôńŝ b́êt́ŵéêń ŷóûŕ âṕp̂ án̂d́ ŷóûŕ ûśêŕŝ, án̂d́ îś â ṕr̂ér̂éq̂úîśît́ê f́ôŕ ĤT́T̂Ṕ/2 âńd̂ ḿâńŷ ńêẃ ŵéb̂ ṕl̂át̂f́ôŕm̂ ÁP̂Íŝ. [Ĺêár̂ń m̂ór̂é](https://web.dev/is-on-https)."
   },
   "lighthouse-core/audits/is-on-https.js | displayValue": {
     "message": "{itemCount, plural,\n    =1 {1 îńŝéĉúr̂é r̂éq̂úêśt̂ f́ôún̂d́}\n    other {# îńŝéĉúr̂é r̂éq̂úêśt̂ś f̂óûńd̂}\n    }"
@@ -716,6 +737,9 @@
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | displayValueTextWithOverride": {
     "message": "Îńt̂ér̂áĉt́îv́ê ón̂ śîḿûĺât́êd́ m̂ób̂íl̂é n̂ét̂ẃôŕk̂ át̂ {timeInMs, number, seconds} ś"
   },
+  "lighthouse-core/audits/load-fast-enough-for-pwa.js | explanationLoadSlow": {
+    "message": "Ŷóûŕ p̂áĝé l̂óâd́ŝ t́ôó ŝĺôẃl̂ý âńd̂ íŝ ńôt́ îńt̂ér̂áĉt́îv́ê ẃît́ĥín̂ 10 śêćôńd̂ś. L̂óôḱ ât́ t̂h́ê óp̂ṕôŕt̂ún̂ít̂íêś âńd̂ d́îáĝńôśt̂íĉś îń t̂h́ê \"Ṕêŕf̂ór̂ḿâńĉé\" ŝéĉt́îón̂ t́ô ĺêár̂ń ĥóŵ t́ô ím̂ṕr̂óv̂é."
+  },
   "lighthouse-core/audits/load-fast-enough-for-pwa.js | failureTitle": {
     "message": "P̂áĝé l̂óâd́ îś n̂ót̂ f́âśt̂ én̂óûǵĥ ón̂ ḿôb́îĺê ńêt́ŵór̂ḱŝ"
   },
@@ -734,6 +758,24 @@
   "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/manual/pwa-cross-browser.js | description": {
+    "message": "T̂ó r̂éâćĥ t́ĥé m̂óŝt́ n̂úm̂b́êŕ ôf́ ûśêŕŝ, śît́êś ŝh́ôúl̂d́ ŵór̂ḱ âćr̂óŝś êv́êŕŷ ḿâj́ôŕ b̂ŕôẃŝér̂. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser)."
+  },
+  "lighthouse-core/audits/manual/pwa-cross-browser.js | title": {
+    "message": "Ŝít̂é ŵór̂ḱŝ ćr̂óŝś-b̂ŕôẃŝér̂"
+  },
+  "lighthouse-core/audits/manual/pwa-each-page-has-url.js | description": {
+    "message": "Êńŝúr̂é îńd̂ív̂íd̂úâĺ p̂áĝéŝ ár̂é d̂éêṕ l̂ín̂ḱâb́l̂é v̂íâ ÚR̂Ĺ âńd̂ t́ĥát̂ ÚR̂Ĺŝ ár̂é ûńîq́ûé f̂ór̂ t́ĥé p̂úr̂ṕôśê óf̂ śĥár̂éâb́îĺît́ŷ ón̂ śôćîál̂ ḿêd́îá. [L̂éâŕn̂ ḿôŕê](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url)."
+  },
+  "lighthouse-core/audits/manual/pwa-each-page-has-url.js | title": {
+    "message": "Êáĉh́ p̂áĝé ĥáŝ á ÛŔL̂"
+  },
+  "lighthouse-core/audits/manual/pwa-page-transitions.js | description": {
+    "message": "T̂ŕâńŝít̂íôńŝ śĥóûĺd̂ f́êél̂ śn̂áp̂ṕŷ áŝ ýôú t̂áp̂ ár̂óûńd̂, év̂én̂ ón̂ á ŝĺôẃ n̂ét̂ẃôŕk̂, á k̂éŷ t́ô ṕêŕĉéîv́êd́ p̂ér̂f́ôŕm̂án̂ćê. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network)."
+  },
+  "lighthouse-core/audits/manual/pwa-page-transitions.js | title": {
+    "message": "P̂áĝé t̂ŕâńŝít̂íôńŝ d́ôń't̂ f́êél̂ ĺîḱê t́ĥéŷ b́l̂óĉḱ ôń t̂h́ê ńêt́ŵór̂ḱ"
+  },
   "lighthouse-core/audits/metrics/estimated-input-latency.js | description": {
     "message": "Êśt̂ím̂át̂éd̂ Ín̂ṕût́ L̂át̂én̂ćŷ 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̂](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency)."
   },
@@ -794,6 +836,18 @@
   "lighthouse-core/audits/network-server-latency.js | title": {
     "message": "Ŝér̂v́êŕ B̂áĉḱêńd̂ Ĺât́êńĉíêś"
   },
+  "lighthouse-core/audits/offline-start-url.js | description": {
+    "message": "Â śêŕv̂íĉé ŵór̂ḱêŕ êńâb́l̂éŝ ýôúr̂ ẃêb́ âṕp̂ t́ô b́ê ŕêĺîáb̂ĺê ín̂ ún̂ṕr̂éd̂íĉt́âb́l̂é n̂ét̂ẃôŕk̂ ćôńd̂ít̂íôńŝ. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)."
+  },
+  "lighthouse-core/audits/offline-start-url.js | failureTitle": {
+    "message": "`start_url` d̂óêś n̂ót̂ ŕêśp̂ón̂d́ ŵít̂h́ â 200 ẃĥén̂ óf̂f́l̂ín̂é"
+  },
+  "lighthouse-core/audits/offline-start-url.js | title": {
+    "message": "`start_url` r̂éŝṕôńd̂ś ŵít̂h́ â 200 ẃĥén̂ óf̂f́l̂ín̂é"
+  },
+  "lighthouse-core/audits/offline-start-url.js | warningCantStart": {
+    "message": "L̂íĝh́t̂h́ôúŝé ĉóûĺd̂ń't̂ ŕêád̂ t́ĥé `start_url` f̂ŕôḿ t̂h́ê ḿâńîf́êśt̂. Áŝ á r̂éŝúl̂t́, t̂h́ê `start_url` ẃâś âśŝúm̂éd̂ t́ô b́ê t́ĥé d̂óĉúm̂én̂t́'ŝ ÚR̂Ĺ. Êŕr̂ór̂ ḿêśŝáĝé: '{manifestWarning}'."
+  },
   "lighthouse-core/audits/performance-budget.js | columnOverBudget": {
     "message": "Ôv́êŕ B̂úd̂ǵêt́"
   },
@@ -806,6 +860,15 @@
   "lighthouse-core/audits/performance-budget.js | title": {
     "message": "P̂ér̂f́ôŕm̂án̂ćê b́ûd́ĝét̂"
   },
+  "lighthouse-core/audits/redirects-http.js | description": {
+    "message": "Îf́ ŷóû'v́ê ál̂ŕêád̂ý ŝét̂ úp̂ H́T̂T́P̂Ś, m̂ák̂é ŝúr̂é t̂h́ât́ ŷóû ŕêd́îŕêćt̂ ál̂ĺ ĤT́T̂Ṕ t̂ŕâf́f̂íĉ t́ô H́T̂T́P̂Ś îń ôŕd̂ér̂ t́ô én̂áb̂ĺê śêćûŕê ẃêb́ f̂éât́ûŕêś f̂ór̂ ál̂ĺ ŷóûŕ ûśêŕŝ. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https)."
+  },
+  "lighthouse-core/audits/redirects-http.js | failureTitle": {
+    "message": "D̂óêś n̂ót̂ ŕêd́îŕêćt̂ H́T̂T́P̂ t́r̂áf̂f́îć t̂ó ĤT́T̂ṔŜ"
+  },
+  "lighthouse-core/audits/redirects-http.js | title": {
+    "message": "R̂éd̂ír̂éĉt́ŝ H́T̂T́P̂ t́r̂áf̂f́îć t̂ó ĤT́T̂Ṕ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̂](https://developers.google.com/web/tools/lighthouse/audits/redirects)."
   },
@@ -971,6 +1034,45 @@
   "lighthouse-core/audits/seo/tap-targets.js | title": {
     "message": "T̂áp̂ t́âŕĝét̂ś âŕê śîźêd́ âṕp̂ŕôṕr̂íât́êĺŷ"
   },
+  "lighthouse-core/audits/service-worker.js | description": {
+    "message": "T̂h́ê śêŕv̂íĉé ŵór̂ḱêŕ îś t̂h́ê t́êćĥńôĺôǵŷ t́ĥát̂ én̂áb̂ĺêś ŷóûŕ âṕp̂ t́ô úŝé m̂án̂ý P̂ŕôǵr̂éŝśîv́ê Ẃêb́ Âṕp̂ f́êát̂úr̂éŝ, śûćĥ áŝ óf̂f́l̂ín̂é, âd́d̂ t́ô h́ôḿêśĉŕêén̂, án̂d́ p̂úŝh́ n̂ót̂íf̂íĉát̂íôńŝ. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker)."
+  },
+  "lighthouse-core/audits/service-worker.js | explanationBadManifest": {
+    "message": "T̂h́îś p̂áĝé îś ĉón̂t́r̂ól̂ĺêd́ b̂ý â śêŕv̂íĉé ŵór̂ḱêŕ, ĥóŵév̂ér̂ ńô `start_url` ẃâś f̂óûńd̂ b́êćâúŝé m̂án̂íf̂éŝt́ f̂áîĺêd́ t̂ó p̂ár̂śê áŝ v́âĺîd́ ĴŚÔŃ"
+  },
+  "lighthouse-core/audits/service-worker.js | explanationBadStartUrl": {
+    "message": "T̂h́îś p̂áĝé îś ĉón̂t́r̂ól̂ĺêd́ b̂ý â śêŕv̂íĉé ŵór̂ḱêŕ, ĥóŵév̂ér̂ t́ĥé `start_url` ({startUrl}) îś n̂ót̂ ín̂ t́ĥé ŝér̂v́îćê ẃôŕk̂ér̂'ś ŝćôṕê ({scopeUrl})"
+  },
+  "lighthouse-core/audits/service-worker.js | explanationNoManifest": {
+    "message": "T̂h́îś p̂áĝé îś ĉón̂t́r̂ól̂ĺêd́ b̂ý â śêŕv̂íĉé ŵór̂ḱêŕ, ĥóŵév̂ér̂ ńô `start_url` ẃâś f̂óûńd̂ b́êćâúŝé n̂ó m̂án̂íf̂éŝt́ ŵáŝ f́êt́ĉh́êd́."
+  },
+  "lighthouse-core/audits/service-worker.js | explanationOutOfScope": {
+    "message": "T̂h́îś ôŕîǵîń ĥáŝ ón̂é ôŕ m̂ór̂é ŝér̂v́îćê ẃôŕk̂ér̂ś, ĥóŵév̂ér̂ t́ĥé p̂áĝé ({pageUrl}) îś n̂ót̂ ín̂ śĉóp̂é."
+  },
+  "lighthouse-core/audits/service-worker.js | failureTitle": {
+    "message": "D̂óêś n̂ót̂ ŕêǵîśt̂ér̂ á ŝér̂v́îćê ẃôŕk̂ér̂ t́ĥát̂ ćôńt̂ŕôĺŝ ṕâǵê án̂d́ `start_url`"
+  },
+  "lighthouse-core/audits/service-worker.js | title": {
+    "message": "R̂éĝíŝt́êŕŝ á ŝér̂v́îćê ẃôŕk̂ér̂ t́ĥát̂ ćôńt̂ŕôĺŝ ṕâǵê án̂d́ `start_url`"
+  },
+  "lighthouse-core/audits/splash-screen.js | description": {
+    "message": "Â t́ĥém̂éd̂ śp̂ĺâśĥ śĉŕêén̂ én̂śûŕêś â h́îǵĥ-q́ûál̂ít̂ý êx́p̂ér̂íêńĉé ŵh́êń ûśêŕŝ ĺâún̂ćĥ ýôúr̂ áp̂ṕ f̂ŕôḿ t̂h́êír̂ h́ôḿêśĉŕêén̂ś. [L̂éâŕn̂ ḿôŕê](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen)."
+  },
+  "lighthouse-core/audits/splash-screen.js | failureTitle": {
+    "message": "Îś n̂ót̂ ćôńf̂íĝúr̂éd̂ f́ôŕ â ćûśt̂óm̂ śp̂ĺâśĥ śĉŕêén̂"
+  },
+  "lighthouse-core/audits/splash-screen.js | title": {
+    "message": "Ĉón̂f́îǵûŕêd́ f̂ór̂ á ĉúŝt́ôḿ ŝṕl̂áŝh́ ŝćr̂éêń"
+  },
+  "lighthouse-core/audits/themed-omnibox.js | description": {
+    "message": "T̂h́ê b́r̂óŵśêŕ âd́d̂ŕêśŝ b́âŕ ĉán̂ b́ê t́ĥém̂éd̂ t́ô ḿât́ĉh́ ŷóûŕ ŝít̂é. [L̂éâŕn̂ ḿôŕê](https://developers.google.com/web/tools/lighthouse/audits/address-bar)."
+  },
+  "lighthouse-core/audits/themed-omnibox.js | failureTitle": {
+    "message": "D̂óêś n̂ót̂ śêt́ â t́ĥém̂é ĉól̂ór̂ f́ôŕ t̂h́ê ád̂d́r̂éŝś b̂ár̂."
+  },
+  "lighthouse-core/audits/themed-omnibox.js | title": {
+    "message": "Ŝét̂ś â t́ĥém̂é ĉól̂ór̂ f́ôŕ t̂h́ê ád̂d́r̂éŝś b̂ár̂."
+  },
   "lighthouse-core/audits/third-party-summary.js | columnMainThreadTime": {
     "message": "M̂áîń T̂h́r̂éâd́ T̂ím̂é"
   },
@@ -1034,6 +1136,42 @@
   "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/viewport.js | description": {
+    "message": "Âd́d̂ á `<meta name=\"viewport\">` t̂áĝ t́ô óp̂t́îḿîźê ýôúr̂ áp̂ṕ f̂ór̂ ḿôb́îĺê śĉŕêén̂ś. [L̂éâŕn̂ ḿôŕê](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag)."
+  },
+  "lighthouse-core/audits/viewport.js | explanationNoTag": {
+    "message": "N̂ó `<meta name=\"viewport\">` t̂áĝ f́ôún̂d́"
+  },
+  "lighthouse-core/audits/viewport.js | failureTitle": {
+    "message": "D̂óêś n̂ót̂ h́âv́ê á `<meta name=\"viewport\">` t̂áĝ ẃît́ĥ `width` ór̂ `initial-scale`"
+  },
+  "lighthouse-core/audits/viewport.js | title": {
+    "message": "Ĥáŝ á `<meta name=\"viewport\">` t̂áĝ ẃît́ĥ `width` ór̂ `initial-scale`"
+  },
+  "lighthouse-core/audits/without-javascript.js | description": {
+    "message": "Ŷóûŕ âṕp̂ śĥóûĺd̂ d́îśp̂ĺâý ŝóm̂é ĉón̂t́êńt̂ ẃĥén̂ J́âv́âŚĉŕîṕt̂ íŝ d́îśâb́l̂éd̂, év̂én̂ íf̂ ít̂'ś ĵúŝt́ â ẃâŕn̂ín̂ǵ t̂ó t̂h́ê úŝér̂ t́ĥát̂ J́âv́âŚĉŕîṕt̂ íŝ ŕêq́ûír̂éd̂ t́ô úŝé t̂h́ê áp̂ṕ. [L̂éâŕn̂ ḿôŕê](https://developers.google.com/web/tools/lighthouse/audits/no-js)."
+  },
+  "lighthouse-core/audits/without-javascript.js | explanation": {
+    "message": "T̂h́ê ṕâǵê b́ôd́ŷ śĥóûĺd̂ ŕêńd̂ér̂ śôḿê ćôńt̂én̂t́ îf́ ît́ŝ śĉŕîṕt̂ś âŕê ńôt́ âv́âíl̂áb̂ĺê."
+  },
+  "lighthouse-core/audits/without-javascript.js | failureTitle": {
+    "message": "D̂óêś n̂ót̂ ṕr̂óv̂íd̂é f̂ál̂ĺb̂áĉḱ ĉón̂t́êńt̂ ẃĥén̂ J́âv́âŚĉŕîṕt̂ íŝ ńôt́ âv́âíl̂áb̂ĺê"
+  },
+  "lighthouse-core/audits/without-javascript.js | title": {
+    "message": "Ĉón̂t́âín̂ś ŝóm̂é ĉón̂t́êńt̂ ẃĥén̂ J́âv́âŚĉŕîṕt̂ íŝ ńôt́ âv́âíl̂áb̂ĺê"
+  },
+  "lighthouse-core/audits/works-offline.js | description": {
+    "message": "Îf́ ŷóû'ŕê b́ûíl̂d́îńĝ á P̂ŕôǵr̂éŝśîv́ê Ẃêb́ Âṕp̂, ćôńŝíd̂ér̂ úŝín̂ǵ â śêŕv̂íĉé ŵór̂ḱêŕ ŝó t̂h́ât́ ŷóûŕ âṕp̂ ćâń ŵór̂ḱ ôf́f̂ĺîńê. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline)."
+  },
+  "lighthouse-core/audits/works-offline.js | failureTitle": {
+    "message": "Ĉúr̂ŕêńt̂ ṕâǵê d́ôéŝ ńôt́ r̂éŝṕôńd̂ ẃît́ĥ á 200 ŵh́êń ôf́f̂ĺîńê"
+  },
+  "lighthouse-core/audits/works-offline.js | title": {
+    "message": "Ĉúr̂ŕêńt̂ ṕâǵê ŕêśp̂ón̂d́ŝ ẃît́ĥ á 200 ŵh́êń ôf́f̂ĺîńê"
+  },
+  "lighthouse-core/audits/works-offline.js | warningNoLoad": {
+    "message": "T̂h́ê ṕâǵê ḿâý n̂ót̂ b́ê ĺôád̂ín̂ǵ ôf́f̂ĺîńê b́êćâúŝé ŷóûŕ t̂éŝt́ ÛŔL̂ ({requested}) ẃâś r̂éd̂ír̂éĉt́êd́ t̂ó \"{final}\". T̂ŕŷ t́êśt̂ín̂ǵ t̂h́ê śêćôńd̂ ÚR̂Ĺ d̂ír̂éĉt́l̂ý."
+  },
   "lighthouse-core/config/default-config.js | a11yAriaGroupDescription": {
     "message": "T̂h́êśê ár̂é ôṕp̂ór̂t́ûńît́îéŝ t́ô ím̂ṕr̂óv̂é t̂h́ê úŝáĝé ôf́ ÂŔÎÁ îń ŷóûŕ âṕp̂ĺîćât́îón̂ ẃĥíĉh́ m̂áŷ én̂h́âńĉé t̂h́ê éx̂ṕêŕîén̂ćê f́ôŕ ûśêŕŝ óf̂ áŝśîśt̂ív̂é t̂éĉh́n̂ól̂óĝý, l̂ík̂é â śĉŕêén̂ ŕêád̂ér̂."
   },
@@ -1130,6 +1268,15 @@
   "lighthouse-core/config/default-config.js | performanceCategoryTitle": {
     "message": "P̂ér̂f́ôŕm̂án̂ćê"
   },
+  "lighthouse-core/config/default-config.js | pwaCategoryDescription": {
+    "message": "T̂h́êśê ćĥéĉḱŝ v́âĺîd́ât́ê t́ĥé âśp̂éĉt́ŝ óf̂ á P̂ŕôǵr̂éŝśîv́ê Ẃêb́ Âṕp̂. [Ĺêár̂ń m̂ór̂é](https://developers.google.com/web/progressive-web-apps/checklist)."
+  },
+  "lighthouse-core/config/default-config.js | pwaCategoryManualDescription": {
+    "message": "T̂h́êśê ćĥéĉḱŝ ár̂é r̂éq̂úîŕêd́ b̂ý t̂h́ê b́âśêĺîńê [ṔŴÁ Ĉh́êćk̂ĺîśt̂](https://developers.google.com/web/progressive-web-apps/checklist) b́ût́ âŕê ńôt́ âút̂óm̂át̂íĉál̂ĺŷ ćĥéĉḱêd́ b̂ý L̂íĝh́t̂h́ôúŝé. T̂h́êý d̂ó n̂ót̂ áf̂f́êćt̂ ýôúr̂ śĉór̂é b̂út̂ ít̂'ś îḿp̂ór̂t́âńt̂ t́ĥát̂ ýôú v̂ér̂íf̂ý t̂h́êḿ m̂án̂úâĺl̂ý."
+  },
+  "lighthouse-core/config/default-config.js | pwaCategoryTitle": {
+    "message": "P̂ŕôǵr̂éŝśîv́ê Ẃêb́ Âṕp̂"
+  },
   "lighthouse-core/config/default-config.js | pwaFastReliableGroupTitle": {
     "message": "F̂áŝt́ âńd̂ ŕêĺîáb̂ĺê"
   },
diff --git a/proto/sample_v2_round_trip.json b/proto/sample_v2_round_trip.json
index 56dc2663b..3668a5f8f 100644
--- a/proto/sample_v2_round_trip.json
+++ b/proto/sample_v2_round_trip.json
@@ -8,7 +8,7 @@
             "title": "`[accesskey]` values are unique"
         },
         "appcache-manifest": {
-            "description": "Application Cache is deprecated. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/appcache).",
+            "description": "Application Cache is deprecated. [Learn more](https://web.dev/appcache-manifest).",
             "displayValue": "Found \"clock.appcache\"",
             "id": "appcache-manifest",
             "score": 0.0,
@@ -16,7 +16,7 @@
             "title": "Uses Application Cache"
         },
         "apple-touch-icon": {
-            "description": "For ideal appearance on iOS when users add to the home screen, define an apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).",
+            "description": "For ideal appearance on iOS when users add a progressive web app to the home screen, define an `apple-touch-icon`. It must point to a non-transparent 192px (or 180px) square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).",
             "id": "apple-touch-icon",
             "score": 0.0,
             "scoreDisplayMode": "binary",
@@ -399,7 +399,7 @@
             "title": "`<dl>`'s contain only properly-ordered `<dt>` and `<dd>` groups, `<script>` or `<template>` elements."
         },
         "deprecations": {
-            "description": "Deprecated APIs will eventually be removed from the browser. [Learn more](https://www.chromestatus.com/features#deprecated).",
+            "description": "Deprecated APIs will eventually be removed from the browser. [Learn more](https://web.dev/deprecations).",
             "details": {
                 "headings": [
                     {
@@ -483,7 +483,7 @@
             "title": "Definition list items are wrapped in `<dl>` elements"
         },
         "doctype": {
-            "description": "Specifying a doctype prevents the browser from switching to quirks-mode. Read more on the [MDN Web Docs page](https://developer.mozilla.org/en-US/docs/Glossary/Doctype)",
+            "description": "Specifying a doctype prevents the browser from switching to quirks-mode. [Learn more](https://web.dev/doctype).",
             "id": "doctype",
             "score": 1.0,
             "scoreDisplayMode": "binary",
@@ -502,7 +502,7 @@
             "title": "Document has a `<title>` element"
         },
         "dom-size": {
-            "description": "Browser engineers recommend pages contain fewer than ~1,500 DOM elements. 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).",
+            "description": "Browser engineers recommend pages contain fewer than ~1,500 DOM elements. 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://web.dev/dom-size).",
             "details": {
                 "headings": [
                     {
@@ -580,7 +580,7 @@
             "title": "Use video formats for animated content"
         },
         "errors-in-console": {
-            "description": "Errors logged to the console indicate unresolved problems. They can come from network request failures and other browser concerns.",
+            "description": "Errors logged to the console indicate unresolved problems. They can come from network request failures and other browser concerns. [Learn more](https://web.dev/errors-in-console)",
             "details": {
                 "headings": [
                     {
@@ -639,7 +639,7 @@
             "title": "Estimated Input Latency"
         },
         "external-anchors-use-rel-noopener": {
-            "description": "Add `rel=\"noopener\"` or `rel=\"noreferrer\"` to any external links to improve performance and prevent security vulnerabilities. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/noopener).",
+            "description": "Add `rel=\"noopener\"` or `rel=\"noreferrer\"` to any external links to improve performance and prevent security vulnerabilities. [Learn more](https://web.dev/external-anchors-use-rel-noopener).",
             "details": {
                 "headings": [
                     {
@@ -801,7 +801,7 @@
             "title": "`<frame>` or `<iframe>` elements have a title"
         },
         "geolocation-on-start": {
-            "description": "Users are mistrustful of or confused by sites that request their location without context. Consider tying the request to a user action instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/geolocation-on-load).",
+            "description": "Users are mistrustful of or confused by sites that request their location without context. Consider tying the request to a user action instead. [Learn more](https://web.dev/geolocation-on-start).",
             "details": {
                 "headings": [
                     {
@@ -974,7 +974,7 @@
             "title": "Image elements do not have `[alt]` attributes"
         },
         "image-aspect-ratio": {
-            "description": "Image display dimensions should match natural aspect ratio. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/aspect-ratio).",
+            "description": "Image display dimensions should match natural aspect ratio. [Learn more](https://web.dev/image-aspect-ratio).",
             "details": {
                 "headings": [
                     {
@@ -1069,7 +1069,7 @@
             "title": "Page isn\u2019t blocked from indexing"
         },
         "is-on-https": {
-            "description": "All sites should be protected with HTTPS, even ones that don't handle sensitive data. HTTPS prevents intruders from tampering with or passively listening in on the communications between your app and your users, and is a prerequisite for HTTP/2 and many new web platform APIs. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/https).",
+            "description": "All sites should be protected with HTTPS, even ones that don't handle sensitive data. HTTPS prevents intruders from tampering with or passively listening in on the communications between your app and your users, and is a prerequisite for HTTP/2 and many new web platform APIs. [Learn more](https://web.dev/is-on-https).",
             "details": {
                 "headings": [
                     {
@@ -1092,7 +1092,7 @@
             "title": "Does not use HTTPS"
         },
         "js-libraries": {
-            "description": "All front-end JavaScript libraries detected on the page.",
+            "description": "All front-end JavaScript libraries detected on the page. [Learn more](https://web.dev/js-libraries).",
             "details": {
                 "headings": [
                     {
@@ -1839,7 +1839,7 @@
             "title": "Server Backend Latencies"
         },
         "no-document-write": {
-            "description": "For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/document-write).",
+            "description": "For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn more](https://web.dev/no-document-write).",
             "details": {
                 "headings": [
                     {
@@ -1875,7 +1875,7 @@
             "title": "Uses `document.write()`"
         },
         "no-vulnerable-libraries": {
-            "description": "Some third-party scripts may contain known security vulnerabilities that are easily identified and exploited by attackers. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/vulnerabilities).",
+            "description": "Some third-party scripts may contain known security vulnerabilities that are easily identified and exploited by attackers. [Learn more](https://web.dev/no-vulnerable-libraries).",
             "details": {
                 "headings": [
                     {
@@ -1915,7 +1915,7 @@
             "title": "Includes front-end JavaScript libraries with known security vulnerabilities"
         },
         "notification-on-start": {
-            "description": "Users are mistrustful of or confused by sites that request to send notifications without context. Consider tying the request to user gestures instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/notifications-on-load).",
+            "description": "Users are mistrustful of or confused by sites that request to send notifications without context. Consider tying the request to user gestures instead. [Learn more](https://web.dev/notification-on-start).",
             "details": {
                 "headings": [
                     {
@@ -1998,7 +1998,7 @@
             "id": "offline-start-url",
             "score": 0.0,
             "scoreDisplayMode": "binary",
-            "title": "start_url does not respond with a 200 when offline",
+            "title": "`start_url` does not respond with a 200 when offline",
             "warnings": []
         },
         "offscreen-content-hidden": {
@@ -2025,7 +2025,7 @@
             "warnings": []
         },
         "password-inputs-can-be-pasted-into": {
-            "description": "Preventing password pasting undermines good security policy. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/password-pasting).",
+            "description": "Preventing password pasting undermines good security policy. [Learn more](https://web.dev/password-inputs-can-be-pasted-into).",
             "details": {
                 "headings": [
                     {
@@ -2177,7 +2177,7 @@
             "title": "Site works cross-browser"
         },
         "pwa-each-page-has-url": {
-            "description": "Ensure individual pages are deep linkable via the URLs and that URLs are unique for the purpose of shareability on social media. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url).",
+            "description": "Ensure individual pages are deep linkable via URL and that URLs are unique for the purpose of shareability on social media. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url).",
             "id": "pwa-each-page-has-url",
             "score": null,
             "scoreDisplayMode": "manual",
@@ -2205,7 +2205,7 @@
             "title": "Avoid multiple page redirects"
         },
         "redirects-http": {
-            "description": "If you've already set up HTTPS, make sure that you redirect all HTTP traffic to HTTPS. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https).",
+            "description": "If you've already set up HTTPS, make sure that you redirect all HTTP traffic to HTTPS in order to enable secure web features for all your users. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https).",
             "id": "redirects-http",
             "score": 0.0,
             "scoreDisplayMode": "binary",
@@ -2427,7 +2427,7 @@
             "id": "service-worker",
             "score": 0.0,
             "scoreDisplayMode": "binary",
-            "title": "Does not register a service worker that controls page and start_url"
+            "title": "Does not register a service worker that controls page and `start_url`"
         },
         "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).",
@@ -2558,7 +2558,7 @@
             "id": "themed-omnibox",
             "score": 0.0,
             "scoreDisplayMode": "binary",
-            "title": "Does not set an address-bar theme color"
+            "title": "Does not set a theme color for the address bar."
         },
         "third-party-summary": {
             "description": "Third-party code can significantly impact load performance. Limit the number of redundant third-party providers and try to load third-party code after your page has primarily finished loading. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/).",
@@ -2785,7 +2785,7 @@
             "title": "User Timing marks and measures"
         },
         "uses-http2": {
-            "description": "HTTP/2 offers many benefits over HTTP/1.1, including binary headers, multiplexing, and server push. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http2).",
+            "description": "HTTP/2 offers many benefits over HTTP/1.1, including binary headers, multiplexing, and server push. [Learn more](https://web.dev/uses-http2).",
             "details": {
                 "headings": [
                     {
@@ -2993,7 +2993,7 @@
             "warnings": []
         },
         "uses-passive-event-listeners": {
-            "description": "Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners).",
+            "description": "Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more](https://web.dev/uses-passive-event-listeners).",
             "details": {
                 "headings": [
                     {
@@ -3183,7 +3183,7 @@
             "title": "`<video>` elements contain a `<track>` element with `[kind=\"description\"]`"
         },
         "viewport": {
-            "description": "Add a viewport meta tag to optimize your app for mobile screens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag).",
+            "description": "Add a `<meta name=\"viewport\">` tag to optimize your app for mobile screens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag).",
             "id": "viewport",
             "score": 1.0,
             "scoreDisplayMode": "binary",


Test patch:
diff --git a/lighthouse-cli/test/fixtures/static-server.js b/lighthouse-cli/test/fixtures/static-server.js
index 2a0cdf6ea..83d60fef7 100644
--- a/lighthouse-cli/test/fixtures/static-server.js
+++ b/lighthouse-cli/test/fixtures/static-server.js
@@ -12,6 +12,7 @@ const http = require('http');
 const zlib = require('zlib');
 const path = require('path');
 const fs = require('fs');
+const mime = require('mime-types');
 const parseQueryString = require('querystring').parse;
 const parseURL = require('url').parse;
 const URLSearchParams = require('url').URLSearchParams;
@@ -63,23 +64,13 @@ function requestHandler(request, response) {
   function sendResponse(statusCode, data) {
     const headers = {'Access-Control-Allow-Origin': '*'};
 
-    if (filePath.endsWith('.js')) {
-      headers['Content-Type'] = 'text/javascript';
-    } else if (filePath.endsWith('.css')) {
-      headers['Content-Type'] = 'text/css';
-    } else if (filePath.endsWith('.svg')) {
-      headers['Content-Type'] = 'image/svg+xml';
-    } else if (filePath.endsWith('.png')) {
-      headers['Content-Type'] = 'image/png';
-    } else if (filePath.endsWith('.gif')) {
-      headers['Content-Type'] = 'image/gif';
-    } else if (filePath.endsWith('.jpg') || filePath.endsWith('.jpeg')) {
-      headers['Content-Type'] = 'image/jpeg';
-    } else if (filePath.endsWith('.webp')) {
-      headers['Content-Type'] = 'image/webp';
-    } else if (filePath.endsWith('.json')) {
-      headers['Content-Type'] = 'application/json';
-    }
+    const contentType = mime.lookup(filePath);
+    const charset = mime.lookup(contentType);
+    // `mime.contentType` appends the correct charset too.
+    // Note: it seems to miss just one case, svg. Doesn't matter much, we'll just allow
+    // svgs to fallback to binary encoding. `Content-Type: image/svg+xml` is sufficient for our use case.
+    // see https://github.com/jshttp/mime-types/issues/66
+    if (contentType) headers['Content-Type'] = mime.contentType(contentType);
 
     let delay = 0;
     let useGzip = false;
@@ -127,7 +118,8 @@ function requestHandler(request, response) {
       return setTimeout(finishResponse, delay, data);
     }
 
-    finishResponse(data);
+    const encoding = charset === 'UTF-8' ? 'utf-8' : 'binary';
+    finishResponse(data, encoding);
   }
 
   function sendRedirect(url) {
@@ -138,8 +130,8 @@ function requestHandler(request, response) {
     response.end();
   }
 
-  function finishResponse(data) {
-    response.write(data, 'binary');
+  function finishResponse(data, encoding) {
+    response.write(data, encoding);
     response.end();
   }
 }
diff --git a/lighthouse-cli/test/smokehouse/byte-efficiency/expectations.js b/lighthouse-cli/test/smokehouse/byte-efficiency/expectations.js
index 9a00707a1..722090bc0 100644
--- a/lighthouse-cli/test/smokehouse/byte-efficiency/expectations.js
+++ b/lighthouse-cli/test/smokehouse/byte-efficiency/expectations.js
@@ -132,12 +132,12 @@ module.exports = [
               },
               {
                 url: 'http://localhost:10200/byte-efficiency/script.js?gzip=1',
-                transferSize: 1136,
+                transferSize: 1158,
                 resourceSize: 52997,
               },
               {
                 url: 'http://localhost:10200/byte-efficiency/script.js',
-                transferSize: 53181,
+                transferSize: 53203,
                 resourceSize: 52997,
               },
               {
diff --git a/lighthouse-cli/test/smokehouse/perf/expectations.js b/lighthouse-cli/test/smokehouse/perf/expectations.js
index 45b1e2399..4ea41b4d5 100644
--- a/lighthouse-cli/test/smokehouse/perf/expectations.js
+++ b/lighthouse-cli/test/smokehouse/perf/expectations.js
@@ -79,8 +79,8 @@ module.exports = [
               {resourceType: 'font', requestCount: 2, size: '80000±1000'},
               {resourceType: 'script', requestCount: 3, size: '55000±1000'},
               {resourceType: 'image', requestCount: 2, size: '28000±1000'},
-              {resourceType: 'document', requestCount: 1, size: '2100±100'},
-              {resourceType: 'other', requestCount: 2, size: '1250±50'},
+              {resourceType: 'document', requestCount: 1, size: '2200±100'},
+              {resourceType: 'other', requestCount: 2, size: '1300±50'},
               {resourceType: 'stylesheet', requestCount: 1, size: '450±100'},
               {resourceType: 'media', requestCount: 0, size: 0},
               {resourceType: 'third-party', requestCount: 0, size: 0},
diff --git a/lighthouse-core/test/audits/offline-start-url-test.js b/lighthouse-core/test/audits/offline-start-url-test.js
index 835f78193..3e238720a 100644
--- a/lighthouse-core/test/audits/offline-start-url-test.js
+++ b/lighthouse-core/test/audits/offline-start-url-test.js
@@ -68,6 +68,7 @@ describe('Offline start_url audit', () => {
     assert.strictEqual(result.score, 1);
     assert.strictEqual(result.explanation, undefined);
     assert.strictEqual(result.warnings.length, 1);
-    assert.ok(result.warnings[0].includes('start_url must be same-origin as document'));
+    expect(result.warnings[0]).toBeDisplayString(
+      /Lighthouse couldn't read the `start_url`.*ERROR: start_url must be same-origin as document/);
   });
 });
diff --git a/lighthouse-core/test/audits/service-worker-test.js b/lighthouse-core/test/audits/service-worker-test.js
index d19ddee1f..1149b18f2 100644
--- a/lighthouse-core/test/audits/service-worker-test.js
+++ b/lighthouse-core/test/audits/service-worker-test.js
@@ -113,10 +113,9 @@ describe('Offline: service worker audit', () => {
     const manifest = {start_url: finalUrl};
 
     const output = ServiceWorker.audit(createArtifacts(swOpts, finalUrl, manifest));
-    expect(output).toMatchObject({
-      score: 0,
-      explanation: expect.stringMatching(new RegExp(`${finalUrl}.*not in scope`)),
-    });
+    assert.strictEqual(output.score, 0);
+    expect(output.explanation).toBeDisplayString('This origin has one or more service workers, ' +
+      'however the page (https://example.com/index.html) is not in scope.');
   });
 
   it('fails when start_url is out of scope', () => {
@@ -128,13 +127,12 @@ describe('Offline: service worker audit', () => {
     const startUrl = 'https://example.com/';
     const manifest = {start_url: startUrl};
 
-    const scopeURL = 'https://example.com/serviceworker';
+    const scopeURL = 'https://example.com/serviceworker/';
 
     const output = ServiceWorker.audit(createArtifacts(swOpts, finalUrl, manifest));
-    expect(output).toMatchObject({
-      score: 0,
-      explanation: expect.stringMatching(new RegExp(`start_url.*${startUrl}.*${scopeURL}`)),
-    });
+    assert.strictEqual(output.score, 0);
+    expect(output.explanation).toBeDisplayString('This page is controlled by a service worker, ' +
+      `however the \`start_url\` (${startUrl}) is not in the service worker's scope (${scopeURL})`);
   });
 
   it('fails when explicit scopeURL puts the page URL out of scope', () => {
@@ -147,10 +145,9 @@ describe('Offline: service worker audit', () => {
     const manifest = {start_url: finalUrl};
 
     const output = ServiceWorker.audit(createArtifacts(swOpts, finalUrl, manifest));
-    expect(output).toMatchObject({
-      score: 0,
-      explanation: expect.stringMatching(new RegExp(`${finalUrl}.*not in scope`)),
-    });
+    assert.strictEqual(output.score, 0);
+    expect(output.explanation).toBeDisplayString('This origin has one or more service workers, ' +
+      `however the page (${finalUrl}) is not in scope.`);
   });
 
   it('fails when explicit scopeURL puts the start_url out of scope', () => {
@@ -165,10 +162,9 @@ describe('Offline: service worker audit', () => {
     const manifest = {start_url: startUrl};
 
     const output = ServiceWorker.audit(createArtifacts(swOpts, finalUrl, manifest));
-    expect(output).toMatchObject({
-      score: 0,
-      explanation: expect.stringMatching(new RegExp(`start_url.*${startUrl}.*${scopeURL}`)),
-    });
+    assert.strictEqual(output.score, 0);
+    expect(output.explanation).toBeDisplayString(
+      /service worker,.*\(.*\) is not in the service worker's scope \(.*\)/);
   });
 
   it('passes when both outside default scope but explicit scopeURL puts it back in', () => {
@@ -239,10 +235,9 @@ describe('Offline: service worker audit', () => {
     const manifest = {start_url: finalUrl};
 
     const output = ServiceWorker.audit(createArtifacts(swOpts, finalUrl, manifest));
-    expect(output).toMatchObject({
-      score: 0,
-      explanation: expect.stringMatching(new RegExp(`${finalUrl}.*not in scope`)),
-    });
+    assert.strictEqual(output.score, 0);
+    expect(output.explanation).toBeDisplayString('This origin has one or more service workers, ' +
+      `however the page (${finalUrl}) is not in scope.`);
   });
 
   it('fails when SW that controls start_url is different than SW that controls page', () => {
@@ -258,13 +253,12 @@ describe('Offline: service worker audit', () => {
     const startUrl = 'https://example.com/index.html';
     const manifest = {start_url: startUrl};
 
-    const scopeURL = 'https://example.com/project';
+    const scopeURL = 'https://example.com/project/';
 
     const output = ServiceWorker.audit(createArtifacts(swOpts, finalUrl, manifest));
-    expect(output).toMatchObject({
-      score: 0,
-      explanation: expect.stringMatching(new RegExp(`start_url.*${startUrl}.*${scopeURL}`)),
-    });
+    assert.strictEqual(output.score, 0);
+    expect(output.explanation).toBeDisplayString('This page is controlled by a service worker, ' +
+      `however the \`start_url\` (${startUrl}) is not in the service worker's scope (${scopeURL})`);
   });
 
   it('fails when a manifest was not found', () => {
@@ -276,10 +270,9 @@ describe('Offline: service worker audit', () => {
     const manifest = null;
 
     const output = ServiceWorker.audit(createArtifacts(swOpts, finalUrl, manifest));
-    expect(output).toMatchObject({
-      score: 0,
-      explanation: expect.stringMatching(/start_url.*no manifest was fetched/),
-    });
+    assert.strictEqual(output.score, 0);
+    expect(output.explanation).toBeDisplayString('This page is controlled by a service worker, ' +
+      'however no `start_url` was found because no manifest was fetched.');
   });
 
   it('fails when a manifest is invalid', () => {
@@ -293,9 +286,8 @@ describe('Offline: service worker audit', () => {
     artifacts.WebAppManifest = manifestParser('{,;}', finalUrl, finalUrl);
 
     const output = ServiceWorker.audit(artifacts);
-    expect(output).toMatchObject({
-      score: 0,
-      explanation: expect.stringMatching(/start_url.*manifest failed to parse as valid JSON/),
-    });
+    assert.strictEqual(output.score, 0);
+    expect(output.explanation).toBeDisplayString('This page is controlled by a service worker, ' +
+      'however no `start_url` was found because manifest failed to parse as valid JSON');
   });
 });
diff --git a/lighthouse-core/test/audits/viewport-test.js b/lighthouse-core/test/audits/viewport-test.js
index a0b63baa4..a129d0268 100644
--- a/lighthouse-core/test/audits/viewport-test.js
+++ b/lighthouse-core/test/audits/viewport-test.js
@@ -19,7 +19,7 @@ describe('Mobile-friendly: viewport audit', () => {
       MetaElements: [],
     }, fakeContext);
     assert.equal(auditResult.score, 0);
-    assert.equal(auditResult.explanation, 'No viewport meta tag found');
+    expect(auditResult.explanation).toBeDisplayString('No `<meta name="viewport">` tag found');
   });
 
   it('fails when HTML contains a non-mobile friendly viewport meta tag', async () => {
diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json
index 43998b04b..332308250 100644
--- a/lighthouse-core/test/results/sample_v2.json
+++ b/lighthouse-core/test/results/sample_v2.json
@@ -14,7 +14,7 @@
     "is-on-https": {
       "id": "is-on-https",
       "title": "Does not use HTTPS",
-      "description": "All sites should be protected with HTTPS, even ones that don't handle sensitive data. HTTPS prevents intruders from tampering with or passively listening in on the communications between your app and your users, and is a prerequisite for HTTP/2 and many new web platform APIs. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/https).",
+      "description": "All sites should be protected with HTTPS, even ones that don't handle sensitive data. HTTPS prevents intruders from tampering with or passively listening in on the communications between your app and your users, and is a prerequisite for HTTP/2 and many new web platform APIs. [Learn more](https://web.dev/is-on-https).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "displayValue": "1 insecure request found",
@@ -37,13 +37,13 @@
     "redirects-http": {
       "id": "redirects-http",
       "title": "Does not redirect HTTP traffic to HTTPS",
-      "description": "If you've already set up HTTPS, make sure that you redirect all HTTP traffic to HTTPS. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https).",
+      "description": "If you've already set up HTTPS, make sure that you redirect all HTTP traffic to HTTPS in order to enable secure web features for all your users. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https).",
       "score": 0,
       "scoreDisplayMode": "binary"
     },
     "service-worker": {
       "id": "service-worker",
-      "title": "Does not register a service worker that controls page and start_url",
+      "title": "Does not register a service worker that controls page and `start_url`",
       "description": "The service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker).",
       "score": 0,
       "scoreDisplayMode": "binary"
@@ -59,7 +59,7 @@
     "viewport": {
       "id": "viewport",
       "title": "Has a `<meta name=\"viewport\">` tag with `width` or `initial-scale`",
-      "description": "Add a viewport meta tag to optimize your app for mobile screens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag).",
+      "description": "Add a `<meta name=\"viewport\">` tag to optimize your app for mobile screens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag).",
       "score": 1,
       "scoreDisplayMode": "binary",
       "warnings": []
@@ -212,7 +212,7 @@
     "errors-in-console": {
       "id": "errors-in-console",
       "title": "Browser errors were logged to the console",
-      "description": "Errors logged to the console indicate unresolved problems. They can come from network request failures and other browser concerns.",
+      "description": "Errors logged to the console indicate unresolved problems. They can come from network request failures and other browser concerns. [Learn more](https://web.dev/errors-in-console)",
       "score": 0,
       "scoreDisplayMode": "binary",
       "numericValue": 5,
@@ -488,7 +488,7 @@
     "apple-touch-icon": {
       "id": "apple-touch-icon",
       "title": "Does not provide a valid `apple-touch-icon`",
-      "description": "For ideal appearance on iOS when users add to the home screen, define an apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).",
+      "description": "For ideal appearance on iOS when users add a progressive web app to the home screen, define an `apple-touch-icon`. It must point to a non-transparent 192px (or 180px) square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "warnings": []
@@ -515,7 +515,7 @@
     },
     "themed-omnibox": {
       "id": "themed-omnibox",
-      "title": "Does not set an address-bar theme color",
+      "title": "Does not set a theme color for the address bar.",
       "description": "The browser address bar can be themed to match your site. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/address-bar).",
       "score": 0,
       "scoreDisplayMode": "binary",
@@ -546,7 +546,7 @@
     "image-aspect-ratio": {
       "id": "image-aspect-ratio",
       "title": "Displays images with incorrect aspect ratio",
-      "description": "Image display dimensions should match natural aspect ratio. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/aspect-ratio).",
+      "description": "Image display dimensions should match natural aspect ratio. [Learn more](https://web.dev/image-aspect-ratio).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "warnings": [],
@@ -587,7 +587,7 @@
     "deprecations": {
       "id": "deprecations",
       "title": "Uses deprecated APIs",
-      "description": "Deprecated APIs will eventually be removed from the browser. [Learn more](https://www.chromestatus.com/features#deprecated).",
+      "description": "Deprecated APIs will eventually be removed from the browser. [Learn more](https://web.dev/deprecations).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "displayValue": "3 warnings found",
@@ -1285,7 +1285,7 @@
     },
     "offline-start-url": {
       "id": "offline-start-url",
-      "title": "start_url does not respond with a 200 when offline",
+      "title": "`start_url` does not respond with a 200 when offline",
       "description": "A service worker enables your web app to be reliable in unpredictable network conditions. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline).",
       "score": 0,
       "scoreDisplayMode": "binary",
@@ -1541,7 +1541,7 @@
     "pwa-each-page-has-url": {
       "id": "pwa-each-page-has-url",
       "title": "Each page has a URL",
-      "description": "Ensure individual pages are deep linkable via the URLs and that URLs are unique for the purpose of shareability on social media. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url).",
+      "description": "Ensure individual pages are deep linkable via URL and that URLs are unique for the purpose of shareability on social media. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url).",
       "score": null,
       "scoreDisplayMode": "manual"
     },
@@ -2629,7 +2629,7 @@
     "appcache-manifest": {
       "id": "appcache-manifest",
       "title": "Uses Application Cache",
-      "description": "Application Cache is deprecated. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/appcache).",
+      "description": "Application Cache is deprecated. [Learn more](https://web.dev/appcache-manifest).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "displayValue": "Found \"clock.appcache\""
@@ -2637,14 +2637,14 @@
     "doctype": {
       "id": "doctype",
       "title": "Page has the HTML doctype",
-      "description": "Specifying a doctype prevents the browser from switching to quirks-mode. Read more on the [MDN Web Docs page](https://developer.mozilla.org/en-US/docs/Glossary/Doctype)",
+      "description": "Specifying a doctype prevents the browser from switching to quirks-mode. [Learn more](https://web.dev/doctype).",
       "score": 1,
       "scoreDisplayMode": "binary"
     },
     "dom-size": {
       "id": "dom-size",
       "title": "Avoids an excessive DOM size",
-      "description": "Browser engineers recommend pages contain fewer than ~1,500 DOM elements. 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).",
+      "description": "Browser engineers recommend pages contain fewer than ~1,500 DOM elements. 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://web.dev/dom-size).",
       "score": 1,
       "scoreDisplayMode": "numeric",
       "numericValue": 31,
@@ -2696,7 +2696,7 @@
     "external-anchors-use-rel-noopener": {
       "id": "external-anchors-use-rel-noopener",
       "title": "Links to cross-origin destinations are unsafe",
-      "description": "Add `rel=\"noopener\"` or `rel=\"noreferrer\"` to any external links to improve performance and prevent security vulnerabilities. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/noopener).",
+      "description": "Add `rel=\"noopener\"` or `rel=\"noreferrer\"` to any external links to improve performance and prevent security vulnerabilities. [Learn more](https://web.dev/external-anchors-use-rel-noopener).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "warnings": [
@@ -2746,7 +2746,7 @@
     "geolocation-on-start": {
       "id": "geolocation-on-start",
       "title": "Requests the geolocation permission on page load",
-      "description": "Users are mistrustful of or confused by sites that request their location without context. Consider tying the request to a user action instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/geolocation-on-load).",
+      "description": "Users are mistrustful of or confused by sites that request their location without context. Consider tying the request to a user action instead. [Learn more](https://web.dev/geolocation-on-start).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "details": {
@@ -2778,7 +2778,7 @@
     "no-document-write": {
       "id": "no-document-write",
       "title": "Uses `document.write()`",
-      "description": "For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/document-write).",
+      "description": "For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn more](https://web.dev/no-document-write).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "details": {
@@ -2814,7 +2814,7 @@
     "no-vulnerable-libraries": {
       "id": "no-vulnerable-libraries",
       "title": "Includes front-end JavaScript libraries with known security vulnerabilities",
-      "description": "Some third-party scripts may contain known security vulnerabilities that are easily identified and exploited by attackers. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/vulnerabilities).",
+      "description": "Some third-party scripts may contain known security vulnerabilities that are easily identified and exploited by attackers. [Learn more](https://web.dev/no-vulnerable-libraries).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "displayValue": "2 vulnerabilities detected",
@@ -2854,7 +2854,7 @@
     "js-libraries": {
       "id": "js-libraries",
       "title": "Detected JavaScript libraries",
-      "description": "All front-end JavaScript libraries detected on the page.",
+      "description": "All front-end JavaScript libraries detected on the page. [Learn more](https://web.dev/js-libraries).",
       "score": 1,
       "scoreDisplayMode": "binary",
       "details": {
@@ -2887,7 +2887,7 @@
     "notification-on-start": {
       "id": "notification-on-start",
       "title": "Requests the notification permission on page load",
-      "description": "Users are mistrustful of or confused by sites that request to send notifications without context. Consider tying the request to user gestures instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/notifications-on-load).",
+      "description": "Users are mistrustful of or confused by sites that request to send notifications without context. Consider tying the request to user gestures instead. [Learn more](https://web.dev/notification-on-start).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "details": {
@@ -2915,7 +2915,7 @@
     "password-inputs-can-be-pasted-into": {
       "id": "password-inputs-can-be-pasted-into",
       "title": "Prevents users to paste into password fields",
-      "description": "Preventing password pasting undermines good security policy. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/password-pasting).",
+      "description": "Preventing password pasting undermines good security policy. [Learn more](https://web.dev/password-inputs-can-be-pasted-into).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "details": {
@@ -2946,7 +2946,7 @@
     "uses-http2": {
       "id": "uses-http2",
       "title": "Does not use HTTP/2 for all of its resources",
-      "description": "HTTP/2 offers many benefits over HTTP/1.1, including binary headers, multiplexing, and server push. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http2).",
+      "description": "HTTP/2 offers many benefits over HTTP/1.1, including binary headers, multiplexing, and server push. [Learn more](https://web.dev/uses-http2).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "displayValue": "15 requests not served via HTTP/2",
@@ -3031,7 +3031,7 @@
     "uses-passive-event-listeners": {
       "id": "uses-passive-event-listeners",
       "title": "Does not use passive listeners to improve scrolling performance",
-      "description": "Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners).",
+      "description": "Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more](https://web.dev/uses-passive-event-listeners).",
       "score": 0,
       "scoreDisplayMode": "binary",
       "details": {
@@ -5134,6 +5134,36 @@
       "lighthouse-core/audits/is-on-https.js | columnInsecureURL": [
         "audits[is-on-https].details.headings[0].text"
       ],
+      "lighthouse-core/audits/redirects-http.js | failureTitle": [
+        "audits[redirects-http].title"
+      ],
+      "lighthouse-core/audits/redirects-http.js | description": [
+        "audits[redirects-http].description"
+      ],
+      "lighthouse-core/audits/service-worker.js | failureTitle": [
+        "audits[service-worker].title"
+      ],
+      "lighthouse-core/audits/service-worker.js | description": [
+        "audits[service-worker].description"
+      ],
+      "lighthouse-core/audits/works-offline.js | failureTitle": [
+        "audits[works-offline].title"
+      ],
+      "lighthouse-core/audits/works-offline.js | description": [
+        "audits[works-offline].description"
+      ],
+      "lighthouse-core/audits/viewport.js | title": [
+        "audits.viewport.title"
+      ],
+      "lighthouse-core/audits/viewport.js | description": [
+        "audits.viewport.description"
+      ],
+      "lighthouse-core/audits/without-javascript.js | title": [
+        "audits[without-javascript].title"
+      ],
+      "lighthouse-core/audits/without-javascript.js | description": [
+        "audits[without-javascript].description"
+      ],
       "lighthouse-core/audits/metrics/first-contentful-paint.js | title": [
         "audits[first-contentful-paint].title"
       ],
@@ -5333,12 +5363,36 @@
       "lighthouse-core/audits/redirects.js | description": [
         "audits.redirects.description"
       ],
+      "lighthouse-core/audits/installable-manifest.js | failureTitle": [
+        "audits[installable-manifest].title"
+      ],
+      "lighthouse-core/audits/installable-manifest.js | description": [
+        "audits[installable-manifest].description"
+      ],
       "lighthouse-core/audits/apple-touch-icon.js | failureTitle": [
         "audits[apple-touch-icon].title"
       ],
       "lighthouse-core/audits/apple-touch-icon.js | description": [
         "audits[apple-touch-icon].description"
       ],
+      "lighthouse-core/audits/splash-screen.js | failureTitle": [
+        "audits[splash-screen].title"
+      ],
+      "lighthouse-core/audits/splash-screen.js | description": [
+        "audits[splash-screen].description"
+      ],
+      "lighthouse-core/audits/themed-omnibox.js | failureTitle": [
+        "audits[themed-omnibox].title"
+      ],
+      "lighthouse-core/audits/themed-omnibox.js | description": [
+        "audits[themed-omnibox].description"
+      ],
+      "lighthouse-core/audits/content-width.js | title": [
+        "audits[content-width].title"
+      ],
+      "lighthouse-core/audits/content-width.js | description": [
+        "audits[content-width].description"
+      ],
       "lighthouse-core/audits/image-aspect-ratio.js | failureTitle": [
         "audits[image-aspect-ratio].title"
       ],
@@ -5430,6 +5484,12 @@
       "lighthouse-core/audits/network-server-latency.js | description": [
         "audits[network-server-latency].description"
       ],
+      "lighthouse-core/audits/offline-start-url.js | failureTitle": [
+        "audits[offline-start-url].title"
+      ],
+      "lighthouse-core/audits/offline-start-url.js | description": [
+        "audits[offline-start-url].description"
+      ],
       "lighthouse-core/audits/performance-budget.js | title": [
         "audits[performance-budget].title"
       ],
@@ -5564,6 +5624,24 @@
       "lighthouse-core/audits/third-party-summary.js | columnMainThreadTime": [
         "audits[third-party-summary].details.headings[2].text"
       ],
+      "lighthouse-core/audits/manual/pwa-cross-browser.js | title": [
+        "audits[pwa-cross-browser].title"
+      ],
+      "lighthouse-core/audits/manual/pwa-cross-browser.js | description": [
+        "audits[pwa-cross-browser].description"
+      ],
+      "lighthouse-core/audits/manual/pwa-page-transitions.js | title": [
+        "audits[pwa-page-transitions].title"
+      ],
+      "lighthouse-core/audits/manual/pwa-page-transitions.js | description": [
+        "audits[pwa-page-transitions].description"
+      ],
+      "lighthouse-core/audits/manual/pwa-each-page-has-url.js | title": [
+        "audits[pwa-each-page-has-url].title"
+      ],
+      "lighthouse-core/audits/manual/pwa-each-page-has-url.js | description": [
+        "audits[pwa-each-page-has-url].description"
+      ],
       "lighthouse-core/audits/accessibility/accesskeys.js | title": [
         "audits.accesskeys.title"
       ],
@@ -6185,6 +6263,15 @@
       "lighthouse-core/config/default-config.js | seoCategoryManualDescription": [
         "categories.seo.manualDescription"
       ],
+      "lighthouse-core/config/default-config.js | pwaCategoryTitle": [
+        "categories.pwa.title"
+      ],
+      "lighthouse-core/config/default-config.js | pwaCategoryDescription": [
+        "categories.pwa.description"
+      ],
+      "lighthouse-core/config/default-config.js | pwaCategoryManualDescription": [
+        "categories.pwa.manualDescription"
+      ],
       "lighthouse-core/config/default-config.js | metricGroupTitle": [
         "categoryGroups.metrics.title"
       ],