Skip to content

Commit

Permalink
Modifying html tests to utilize jsx and fixing tests to include all p…
Browse files Browse the repository at this point in the history
…luginPaths

Signed-off-by: Zashary Maskus-Lavin <[email protected]>
  • Loading branch information
zashary committed Sep 25, 2023
1 parent 8221fea commit a418c79
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 182 deletions.
24 changes: 0 additions & 24 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -925,30 +925,6 @@ license.
The following developer guide rules are specific for working with the React framework.
#### Prefer reactDirective over react-component
When using `ngReact` to embed your react components inside Angular HTML, prefer the
`reactDirective` service over the `react-component` directive.
You can read more about these two ngReact methods [here](https://github.com/ngReact/ngReact#features).
Using `react-component` means adding a bunch of components into angular, while `reactDirective` keeps them isolated, and is also a more succinct syntax.
**Good:**
```html
<hello-component
fname="person.fname"
lname="person.lname"
watch-depth="reference"
></hello-component>
```
**Bad:**
```html
<react-component name="HelloComponent" props="person" watch-depth="reference" />
```
#### Name action functions and prop functions appropriately
Name action functions in the form of a strong verb and passed properties in the form of on<Subject><Change>. E.g:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -466,4 +466,4 @@
"node": ">=14.20.1 <19",
"yarn": "^1.22.10"
}
}
}
11 changes: 0 additions & 11 deletions packages/osd-i18n/GUIDELINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,6 @@ The long term plan is to rely on using `FormattedMessage` and `i18n.translate()`
Currently, we support the following ReactJS `i18n` tools, but they will be removed in future releases:
- Usage of `props.intl.formatmessage()` (where `intl` is passed to `props` by `injectI18n` HOC).

#### In AngularJS

The long term plan is to rely on using `i18n.translate()` by statically importing `i18n` from the `@osd/i18n` package. **Avoid using the `i18n` filter and the `i18n` service injected in controllers, directives, services.**

- Call JS function `i18n.translate()` from the `@osd/i18n` package.
- Use `i18nId` directive in template.

Currently, we support the following AngluarJS `i18n` tools, but they will be removed in future releases:
- Usage of `i18n` service in controllers, directives, services by injecting it.
- Usage of `i18n` filter in template for attribute translation. Note: Use one-time binding ("{{:: ... }}") in filters wherever it's possible to prevent unnecessary expression re-evaluation.

#### In JavaScript

- Use `i18n.translate()` in NodeJS or any other framework agnostic code, where `i18n` is the I18n engine from `@osd/i18n` package.
Expand Down
101 changes: 4 additions & 97 deletions packages/osd-i18n/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# I18n

