` tag),
+- ToggleSwitch and etc.
There is one more complex case, when we have to divide a single expression into different labels.
For example the message before translation looks like:
- ```js
+ ```html
The following deprecated languages are in use: {deprecatedLangsInUse.join(', ')}. Support for these languages will be removed in the next major version of Kibana and Elasticsearch. Convert your scripted fields to Painless to avoid any problems.
@@ -78,10 +80,33 @@ In case when `indicesLength` has value 1, the result string will be "`1 index`".
## Best practices
+### Usage of appropriate component
+
+#### In ReactJS
+
+- You should use `` most of the time.
+- In case when the string is expected (`aria-label`, `placeholder`), use `props.intl.formatmessage()` (where `intl` is passed to `props` by `injectI18n` HOC).
+- In case if none of the above can not be applied (e.g. it's needed to translate any code that doesn't have access to the component props), you can call JS function `i18n.translate()` from `@kbn/i18n` package.
+
+#### In AngularJS
+
+- Use `i18n` service in controllers, directives, services by injected it.
+- Use `i18nId` directive in template.
+- Use `i18n` filter in template for attribute translation.
+- In case if none of the above can not be applied, you can call JS function `i18n.translate()` from `@kbn/i18n` package.
+
+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 `@kbn/i18n` package.
### Naming convention
The message ids chosen for message keys should always be descriptive of the string, and its role in the interface (button label, title, etc.). Think of them as long variable names. When you have to change a message id, adding a progressive number to the existing key should always be used as a last resort.
+Here's a rule of id maning:
+
+`{plugin}.{area}.[{sub-area}].{element}`
- Message id should start with namespace that identifies a functional area of the app (`common.ui` or `common.server`) or a plugin (`kbn`, `vega`, etc.).
@@ -146,7 +171,6 @@ The message ids chosen for message keys should always be descriptive of the stri
/>
```
-
### Defining type for message
Each message id should end with a type of the message.
@@ -251,6 +275,82 @@ For example:
/>
```
+### Variety of `values`
+
+- Variables
+
+ ```html
+
+ ```
+
+ ```html
+
+ ```
+
+- Labels and variables in tag
+
+ ```html
+
+ ```
+
+ -----------------------------------------------------------
+ **BUT** we can not use tags that should be compiled:
+
+ ```html
+
+ ```
+
+ To void injections vulnerability, `i18nId` directive doesn't compile its values.
+
+ -----------------------------------------------------------
+
+ ```html
+ {characterList} }}
+ />
+ ```
+
+ ```html
+
+
+
+ ),
+ }}
+ />
+ ```
+
+- Non-translatable text such as property name.
+
+ ```html
+
+ ```
### Text with plurals
@@ -258,7 +358,7 @@ The numeric input is mapped to a plural category, some subset of "zero", "one",
Here is an example of message translation depending on a plural category:
-```js
+```html
@@ -279,8 +379,6 @@ Splitting sentences into several keys often inadvertently presumes a grammar, a
If this group of sentences is separated it’s possible that the context of the `'it'` in `'close it'` will be lost.
-
-
### Unit tests
Testing React component that uses the `injectI18n` higher-order component is more complicated because `injectI18n()` creates a wrapper component around the original component.
@@ -333,3 +431,26 @@ it('should render normally', async () => {
});
// ...
```
+
+## Development steps
+
+1. Localize label with the suitable i18n component.
+
+2. Make sure that UI still looks correct and is functioning properly (e.g. click handler is processed, checkbox is checked/unchecked, etc.).
+
+3. Check functionality of an element (button is clicked, checkbox is checked/unchecked, etc.).
+
+4. Run i18n validation tool and skim through created `en.json`:
+ ```js
+ node scripts/i18n_check --output ./
+ ```
+
+5. Run linters and type checker as you normally do.
+
+6. Run tests.
+
+7. Run Kibana with enabled pseudo-locale (either pass `--i18n.locale=en-xa` as a command-line argument or add it to the `kibana.yml`) and observe the text you've just localized.
+
+ If you did everything correctly, it should turn into something like this `Ĥéļļļô ŴŴôŕļļð!` assuming your text was `Hello World!`.
+
+8. Check that CI is green.
diff --git a/packages/kbn-i18n/README.md b/packages/kbn-i18n/README.md
index e8500aef407f..fa4b80d09e4b 100644
--- a/packages/kbn-i18n/README.md
+++ b/packages/kbn-i18n/README.md
@@ -1,14 +1,14 @@
# I18n
-Kibana relies on several UI frameworks (React and Angular) and
+Kibana relies on several UI frameworks (ReactJS and AngularJS) and
requires localization in different environments (browser and NodeJS).
Internationalization engine is framework agnostic and consumable in
-all parts of Kibana (React, Angular and NodeJS). In order to simplify
+all parts of Kibana (ReactJS, AngularJS 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 Angular. [React-intl](https://github.com/yahoo/react-intl)
+components for AngularJS. [React-intl](https://github.com/yahoo/react-intl)
is built around [intl-messageformat](https://github.com/yahoo/intl-messageformat),
-so both React and Angular frameworks use the same engine and the same
+so both React and AngularJS frameworks use the same engine and the same
message syntax.
## Localization files
@@ -42,7 +42,7 @@ Using comments can help to understand which section of the application
the localization key is used for. Also `namespaces`
are used in order to simplify message location search. For example, if
we are going to translate the title of `/management/sections/objects/_objects.html`
-file, we should use message path like this: `'MANAGEMENT.OBJECTS.TITLE'`.
+file, we should use message path like this: `'management.objects.objectsTitle'`.
Each Kibana plugin has a separate folder with translation files located at
```
@@ -199,6 +199,20 @@ export const HELLO_WORLD = i18n.translate('hello.wonderful.world', {
}),
```
+One more example with a parameter:
+
+```js
+import { i18n } from '@kbn/i18n';
+
+export function getGreetingMessage(userName) {
+ return i18n.translate('hello.wonderful.world', {
+ defaultMessage: 'Greetings, {name}!',
+ values: { name: userName },
+ context: 'This is greeting message for main screen.'
+ });
+}
+```
+
We're also able to use all methods exposed by the i18n engine
(see [I18n engine](#i18n-engine) section above for more details).
@@ -273,8 +287,21 @@ Optionally we can pass `description` prop into `FormattedMessage` component.
This prop is optional context comment that will be extracted by i18n tools
and added as a comment next to translation message at `defaultMessages.json`
+In case when ReactJS component is rendered with the help of `reactDirective` AngularJS service, it's necessary to use React HOC `injectI18nProvider` to pass `intl` object to `FormattedMessage` component via context.
-#### Attributes translation in React
+```js
+import { injectI18nProvider } from '@kbn/i18n/react';
+import { Header } from './components/header';
+
+module.directive('headerGlobalNav', (reactDirective) => {
+ return reactDirective(injectI18nProvider(Header));
+});
+
+```
+
+**NOTE:** To minimize the chance of having multiple `I18nProvider` components in the React tree, try to use `injectI18nProvider` or `I18nProvider` only to wrap the topmost component that you render, e.g. the one that's passed to `reactDirective` or `ReactDOM.render`.
+
+### Attributes translation in React
React wrapper provides an ability to inject the imperative formatting API into a React component via its props using `injectI18n` Higher-Order Component. This should be used when your React component needs to format data to a string value where a React element is not suitable; e.g., a `title` or `aria` attribute. In order to use it you should wrap your component with `injectI18n` Higher-Order Component. The formatting API will be provided to the wrapped component via `props.intl`.
@@ -324,7 +351,7 @@ class MyComponentContent extends React.Component {
@@ -335,9 +362,9 @@ class MyComponentContent extends React.Component {
export const MyComponent = injectI18n(MyComponentContent);
```
-## Angular
+## AngularJS
-Angular wrapper has 4 entities: translation `provider`, `service`, `directive`
+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.
@@ -414,12 +441,13 @@ loaded automatically. After that we can use i18n directive in Angular templates:
>
```
-In order to translate attributes in Angular we should use `i18nFilter`:
+In order to translate attributes in AngularJS we should use `i18nFilter`:
```html
```