-
Notifications
You must be signed in to change notification settings - Fork 0
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
Break dependency on lit/localize #126
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
@@ -187,28 +182,37 @@ const LinkButton: FC<PropsWithChildren<{ href: string }>> = ({ | |||
</a> | |||
); | |||
|
|||
const renderIncentiveCard = (key: Key, incentive: Incentive) => ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I turned this into its own component because passing the Key around felt like not what you're supposed to do
@@ -301,7 +306,9 @@ const renderCardCollection = (incentives: Incentive[]) => ( | |||
{incentives | |||
// Put IRA rebates after everything else | |||
.sort((a, b) => +isIRARebate(a) - +isIRARebate(b)) | |||
.map((incentive, index) => renderIncentiveCard(index, incentive))} | |||
.map((incentive, index) => ( | |||
<IncentiveCard key={index} incentive={incentive} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you're not supposed to use a numerical index as a key, but incentives don't have identifiers for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be in favor of returning one!
@@ -397,7 +393,6 @@ export const StateCalculator: FC<{ | |||
stateId={stateId} | |||
initialValues={getInitialFormValues()} | |||
showEmailField={!!showEmail && !emailSubmitted} | |||
showProjectField={true} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got rid of this as a drive-by change since it's always true now
2f65630
to
9583ec8
Compare
@@ -42,23 +43,23 @@ export const IconTabBar: FC<Props> = ({ tabs, selectedTab, onTabSelected }) => { | |||
)} | |||
role="tab" | |||
aria-selected={isSelected} | |||
aria-label={PROJECTS[project].label()} | |||
aria-label={PROJECTS[project].label(msg)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading these as "label using msg (to translate it)" - would it be clearer as "translated msg of label"? i.e. msg(PROJECTS[project].label)
to avoid passing the msg
helper into every label? Not sure if that breaks the use of hooks to trigger a re-render if the locale changes, but I don't think it does?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, because we're still using lit localize at build time to extract strings, we still need a msg
call site to allow it to know which strings need translations. Is that it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, we're still using lit/localize-tools
, which needs the argument to msg
to be a string literal so it can extract it.
@@ -24,7 +23,7 @@ | |||
"lint": "tsc --noEmit && prettier --check . && eslint .", | |||
"prepare": "husky install", | |||
"strings:extract": "lit-localize extract", | |||
"strings:build": "lit-localize build && prettier --write src/locales" | |||
"strings:build": "lit-localize build && sed -e 's!@lit/localize!../str!' -i '' src/i18n/strings/*.ts && prettier --write src/i18n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we get this from a dev dependency now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean sed
? It's included in every Unix-like OS. (Also this command is only run in local dev, not in CI, so it only needs to work on macOS.)
To be clear, the command is making this change: src/i18n/strings/es.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I meant the lit-localize
command - I see there's still lit/localize-tools
in dev deps, which is what I was checking. From the diff I could only see the removal of the runtime package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes sense. Definitely jumping through a few hoops to use the msg
function to tag things. I wonder if there's a way to use tagged strings to make them magically re-render based on context? Or if we should evaluate an alternative approach - I've used t('some.string.id')
in other apps, with every language in a strings file (yaml or json) and english doesn't need to be hard-coded at the point of use. That might help avoid threading the msg function through, if we want to part ways with the lit toolchain entirely?
But this seems fine to keep us moving toward React-all-the-things.
Note: I'm relying on the Cypress test here to verify the translated behavior is actually working, since we don't have a preview link with Spanish. It might be worth putting the preview build into a local copy of our consumer site to test more thoroughly? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I admit this is a bit of a weird Frankenstein approach, but my rationale was:
- This minimized the grunt work of going around changing every single
msg
callsite - It lets us keep using the same build-time translation workflow/tooling
- I looked at alternatives, including the apparent standard
react-i18next
. It uses thet('string.id')
approach, which I actually don't like -- I think having the user-visible English string in situ is a plus. You can see what string will show up just by scanning the usage site; you don't need to flip to another file.
This approach has the key limitation that it doesn't handle localizing strings that need to interpolate HTML, rather than just a string. (We had this problem in a recent PR with needing to localize View our <a href="...">terms</a>
.)
We may very well switch to another localization framework at some point, but I think this is the quickest reasonable way forward for now.
@@ -24,7 +23,7 @@ | |||
"lint": "tsc --noEmit && prettier --check . && eslint .", | |||
"prepare": "husky install", | |||
"strings:extract": "lit-localize extract", | |||
"strings:build": "lit-localize build && prettier --write src/locales" | |||
"strings:build": "lit-localize build && sed -e 's!@lit/localize!../str!' -i '' src/i18n/strings/*.ts && prettier --write src/i18n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean sed
? It's included in every Unix-like OS. (Also this command is only run in local dev, not in CI, so it only needs to work on macOS.)
To be clear, the command is making this change: src/i18n/strings/es.ts
@@ -42,23 +43,23 @@ export const IconTabBar: FC<Props> = ({ tabs, selectedTab, onTabSelected }) => { | |||
)} | |||
role="tab" | |||
aria-selected={isSelected} | |||
aria-label={PROJECTS[project].label()} | |||
aria-label={PROJECTS[project].label(msg)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, we're still using lit/localize-tools
, which needs the argument to msg
to be a string literal so it can extract it.
## Description This is unfortunately a huge PR, so here's a detailed guide. Using `@lit/localize` at runtime also entails depending on the entirety of Lit; the localize package is not factored so that you can just use the runtime string-substitution part. So we gotta get rid of it. The idea here was to be minimally disruptive by simply swapping out the `msg` and `str` functions from lit-localize with our own implementations. That's in `src/i18n/use-translated.ts` and `src/i18n/str.ts`. The `strings:build` command now also invokes sed to adjust the import in the generated translated-strings files. Rather than putting the current locale in global state like Lit does, I put it in React context, accessed via the new custom hook `useTranslated`. The hook returns the `msg` function, bound to the current locale; callsites then use that msg function as before. Most of the changed files are just plumbing in the new hook and/or msg function. The most substantive changes are in: - `state-calculator.tsx` where the locale context is set - `calculator.tsx` -- this is the old embed, which was never actually localized; I removed all the `msg` calls here since we're getting rid of it Soon™ - `i18n/use-translated.ts` -- the core of the new stuff ## Test Plan - Run `yarn strings:extract` and make sure there are no changes to es.xlf. - Run `yarn strings:build` and make sure there are no changes. - View the calculator and hit Calculate; make sure all strings come up looking correct in English. Add the `lang="es"` attribute to the calculator element and make sure the UI immediately switches to Spanish, except for incentive blurbs (which come from the API). Hit Calculate again and make sure the incentive blurbs are now in Spanish.
9583ec8
to
89527fa
Compare
Description
This is unfortunately a huge PR, so here's a detailed guide.
Using
@lit/localize
at runtime also entails depending on theentirety of Lit; the localize package is not factored so that you can
just use the runtime string-substitution part. So we gotta get rid of it.
The idea here was to be minimally disruptive by simply swapping out
the
msg
andstr
functions from lit-localize with our ownimplementations. That's in
src/i18n/use-translated.ts
andsrc/i18n/str.ts
. Thestrings:build
command now also invokes sed toadjust the import in the generated translated-strings files.
Rather than putting the current locale in global state like Lit does,
I put it in React context, accessed via the new custom hook
useTranslated
. The hook returns themsg
function, bound to thecurrent locale; callsites then use that msg function as before.
Most of the changed files are just plumbing in the new hook and/or msg
function. The most substantive changes are in:
state-calculator.tsx
where the locale context is setcalculator.tsx
-- this is the old embed, which was never actuallylocalized; I removed all the
msg
calls here since we're gettingrid of it Soon™
i18n/use-translated.ts
-- the core of the new stuffTest Plan
Run
yarn strings:extract
and make sure there are no changes to es.xlf.Run
yarn strings:build
and make sure there are no changes.View the calculator and hit Calculate; make sure all strings come up
looking correct in English. Add the
lang="es"
attribute to thecalculator element and make sure the UI immediately switches to
Spanish, except for incentive blurbs (which come from the API).
Hit Calculate again and make sure the incentive blurbs are now in Spanish.