kibana/x-pack/plugins/canvas/i18n
Ahmad Bamieh f1c854b9db
[i18n][system upgrade] Upgrade Intl Packages from v2 to v6 (#179506)
## 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:
https://github.com/elastic/kibana/issues/180244)
- [x] Fix jest tests using rendered `Provider`
- [x] Remove no longer valid i18n packages documentation (tracker:
https://github.com/elastic/kibana/issues/180259)

Closes https://github.com/elastic/kibana/issues/178968
Closes https://github.com/elastic/kibana/issues/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>
2024-06-02 16:50:33 +03:00
..
elements [Canvas] PieVis integration to Canvas. (#124325) 2022-08-15 15:03:55 +03:00
functions [i18n][system upgrade] Upgrade Intl Packages from v2 to v6 (#179506) 2024-06-02 16:50:33 +03:00
templates [eslint] fix and skip violations for cross-boundary imports (#136911) 2022-07-29 13:57:55 -05:00
capabilities.ts
constants.ts [Data] Add updated essql expression function (#132332) 2022-05-26 21:29:09 +02:00
errors.ts [i18n][system upgrade] Upgrade Intl Packages from v2 to v6 (#179506) 2024-06-02 16:50:33 +03:00
expression_types.ts
index.ts
lib.ts
README.md
renderers.ts
shortcuts.ts
tags.ts
transitions.ts
ui.ts [Canvas] Use data views service (#139610) 2022-10-24 12:33:36 -07:00
units.ts

Canvas and Internationalization (i18n)

Creating i18n strings in Kibana requires use of the @kbn/i18n library. The following outlines the strategy for localizing strings in Canvas

Why i18n Dictionaries

In Canvas, we prefer to use "dictionaries" of i18n strings over including translation inline. There are a number of reasons for this.

API Signature is Lengthy

A call to localize a string can look something like this:

i18n.translate('xpack.canvas.functions.alterColumn.args.columnHelpText', {
  defaultMessage: 'The name of the column to alter.',
}),

But it can also look something like this:

i18n.translate('xpack.canvas.functions.alterColumnHelpText', {
  defaultMessage:
    'Converts between core types, including {list}, and {end}, and rename columns. ' +
    'See also {mapColumnFn} and {staticColumnFn}.',
  values: {
    list: Object.values(DATATABLE_COLUMN_TYPES)
      .slice(0, -1)
      .map(type => `\`${type}\``)
      .join(', '),
    end: Object.values(DATATABLE_COLUMN_TYPES).slice(-1)[0],
    mapColumnFn: '`mapColumn`',
    staticColumnFn: '`staticColumn`',
  },
});

In either case, including all of this code inline, where the string is ultimately utilized, makes the code look very uneven, or even complicated. By externalizing the construction of localized strings, we can reduce both of these examples:

import { FunctionStrings } from './some/i18n/dictionary';
const { AlterColumn: strings } = FunctionStrings;

const help = strings.getColumnHelpText();
const moreHelp = strings.getAlterColumnHelpText();

Reducing Duplication, Auditing

By externalizing our strings into these functional dictionaries, we also make identifying duplicate strings easier... thus removing workload from translation teams. We can also deprecate functions. And Since they're written in Typescript, finding usage is easier, so we can easily remove them if a string is no longer used.

It will also make writing more advanced auditing tools easier.

Creating i18n Dictionaries

There are some Best Practices™️ to follow when localizing Canvas strings:

  • Create dictionaries in /canvas/i18n.
    • Organize first by the top-level subject or directory, (e.g. functions, renderers, components, etc).
  • Don't create too many files. Prefer to eventually split up a dictionary rather than start with many small ones.
    • Let's avoid ten files with two strings apiece, for example.
  • Create functions that produce a string, rather than properties of strings.
    • Prefer getSomeString({...values}) => i18n.translate(...);.
    • Avoid someString: i18n.translate(...);.
    • Standardizes the practice, also allows for passing dynamic values to the localized string in the future.
    • Exception to this is the dictionary for Canvas Functions, which use a more complex dictionary, influenced by data/interpreter.

Constants

In some cases, there are proper nouns or other words that should not be translated, (e.g. 'Canvas', 'JavaScript', 'momentJS', etc). We've created /canvas/i18n/constants.ts to collect these words. Use and add to these constants as necessary.