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

[EuiSuperDatePicker] i18n all remaining static copy + convert prettyDuration to hook/component #5743

Merged
merged 31 commits into from
Mar 28, 2022

Conversation

cee-chen
Copy link
Member

@cee-chen cee-chen commented Mar 23, 2022

Summary

closes #5398

Screencaps

Now tab Before/After
1b 1

Relative units dropdown before/after
2b 2

Absolute tab before/after (note that I switched the locale to ja in the after screenshot for testing)
3b 3

Quick select before/after
4b 4

Refresh interval before/after
5b 5

Review

I strongly recommend following along by commit - it gets particularly gnarly after the 5th commit. I tried my best to keep things atomic, but this is an inherently difficult PR to grok due to the nature of the changes that had to be made.

I'm going to quote a Slack conversation I had with @thompsongl that captured why these changes were so difficult and why this approach was the least worst of all worlds:

We've got a pretty gnarly tangle of un-i18n'd string interpolation, the problem being that we have them in top-level exported constants being used interchangeably in both vanilla JS util functions (which can't have hooks or react context) and React components. An extra snag is that most of the super date picker components are old school class components, meaning they can't use useEuiI18n hooks. At this point I think my options are:

  1. Convert everything (including vanilla util fns) into custom use hooks so they can call useEuiI18n and all class components into functional components that can use hooks
  2. Somehow magically figure out a helper that allows us to use our i18n helpers in vanilla JS outside of a React component or hook
  3. Set a top level render component that passes down the i18n'd strings/arrays/objects as a constant and waterfall those i18n'd constants as props/args to every use of them (extremely verbose but probably the least technically unknown)

Another snag: we're exporting prettyDuration, a vanilla JS util that contain un-i18n'd strings. There's simply no way I can see to leave prettyDuration as a util fn and not convert it into a hook and still contain i18n'd strings.

Greg Thompson
If we want to get this in for 8.2, option 3 is definitely the safest.
Not sure option 2 is even possible because the i18n stuff is all based on React context

Checklist

- [ ] Checked in both light and dark modes
- [ ] Checked in mobile
- [ ] Checked Code Sandbox works for any docs examples
- [ ] Checked for accessibility including keyboard-only and screenreader modes
- [ ] Updated the Figma library counterpart

  • Checked in Chrome, Safari, Edge, and Firefox
  • Props have proper autodocs and playground toggles
  • Added or updated jest and cypress tests
  • Updated documentation
  • Checked for breaking changes and labeled appropriately
  • A changelog entry exists and is marked appropriately
  • Revert [REVERT] commit

- Example setup of how we'll be handling all other non-i18n'd constants used throughout datepicker
…ePicker

+ update external/internal required props accordingly - there were a lot of props marked required that weren't actually required because they had defaults
- it has no i18n strings and can be a constant
+ refactor prettyInterval structure with `useI18nUnits` helper - because hooks can't be called conditionally, I had to rewrite a good amount to try and keep the code readable
- This one was tricky and what necessitated the render prop wrapper, because commonlyUsedRanges uses it as a default prop - this was the most straightforward way I could think of to pass it as a fallback
@cee-chen cee-chen added this to the Elastic Stack 8.2 milestone Mar 23, 2022
@cee-chen cee-chen changed the title I18n super date picker [EuiSuperDatePicker] i18n all remaining static copy + convert prettyDuration to hook/component Mar 23, 2022
- cannot be a vanilla JS util any longer if it's using i18n (which requires React context)
- a hook+component should covert both cases where a string is required vs rendering JSX

- reorder utils slightly

- update formatTimeString to hook as well to render i18n strings
Copy link
Contributor

@thompsongl thompsongl left a comment

Choose a reason for hiding this comment

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

A couple more imports to update:

WARNING in ./views/pretty_duration/pretty_duration_example.js 21:56-76
"export 'commonDurationRanges' was not found in '../../../../src/components'
WARNING in ./views/pretty_duration/pretty_duration.js 56:62-76
"export 'prettyDuration' was not found in '../../../../src/components'

@cee-chen
Copy link
Member Author

Doh, I totally missed that we had a documentation page for prettyDuration 🤦‍♀️ on it!

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5743/

@cee-chen
Copy link
Member Author

I can confirm this works locally despite the typing:

Casting as works but I'm not sure if there's a more elegant way of overriding Action<>'s getDisplayName definition 🤔

@thompsongl
Copy link
Contributor

Casting as works but I'm not sure if there's a more elegant way of overriding Action<>'s getDisplayName definition

I think we should 1) cast and 2) check with the Action owners about expanding the type to ReactNode or something more accommodating

