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

i18n: add i18n for LighthouseError messages #6457

merged 27 commits into from
Dec 10, 2018

Conversation

exterkamp
Copy link
Member

Summary
Adds i18n strings for LighthouseError messages. Adds support for substituting in values into an error message through i18n UIString.

Related Issues/PRs
fixes: #6441

lighthouse-core/lib/lh-error.js Outdated Show resolved Hide resolved
lighthouse-core/lib/lh-error.js Show resolved Hide resolved
lighthouse-core/lib/lh-error.js Outdated Show resolved Hide resolved
@@ -173,7 +173,7 @@ class GatherRunner {
const insecureDescriptions = explanations
.filter(exp => exp.securityState === 'insecure')
.map(exp => exp.description);
errorDef.message += ` ${insecureDescriptions.join(' ')}`;
errorDef.substitute = `${insecureDescriptions.join(' ')}`;
Copy link
Member

Choose a reason for hiding this comment

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

this join seems like it could be a problem. Are these descriptions localized by chrome?

lighthouse-core/lib/lh-error.js Outdated Show resolved Hide resolved
lighthouse-core/runner.js Outdated Show resolved Hide resolved
lighthouse-core/test/gather/driver-test.js Outdated Show resolved Hide resolved
lighthouse-core/lib/lh-error.js Outdated Show resolved Hide resolved
@@ -24,7 +59,7 @@ class LighthouseError extends Error {
super(errorDefinition.code);
this.name = 'LHError';
this.code = errorDefinition.code;
this.friendlyMessage = errorDefinition.message;
this.friendlyMessage = str_(errorDefinition.message, errorDefinition);
Copy link
Member Author

Choose a reason for hiding this comment

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

@patrickhulce @brendankenny is this what you had in mind?

Copy link
Collaborator

Choose a reason for hiding this comment

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

perfect 👌

Copy link
Member

Choose a reason for hiding this comment

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

What if the second argument to the LHError constructor becomes the replacement values? I feel like it would be conceptually easier to understand without having all of it on the first argument and

const err = new LHError(
  LHError.errors.PROTOCOL_TIMEOUT,
  protocolMethod: `${method}`,
);
reject(err);

is a lot easier to understand at a glance than

const err = new LHError({
  ...LHError.errors.PROTOCOL_TIMEOUT,
  protocolMethod: `${method}`,
});
reject(err);

I was going to say ideally we'd do this but the existing properties second arg makes that a problem, but I think it's actually fine. The only place we ever use that argument is in this file for fromProtocolMessage(), and we use it for protocolMethod and protocolError, one of which is already an icu argument name :)

What if properties just did both? It puts its properties on the error object (as it does now), and it gets passed into str_ (as errorDefinition is doing here).

Copy link
Member

Choose a reason for hiding this comment

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

I think later on we should do stricter type checking...if you use a string that needs a replacement, you should have to provide the replacement

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

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

love the new naming, thanks so much @exterkamp!

@@ -24,7 +59,7 @@ class LighthouseError extends Error {
super(errorDefinition.code);
this.name = 'LHError';
this.code = errorDefinition.code;
this.friendlyMessage = errorDefinition.message;
this.friendlyMessage = str_(errorDefinition.message, errorDefinition);
Copy link
Collaborator

Choose a reason for hiding this comment

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

perfect 👌

lighthouse-core/runner.js Outdated Show resolved Hide resolved
@@ -1059,7 +1062,8 @@ describe('GatherRunner', function() {
config: new Config({}),
}).then(artifacts => {
assert.equal(artifacts.LighthouseRunWarnings.length, 1);
assert.ok(/unable.*load the page/.test(artifacts.LighthouseRunWarnings[0]));
expect(artifacts.LighthouseRunWarnings[0])
Copy link
Collaborator

Choose a reason for hiding this comment

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

at this stage don't we want to assert that the string itself has already gone through the i18n process and is just a normal string?

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 it was already doing that? I might not get what you're implying. I made the check more accurate since the regex was a little plain.

Copy link
Collaborator

Choose a reason for hiding this comment

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

well I mean that toBeDisplayString does the whole getFormatted dance for you, so I'm thinkin' we want at least one test that checks if our code is using getFormatted at all the right places.

but maybe I'm misunderstanding or we haven't decided to have the strings in artifacts be pre-resolved yet and the test you added in runner-test is good enough for now?

hope that makes more sense

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh I think I get what you're saying, we should check that the translation did actually get filled in and we don't expose any un-translated strings to the user, right?

I think that the test in runner captures the only new place we have translation. Everything here is translated at the same time all other lhr strings are. Do we have some coverage for that/should we add some for these new strings?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah exactly. I was somewhat wondering outloud if the artifacts is one new place we should do the translation.

lighthouse-core/test/runner-test.js Outdated Show resolved Hide resolved
lighthouse-core/test/runner-test.js Outdated Show resolved Hide resolved
lighthouse-core/lib/lh-error.js Outdated Show resolved Hide resolved
@@ -24,7 +59,7 @@ class LighthouseError extends Error {
super(errorDefinition.code);
this.name = 'LHError';
this.code = errorDefinition.code;
this.friendlyMessage = errorDefinition.message;
this.friendlyMessage = str_(errorDefinition.message, errorDefinition);
Copy link
Member

Choose a reason for hiding this comment

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

What if the second argument to the LHError constructor becomes the replacement values? I feel like it would be conceptually easier to understand without having all of it on the first argument and

const err = new LHError(
  LHError.errors.PROTOCOL_TIMEOUT,
  protocolMethod: `${method}`,
);
reject(err);

is a lot easier to understand at a glance than

const err = new LHError({
  ...LHError.errors.PROTOCOL_TIMEOUT,
  protocolMethod: `${method}`,
});
reject(err);

I was going to say ideally we'd do this but the existing properties second arg makes that a problem, but I think it's actually fine. The only place we ever use that argument is in this file for fromProtocolMessage(), and we use it for protocolMethod and protocolError, one of which is already an icu argument name :)

What if properties just did both? It puts its properties on the error object (as it does now), and it gets passed into str_ (as errorDefinition is doing here).

@@ -24,7 +59,7 @@ class LighthouseError extends Error {
super(errorDefinition.code);
this.name = 'LHError';
this.code = errorDefinition.code;
this.friendlyMessage = errorDefinition.message;
this.friendlyMessage = str_(errorDefinition.message, errorDefinition);
Copy link
Member

Choose a reason for hiding this comment

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

I think later on we should do stricter type checking...if you use a string that needs a replacement, you should have to provide the replacement

lhrRuntimeError: true,
errorDetails: 'No Failure Description Loaded.',
Copy link
Member

Choose a reason for hiding this comment

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

do we want defaults for these? What happens if you don't provide them? Throwing if you don't provide one seems ideal

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah the ICU message translator throws. With a 'value that wasn't replaced', good point, we probably do want to throw in order to avoid a silent lack of real replacement.

@paulirish
Copy link
Member

ready for review?

@exterkamp
Copy link
Member Author

Not ready to review yet 😄 should land #6579 first and then merge those changes in and update to i18n that new error!

@exterkamp
Copy link
Member Author

Should be g2g, ptal

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

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

LGTM % nits!

errorDef.message += ` ${netErr}.`;
return new LHError(
LHError.errors.FAILED_DOCUMENT_REQUEST,
{errorDetails: `${netErr}`}
Copy link
Collaborator

Choose a reason for hiding this comment

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

do these still need to be in string templates?

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'

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

} catch (err) {
assert.equal(err.code, LHError.errors.PROTOCOL_TIMEOUT.code);
assert.ok(/^Waiting for DevTools protocol.*Method: Method.Failure/.test(err.friendlyMessage),
'should have prevented run');
Copy link
Collaborator

Choose a reason for hiding this comment

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

should have a localized error message :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh I see now my comment was confusing! I was suggesting to change the failure message to should have a localized error message not to change the test :)

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

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

Hold up a sec, maybe we forgot something for LR? Travis is unhappy with runLighthouseInLR https://travis-ci.org/GoogleChrome/lighthouse/builds/460924770#L1067

lighthouse-core/test/runner-test.js Outdated Show resolved Hide resolved
Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

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

LGTM!

} catch (err) {
assert.equal(err.code, LHError.errors.PROTOCOL_TIMEOUT.code);
assert.ok(/^Waiting for DevTools protocol.*Method: Method.Failure/.test(err.friendlyMessage),
'should have prevented run');
Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh I see now my comment was confusing! I was suggesting to change the failure message to should have a localized error message not to change the test :)

lighthouse-core/test/runner-test.js Outdated Show resolved Hide resolved
@googlebot
Copy link

So there's good news and bad news.

👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there.

😕 The bad news is that it appears that one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request.

Note to project maintainer: This is a terminal state, meaning the cla/google commit status will not change from this state. It's up to you to confirm consent of all the commit author(s), set the cla label to yes (if enabled on your project), and then merge this pull request when appropriate.

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

really liking this last form for the LHError constructor.

Mostly some style nits and two bigger questions left from me

lighthouse-core/gather/driver.js Outdated Show resolved Hide resolved
const asyncTimeout = setTimeout((_ => {
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.

lighthouse-core/gather/gather-runner.js Outdated Show resolved Hide resolved
@@ -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.

🎉

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
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

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!

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.

🎉

Copy link
Member

@paulirish paulirish left a comment

Choose a reason for hiding this comment

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

Digging it. small tweaks but I like the overall.

lighthouse-core/lib/lh-error.js Outdated Show resolved Hide resolved
lighthouse-core/lib/lh-error.js Outdated Show resolved Hide resolved
lighthouse-core/lib/lh-error.js Outdated Show resolved Hide resolved
lighthouse-core/lib/lh-error.js Outdated Show resolved Hide resolved
paulirish and others added 2 commits December 7, 2018 15:21
Added paulirish's suggestions for i18n strings.

Co-Authored-By: exterkamp <[email protected]>
Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Localize the error messages
6 participants