OpenSearch Dashboards relies on several UI frameworks (ReactJS and AngularJS) and
OpenSearch Dashboards relies on UI frameworks (ReactJS) and
requires localization in different environments (browser and NodeJS).
Internationalization engine is framework agnostic and consumable in
all parts of OpenSearch Dashboards (ReactJS, AngularJS and NodeJS). In order to simplify
all parts of OpenSearch Dashboards (ReactJS and NodeJS). In order to simplify
internationalization in UI frameworks, the additional abstractions are
built around the I18n engine: `react-intl` for React and custom
components for AngularJS. [React-intl](https://github.com/yahoo/react-intl)
built around the I18n engine: `react-intl` for React. [React-intl](https://github.com/yahoo/react-intl)
is built around [intl-messageformat](https://github.com/yahoo/intl-messageformat),
so both React and AngularJS frameworks use the same engine and the same
so the React framework uses the same engine and the same
message syntax.

## Localization files
Expand Down Expand Up @@ -343,98 +342,6 @@ export const MyComponent = injectI18n(
);
```

## AngularJS

The long term plan is to rely on using `i18n.translate()` by statically importing `i18n` from the `@osd/i18n` package. **Avoid using the `i18n` filter and the `i18n` service injected in controllers, directives, services.**

AngularJS wrapper has 4 entities: translation `provider`, `service`, `directive`
and `filter`. Both the directive and the filter use the translation `service`
with i18n engine under the hood.

The translation `provider` is used for `service` configuration and
has the following methods:
- `addMessages(messages: Map<string, string>, [locale: string])` - provides a way to register
translations with the library
- `setLocale(locale: string)` - tells the library which language to use by given
language key
- `getLocale()` - returns the current locale
- `setDefaultLocale(locale: string)` - tells the library which language to fallback
when missing translations
- `getDefaultLocale()` - returns the default locale
- `setFormats(formats: object)` - supplies a set of options to the underlying formatter
- `getFormats()` - returns current formats
- `getRegisteredLocales()` - returns array of locales having translations
- `init(messages: Map<string, string>)` - initializes the engine

The translation `service` provides only one method:
- `i18n(id: string, { values: object, defaultMessage: string, description: string })`
translate message by id

The translation `filter` is used for attributes translation and has
the following syntax:
```
{{ ::'translationId' | i18n: { values: object, defaultMessage: string, description: string } }}
```

Where:
- `translationId` - translation id to be translated
- `values` - values to pass into translation
- `defaultMessage` - will be used unless translation was successful (the final
fallback in english, will be used for generating `en.json`)
- `description` - optional context comment that will be extracted by i18n tools
and added as a comment next to translation message at `defaultMessages.json`

The translation `directive` has the following syntax:
```html
<ANY
i18n-id="{string}"
i18n-default-message="{string}"
[i18n-values="{object}"]
[i18n-description="{string}"]
></ANY>
```

Where:
- `i18n-id` - translation id to be translated
- `i18n-default-message` - will be used unless translation was successful
- `i18n-values` - values to pass into translation
- `i18n-description` - optional context comment that will be extracted by i18n tools
and added as a comment next to translation message at `defaultMessages.json`

If HTML rendering in `i18n-values` is required then value key in `i18n-values` object
should have `html_` prefix. Otherwise the value will be inserted to the message without
HTML rendering.\
Example:
```html
<p
i18n-id="namespace.id"
i18n-default-message="Text with an emphasized {text}."
i18n-values="{
html_text: '<em>text</em>',
}"
></p>
```

Angular `I18n` module is placed into `autoload` module, so it will be
loaded automatically. After that we can use i18n directive in Angular templates:
```html
<span
i18n-id="welcome"
i18n-default-message="Hello!"
></span>
```

In order to translate attributes in AngularJS we should use `i18nFilter`:
```html
<input
type="text"
placeholder="{{ ::'osd.management.objects.searchAriaLabel' | i18n: {
defaultMessage: 'Search { title } Object',
values: { title }
} }}"
>
```

## I18n tools

In order to simplify localization process, some additional tools were implemented:
Expand Down
5 changes: 0 additions & 5 deletions packages/osd-i18n/angular/package.json

This file was deleted.

29 changes: 1 addition & 28 deletions src/dev/i18n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Description

The tool is used to extract default messages from all `*.{js, ts, jsx, tsx, html }` files in provided plugins directories to a JSON file.
The tool is used to extract default messages from all `*.{js, ts, jsx, tsx }` files in provided plugins directories to a JSON file.

It uses Babel to parse code and build an AST for each file or a single JS expression if whole file parsing is impossible. The tool is able to validate, extract and match IDs, default messages and descriptions only if they are defined statically and together, otherwise it will fail with detailed explanation. That means one can't define ID in one place and default message in another, or use function call to dynamically create default message etc.

Expand All @@ -18,33 +18,6 @@ The `defaultMessage` must contain ICU references to all keys in the `values` and

The `description` is optional, `values` is optional too unless `defaultMessage` references to it.

* **Angular (.html)**

* **Filter**

```
{{ ::'pluginNamespace.messageId' | i18n: {
defaultMessage: 'Default message string literal, {key}',
values: { key: 'value' },
description: 'Message context or description'
} }}
```
* Don't break `| i18n: {` with line breaks, and don't skip whitespaces around `i18n:`.
* `::` operator is optional. Omit it if you need data binding for the `values`.
* **Directive**
```html
<p
i18n-id="pluginNamespace.messageId"
i18n-default-message="Default message string literal, {key}. {emphasizedText}"
i18n-values="{ key: value, html_emphasizedText: htmlString }"
i18n-description="Message context or description"
></p>
```
* `html_` prefixes will be removed from `i18n-values` keys before validation.

* **React (.jsx, .tsx)**

Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

i18n('plugin_2.message-id', { defaultMessage: 'Message text' });
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

/* eslint-disable */
class Component extends PureComponent {
render() {
return (
<div data-transclude-slots>
<FormattedMessage
id="plugin_4.id_1"
defaultMessage="Message 4"
/>
</div>
);
}
}

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

17 changes: 10 additions & 7 deletions src/dev/i18n/extract_default_translations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const pluginsPaths = [
path.join(fixturesPath, 'test_plugin_2'),
path.join(fixturesPath, 'test_plugin_3'),
path.join(fixturesPath, 'test_plugin_3_additional_path'),
path.join(fixturesPath, 'test_plugin_4'),
];

const config = {
Expand All @@ -52,17 +53,19 @@ const config = {
'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3',
'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3_additional_path',
],
plugin_4: ['src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_4'],
},
exclude: [],
};

describe('dev/i18n/extract_default_translations', () => {
test('extracts messages from path to map', async () => {
const [pluginPath] = pluginsPaths;
const resultMap = new Map();
for (const pluginPath of pluginsPaths) {
const resultMap = new Map();

await extractMessagesFromPathToMap(pluginPath, resultMap, config, new ErrorReporter());
expect([...resultMap].sort()).toMatchSnapshot();
await extractMessagesFromPathToMap(pluginPath, resultMap, config, new ErrorReporter());
expect([...resultMap].sort()).toMatchSnapshot();
}
});

test('throws on id collision', async () => {
Expand All @@ -88,11 +91,11 @@ describe('dev/i18n/extract_default_translations', () => {
const id = 'plugin_3.message-id';
const filePath1 = path.resolve(
__dirname,
'__fixtures__/extract_default_translations/test_plugin_3/test_file.html'
'__fixtures__/extract_default_translations/test_plugin_3/test_file.jsx'
);
const filePath2 = path.resolve(
__dirname,
'__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.html'
'__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.jsx'
);
expect(() => validateMessageNamespace(id, filePath1, config.paths)).not.toThrow();
expect(() => validateMessageNamespace(id, filePath2, config.paths)).not.toThrow();
Expand All @@ -103,7 +106,7 @@ describe('dev/i18n/extract_default_translations', () => {
const id = 'wrong_plugin_namespace.message-id';
const filePath = path.resolve(
__dirname,
'__fixtures__/extract_default_translations/test_plugin_2/test_file.html'
'__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx'
);

expect(() => validateMessageNamespace(id, filePath, config.paths, { report })).not.toThrow();
Expand Down

0 comments on commit a418c79

Please sign in to comment.