value: 'y',
},
{
text: useEuiI18n('euiTimeOptions.secondsFromNow', 'Seconds from now'),
Copy link
Member

Choose a reason for hiding this comment

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

These values will show on the UI with a time unit next to them, right? eg 5 Seconds from now

If that is the case then Ideally we'd want to pass the time unit as a variable to the i18n message:

{unitAmount} Seconds from now

This way we can move the variable around depending on the language. In some languages, it doesn't make sense to have the number at the beginning of the text.

Will that require a lot of changes to implement?

Copy link
Member Author

@cee-chen cee-chen Mar 24, 2022

Choose a reason for hiding this comment

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

Hey @Bamieh! These specific values are actually not shown in the UI with units in front of them, they are only shown in the below highlighted dropdown/EuiSelect:

159788498-1f42b131-ed80-43e6-a285-991f3ef24be4

The actual value that displays in the datepicker input (that toggles the popover) typically comes from Moment itself if it's not a quick range label (e.g. year to date), and moment should be handling the i18n of that via the locale prop.

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, actually, I hyperfocused on the line you selected and not on the general principal of what you were saying. There is a place in PrettyDuration where we concatenate time, time units, and 'last/next' together. I'll flag that and move our convo there if that's cool

Copy link
Contributor

@thompsongl thompsongl Mar 24, 2022

Choose a reason for hiding this comment

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

Yes, the ~30分前 in the screenshot above is from moment not from EUI's i18n strings.

? timeUnitsPlural[countTimeUnit]
: timeUnits[countTimeUnit];

relativeDuration = `${timeTense} ${relativeParts.count} ${countTimeUnitFullName}`;
Copy link
Member Author

Choose a reason for hiding this comment

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

Re-tagging @Bamieh on this discussion:

Ideally we'd want to pass the time unit as a variable to the i18n message:
{unitAmount} Seconds from now
This way we can move the variable around depending on the language. In some languages, it doesn't make sense to have the number at the beginning of the text.

This is the primary place I can think of where we concatenate something like that. This string outputs something like Last 30 minutes or Next 1 hour.

@Bamieh just to make sure I'm understanding you, you would rather see this as a series of i18n tokens, is that correct? Would something like

Last {timeAmount} {timeUnit}
Next {timeAmount} {timeUnit}

work or do you need it to be more granular and have us implement something like last Last {timeAmount} second(s) (so a token for each unit type and pluralization?... crossing my fingers that you don't need this as that would be a really high amount of tokens 😬)

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 also want to quickly flag that we are using some of these minutes/seconds tokens as-is with no time amount in front of them for our dropdown selections. So we were essentially trying to use concatenation to avoid doubling the amount of tokens (for the dropdowns and the output). But you're saying we should be doubling up for better translation, correct?

Copy link
Member

Choose a reason for hiding this comment

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

Syncing on slack to reduce the amount of back and forth inside the PR. Will reflect the resolution here on github once we reach it

if (shortHand) return `${intervalInDays} d`;
units = intervalInDays > 1 ? 'days' : 'day';
return `${intervalInDays} ${units}`;
return `${interval} ${units}`;
Copy link
Member Author

Choose a reason for hiding this comment

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

@Bamieh Also wanted to flag this pretty interval concatenation. This is meant to output things like: 30 minutes, 1 minute, 1 m. Are you saying we should also have a token for each unit type? i.e., one for {timeUnit} minute(s) and one for {timeUnit} m?

Per i18n team feedback, we shouldn't be chopping units up like this and concatenating them manually - we should prefer to attempt to tokenize whole sentences to better accommodate the different grammars of different languages

+ add more misc comment documentation

+ capitalize units in timeUnitOptions to match relativeOptions
- preferring to let languages translate each phrase per unit
- allows consuming applications to determine grammar/number placement, pluralization, etc
@cee-chen cee-chen requested a review from Bamieh March 24, 2022 22:13
- allows consuming applications to determine grammar/number placement, pluralization, etc

- pretty_duration has its own custom i18n strings not shared with other parts of EuiDatePicker, so I put them at the top of the file instead of in time_options

+ refactored/greatly simplified relativeDuration logic - a lot of the necessary keys were already in relativeParts, no need to substr, get timeTense, etc
- structure is still somewhat weird due to not being able to call hooks conditionally

+ DRY out TimeUnitAllId for string maps with both `+` and `-` time units

+ add comment docblocks for readability/organization
- <EuiI18n> with `tokens` arrays was not passing an i18nMappingFunc

- i18n tokens that passed a function were not passing the rendered function output to the i18nMappingFunc

- Updated the i18n documentation page to show both of these examples now working with the babelfish toggled on
@cee-chen
Copy link
Member Author

@Bamieh Alrighty, I should have pushed up all i18n token changes that I think follow the best practices you've outlined. Please let me know if it would be easier for you if I listed out all the tokens in a text file or similar instead of you having to parse through my commits/diffs.

@thompsongl the i18n babelfish issues we discussed in Slack are in d53c0a2. Honestly I have no idea what on earth is going on with the typing in that file so I just cast a bunch and crossed my fingers. It does appear to be working per my dev environment testing, although it also makes me wonder why the unit tests never caught the issue 😅 I'm fully open to reverting the commit in this PR before it merges and opening a follow-up PR with the fix + perhaps more robust unit tests.

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5743/

Copy link
Member

@Bamieh Bamieh left a comment

Choose a reason for hiding this comment

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

LGTM thank you!

src-docs/src/views/super_date_picker/super_date_picker.tsx Outdated Show resolved Hide resolved
src/components/i18n/i18n.tsx Outdated Show resolved Hide resolved
@@ -113,7 +113,7 @@ exports[`EuiQuickSelect is rendered 1`] = `
values={
Object {
"timeTense": "last",
"timeUnit": "minutes",
"timeUnit": "Minutes",
Copy link
Contributor

Choose a reason for hiding this comment

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

Just noting the casing change. Was there a specific reason for this? Seems fine to me regardless

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 was trying to match our other dropdown selection in the relative tab that says Minutes ago, Seconds ago, etc. I also thought that capitalizing it would lowkey make it harder to concatenate with other strings in the future haha

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_5743/

Copy link
Contributor

@thompsongl thompsongl left a comment

Choose a reason for hiding this comment

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

LGTM!
I did another build with Kibana that resulted in the expected type changes

@cee-chen
Copy link
Member Author

Awesome, thanks y'all. Is this good to merge now or do we need to wait for any Kibana/FF timing or anything?

@cee-chen
Copy link
Member Author

Chandler has given the green light on this, so going to go ahead and merge + do a release in the next 10 minutes.

@cee-chen cee-chen merged commit 037c4ec into elastic:main Mar 28, 2022
@cee-chen cee-chen deleted the i18n-super-date-picker branch March 28, 2022 18:33
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.

[EuiSuperDatePicker] Ensure i18n coverage
4 participants