Skip to content

add MetaMaskTranslation component#10405

Merged
brad-decker merged 6 commits intodevelopfrom
metamask-translation-component
Feb 12, 2021
Merged

add MetaMaskTranslation component#10405
brad-decker merged 6 commits intodevelopfrom
metamask-translation-component

Conversation

@brad-decker
Copy link
Copy Markdown
Contributor

@brad-decker brad-decker commented Feb 8, 2021

Rationale

The template methods of the future addEthereumChain confirmation UI are passed in the t function for i18n strings. The problem lies in special cases where t requires JSX replacements, such as embedding a link in a string of text. To do this in normal situations you'd simply call:

content: t('yourKey', [<a>t('linkKey')</a>])

This could be done in the current setup, however, the result would require React to be in scope with the template files which I was trying to avoid. It'd also end up embedding the resulting string with jsx into the template object which seems like it could be prone to memory leaks.

This solution adds a new MetaMaskTranslation component that can render the MetaMask template objects. Additional checks are performed to ensure a maximum depth.

content: {
  element: 'MetaMaskTranslation',
  key: 'thekey',
  props: {
    translationKey: 'yourKey',
    variables: [{
      element: 'a',
      key: 'link',
      children: {
        element: 'MetaMaskTranslation',
        props: {
          translationKey: 'linkKey',
        }
      }
   }]
  }
}

To provide a path forward for requiring t to only be called with string literals, I have proposed some changes to the verify-locale script that would check for either t('$1') or translationKey: '$1' when determining if keys had been used. It also provides a mechanism for gradually switching verify-locales over to use the more strict search.

@metamaskbot
Copy link
Copy Markdown
Collaborator

Builds ready [14cd336]
Page Load Metrics (576 ± 29 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint469261126
domContentLoaded3986525745929
load3996535766029
domInteractive3976515745929

@brad-decker brad-decker marked this pull request as ready for review February 8, 2021 23:22
@brad-decker brad-decker requested review from a team and kumavis as code owners February 8, 2021 23:22
@brad-decker brad-decker requested a review from Gudahtt February 8, 2021 23:22
@kumavis
Copy link
Copy Markdown
Member

kumavis commented Feb 9, 2021

i tend to not camelcase metamask in code, so id prefer MetamaskTranslation. will defer to whatever has already been standardized

import Box from '../../ui/box';
import MetaMaskTranslation from '../metamask-translation';

export const safeComponentList = {
Copy link
Copy Markdown
Member

@kumavis kumavis Feb 9, 2021

Choose a reason for hiding this comment

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

what is this list? what does "safe" mean here and how do we use the list

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

the word safe in this variable name is sort of an aspirational word. In its current iteration, it acts as the map from the element key in the template language to a real component. It is an attenuation point from 'every HTML element and component in the application' to a small number of components that we want to support in the template system.

In the future, i'd like to extend this implementation to also providing 'allow/deny' lists for props and prop validation. This way we could "safely" allow third parties to add templates with 'a' tags or 'button' where we can isolate the types of actions that can happen on those click handlers.

@kumavis
Copy link
Copy Markdown
Member

kumavis commented Feb 9, 2021

can we get a storybook usage example 😁

@brad-decker
Copy link
Copy Markdown
Contributor Author

can we get a storybook usage example 😁

Absolutely, @kumavis!

@brad-decker
Copy link
Copy Markdown
Contributor Author

image
Added a very mindful and present story. (default input with da selected in language selector) (language selector is awesome btw :D 🎉 )

@metamaskbot
Copy link
Copy Markdown
Collaborator

Builds ready [3a859a3]
Page Load Metrics (609 ± 34 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint52876284
domContentLoaded4037946087034
load4047956097034
domInteractive4037936077034

@brad-decker
Copy link
Copy Markdown
Contributor Author

i tend to not camelcase metamask in code, so id prefer MetamaskTranslation. will defer to whatever has already been standardized

MetaMaskTemplateRenderer landed ahead of this. I'll do a follow pr to change both

});
const javascriptFilesToStrictSearch = await glob(globsToStrictSearch);

const strictSearchRegex = /\bt\('(\w+)'\)|\bt\('(\W+)'\)|\btranslationKey:\s'(\w+)'|\btranslationKey:\s'(\W+)'/gu;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Phew, OK, having trouble dissecting this one.

/\bt\('(\w+)'\)/

^ That makes sense. It looks for t('anykey'). Got it.

But this one:

\bt\('(\W+)'\)

No idea what's going on there. Why would we want to match non-word characters in single quotes?

Also, neither of these accounts for this:

t(
  'veryLongKeyThatWasPutOnASeparateLine'
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I removed the \W checks and added a 0 or more whitespace selector before and after the string to catch cases where the call is broke up onto multiple lines. For the object notation selector i added a 0 or more whitespace selector after the colon to allow for multiline checks there as well.

* to make sure that the tree has a single root node, with maximum two leaves.
* Each subnode can have a maximum of one child that must be a string.
*
* This enforces a maximum recursion depth of 2, preventing translation strings
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We might already have a case that doesn't meet these criteria: a link with a tooltip. The tooltip wrapper would be the first component, then the link would be the child, then the link text would be the child of the link.

Maybe the template components won't work like that, I don't know. I guess we'll see, and we can always adjust this later if we need to.

We'll need to be careful not to let this become a perverse incentive, and design weird components like LinkWithTooltip to get around this.

@metamaskbot
Copy link
Copy Markdown
Collaborator

Builds ready [c640481]
Page Load Metrics (647 ± 39 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint479765126
domContentLoaded3977836468139
load3987846478139
domInteractive3977836458039

Copy link
Copy Markdown
Member

@Gudahtt Gudahtt left a comment

Choose a reason for hiding this comment

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

LGTM, great work! The strategy for gradually phasing in strict validation for the string-literal translation keys is a fantastic idea.

@brad-decker brad-decker merged commit 8e2d4e6 into develop Feb 12, 2021
@brad-decker brad-decker deleted the metamask-translation-component branch February 12, 2021 17:24
@github-actions github-actions bot locked and limited conversation to collaborators Feb 12, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants