Skip to content

Commit

Permalink
[i18n][system upgrade] Upgrade Intl Packages from v2 to v6 (#179506)
Browse files Browse the repository at this point in the history
## Summary

Upgrading intl packages from v2 to v6

### Packages upgrade:

- [x] Add @formatjs packages
- [x] `react-intl` Upgraded
- [x] `intl-messageformat` Upgraded
- [x] `intl-format-cache` removed
- [x] `intl-relativeformat` removed
- [x] `intl-messageformat-parser` removed

### Todo list:
- [x] Refactor HTML tags
- [x] Refactor Upgrade tags
- [x] Refactor `kbn-i18n`
- [x] Refactor `kbn-i18n-react`
- [x] Refactor `FormattedRelative` to `FormattedRelativeTime`
- [x] Refactor polyfills
- [x] Refactor IntlShape types
- [x] Rewrite Providers
- [x] Rewrite tests using i18n
- [x] Removed current pseudolocale implementation (tracker:
#180244)
- [x] Fix jest tests using rendered `Provider`
- [x] Remove no longer valid i18n packages documentation (tracker:
#180259)

Closes #178968
Closes #38642


## Notes to code reviewers
For team other than the core team, please review your plugins code
changes by filtering files by codeowners.


### Test Snapshot updates
Most of the changes are refactors of renamed functions and changed ICU
syntax.

The main updates are snapshot changes where `FormattedMessage` is now
memoized so snapshots capturing the html tree needed to be updated to
use `<Memo(MemoizedFormattedMessage)` instead of `<FormattedMessage`


### ICU now supports HTML tags:
before:
```
<FormattedMessage
  defaultMessage="To buy a shoe, { link } and { cta }"
  values={{
    link: (
      <a class="external_link" target="_blank" href="https://www.shoe.com/">
        visit our website
      </a>
    ),
    cta: <strong class="important">eat a shoe</strong>,
  }}
/>
```
after:
```
<FormattedMessage
  defaultMessage="To buy a shoe, <a>visit our website</a> and <cta>eat a shoe</cta>"
  values={{
    a: msg => (
      <a class="external_link" target="_blank" href="https://www.shoe.com/">
        {msg}
      </a>
    ),
    cta: msg => <strong class="important">{msg}</strong>,
  }}
/>
```

### Escape character to prevent ICU parsing changed from double slashes
to single quotes:
before: `\\{escaped\\}`
after: `'{escaped}'`

### No need for Intl Shape
the new packages under formatJS are written in typescript and come with
types support out of the box so no need to set types when using i18n.

Renamed `InjectedIntlProps` with `WrappedComponentProps`.
Removed `prop-types` and `intlShape` in favor of `IntlShape`.

### FormattedRelative has been renamed to FormattedRelativeTime and its
API has changed significantly. See
[FormattedRelativeTime](https://formatjs.io/docs/react-intl/upgrade-guide-3x#formattedrelativetime)
for more details.

### All tags specified must have corresponding values and will throw
error if it's missing
All tags are now parsed and expected to be formatted properly (all
opened tags must be closed).

To skip this check you can use the `ignoreTag: true` property

```
i18n.translate('xpack.apm.agentConfig.captureJmxMetrics.description', {
      defaultMessage: 'This is not an HTML tag <JMX object name pattern>' +
      ignoreTag: true,
    }),
```

**When do I use ignore tags?**

If your message has HTML tags, it is preferred not to ignore the Tag to
have some string verification that the html tags you are adding are
properly formatted and closed.

If it the text between brackets is not an HTML tag and it is just a
fomat preference then using `ignoreTag` makes sense.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Tiago Costa <tiago.costa@elastic.co>
  • Loading branch information
3 people committed Jun 2, 2024
1 parent 2570872 commit f1c854b
Show file tree
Hide file tree
Showing 639 changed files with 4,455 additions and 7,255 deletions.
17 changes: 16 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,22 @@ module.exports = {
'jsx-a11y/click-events-have-key-events': 'off',
},
},

/**
* FormatJS linter for i18n code.
* https://formatjs.io/docs/tooling/linter
*/
{
files: [
'src/**/*.{js,mjs,ts,tsx}',
'x-pack/**/*.{js,mjs,ts,tsx}',
'packages/**/*.{js,mjs,ts,tsx}',
],
plugins: ['formatjs'],
rules: {
'formatjs/enforce-default-message': ['error', 'anything'],
'formatjs/enforce-description': 'off',
},
},
/**
* Files that require dual-license headers, settings
* are overridden below for files that require Elastic
Expand Down
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,7 @@ x-pack/test/observability_ai_assistant_functional @elastic/obs-ai-assistant
/config/ @elastic/kibana-core
/typings/ @elastic/kibana-core
/test/analytics @elastic/kibana-core
/packages/kbn-test/src/jest/setup/mocks.kbn_i18n_react.js @elastic/kibana-core
/x-pack/test/saved_objects_field_count/ @elastic/kibana-core
/x-pack/test_serverless/**/test_suites/common/saved_objects_management/ @elastic/kibana-core
/x-pack/test_serverless/api_integration/test_suites/common/core/ @elastic/kibana-core
Expand Down
20 changes: 11 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"**/globule/minimatch": "^3.1.2",
"**/hoist-non-react-statics": "^3.3.2",
"**/isomorphic-fetch/node-fetch": "^2.6.7",
"**/react-intl/**/@types/react": "^17.0.45",
"**/remark-parse/trim": "1.0.1",
"**/sharp": "0.32.6",
"**/typescript": "4.9.5",
Expand Down Expand Up @@ -124,6 +125,11 @@
"@emotion/server": "^11.11.0",
"@emotion/styled": "^11.11.0",
"@faker-js/faker": "^8.3.1",
"@formatjs/icu-messageformat-parser": "^2.7.6",
"@formatjs/intl": "^2.10.2",
"@formatjs/intl-pluralrules": "^5.2.12",
"@formatjs/intl-relativetimeformat": "^11.2.12",
"@formatjs/intl-utils": "^3.8.4",
"@grpc/grpc-js": "^1.6.8",
"@hapi/accept": "^5.0.2",
"@hapi/boom": "^9.1.4",
Expand Down Expand Up @@ -1030,11 +1036,6 @@
"icalendar": "0.7.1",
"immer": "^9.0.21",
"inquirer": "^7.3.3",
"intl": "^1.2.5",
"intl-format-cache": "^2.1.0",
"intl-messageformat": "^2.2.0",
"intl-messageformat-parser": "^1.4.0",
"intl-relativeformat": "^2.1.0",
"io-ts": "^2.0.5",
"ipaddr.js": "2.0.0",
"isbinaryfile": "4.0.2",
Expand Down Expand Up @@ -1110,7 +1111,7 @@
"react-fast-compare": "^2.0.4",
"react-grid-layout": "^1.3.4",
"react-hook-form": "^7.44.2",
"react-intl": "^2.8.0",
"react-intl": "6.6.6",
"react-is": "^17.0.2",
"react-markdown": "^6.0.3",
"react-monaco-editor": "^0.54.0",
Expand Down Expand Up @@ -1221,6 +1222,8 @@
"@elastic/synthetics": "^1.5.0",
"@emotion/babel-preset-css-prop": "^11.11.0",
"@emotion/jest": "^11.11.0",
"@formatjs/cli": "^6.2.8",
"@formatjs/cli-lib": "^6.3.8",
"@frsource/cypress-plugin-visual-regression-diff": "^3.3.10",
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@istanbuljs/schema": "^0.1.2",
Expand Down Expand Up @@ -1455,7 +1458,6 @@
"@types/hjson": "^2.4.2",
"@types/http-proxy": "^1.17.4",
"@types/inquirer": "^7.3.1",
"@types/intl-relativeformat": "^2.1.0",
"@types/jest": "^29.5.3",
"@types/jquery": "^3.3.31",
"@types/js-levenshtein": "^1.1.0",
Expand Down Expand Up @@ -1506,7 +1508,6 @@
"@types/react": "^17.0.45",
"@types/react-dom": "^17.0.17",
"@types/react-grid-layout": "^1.3.2",
"@types/react-intl": "^2.3.15",
"@types/react-is": "^17.0.3",
"@types/react-recompose": "^0.33.4",
"@types/react-router": "^5.1.20",
Expand Down Expand Up @@ -1593,11 +1594,12 @@
"ejs": "^3.1.10",
"enzyme": "^3.11.0",
"enzyme-to-json": "^3.6.2",
"eslint": "^8.46.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-ban": "^1.6.0",
"eslint-plugin-cypress": "^2.15.1",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-formatjs": "^4.12.2",
"eslint-plugin-import": "^2.28.0",
"eslint-plugin-jest": "^27.2.3",
"eslint-plugin-jsx-a11y": "^6.7.1",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import React from 'react';
import { mount } from 'enzyme';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import { ServerStatus } from './server_status';
import { StatusState } from '../lib';

Expand All @@ -22,7 +22,7 @@ const getStatus = (parts: Partial<StatusState> = {}): StatusState => ({
describe('ServerStatus', () => {
it('renders correctly for green state', () => {
const status = getStatus();
const component = mount(<ServerStatus serverState={status} name="My Computer" />);
const component = mountWithIntl(<ServerStatus serverState={status} name="My Computer" />);
expect(component.find('EuiTitle').text()).toMatchInlineSnapshot(`"Kibana status is Green"`);
expect(component.find('EuiBadge').render()).toMatchSnapshot();
});
Expand All @@ -32,7 +32,7 @@ describe('ServerStatus', () => {
id: 'degraded',
title: 'Yellow',
});
const component = mount(<ServerStatus serverState={status} name="My Computer" />);
const component = mountWithIntl(<ServerStatus serverState={status} name="My Computer" />);
expect(component.find('EuiTitle').text()).toMatchInlineSnapshot(`"Kibana status is Yellow"`);
expect(component.find('EuiBadge').render()).toMatchSnapshot();
});
Expand All @@ -42,16 +42,16 @@ describe('ServerStatus', () => {
id: 'unavailable',
title: 'Red',
});
const component = mount(<ServerStatus serverState={status} name="My Computer" />);
const component = mountWithIntl(<ServerStatus serverState={status} name="My Computer" />);
expect(component.find('EuiTitle').text()).toMatchInlineSnapshot(`"Kibana status is Red"`);
expect(component.find('EuiBadge').render()).toMatchSnapshot();
});

it('displays the correct `name`', () => {
let component = mount(<ServerStatus serverState={getStatus()} name="Localhost" />);
let component = mountWithIntl(<ServerStatus serverState={getStatus()} name="Localhost" />);
expect(component.find('EuiText').text()).toMatchInlineSnapshot(`"Localhost"`);

component = mount(<ServerStatus serverState={getStatus()} name="Kibana" />);
component = mountWithIntl(<ServerStatus serverState={getStatus()} name="Kibana" />);
expect(component.find('EuiText').text()).toMatchInlineSnapshot(`"Kibana"`);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -560,19 +560,17 @@ describe('breadcrumbs', () => {
<EuiContextMenuItem
icon="gear"
>
<FormattedMessage
<Memo(MemoizedFormattedMessage)
defaultMessage="Manage project"
id="core.ui.primaryNav.cloud.linkToProject"
values={Object {}}
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="grid"
>
<FormattedMessage
<Memo(MemoizedFormattedMessage)
defaultMessage="View all projects"
id="core.ui.primaryNav.cloud.linkToAllProjects"
values={Object {}}
/>
</EuiContextMenuItem>,
]
Expand Down Expand Up @@ -625,19 +623,17 @@ describe('breadcrumbs', () => {
<EuiContextMenuItem
icon="gear"
>
<FormattedMessage
<Memo(MemoizedFormattedMessage)
defaultMessage="Manage project"
id="core.ui.primaryNav.cloud.linkToProject"
values={Object {}}
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="grid"
>
<FormattedMessage
<Memo(MemoizedFormattedMessage)
defaultMessage="View all projects"
id="core.ui.primaryNav.cloud.linkToAllProjects"
values={Object {}}
/>
</EuiContextMenuItem>,
]
Expand Down Expand Up @@ -684,19 +680,17 @@ describe('breadcrumbs', () => {
<EuiContextMenuItem
icon="gear"
>
<FormattedMessage
<Memo(MemoizedFormattedMessage)
defaultMessage="Manage project"
id="core.ui.primaryNav.cloud.linkToProject"
values={Object {}}
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="grid"
>
<FormattedMessage
<Memo(MemoizedFormattedMessage)
defaultMessage="View all projects"
id="core.ui.primaryNav.cloud.linkToAllProjects"
values={Object {}}
/>
</EuiContextMenuItem>,
]
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit f1c854b

Please sign in to comment.