Repository: GoogleChrome/lighthouse
Pull request #5981 (2018-09-06 22:04:10)

i18n: mv locale files

brendankenny: need to merge/rebase. All sorts of weirdness going on with the file moves, I guess
paulirish: ptal

Patch:
diff --git a/lighthouse-core/audits/bootup-time.js b/lighthouse-core/audits/bootup-time.js
index f86e89baa..b3fc21958 100644
--- a/lighthouse-core/audits/bootup-time.js
+++ b/lighthouse-core/audits/bootup-time.js
@@ -8,7 +8,7 @@
 const Audit = require('./audit');
 const NetworkRequest = require('../lib/network-request');
 const {taskGroups} = require('../lib/task-groups');
-const i18n = require('../lib/i18n');
+const i18n = require('../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Title of a diagnostic audit that provides detail on the time spent executing javascript files during the load. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
diff --git a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js
index 49e167517..5f968d2f0 100644
--- a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js
+++ b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js
@@ -8,7 +8,7 @@
 const Audit = require('../audit');
 const linearInterpolation = require('../../lib/statistics').linearInterpolation;
 const Interactive = require('../../gather/computed/metrics/lantern-interactive');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const str_ = i18n.createMessageInstanceIdFn(__filename, {});
 
diff --git a/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js b/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js
index 2fae044a4..27483d87f 100644
--- a/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js
+++ b/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js
@@ -10,7 +10,7 @@
 
 const NetworkRequest = require('../../lib/network-request');
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to use video formats rather than animated GIFs, which are wasteful. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/byte-efficiency/offscreen-images.js b/lighthouse-core/audits/byte-efficiency/offscreen-images.js
index 66c6f5f92..fca48e3ad 100644
--- a/lighthouse-core/audits/byte-efficiency/offscreen-images.js
+++ b/lighthouse-core/audits/byte-efficiency/offscreen-images.js
@@ -12,7 +12,7 @@
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const Sentry = require('../../lib/sentry');
 const URL = require('../../lib/url-shim');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to defer loading offscreen images. Offscreen images are images located outside of the visible browser viewport. As they are unseen by the user and slow down page load, they should be loaded later, closer to when the user is going to see them. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js b/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js
index f823e655e..f8440e1f2 100644
--- a/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js
+++ b/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js
@@ -10,7 +10,7 @@
 'use strict';
 
 const Audit = require('../audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 const BaseNode = require('../../lib/dependency-graph/base-node');
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const UnusedCSS = require('./unused-css-rules');
diff --git a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js
index a2a63cbfe..fa64d53c2 100644
--- a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js
+++ b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Title of a diagnostic audit that provides detail on large network resources required during page load. 'Payloads' is roughly equivalent to 'resources'. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
diff --git a/lighthouse-core/audits/byte-efficiency/unminified-css.js b/lighthouse-core/audits/byte-efficiency/unminified-css.js
index 7b0bcd609..61b533f28 100644
--- a/lighthouse-core/audits/byte-efficiency/unminified-css.js
+++ b/lighthouse-core/audits/byte-efficiency/unminified-css.js
@@ -7,7 +7,7 @@
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const UnusedCSSRules = require('./unused-css-rules');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to minify (remove whitespace) the page's CSS code. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/byte-efficiency/unminified-javascript.js b/lighthouse-core/audits/byte-efficiency/unminified-javascript.js
index 79b424a76..6b505aaf4 100644
--- a/lighthouse-core/audits/byte-efficiency/unminified-javascript.js
+++ b/lighthouse-core/audits/byte-efficiency/unminified-javascript.js
@@ -7,7 +7,7 @@
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const esprima = require('esprima');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to minify the page’s JS code to reduce file size. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/byte-efficiency/unused-css-rules.js b/lighthouse-core/audits/byte-efficiency/unused-css-rules.js
index cce908a9c..3076546d3 100644
--- a/lighthouse-core/audits/byte-efficiency/unused-css-rules.js
+++ b/lighthouse-core/audits/byte-efficiency/unused-css-rules.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to remove content from their CSS that isn’t needed immediately and instead load that content at a later time. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/byte-efficiency/unused-javascript.js b/lighthouse-core/audits/byte-efficiency/unused-javascript.js
index 7e9eace91..f13e0272f 100644
--- a/lighthouse-core/audits/byte-efficiency/unused-javascript.js
+++ b/lighthouse-core/audits/byte-efficiency/unused-javascript.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to remove JavaScript that is never evaluated during page load. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js b/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js
index ef85a7cf3..ab4afaf59 100644
--- a/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js
+++ b/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js
@@ -12,7 +12,7 @@ const Audit = require('../audit');
 const NetworkRequest = require('../../lib/network-request');
 const URL = require('../../lib/url-shim');
 const linearInterpolation = require('../../lib/statistics').linearInterpolation;
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Title of a diagnostic audit that provides detail on the cache policy applies to the page's static assets. Cache refers to browser disk cache, which keeps old versions of network resources around for future use. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js
index b0c5853f3..0ffd9a7e8 100644
--- a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js
+++ b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js
@@ -11,7 +11,7 @@
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const URL = require('../../lib/url-shim');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to encode images with optimization (better compression). This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js
index e2817755b..bd2293524 100644
--- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js
+++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js
@@ -16,7 +16,7 @@
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const Sentry = require('../../lib/sentry');
 const URL = require('../../lib/url-shim');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to resize images to match the display dimensions. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/byte-efficiency/uses-text-compression.js b/lighthouse-core/audits/byte-efficiency/uses-text-compression.js
index fa1fcf3dd..aa706f66f 100644
--- a/lighthouse-core/audits/byte-efficiency/uses-text-compression.js
+++ b/lighthouse-core/audits/byte-efficiency/uses-text-compression.js
@@ -11,7 +11,7 @@
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const URL = require('../../lib/url-shim');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to enable text compression (like gzip) in order to enhance the performance of a page. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/byte-efficiency/uses-webp-images.js b/lighthouse-core/audits/byte-efficiency/uses-webp-images.js
index 228cd1ad8..56e58a2a6 100644
--- a/lighthouse-core/audits/byte-efficiency/uses-webp-images.js
+++ b/lighthouse-core/audits/byte-efficiency/uses-webp-images.js
@@ -10,7 +10,7 @@
 
 const ByteEfficiencyAudit = require('./byte-efficiency-audit');
 const URL = require('../../lib/url-shim');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to serve images in newer and more efficient image formats in order to enhance the performance of a page. A non-modern image format was designed 20+ years ago. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/critical-request-chains.js b/lighthouse-core/audits/critical-request-chains.js
index 473445ab5..23653f351 100644
--- a/lighthouse-core/audits/critical-request-chains.js
+++ b/lighthouse-core/audits/critical-request-chains.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const Audit = require('./audit');
-const i18n = require('../lib/i18n');
+const i18n = require('../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to reduce the depth of critical network requests to enhance initial load of a page. Critical request chains are series of dependent network requests that are important for page rendering. For example, here's a 4-request-deep chain: The biglogo.jpg image is required, but is requested via the styles.css style code, which is requested by the initialize.js javascript, which is requested by the page's HTML. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/audits/dobetterweb/dom-size.js b/lighthouse-core/audits/dobetterweb/dom-size.js
index 9e3f4aad7..72cccf38f 100644
--- a/lighthouse-core/audits/dobetterweb/dom-size.js
+++ b/lighthouse-core/audits/dobetterweb/dom-size.js
@@ -14,7 +14,7 @@
 
 const Audit = require('../audit');
 const Util = require('../../report/html/renderer/util.js');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const MAX_DOM_NODES = 1500;
 const MAX_DOM_TREE_WIDTH = 60;
diff --git a/lighthouse-core/audits/font-display.js b/lighthouse-core/audits/font-display.js
index 080c43997..a8115628d 100644
--- a/lighthouse-core/audits/font-display.js
+++ b/lighthouse-core/audits/font-display.js
@@ -8,7 +8,7 @@
 const Audit = require('./audit');
 const NetworkRequest = require('../lib/network-request');
 const allowedFontFaceDisplays = ['block', 'fallback', 'optional', 'swap'];
-const i18n = require('../lib/i18n');
+const i18n = require('../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Title of a diagnostic audit that provides detail on if all the text on a webpage was visible while the page was loading its webfonts. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
diff --git a/lighthouse-core/audits/mainthread-work-breakdown.js b/lighthouse-core/audits/mainthread-work-breakdown.js
index 80f2d9405..7b94b0cd7 100644
--- a/lighthouse-core/audits/mainthread-work-breakdown.js
+++ b/lighthouse-core/audits/mainthread-work-breakdown.js
@@ -12,7 +12,7 @@
 
 const Audit = require('./audit');
 const {taskGroups} = require('../lib/task-groups');
-const i18n = require('../lib/i18n');
+const i18n = require('../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Title of a diagnostic audit that provides detail on the main thread work the browser did to load the page. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
diff --git a/lighthouse-core/audits/metrics/estimated-input-latency.js b/lighthouse-core/audits/metrics/estimated-input-latency.js
index 653662c1f..b9e6b1a2e 100644
--- a/lighthouse-core/audits/metrics/estimated-input-latency.js
+++ b/lighthouse-core/audits/metrics/estimated-input-latency.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const Audit = require('../audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** The name of the metric that marks the estimated time between the page receiving input (a user clicking, tapping, or typing) and the page responding. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit. */
diff --git a/lighthouse-core/audits/metrics/first-contentful-paint.js b/lighthouse-core/audits/metrics/first-contentful-paint.js
index da0ee6f24..8bc8e74b8 100644
--- a/lighthouse-core/audits/metrics/first-contentful-paint.js
+++ b/lighthouse-core/audits/metrics/first-contentful-paint.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const Audit = require('../audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** The name of the metric that marks the time at which the first text or image is painted by the browser. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit. */
diff --git a/lighthouse-core/audits/metrics/first-cpu-idle.js b/lighthouse-core/audits/metrics/first-cpu-idle.js
index 92824a1f2..51ae2a169 100644
--- a/lighthouse-core/audits/metrics/first-cpu-idle.js
+++ b/lighthouse-core/audits/metrics/first-cpu-idle.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const Audit = require('../audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** The name of the metric that marks when the page has displayed content and the CPU is not busy executing the page's scripts. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit. */
diff --git a/lighthouse-core/audits/metrics/first-meaningful-paint.js b/lighthouse-core/audits/metrics/first-meaningful-paint.js
index ce7f79954..8663f2a31 100644
--- a/lighthouse-core/audits/metrics/first-meaningful-paint.js
+++ b/lighthouse-core/audits/metrics/first-meaningful-paint.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const Audit = require('../audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** The name of the metric that marks the time at which a majority of the content has been painted by the browser. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit. */
diff --git a/lighthouse-core/audits/metrics/interactive.js b/lighthouse-core/audits/metrics/interactive.js
index 455fb94cf..89cf7bc25 100644
--- a/lighthouse-core/audits/metrics/interactive.js
+++ b/lighthouse-core/audits/metrics/interactive.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const Audit = require('../audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** The name of the metric that marks the time at which the page is fully loaded and is able to quickly respond to user input (clicks, taps, and keypresses feel responsive). Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit. */
diff --git a/lighthouse-core/audits/metrics/speed-index.js b/lighthouse-core/audits/metrics/speed-index.js
index 28cab1ebe..a93028c20 100644
--- a/lighthouse-core/audits/metrics/speed-index.js
+++ b/lighthouse-core/audits/metrics/speed-index.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const Audit = require('../audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** The name of the metric that summarizes how quickly the page looked visually complete. The name of this metric is largely abstract and can be loosely translated. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit. */
diff --git a/lighthouse-core/audits/redirects.js b/lighthouse-core/audits/redirects.js
index 4ad4bd7f9..82cf9a8be 100644
--- a/lighthouse-core/audits/redirects.js
+++ b/lighthouse-core/audits/redirects.js
@@ -7,7 +7,7 @@
 
 const Audit = require('./audit');
 const UnusedBytes = require('./byte-efficiency/byte-efficiency-audit');
-const i18n = require('../lib/i18n');
+const i18n = require('../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to eliminate the redirects taken through multiple URLs to load the page. This is shown in a list of audits that Lighthouse generates. */
diff --git a/lighthouse-core/audits/time-to-first-byte.js b/lighthouse-core/audits/time-to-first-byte.js
index 74807cb66..accad1b8a 100644
--- a/lighthouse-core/audits/time-to-first-byte.js
+++ b/lighthouse-core/audits/time-to-first-byte.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const Audit = require('./audit');
-const i18n = require('../lib/i18n');
+const i18n = require('../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Title of a diagnostic audit that provides detail on how long it took from starting a request to when the server started responding. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
diff --git a/lighthouse-core/audits/user-timings.js b/lighthouse-core/audits/user-timings.js
index d3015f13d..e20bbf0fa 100644
--- a/lighthouse-core/audits/user-timings.js
+++ b/lighthouse-core/audits/user-timings.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const Audit = require('./audit');
-const i18n = require('../lib/i18n');
+const i18n = require('../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Descriptive title of a diagnostic audit that provides details on any timestamps generated by the page. User Timing refers to the 'User Timing API', which enables a website to record specific times as 'marks', or spans of time as 'measures'. */
diff --git a/lighthouse-core/audits/uses-rel-preconnect.js b/lighthouse-core/audits/uses-rel-preconnect.js
index e25ddd618..97840c718 100644
--- a/lighthouse-core/audits/uses-rel-preconnect.js
+++ b/lighthouse-core/audits/uses-rel-preconnect.js
@@ -8,7 +8,7 @@
 
 const Audit = require('./audit');
 const UnusedBytes = require('./byte-efficiency/byte-efficiency-audit');
-const i18n = require('../lib/i18n');
+const i18n = require('../lib/i18n/i18n.js');
 
 // Preconnect establishes a "clean" socket. Chrome's socket manager will keep an unused socket
 // around for 10s. Meaning, the time delta between processing preconnect a request should be <10s,
diff --git a/lighthouse-core/audits/uses-rel-preload.js b/lighthouse-core/audits/uses-rel-preload.js
index b11064563..af32e7615 100644
--- a/lighthouse-core/audits/uses-rel-preload.js
+++ b/lighthouse-core/audits/uses-rel-preload.js
@@ -9,7 +9,7 @@ const URL = require('../lib/url-shim');
 const Audit = require('./audit');
 const UnusedBytes = require('./byte-efficiency/byte-efficiency-audit');
 const CriticalRequestChains = require('../gather/computed/critical-request-chains');
-const i18n = require('../lib/i18n');
+const i18n = require('../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Imperative title of a Lighthouse audit that tells the user to use <link rel=preload> to initiate important network requests earlier during page load. This is displayed in a list of audit titles that Lighthouse generates. */
diff --git a/lighthouse-core/config/config.js b/lighthouse-core/config/config.js
index c10975d47..e0f6ceb80 100644
--- a/lighthouse-core/config/config.js
+++ b/lighthouse-core/config/config.js
@@ -9,7 +9,7 @@ const defaultConfigPath = './default-config.js';
 const defaultConfig = require('./default-config.js');
 const fullConfig = require('./full-config.js');
 const constants = require('./constants.js');
-const i18n = require('./../lib/i18n.js');
+const i18n = require('./../lib/i18n/i18n.js');
 
 const isDeepEqual = require('lodash.isequal');
 const log = require('lighthouse-logger');
diff --git a/lighthouse-core/config/default-config.js b/lighthouse-core/config/default-config.js
index 00e74f212..dd6088a9c 100644
--- a/lighthouse-core/config/default-config.js
+++ b/lighthouse-core/config/default-config.js
@@ -8,7 +8,7 @@
 /* eslint-disable max-len */
 
 const constants = require('./constants');
-const i18n = require('../lib/i18n');
+const i18n = require('../lib/i18n/i18n.js');
 
 const UIStrings = {
   /** Title of the Performance category of audits. Equivalent to 'Web performance', this term is inclusive of all web page speed and loading optimization topics. Also used as a label of a score gauge; try to limit to 20 characters. */
diff --git a/lighthouse-core/lib/locales/en-US.json b/lighthouse-core/lib/i18n/en-US.json
similarity index 98%
rename from lighthouse-core/lib/locales/en-US.json
rename to lighthouse-core/lib/i18n/en-US.json
index 125c20acc..00f04fd28 100644
--- a/lighthouse-core/lib/locales/en-US.json
+++ b/lighthouse-core/lib/i18n/en-US.json
@@ -383,43 +383,43 @@
     "message": "Performance",
     "description": "Title of the Performance category of audits. Equivalent to 'Web performance', this term is inclusive of all web page speed and loading optimization topics. Also used as a label of a score gauge; try to limit to 20 characters."
   },
-  "lighthouse-core/lib/i18n.js | columnCacheTTL": {
+  "lighthouse-core/lib/i18n/i18n.js | columnCacheTTL": {
     "message": "Cache TTL",
     "description": "Label for the TTL column in data tables, entries will be the time to live value of the cache header on a web resource"
   },
-  "lighthouse-core/lib/i18n.js | columnSize": {
+  "lighthouse-core/lib/i18n/i18n.js | columnSize": {
     "message": "Size (KB)",
     "description": "Label for the size column in data tables, entries will be the size of a web resource in kilobytes"
   },
-  "lighthouse-core/lib/i18n.js | columnTimeSpent": {
+  "lighthouse-core/lib/i18n/i18n.js | columnTimeSpent": {
     "message": "Time Spent",
     "description": "Label for the time spent column in data tables, entries will be the number of milliseconds spent during a particular activity"
   },
-  "lighthouse-core/lib/i18n.js | columnURL": {
+  "lighthouse-core/lib/i18n/i18n.js | columnURL": {
     "message": "URL",
     "description": "Label for the URL column in data tables, entries will be the URL of a web resource"
   },
-  "lighthouse-core/lib/i18n.js | columnWastedBytes": {
+  "lighthouse-core/lib/i18n/i18n.js | columnWastedBytes": {
     "message": "Potential Savings (KB)",
     "description": "Label for the wasted bytes column in data tables, entries will be the number of kilobytes the user could reduce their page by if they implemented the suggestions"
   },
-  "lighthouse-core/lib/i18n.js | columnWastedMs": {
+  "lighthouse-core/lib/i18n/i18n.js | columnWastedMs": {
     "message": "Potential Savings (ms)",
     "description": "Label for the wasted bytes column in data tables, entries will be the number of milliseconds the user could reduce page load by if they implemented the suggestions"
   },
-  "lighthouse-core/lib/i18n.js | displayValueByteSavings": {
+  "lighthouse-core/lib/i18n/i18n.js | displayValueByteSavings": {
     "message": "Potential savings of {wastedBytes, number, bytes} KB",
     "description": "Label shown per-audit to show how many bytes smaller the page could be if the user implemented the suggestions. The `{wastedBytes}` placeholder will be replaced with the number of bytes, shown in kilobytes (e.g. 148 KB)"
   },
-  "lighthouse-core/lib/i18n.js | displayValueMsSavings": {
+  "lighthouse-core/lib/i18n/i18n.js | displayValueMsSavings": {
     "message": "Potential savings of {wastedMs, number, milliseconds} ms",
     "description": "Label shown per-audit to show how many milliseconds faster the page load could be if the user implemented the suggestions. The `{wastedMs}` placeholder will be replaced with the time duration, shown in milliseconds (e.g. 140 ms)"
   },
-  "lighthouse-core/lib/i18n.js | ms": {
+  "lighthouse-core/lib/i18n/i18n.js | ms": {
     "message": "{timeInMs, number, milliseconds} ms",
     "description": "Used to show the duration in milliseconds that something lasted. The `{timeInMs}` placeholder will be replaced with the time duration, shown in milliseconds (e.g. 63 ms)"
   },
-  "lighthouse-core/lib/i18n.js | seconds": {
+  "lighthouse-core/lib/i18n/i18n.js | seconds": {
     "message": "{timeInMs, number, seconds} s",
     "description": "Used to show the duration in seconds that something lasted. The {timeInMs} placeholder will be replaced with the time duration, shown in seconds (e.g. 5.2 s)"
   },
diff --git a/lighthouse-core/lib/i18n.js b/lighthouse-core/lib/i18n/i18n.js
similarity index 99%
rename from lighthouse-core/lib/i18n.js
rename to lighthouse-core/lib/i18n/i18n.js
index 4521fcd10..43309bc03 100644
--- a/lighthouse-core/lib/i18n.js
+++ b/lighthouse-core/lib/i18n/i18n.js
@@ -11,9 +11,9 @@ const log = require('lighthouse-logger');
 const MessageFormat = require('intl-messageformat').default;
 const MessageParser = require('intl-messageformat-parser');
 const lookupClosestLocale = require('lookup-closest-locale');
-const LOCALES = require('./locales');
+const LOCALES = require('./locales.js');
 
-const LH_ROOT = path.join(__dirname, '../../');
+const LH_ROOT = path.join(__dirname, '../../../');
 const MESSAGE_INSTANCE_ID_REGEX = /(.* \| .*) # (\d+)$/;
 // Above regex is very slow against large strings. Use QUICK_REGEX as a much quicker discriminator.
 const MESSAGE_INSTANCE_ID_QUICK_REGEX = / # \d+$/;
diff --git a/lighthouse-core/lib/i18n/locales.js b/lighthouse-core/lib/i18n/locales.js
new file mode 100644
index 000000000..0ee75974a
--- /dev/null
+++ b/lighthouse-core/lib/i18n/locales.js
@@ -0,0 +1,87 @@
+/**
+ * @license Copyright 2018 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+ */
+'use strict';
+
+/** @fileoverview
+ * Define message file to be used for a given locale. A few aliases are defined below.
+ *
+ * Google locale inheritance rules: https://goto.google.com/ccssm
+ * CLDR language aliases: https://www.unicode.org/cldr/charts/latest/supplemental/aliases.html
+ */
+
+/** @typedef {Record<string, {message: string}>} LocaleMessages */
+
+// The keys within this const must exactly match the LH.Locale type in externs.d.ts
+/** @type {Record<LH.Locale, LocaleMessages>} */
+const locales = {
+  'en-US': require('./en-US.json'), // The 'source' strings, with descriptions
+  // @ts-ignore - tsc bug, something about en/en-US pointing to same file. https://github.com/Microsoft/TypeScript/issues/26307
+  'en': require('./en-US.json'), // According to CLDR/ICU, 'en' == 'en-US' dates/numbers (Why?!)
+
+  // TODO: en-GB has just ~10 messages that are different from en-US. We should only ship those.
+  'en-AU': require('./locales/en-GB.json'), // Alias of 'en-GB'
+  'en-GB': require('./locales/en-GB.json'), // Alias of 'en-GB'
+  'en-IE': require('./locales/en-GB.json'), // Alias of 'en-GB'
+  'en-SG': require('./locales/en-GB.json'), // Alias of 'en-GB'
+  'en-ZA': require('./locales/en-GB.json'), // Alias of 'en-GB'
+  'en-IN': require('./locales/en-GB.json'), // Alias of 'en-GB'
+
+  // All locales from here have a messages file, though we allow fallback to the base locale when the files are identical
+  'ar-XB': require('./locales/ar-XB.json'), // psuedolocalization
+  'ar': require('./locales/ar.json'),
+  'bg': require('./locales/bg.json'),
+  'bs': require('./locales/hr.json'), // Alias of 'hr'
+  'ca': require('./locales/ca.json'),
+  'cs': require('./locales/cs.json'),
+  'da': require('./locales/da.json'),
+  'de': require('./locales/de.json'), // de-AT, de-CH identical, so they fall back into de
+  'el': require('./locales/el.json'),
+  'en-XA': require('./locales/en-XA.json'), // psuedolocalization
+  'es': require('./locales/es.json'),
+  'fi': require('./locales/fi.json'),
+  'fil': require('./locales/fil.json'),
+  'fr': require('./locales/fr.json'), // fr-CH identical, so it falls back into fr
+  'he': require('./locales/he.json'),
+  'hi': require('./locales/hi.json'),
+  'hr': require('./locales/hr.json'),
+  'hu': require('./locales/hu.json'),
+  'gsw': require('./locales/de.json'), // swiss german. identical (for our purposes) to 'de'
+  'id': require('./locales/id.json'),
+  'in': require('./locales/id.json'), // Alias of 'id'
+  'it': require('./locales/it.json'),
+  'iw': require('./locales/he.json'), // Alias of 'he'
+  'ja': require('./locales/ja.json'),
+  'ko': require('./locales/ko.json'),
+  'ln': require('./locales/fr.json'), // Alias of 'fr'
+  'lt': require('./locales/lt.json'),
+  'lv': require('./locales/lv.json'),
+  'mo': require('./locales/ro.json'), // Alias of 'ro'
+  'nl': require('./locales/nl.json'),
+  'nb': require('./locales/no.json'), // Alias of 'no'
+  'no': require('./locales/no.json'),
+  'pl': require('./locales/pl.json'),
+  'pt': require('./locales/pt.json'), // pt-BR identical, so it falls back into pt
+  'pt-PT': require('./locales/pt-PT.json'),
+  'ro': require('./locales/ro.json'),
+  'ru': require('./locales/ru.json'),
+  'sk': require('./locales/sk.json'),
+  'sl': require('./locales/sl.json'),
+  'sr': require('./locales/sr.json'),
+  'sr-Latn': require('./locales/sr-Latn.json'),
+  'sv': require('./locales/sv.json'),
+  'ta': require('./locales/ta.json'),
+  'te': require('./locales/te.json'),
+  'th': require('./locales/th.json'),
+  'tl': require('./locales/fil.json'), // Alias of 'fil'
+  'tr': require('./locales/tr.json'),
+  'uk': require('./locales/uk.json'),
+  'vi': require('./locales/vi.json'),
+  'zh': require('./locales/zh.json'), // zh-CN identical, so it falls back into zh
+  'zh-HK': require('./locales/zh-HK.json'),
+  'zh-TW': require('./locales/zh-TW.json'),
+};
+
+module.exports = locales;
diff --git a/lighthouse-core/lib/locales/ar-XB.json b/lighthouse-core/lib/i18n/locales/ar-XB.json
similarity index 100%
rename from lighthouse-core/lib/locales/ar-XB.json
rename to lighthouse-core/lib/i18n/locales/ar-XB.json
diff --git a/lighthouse-core/lib/locales/ar.json b/lighthouse-core/lib/i18n/locales/ar.json
similarity index 100%
rename from lighthouse-core/lib/locales/ar.json
rename to lighthouse-core/lib/i18n/locales/ar.json
diff --git a/lighthouse-core/lib/locales/bg.json b/lighthouse-core/lib/i18n/locales/bg.json
similarity index 100%
rename from lighthouse-core/lib/locales/bg.json
rename to lighthouse-core/lib/i18n/locales/bg.json
diff --git a/lighthouse-core/lib/locales/ca.json b/lighthouse-core/lib/i18n/locales/ca.json
similarity index 100%
rename from lighthouse-core/lib/locales/ca.json
rename to lighthouse-core/lib/i18n/locales/ca.json
diff --git a/lighthouse-core/lib/locales/cs.json b/lighthouse-core/lib/i18n/locales/cs.json
similarity index 100%
rename from lighthouse-core/lib/locales/cs.json
rename to lighthouse-core/lib/i18n/locales/cs.json
diff --git a/lighthouse-core/lib/locales/da.json b/lighthouse-core/lib/i18n/locales/da.json
similarity index 100%
rename from lighthouse-core/lib/locales/da.json
rename to lighthouse-core/lib/i18n/locales/da.json
diff --git a/lighthouse-core/lib/locales/de.json b/lighthouse-core/lib/i18n/locales/de.json
similarity index 100%
rename from lighthouse-core/lib/locales/de.json
rename to lighthouse-core/lib/i18n/locales/de.json
diff --git a/lighthouse-core/lib/locales/el.json b/lighthouse-core/lib/i18n/locales/el.json
similarity index 100%
rename from lighthouse-core/lib/locales/el.json
rename to lighthouse-core/lib/i18n/locales/el.json
diff --git a/lighthouse-core/lib/locales/en-GB.json b/lighthouse-core/lib/i18n/locales/en-GB.json
similarity index 100%
rename from lighthouse-core/lib/locales/en-GB.json
rename to lighthouse-core/lib/i18n/locales/en-GB.json
diff --git a/lighthouse-core/lib/locales/en-XA.json b/lighthouse-core/lib/i18n/locales/en-XA.json
similarity index 100%
rename from lighthouse-core/lib/locales/en-XA.json
rename to lighthouse-core/lib/i18n/locales/en-XA.json
diff --git a/lighthouse-core/lib/locales/es.json b/lighthouse-core/lib/i18n/locales/es.json
similarity index 100%
rename from lighthouse-core/lib/locales/es.json
rename to lighthouse-core/lib/i18n/locales/es.json
diff --git a/lighthouse-core/lib/locales/fi.json b/lighthouse-core/lib/i18n/locales/fi.json
similarity index 100%
rename from lighthouse-core/lib/locales/fi.json
rename to lighthouse-core/lib/i18n/locales/fi.json
diff --git a/lighthouse-core/lib/locales/fil.json b/lighthouse-core/lib/i18n/locales/fil.json
similarity index 100%
rename from lighthouse-core/lib/locales/fil.json
rename to lighthouse-core/lib/i18n/locales/fil.json
diff --git a/lighthouse-core/lib/locales/fr.json b/lighthouse-core/lib/i18n/locales/fr.json
similarity index 100%
rename from lighthouse-core/lib/locales/fr.json
rename to lighthouse-core/lib/i18n/locales/fr.json
diff --git a/lighthouse-core/lib/locales/he.json b/lighthouse-core/lib/i18n/locales/he.json
similarity index 100%
rename from lighthouse-core/lib/locales/he.json
rename to lighthouse-core/lib/i18n/locales/he.json
diff --git a/lighthouse-core/lib/locales/hi.json b/lighthouse-core/lib/i18n/locales/hi.json
similarity index 100%
rename from lighthouse-core/lib/locales/hi.json
rename to lighthouse-core/lib/i18n/locales/hi.json
diff --git a/lighthouse-core/lib/locales/hr.json b/lighthouse-core/lib/i18n/locales/hr.json
similarity index 100%
rename from lighthouse-core/lib/locales/hr.json
rename to lighthouse-core/lib/i18n/locales/hr.json
diff --git a/lighthouse-core/lib/locales/hu.json b/lighthouse-core/lib/i18n/locales/hu.json
similarity index 100%
rename from lighthouse-core/lib/locales/hu.json
rename to lighthouse-core/lib/i18n/locales/hu.json
diff --git a/lighthouse-core/lib/locales/id.json b/lighthouse-core/lib/i18n/locales/id.json
similarity index 100%
rename from lighthouse-core/lib/locales/id.json
rename to lighthouse-core/lib/i18n/locales/id.json
diff --git a/lighthouse-core/lib/locales/it.json b/lighthouse-core/lib/i18n/locales/it.json
similarity index 100%
rename from lighthouse-core/lib/locales/it.json
rename to lighthouse-core/lib/i18n/locales/it.json
diff --git a/lighthouse-core/lib/locales/ja.json b/lighthouse-core/lib/i18n/locales/ja.json
similarity index 100%
rename from lighthouse-core/lib/locales/ja.json
rename to lighthouse-core/lib/i18n/locales/ja.json
diff --git a/lighthouse-core/lib/locales/ko.json b/lighthouse-core/lib/i18n/locales/ko.json
similarity index 100%
rename from lighthouse-core/lib/locales/ko.json
rename to lighthouse-core/lib/i18n/locales/ko.json
diff --git a/lighthouse-core/lib/locales/lt.json b/lighthouse-core/lib/i18n/locales/lt.json
similarity index 100%
rename from lighthouse-core/lib/locales/lt.json
rename to lighthouse-core/lib/i18n/locales/lt.json
diff --git a/lighthouse-core/lib/locales/lv.json b/lighthouse-core/lib/i18n/locales/lv.json
similarity index 100%
rename from lighthouse-core/lib/locales/lv.json
rename to lighthouse-core/lib/i18n/locales/lv.json
diff --git a/lighthouse-core/lib/locales/nl.json b/lighthouse-core/lib/i18n/locales/nl.json
similarity index 100%
rename from lighthouse-core/lib/locales/nl.json
rename to lighthouse-core/lib/i18n/locales/nl.json
diff --git a/lighthouse-core/lib/locales/no.json b/lighthouse-core/lib/i18n/locales/no.json
similarity index 100%
rename from lighthouse-core/lib/locales/no.json
rename to lighthouse-core/lib/i18n/locales/no.json
diff --git a/lighthouse-core/lib/locales/pl.json b/lighthouse-core/lib/i18n/locales/pl.json
similarity index 100%
rename from lighthouse-core/lib/locales/pl.json
rename to lighthouse-core/lib/i18n/locales/pl.json
diff --git a/lighthouse-core/lib/locales/pt-PT.json b/lighthouse-core/lib/i18n/locales/pt-PT.json
similarity index 100%
rename from lighthouse-core/lib/locales/pt-PT.json
rename to lighthouse-core/lib/i18n/locales/pt-PT.json
diff --git a/lighthouse-core/lib/locales/pt.json b/lighthouse-core/lib/i18n/locales/pt.json
similarity index 100%
rename from lighthouse-core/lib/locales/pt.json
rename to lighthouse-core/lib/i18n/locales/pt.json
diff --git a/lighthouse-core/lib/locales/ro.json b/lighthouse-core/lib/i18n/locales/ro.json
similarity index 100%
rename from lighthouse-core/lib/locales/ro.json
rename to lighthouse-core/lib/i18n/locales/ro.json
diff --git a/lighthouse-core/lib/locales/ru.json b/lighthouse-core/lib/i18n/locales/ru.json
similarity index 100%
rename from lighthouse-core/lib/locales/ru.json
rename to lighthouse-core/lib/i18n/locales/ru.json
diff --git a/lighthouse-core/lib/locales/sk.json b/lighthouse-core/lib/i18n/locales/sk.json
similarity index 100%
rename from lighthouse-core/lib/locales/sk.json
rename to lighthouse-core/lib/i18n/locales/sk.json
diff --git a/lighthouse-core/lib/locales/sl.json b/lighthouse-core/lib/i18n/locales/sl.json
similarity index 100%
rename from lighthouse-core/lib/locales/sl.json
rename to lighthouse-core/lib/i18n/locales/sl.json
diff --git a/lighthouse-core/lib/locales/sr-Latn.json b/lighthouse-core/lib/i18n/locales/sr-Latn.json
similarity index 100%
rename from lighthouse-core/lib/locales/sr-Latn.json
rename to lighthouse-core/lib/i18n/locales/sr-Latn.json
diff --git a/lighthouse-core/lib/locales/sr.json b/lighthouse-core/lib/i18n/locales/sr.json
similarity index 100%
rename from lighthouse-core/lib/locales/sr.json
rename to lighthouse-core/lib/i18n/locales/sr.json
diff --git a/lighthouse-core/lib/locales/sv.json b/lighthouse-core/lib/i18n/locales/sv.json
similarity index 100%
rename from lighthouse-core/lib/locales/sv.json
rename to lighthouse-core/lib/i18n/locales/sv.json
diff --git a/lighthouse-core/lib/locales/ta.json b/lighthouse-core/lib/i18n/locales/ta.json
similarity index 100%
rename from lighthouse-core/lib/locales/ta.json
rename to lighthouse-core/lib/i18n/locales/ta.json
diff --git a/lighthouse-core/lib/locales/te.json b/lighthouse-core/lib/i18n/locales/te.json
similarity index 100%
rename from lighthouse-core/lib/locales/te.json
rename to lighthouse-core/lib/i18n/locales/te.json
diff --git a/lighthouse-core/lib/locales/th.json b/lighthouse-core/lib/i18n/locales/th.json
similarity index 100%
rename from lighthouse-core/lib/locales/th.json
rename to lighthouse-core/lib/i18n/locales/th.json
diff --git a/lighthouse-core/lib/locales/tr.json b/lighthouse-core/lib/i18n/locales/tr.json
similarity index 100%
rename from lighthouse-core/lib/locales/tr.json
rename to lighthouse-core/lib/i18n/locales/tr.json
diff --git a/lighthouse-core/lib/locales/uk.json b/lighthouse-core/lib/i18n/locales/uk.json
similarity index 100%
rename from lighthouse-core/lib/locales/uk.json
rename to lighthouse-core/lib/i18n/locales/uk.json
diff --git a/lighthouse-core/lib/locales/vi.json b/lighthouse-core/lib/i18n/locales/vi.json
similarity index 100%
rename from lighthouse-core/lib/locales/vi.json
rename to lighthouse-core/lib/i18n/locales/vi.json
diff --git a/lighthouse-core/lib/locales/zh-HK.json b/lighthouse-core/lib/i18n/locales/zh-HK.json
similarity index 100%
rename from lighthouse-core/lib/locales/zh-HK.json
rename to lighthouse-core/lib/i18n/locales/zh-HK.json
diff --git a/lighthouse-core/lib/locales/zh-TW.json b/lighthouse-core/lib/i18n/locales/zh-TW.json
similarity index 100%
rename from lighthouse-core/lib/locales/zh-TW.json
rename to lighthouse-core/lib/i18n/locales/zh-TW.json
diff --git a/lighthouse-core/lib/locales/zh.json b/lighthouse-core/lib/i18n/locales/zh.json
similarity index 100%
rename from lighthouse-core/lib/locales/zh.json
rename to lighthouse-core/lib/i18n/locales/zh.json
diff --git a/lighthouse-core/lib/locales/index.js b/lighthouse-core/lib/locales/index.js
deleted file mode 100644
index 7ea013682..000000000
--- a/lighthouse-core/lib/locales/index.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * @license Copyright 2018 Google Inc. All Rights Reserved.
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
- */
-'use strict';
-
-/** @fileoverview
- * Define message file to be used for a given locale. A few aliases are defined below.
- *
- * Google locale inheritance rules: https://goto.google.com/ccssm
- * CLDR language aliases: https://www.unicode.org/cldr/charts/latest/supplemental/aliases.html
- */
-
-/** @typedef {Record<string, {message: string}>} LocaleMessages */
-
-// The keys within this const must exactly match the LH.Locale type in externs.d.ts
-/** @type {Record<LH.Locale, LocaleMessages>} */
-const locales = {
-  'en-US': require('./en-US.json'), // The 'source' strings, with descriptions
-  // @ts-ignore - tsc bug, something about en/en-US pointing to same file. https://github.com/Microsoft/TypeScript/issues/26307
-  'en': require('./en-US.json'), // According to CLDR/ICU, 'en' == 'en-US' dates/numbers (Why?!)
-
-  // TODO: en-GB has just ~10 messages that are different from en-US. We should only ship those.
-  'en-AU': require('./en-GB.json'), // Alias of 'en-GB'
-  'en-GB': require('./en-GB.json'), // Alias of 'en-GB'
-  'en-IE': require('./en-GB.json'), // Alias of 'en-GB'
-  'en-SG': require('./en-GB.json'), // Alias of 'en-GB'
-  'en-ZA': require('./en-GB.json'), // Alias of 'en-GB'
-  'en-IN': require('./en-GB.json'), // Alias of 'en-GB'
-
-  // All locales from here have a messages file, though we allow fallback to the base locale when the files are identical
-  'ar-XB': require('./ar-XB.json'), // psuedolocalization
-  'ar': require('./ar.json'),
-  'bg': require('./bg.json'),
-  'bs': require('./hr.json'), // Alias of 'hr'
-  'ca': require('./ca.json'),
-  'cs': require('./cs.json'),
-  'da': require('./da.json'),
-  'de': require('./de.json'), // de-AT, de-CH identical, so they fall back into de
-  'el': require('./el.json'),
-  'en-XA': require('./en-XA.json'), // psuedolocalization
-  'es': require('./es.json'),
-  'fi': require('./fi.json'),
-  'fil': require('./fil.json'),
-  'fr': require('./fr.json'), // fr-CH identical, so it falls back into fr
-  'he': require('./he.json'),
-  'hi': require('./hi.json'),
-  'hr': require('./hr.json'),
-  'hu': require('./hu.json'),
-  'gsw': require('./de.json'), // swiss german. identical (for our purposes) to 'de'
-  'id': require('./id.json'),
-  'in': require('./id.json'), // Alias of 'id'
-  'it': require('./it.json'),
-  'iw': require('./he.json'), // Alias of 'he'
-  'ja': require('./ja.json'),
-  'ko': require('./ko.json'),
-  'ln': require('./fr.json'), // Alias of 'fr'
-  'lt': require('./lt.json'),
-  'lv': require('./lv.json'),
-  'mo': require('./ro.json'), // Alias of 'ro'
-  'nl': require('./nl.json'),
-  'nb': require('./no.json'), // Alias of 'no'
-  'no': require('./no.json'),
-  'pl': require('./pl.json'),
-  'pt': require('./pt.json'), // pt-BR identical, so it falls back into pt
-  'pt-PT': require('./pt-PT.json'),
-  'ro': require('./ro.json'),
-  'ru': require('./ru.json'),
-  'sk': require('./sk.json'),
-  'sl': require('./sl.json'),
-  'sr': require('./sr.json'),
-  'sr-Latn': require('./sr-Latn.json'),
-  'sv': require('./sv.json'),
-  'ta': require('./ta.json'),
-  'te': require('./te.json'),
-  'th': require('./th.json'),
-  'tl': require('./fil.json'), // Alias of 'fil'
-  'tr': require('./tr.json'),
-  'uk': require('./uk.json'),
-  'vi': require('./vi.json'),
-  'zh': require('./zh.json'), // zh-CN identical, so it falls back into zh
-  'zh-HK': require('./zh-HK.json'),
-  'zh-TW': require('./zh-TW.json'),
-};
-
-module.exports = locales;
diff --git a/lighthouse-core/runner.js b/lighthouse-core/runner.js
index 194edad86..f7574bf14 100644
--- a/lighthouse-core/runner.js
+++ b/lighthouse-core/runner.js
@@ -11,7 +11,7 @@ const GatherRunner = require('./gather/gather-runner');
 const ReportScoring = require('./scoring');
 const Audit = require('./audits/audit');
 const log = require('lighthouse-logger');
-const i18n = require('./lib/i18n');
+const i18n = require('./lib/i18n/i18n.js');
 const assetSaver = require('./lib/asset-saver');
 const fs = require('fs');
 const path = require('path');
diff --git a/lighthouse-core/scripts/i18n/assert-strings-collected.sh b/lighthouse-core/scripts/i18n/assert-strings-collected.sh
index df9a2e90d..c6e4384b8 100755
--- a/lighthouse-core/scripts/i18n/assert-strings-collected.sh
+++ b/lighthouse-core/scripts/i18n/assert-strings-collected.sh
@@ -18,7 +18,7 @@ colorText() {
   printf "\\n$2$1%b\\n" '\033[0m'
 }
 
-collectedstringsPath="$lhroot_path/lighthouse-core/lib/locales/en-US.json";
+collectedstringsPath="$lhroot_path/lighthouse-core/lib/i18n/en-US.json";
 currentstringsPath="$lh_tmp_path/current_strings.json";
 freshstringsPath="$lh_tmp_path/fresh_strings.json";
 
@@ -45,6 +45,6 @@ if [ $retVal -eq 0 ]; then
   colorText "✅  PASS. All strings have been collected." "$green"
 else
   colorText "❌  FAIL. Strings have changed." "$red"
-  echo "Commit the changes to lighthouse-core/lib/locales/ update the strings."
+  echo "Commit the changes to lighthouse-core/lib/i18n/ update the strings."
 fi
 exit $retVal
diff --git a/lighthouse-core/scripts/i18n/collect-strings.js b/lighthouse-core/scripts/i18n/collect-strings.js
index 4a5fc225e..f69a604bb 100644
--- a/lighthouse-core/scripts/i18n/collect-strings.js
+++ b/lighthouse-core/scripts/i18n/collect-strings.js
@@ -98,11 +98,10 @@ function collectAllStringsInDir(dir, strings = {}) {
 }
 
 /**
- * @param {LH.Locale} locale
  * @param {Record<string, ICUMessageDefn>} strings
  */
-function writeStringsToLocaleFormat(locale, strings) {
-  const fullPath = path.join(LH_ROOT, `lighthouse-core/lib/locales/${locale}.json`);
+function writeEnStringsToLocaleFormat(strings) {
+  const fullPath = path.join(LH_ROOT, `lighthouse-core/lib/i18n/en-US.json`);
   /** @type {Record<string, ICUMessageDefn>} */
   const output = {};
   const sortedEntries = Object.entries(strings).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
@@ -116,5 +115,5 @@ function writeStringsToLocaleFormat(locale, strings) {
 const strings = collectAllStringsInDir(path.join(LH_ROOT, 'lighthouse-core'));
 console.log('Collected!');
 
-writeStringsToLocaleFormat('en-US', strings);
+writeEnStringsToLocaleFormat(strings);
 console.log('Written to disk!');
diff --git a/lighthouse-extension/app/src/lighthouse-background.js b/lighthouse-extension/app/src/lighthouse-background.js
index 7e010aa82..602f7b80c 100644
--- a/lighthouse-extension/app/src/lighthouse-background.js
+++ b/lighthouse-extension/app/src/lighthouse-background.js
@@ -9,7 +9,7 @@ const lighthouse = require('../../../lighthouse-core/index');
 const RawProtocol = require('../../../lighthouse-core/gather/connections/raw');
 const Config = require('../../../lighthouse-core/config/config');
 const defaultConfig = require('../../../lighthouse-core/config/default-config.js');
-const i18n = require('../../../lighthouse-core/lib/i18n');
+const i18n = require('../../../lighthouse-core/lib/i18n/i18n.js');
 const log = require('lighthouse-logger');
 
 /** @typedef {import('../../../lighthouse-core/gather/connections/connection.js')} Connection */


Test patch:
diff --git a/lighthouse-core/test/config/config-test.js b/lighthouse-core/test/config/config-test.js
index 2c73e490a..12b315ab6 100644
--- a/lighthouse-core/test/config/config-test.js
+++ b/lighthouse-core/test/config/config-test.js
@@ -12,7 +12,7 @@ const defaultConfig = require('../../config/default-config.js');
 const log = require('lighthouse-logger');
 const Gatherer = require('../../gather/gatherers/gatherer');
 const Audit = require('../../audits/audit');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../lib/i18n/i18n.js');
 
 /* eslint-env jest */
 
diff --git a/lighthouse-core/test/lib/i18n-test.js b/lighthouse-core/test/lib/i18n/i18n-test.js
similarity index 93%
rename from lighthouse-core/test/lib/i18n-test.js
rename to lighthouse-core/test/lib/i18n/i18n-test.js
index 69e09c6b6..edd90fcf1 100644
--- a/lighthouse-core/test/lib/i18n-test.js
+++ b/lighthouse-core/test/lib/i18n/i18n-test.js
@@ -6,7 +6,7 @@
 'use strict';
 
 const path = require('path');
-const i18n = require('../../lib/i18n');
+const i18n = require('../../../lib/i18n/i18n.js');
 
 /* eslint-env jest */
 
@@ -37,14 +37,14 @@ describe('i18n', () => {
       const templates = {daString: 'use me!'};
       const formatter = i18n.createMessageInstanceIdFn(fakeFile, templates);
 
-      const expected = 'lighthouse-core/test/lib/fake-file.js | daString # 0';
+      const expected = 'lighthouse-core/test/lib/i18n/fake-file.js | daString # 0';
       expect(formatter(templates.daString, {x: 1})).toBe(expected);
     });
   });
 
   describe('#replaceIcuMessageInstanceIds', () => {
     it('replaces the references in the LHR', () => {
-      const templateID = 'lighthouse-core/test/lib/fake-file.js | daString';
+      const templateID = 'lighthouse-core/test/lib/i18n/fake-file.js | daString';
       const reference = templateID + ' # 0';
       const lhr = {audits: {'fake-audit': {title: reference}}};
 
diff --git a/lighthouse-core/test/lib/locales/index-test.js b/lighthouse-core/test/lib/i18n/locales-test.js
similarity index 95%
rename from lighthouse-core/test/lib/locales/index-test.js
rename to lighthouse-core/test/lib/i18n/locales-test.js
index 1a33d3310..cf66ca390 100644
--- a/lighthouse-core/test/lib/locales/index-test.js
+++ b/lighthouse-core/test/lib/i18n/locales-test.js
@@ -5,7 +5,7 @@
  */
 'use strict';
 
-const locales = require('../../../lib/locales/index.js');
+const locales = require('../../../lib/i18n/locales.js');
 const assert = require('assert');
 
 /* eslint-env jest */
diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json
index f911cfb05..691428277 100644
--- a/lighthouse-core/test/results/sample_v2.json
+++ b/lighthouse-core/test/results/sample_v2.json
@@ -3440,7 +3440,7 @@
       "lighthouse-core/audits/metrics/first-contentful-paint.js | description": [
         "audits[first-contentful-paint].description"
       ],
-      "lighthouse-core/lib/i18n.js | seconds": [
+      "lighthouse-core/lib/i18n/i18n.js | seconds": [
         {
           "values": {
             "timeInMs": 3969.135
@@ -3502,7 +3502,7 @@
       "lighthouse-core/audits/metrics/estimated-input-latency.js | description": [
         "audits[estimated-input-latency].description"
       ],
-      "lighthouse-core/lib/i18n.js | ms": [
+      "lighthouse-core/lib/i18n/i18n.js | ms": [
         {
           "values": {
             "timeInMs": 16
@@ -3571,7 +3571,7 @@
       "lighthouse-core/audits/mainthread-work-breakdown.js | columnCategory": [
         "audits[mainthread-work-breakdown].details.headings[0].text"
       ],
-      "lighthouse-core/lib/i18n.js | columnTimeSpent": [
+      "lighthouse-core/lib/i18n/i18n.js | columnTimeSpent": [
         "audits[mainthread-work-breakdown].details.headings[1].text"
       ],
       "lighthouse-core/audits/bootup-time.js | title": [
@@ -3580,7 +3580,7 @@
       "lighthouse-core/audits/bootup-time.js | description": [
         "audits[bootup-time].description"
       ],
-      "lighthouse-core/lib/i18n.js | columnURL": [
+      "lighthouse-core/lib/i18n/i18n.js | columnURL": [
         "audits[bootup-time].details.headings[0].text",
         "audits[uses-long-cache-ttl].details.headings[0].text",
         "audits[total-byte-weight].details.headings[0].text",
@@ -3630,10 +3630,10 @@
           "path": "audits[uses-long-cache-ttl].displayValue"
         }
       ],
-      "lighthouse-core/lib/i18n.js | columnCacheTTL": [
+      "lighthouse-core/lib/i18n/i18n.js | columnCacheTTL": [
         "audits[uses-long-cache-ttl].details.headings[1].text"
       ],
-      "lighthouse-core/lib/i18n.js | columnSize": [
+      "lighthouse-core/lib/i18n/i18n.js | columnSize": [
         "audits[uses-long-cache-ttl].details.headings[2].text",
         "audits[total-byte-weight].details.headings[1].text",
         "audits[render-blocking-resources].details.headings[1].label",
@@ -3667,7 +3667,7 @@
       "lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | description": [
         "audits[render-blocking-resources].description"
       ],
-      "lighthouse-core/lib/i18n.js | displayValueMsSavings": [
+      "lighthouse-core/lib/i18n/i18n.js | displayValueMsSavings": [
         {
           "values": {
             "wastedMs": 1129
@@ -3675,7 +3675,7 @@
           "path": "audits[render-blocking-resources].displayValue"
         }
       ],
-      "lighthouse-core/lib/i18n.js | columnWastedMs": [
+      "lighthouse-core/lib/i18n/i18n.js | columnWastedMs": [
         "audits[render-blocking-resources].details.headings[2].label"
       ],
       "lighthouse-core/audits/byte-efficiency/unminified-css.js | title": [
@@ -3690,7 +3690,7 @@
       "lighthouse-core/audits/byte-efficiency/unminified-javascript.js | description": [
         "audits[unminified-javascript].description"
       ],
-      "lighthouse-core/lib/i18n.js | displayValueByteSavings": [
+      "lighthouse-core/lib/i18n/i18n.js | displayValueByteSavings": [
         {
           "values": {
             "wastedBytes": 30470
@@ -3710,7 +3710,7 @@
           "path": "audits[uses-text-compression].displayValue"
         }
       ],
-      "lighthouse-core/lib/i18n.js | columnWastedBytes": [
+      "lighthouse-core/lib/i18n/i18n.js | columnWastedBytes": [
         "audits[unminified-javascript].details.headings[2].label",
         "audits[uses-webp-images].details.headings[3].label",
         "audits[uses-text-compression].details.headings[2].label"
diff --git a/lighthouse-core/test/test-utils.js b/lighthouse-core/test/test-utils.js
index 222d6ed27..2a785f51d 100644
--- a/lighthouse-core/test/test-utils.js
+++ b/lighthouse-core/test/test-utils.js
@@ -7,7 +7,7 @@
 
 /* eslint-env jest */
 
-const i18n = require('../lib/i18n');
+const i18n = require('../lib/i18n/i18n.js');
 
 expect.extend({
   toBeDisplayString(received, expected) {