Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i18n: add i18n for LighthouseError messages #6457

Merged
merged 27 commits into from
Dec 10, 2018
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d1be64d
Added i18n for runtime error messages.
exterkamp Nov 2, 2018
6799fab
Merge branch 'master' into i18n-errors
exterkamp Nov 2, 2018
3db8628
Formatting on substitutes.
exterkamp Nov 2, 2018
b9c4b4e
Removed redundant 'Method'.
exterkamp Nov 2, 2018
9b6062b
All backticks for variable interpolation highlighting
exterkamp Nov 2, 2018
b0d369f
Thanks linter!
exterkamp Nov 2, 2018
1481b08
Added substitution check to PROTOCOL_TIMEOUT.
exterkamp Nov 2, 2018
cf8e569
Using test-utils toBeDisplayString.
exterkamp Nov 2, 2018
df29d17
Added descriptive ICU names from feedback.
exterkamp Nov 5, 2018
020056a
Added Method back into test for protocol timeout.
exterkamp Nov 5, 2018
56e3daf
Removed unnecessary var.
exterkamp Nov 5, 2018
e764e2d
Fixed i18n of errors and added test to make sure it works!
exterkamp Nov 5, 2018
7a65d02
Better gather runner test comparison.
exterkamp Nov 5, 2018
a6cd8bc
Linter caught me slippin'
exterkamp Nov 5, 2018
02765e4
Moved icu args to properties.
exterkamp Nov 14, 2018
a991732
Merge branch 'master' into i18n-errors
exterkamp Nov 28, 2018
879c333
Added early exit. Fixed i18n punc. formatting.
exterkamp Nov 28, 2018
345d3e4
Collected new strings, and fixed sec-msg test.
exterkamp Nov 28, 2018
9a87b04
Formatting fix.
exterkamp Nov 28, 2018
9beb615
Fixed LR test.
exterkamp Nov 28, 2018
bdfcc4e
Removed backticks, and added localized test.
exterkamp Nov 28, 2018
c56c83f
I was led astray.
exterkamp Nov 28, 2018
9857760
Update lighthouse-core/test/runner-test.js
patrickhulce Nov 29, 2018
d1e1943
Switched up string templates.
exterkamp Nov 29, 2018
c6cfb3b
Merge branch 'i18n-errors' of github.com:GoogleChrome/lighthouse into…
exterkamp Nov 29, 2018
14ceeb2
Apply suggestions from code review
paulirish Dec 7, 2018
972ddb5
Updated strings and test.
exterkamp Dec 7, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion clients/test/lightrider-entry-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ const LHError = require('../../lighthouse-core/lib/lh-error.js');
describe('lightrider-entry', () => {
describe('#runLighthouseInLR', () => {
it('returns a runtimeError LHR when lighthouse throws a runtimeError', async () => {
const connectionError = new LHError(LHError.errors.FAILED_DOCUMENT_REQUEST);
const connectionError = new LHError(
LHError.errors.FAILED_DOCUMENT_REQUEST,
{errorDetails: 'Bad connection req'}
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens with the parsedResult.runtimeError.message.includes check below? Are they expected to be localized by that point? Can we assert that? (and can we check that the errorDetails replacement worked)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is what the includes checks, that the runtime message in the end is the same as the orig friendly msg post a round of translation since it is outside of the Runner.run which translates errors before it exits. It uses the includes instead of equal b/c LR appends the msg to the friendly msg which may not be desired? It's the first I'm really seeing it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had noticed that runLighthouseInLR doesn't run i18n on its generated output, hence the question, but the new err.friendlyMessage = i18n.getFormatted(err.friendlyMessage, settings.locale); line in runner.js translates the error friendlyMessage before it gets back to runLighthouseInLR, so localization is good to go!

assert.strictEqual(connectionError.lhrRuntimeError, true);
const mockConnection = {
async connect() {
Expand Down
8 changes: 5 additions & 3 deletions lighthouse-core/gather/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,11 @@ class Driver {
const timeout = this._nextProtocolTimeout;
this._nextProtocolTimeout = DEFAULT_PROTOCOL_TIMEOUT;
return new Promise(async (resolve, reject) => {
const asyncTimeout = setTimeout((() => {
const err = new LHError(LHError.errors.PROTOCOL_TIMEOUT);
err.message += ` Method: ${method}`;
const asyncTimeout = setTimeout((_ => {
exterkamp marked this conversation as resolved.
Show resolved Hide resolved
const err = new LHError(
LHError.errors.PROTOCOL_TIMEOUT,
{protocolMethod: `${method}`}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can drop the template string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional style nit: a lot of these new LHError() lines could also fit on one line now. Was it intentional breaking them up over 4? I feel like they're more readable on one, but understand if you feel otherwise

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I like splitting complex constructors into lines, and with it all on one line it skirts the line of es-lint yelling that the lines are too long. I feel like 4 lines is more readable than crammed onto 1.

);
reject(err);
}), timeout);
try {
Expand Down
28 changes: 14 additions & 14 deletions lighthouse-core/gather/gather-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,8 @@ class GatherRunner {
return URL.equalWithExcludedFragments(record.url, url);
});

let errorDef;
if (!mainRecord) {
errorDef = LHError.errors.NO_DOCUMENT_REQUEST;
return new LHError(LHError.errors.NO_DOCUMENT_REQUEST);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

} else if (mainRecord.failed) {
const netErr = mainRecord.localizedFailDescription;
// Match all resolution and DNS failures
Expand All @@ -158,18 +157,18 @@ class GatherRunner {
netErr === 'net::ERR_NAME_RESOLUTION_FAILED' ||
netErr.startsWith('net::ERR_DNS_')
) {
errorDef = LHError.errors.DNS_FAILURE;
return new LHError(LHError.errors.DNS_FAILURE);
} else {
errorDef = {...LHError.errors.FAILED_DOCUMENT_REQUEST};
errorDef.message += ` ${netErr}.`;
return new LHError(
LHError.errors.FAILED_DOCUMENT_REQUEST,
{errorDetails: netErr}
);
}
} else if (mainRecord.hasErrorStatusCode()) {
errorDef = {...LHError.errors.ERRORED_DOCUMENT_REQUEST};
errorDef.message += ` Status code: ${mainRecord.statusCode}.`;
}

if (errorDef) {
return new LHError(errorDef);
return new LHError(
LHError.errors.ERRORED_DOCUMENT_REQUEST,
{statusCode: mainRecord.statusCode.toString()}
exterkamp marked this conversation as resolved.
Show resolved Hide resolved
);
}
}

Expand All @@ -180,12 +179,13 @@ class GatherRunner {
*/
static assertNoSecurityIssues({securityState, explanations}) {
if (securityState === 'insecure') {
const errorDef = {...LHError.errors.INSECURE_DOCUMENT_REQUEST};
const insecureDescriptions = explanations
.filter(exp => exp.securityState === 'insecure')
.map(exp => exp.description);
errorDef.message += ` ${insecureDescriptions.join(' ')}`;
throw new LHError(errorDef);
throw new LHError(
LHError.errors.INSECURE_DOCUMENT_REQUEST,
{securityMessages: insecureDescriptions.join(' ')}
);
}
}

Expand Down
52 changes: 52 additions & 0 deletions lighthouse-core/lib/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,58 @@
"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)"
},
"lighthouse-core/lib/lh-error.js | badTraceRecording": {
"message": "Something went wrong with recording the trace over your page load. Please run Lighthouse again.",
"description": "Error message explaining that the network trace was not able to be recorded for the Lighthouse run."
},
"lighthouse-core/lib/lh-error.js | didntCollectScreenshots": {
"message": "Chrome didn't collect any screenshots during the page load. Please make sure there is content visible on the page, and then try re-running Lighthouse.",
"description": "Error message explaining that the Lighthouse run was not able to collect screenshots through Chrome."
},
"lighthouse-core/lib/lh-error.js | dnsFailure": {
"message": "DNS servers could not resolve the provided domain.",
"description": "Error message explaining that the requested page could not be resolved by the DNS server."
},
"lighthouse-core/lib/lh-error.js | internalChromeError": {
"message": "An internal Chrome error occurred. Please restart Chrome and try re-running Lighthouse.",
"description": "Error message explaining that Chrome has encountered an error during the Lighthouse run, and that Chrome should be restarted."
},
"lighthouse-core/lib/lh-error.js | pageLoadFailed": {
"message": "Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests.",
"description": "Error message explaining that Lighthouse could not load the requested URL and the steps that might be taken to fix the unreliability."
},
"lighthouse-core/lib/lh-error.js | pageLoadFailedHung": {
"message": "Lighthouse was unable to reliably load the URL you requested because the page stopped responding.",
"description": "Error message explaining that Lighthouse couldn't complete because the page has stopped responding to its instructions."
},
"lighthouse-core/lib/lh-error.js | pageLoadFailedInsecure": {
"message": "The URL you have provided does not have valid security credentials. {securityMessages}.",
"description": "Error message explaining that the credentials included in the Lighthouse run were invalid, so the URL cannot be accessed."
},
"lighthouse-core/lib/lh-error.js | pageLoadFailedWithDetails": {
"message": "Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. Detailed Error: {errorDetails}.",
"description": "Error message explaining that Lighthouse could not load the requested URL and the steps that might be taken to fix the unreliability."
},
"lighthouse-core/lib/lh-error.js | pageLoadFailedWithStatusCode": {
"message": "Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. Status Code: {statusCode}.",
"description": "Error message explaining that Lighthouse could not load the requested URL and the steps that might be taken to fix the unreliability."
},
"lighthouse-core/lib/lh-error.js | pageLoadTookTooLong": {
"message": "Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse.",
"description": "Error message explaining that the page loaded too slowly to perform a Lighthouse run."
},
"lighthouse-core/lib/lh-error.js | protocolTimeout": {
"message": "Waiting for DevTools protocol response has exceeded the allotted time. Method: {protocolMethod}.",
"description": "Error message explaining that the Chrome Devtools protocol has exceeded the maximum timeout allowed."
},
"lighthouse-core/lib/lh-error.js | requestContentTimeout": {
"message": "Fetching resource content has exceeded the allotted time",
"description": "Error message explaining that fetching the resources of the webpage has taken longer than the maximum time."
},
"lighthouse-core/lib/lh-error.js | urlInvalid": {
"message": "The URL you have provided appears to be invalid.",
"description": "Error message explaining that the provided URL Lighthouse points to is not valid, and cannot be loaded."
},
"lighthouse-core/report/html/renderer/util.js | auditGroupExpandTooltip": {
"message": "Show audits",
"description": "The tooltip text on an expandable chevron icon. Clicking the icon expands a section to reveal a list of audit results that was hidden by default."
Expand Down
103 changes: 72 additions & 31 deletions lighthouse-core/lib/lh-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,40 @@
*/
'use strict';

const strings = require('./strings');
const i18n = require('./i18n/i18n.js');

/* eslint-disable max-len */
const UIStrings = {
/** Error message explaining that the Lighthouse run was not able to collect screenshots through Chrome.*/
didntCollectScreenshots: `Chrome didn't collect any screenshots during the page load. Please make sure there is content visible on the page, and then try re-running Lighthouse.`,
/** Error message explaining that the network trace was not able to be recorded for the Lighthouse run. */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe for some of these descriptions we should include for the translators the distinction of whether it was something weird that retrying should fix, or something that the user needs to fix and try again? I'm thinking a stock explanation.

This is an error message that was because of certain user conditions they must fix before retrying.
vs.
This is an error message that was because of irregularities with Lighthouse that should be fixed by trying again.

just spitballin'

badTraceRecording: 'Something went wrong with recording the trace over your page load. Please run Lighthouse again.',
/** Error message explaining that the page loaded too slowly to perform a Lighthouse run. */
pageLoadTookTooLong: 'Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse.',
/** Error message explaining that Lighthouse could not load the requested URL and the steps that might be taken to fix the unreliability. */
pageLoadFailed: 'Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests.',
exterkamp marked this conversation as resolved.
Show resolved Hide resolved
/** Error message explaining that Lighthouse could not load the requested URL and the steps that might be taken to fix the unreliability. */
pageLoadFailedWithStatusCode: 'Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. Status Code: {statusCode}.',
exterkamp marked this conversation as resolved.
Show resolved Hide resolved
/** Error message explaining that Lighthouse could not load the requested URL and the steps that might be taken to fix the unreliability. */
pageLoadFailedWithDetails: 'Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. Detailed Error: {errorDetails}.',
exterkamp marked this conversation as resolved.
Show resolved Hide resolved
/** Error message explaining that the credentials included in the Lighthouse run were invalid, so the URL cannot be accessed. */
pageLoadFailedInsecure: 'The URL you have provided does not have valid security credentials. {securityMessages}.',
exterkamp marked this conversation as resolved.
Show resolved Hide resolved
/** Error message explaining that Chrome has encountered an error during the Lighthouse run, and that Chrome should be restarted. */
internalChromeError: 'An internal Chrome error occurred. Please restart Chrome and try re-running Lighthouse.',
/** Error message explaining that fetching the resources of the webpage has taken longer than the maximum time. */
requestContentTimeout: 'Fetching resource content has exceeded the allotted time',
/** Error message explaining that the provided URL Lighthouse points to is not valid, and cannot be loaded. */
urlInvalid: 'The URL you have provided appears to be invalid.',
/** Error message explaining that the Chrome Devtools protocol has exceeded the maximum timeout allowed. */
protocolTimeout: 'Waiting for DevTools protocol response has exceeded the allotted time. Method: {protocolMethod}.',
exterkamp marked this conversation as resolved.
Show resolved Hide resolved
/** Error message explaining that the requested page could not be resolved by the DNS server. */
dnsFailure: 'DNS servers could not resolve the provided domain.',
/** Error message explaining that Lighthouse couldn't complete because the page has stopped responding to its instructions. */
pageLoadFailedHung: 'Lighthouse was unable to reliably load the URL you requested because the page stopped responding.',
};

const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);


/**
* @typedef LighthouseErrorDefinition
Expand All @@ -24,7 +57,7 @@ class LighthouseError extends Error {
super(errorDefinition.code);
this.name = 'LHError';
this.code = errorDefinition.code;
this.friendlyMessage = errorDefinition.message;
this.friendlyMessage = str_(errorDefinition.message, properties);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

this.lhrRuntimeError = !!errorDefinition.lhrRuntimeError;
if (properties) Object.assign(this, properties);

Expand Down Expand Up @@ -70,128 +103,136 @@ const ERRORS = {
// Screenshot/speedline errors
NO_SPEEDLINE_FRAMES: {
code: 'NO_SPEEDLINE_FRAMES',
message: strings.didntCollectScreenshots,
message: UIStrings.didntCollectScreenshots,
lhrRuntimeError: true,
},
SPEEDINDEX_OF_ZERO: {
code: 'SPEEDINDEX_OF_ZERO',
message: strings.didntCollectScreenshots,
message: UIStrings.didntCollectScreenshots,
lhrRuntimeError: true,
},
NO_SCREENSHOTS: {
code: 'NO_SCREENSHOTS',
message: strings.didntCollectScreenshots,
message: UIStrings.didntCollectScreenshots,
lhrRuntimeError: true,
},
INVALID_SPEEDLINE: {
code: 'INVALID_SPEEDLINE',
message: strings.didntCollectScreenshots,
message: UIStrings.didntCollectScreenshots,
lhrRuntimeError: true,
},

// Trace parsing errors
NO_TRACING_STARTED: {
code: 'NO_TRACING_STARTED',
message: strings.badTraceRecording,
message: UIStrings.badTraceRecording,
lhrRuntimeError: true,
},
NO_NAVSTART: {
code: 'NO_NAVSTART',
message: strings.badTraceRecording,
message: UIStrings.badTraceRecording,
lhrRuntimeError: true,
},
NO_FCP: {
code: 'NO_FCP',
message: strings.badTraceRecording,
message: UIStrings.badTraceRecording,
lhrRuntimeError: true,
},
NO_DCL: {
code: 'NO_DCL',
message: strings.badTraceRecording,
message: UIStrings.badTraceRecording,
lhrRuntimeError: true,
},
NO_FMP: {
code: 'NO_FMP',
message: strings.badTraceRecording,
message: UIStrings.badTraceRecording,
},

// TTI calculation failures
FMP_TOO_LATE_FOR_FCPUI: {code: 'FMP_TOO_LATE_FOR_FCPUI', message: strings.pageLoadTookTooLong},
NO_FCPUI_IDLE_PERIOD: {code: 'NO_FCPUI_IDLE_PERIOD', message: strings.pageLoadTookTooLong},
NO_TTI_CPU_IDLE_PERIOD: {code: 'NO_TTI_CPU_IDLE_PERIOD', message: strings.pageLoadTookTooLong},
FMP_TOO_LATE_FOR_FCPUI: {code: 'FMP_TOO_LATE_FOR_FCPUI', message: UIStrings.pageLoadTookTooLong},
NO_FCPUI_IDLE_PERIOD: {code: 'NO_FCPUI_IDLE_PERIOD', message: UIStrings.pageLoadTookTooLong},
NO_TTI_CPU_IDLE_PERIOD: {code: 'NO_TTI_CPU_IDLE_PERIOD', message: UIStrings.pageLoadTookTooLong},
NO_TTI_NETWORK_IDLE_PERIOD: {
code: 'NO_TTI_NETWORK_IDLE_PERIOD',
message: strings.pageLoadTookTooLong,
message: UIStrings.pageLoadTookTooLong,
},

// Page load failures
NO_DOCUMENT_REQUEST: {
code: 'NO_DOCUMENT_REQUEST',
message: strings.pageLoadFailed,
message: UIStrings.pageLoadFailed,
lhrRuntimeError: true,
},
/* Used when DevTools reports loading failed. Usually an internal (Chrome) issue. */
/* Used when DevTools reports loading failed. Usually an internal (Chrome) issue.
* Requries an additional `errorDetails` field for translation.
*/
FAILED_DOCUMENT_REQUEST: {
code: 'FAILED_DOCUMENT_REQUEST',
message: strings.pageLoadFailed,
message: UIStrings.pageLoadFailedWithDetails,
lhrRuntimeError: true,
},
/* Used when status code is 4xx or 5xx. */
/* Used when status code is 4xx or 5xx.
* Requires an additional `statusCode` field for translation.
*/
ERRORED_DOCUMENT_REQUEST: {
code: 'ERRORED_DOCUMENT_REQUEST',
message: strings.pageLoadFailed,
message: UIStrings.pageLoadFailedWithStatusCode,
lhrRuntimeError: true,
},
/* Used when security error prevents page load. */
/* Used when security error prevents page load.
* Requires an additional `securityMessages` field for translation.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we're going to forget to update these if they ever change, but I'm not sure what to do about it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well the current plan is that if we change them, then there will be a ton of errors thrown from str_ because it is missing the required substitution fields. But I'm not sure that's a long term strategy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could maybe figure out something with typescript, but I can't think of a way without keeping a manual list of error codes that will require an extra field.

@exterkamp would you want to do that? or wait on it.

main issue with

there will be a ton of errors thrown from str_

is we need to make sure error cases are getting hit in unit tests and that the unit tests aren't testing for any throw, since str_ errors would fit that bill

*/
INSECURE_DOCUMENT_REQUEST: {
code: 'INSECURE_DOCUMENT_REQUEST',
message: strings.pageLoadFailedInsecure,
message: UIStrings.pageLoadFailedInsecure,
lhrRuntimeError: true,
},
/* Used when the page stopped responding and did not finish loading. */
PAGE_HUNG: {
code: 'PAGE_HUNG',
message: strings.pageLoadFailedHung,
message: UIStrings.pageLoadFailedHung,
lhrRuntimeError: true,
},

// Protocol internal failures
TRACING_ALREADY_STARTED: {
code: 'TRACING_ALREADY_STARTED',
message: strings.internalChromeError,
message: UIStrings.internalChromeError,
pattern: /Tracing.*started/,
lhrRuntimeError: true,
},
PARSING_PROBLEM: {
code: 'PARSING_PROBLEM',
message: strings.internalChromeError,
message: UIStrings.internalChromeError,
pattern: /Parsing problem/,
lhrRuntimeError: true,
},
READ_FAILED: {
code: 'READ_FAILED',
message: strings.internalChromeError,
message: UIStrings.internalChromeError,
pattern: /Read failed/,
lhrRuntimeError: true,
},

// URL parsing failures
INVALID_URL: {
code: 'INVALID_URL',
message: strings.urlInvalid,
message: UIStrings.urlInvalid,
},

// Protocol timeout failures
/* Protocol timeout failures
* Requires an additional `icuProtocolMethod` field for translation.
*/
PROTOCOL_TIMEOUT: {
code: 'PROTOCOL_TIMEOUT',
message: strings.protocolTimeout,
message: UIStrings.protocolTimeout,
lhrRuntimeError: true,
},

// DNS failure on main document (no resolution, timed out, etc)
DNS_FAILURE: {
code: 'DNS_FAILURE',
message: strings.dnsFailure,
message: UIStrings.dnsFailure,
lhrRuntimeError: true,
},

Expand All @@ -203,4 +244,4 @@ LighthouseError.errors = ERRORS;
LighthouseError.NO_ERROR = 'NO_ERROR';
LighthouseError.UNKNOWN_ERROR = 'UNKNOWN_ERROR';
module.exports = LighthouseError;

module.exports.UIStrings = UIStrings;
21 changes: 0 additions & 21 deletions lighthouse-core/lib/strings.js

This file was deleted.

Loading