# wix-i18n-config

This config encapsulates i18next initialization so it works out of the box with react apps.
It also solves versioning issues between i18next and react-i18next by exposing compatible versions.

Wrapper supports ICU messages format.

## Install

`yarn add @wix/wix-i18n-config`

## Usage


### Loading translations async

```js
import { initI18n, withTranslation, I18nextProvider, useTranslation } from '@wix/wix-i18n-config';

const App = withTranslation()(({ t }) => (
  <div>{t('foo', { baz: 'hello world' })}</div>
));

// or using hooks
const App = () => {
  const [t] = useTranslation();
  return <div>{t('foo', { baz: 'hello world' })}</div>
};

const i18n = initI18n({
  disableAutoInit: true,
  locale: 'en',
  asyncMessagesLoader: locale => import(`./path/to/locale/messages_${locale}.json`),
});

i18n.init(); // can also be awaited

ReactDOM.render(
  <I18nextProvider i18n={i18n}>
    <App/>
  </I18nextProvider>,
  container,
);
```

### Passing translations directly

Should be used when:
1. Your server already renders the message files with your initial HTML.;
2. You want to bundle all of the translation files.;

Example project with this setup can be found at [full stack project](https://github.com/wix/yoshi/tree/master/packages/create-yoshi-app/templates/fullstack/javascript).

```js
import { initI18n, withTranslation, I18nextProvider } from '@wix/wix-i18n-config';

const App = withTranslation()(({ t }) => (
  <div>{t('foo', { baz: 'hello world' })}</div>
));

const i18n = initI18n({
  disableAutoInit: true,
  locale: 'en',
  messages: {foo: 'Bar {baz}'},
});

i18n.init(); // can also be awaited

ReactDOM.render(
  <I18nextProvider i18n={i18n}>
    <App/>
  </I18nextProvider>,
  container,
);

```

## API
### `initI18n({ locale, messages?, asyncMessagesLoader? })`

Configures i18next instance with ICU message support and return the instance itself.
Messages can be passed directly in `messages` param or loaded async with `asyncMessagesLoader`.


Parameters:

| Name | Type | Description |
|------:|:-----|---------|
| `locale` | String **(required)**| locale of your messages, i.e. 'en'; |
| `messages` | Object | translations for the locale in format of '{key: 'message'}', usually it's the content of your 'messages_xx.json' |
| `asyncMessagesLoader` | Function | function that receives locale and return promise that resolves to messages. |
| `disableAutoInit` | Boolean | disables automatic fetching of i18n assets. |
| `useSuspense` | Boolean | when waiting for translation files, `i18next-react` triggers [Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html). in case you want to use Suspense, set this to `true` and wrap your translated components [like in this example](https://react.i18next.com/latest/using-with-hooks#using-the-withtranslation-hoc). default: `false`. This also triggers suspense if polyfill is loading. |
| `wait` | Boolean | Prevent rendering until react translation files and polyfills will be loaded. Useful if useSuspense is disabled. Make sure to pass it only for client-side rendering. |
| `parseErrorHandler` | Function |  Will be run when parser throws an error. Can return any string, which can be used as a fallback, in case of broken translation. If omitted, the default swallows the error and returns the unsubstituted string |
| `icuOptions` | Object |  pass options to i18next [ICU plugin](https://github.com/i18next/i18next-icu). Allows to set custom format styles for the supported formatters [link](https://github.com/formatjs/intl-messageformat#user-defined-formats)|

**[withTranslation](https://react.i18next.com/latest/withtranslation-hoc)**, **[I18nextProvider](https://react.i18next.com/latest/i18nextprovider)**, **[useTranslation](https://react.i18next.com/latest/usetranslation-hook)** and **[Trans](https://react.i18next.com/latest/trans-component)** are exported from [react-i18next](https://react.i18next.com). If your React version does not support hooks (16.8 and above), please use version ^3.


### `initI18nWithoutPolyfills({ locale, messages?, asyncMessagesLoader? })`
Same as `initI18n`, but without implicitly-loaded polyfills (for environments where polyfills are already preconfigured).

It accepts the same options except the one directly related to polyfills: `polyfillRelativeTime`.

### `initI18nWithoutICU({ locale, messages?, asyncMessagesLoader? })`
Same as `initI18n`, but without ICU logic.
Since it could be tricky to migrate some non-ICU translations to ICU, the library provides a way to disable ICU logic.

To exclude ICU-related libraries and polyfills from the bundle relying on tree-shaking, it's being exported as a separate namespace.

It accepts the same options except the ones directly related to ICU: `icuOptions`, `parseErrorHandler`, and `polyfillRelativeTime`.

## Changing language

`withTranslation` HOC injects the `i18n` object, which can be used to changes language and other configuration once the application is loaded.

##### Asynchronous strategy
Simply use the `i18n.changeLanguage()` method.

##### Synchronous strategy
Before calling the `i18n.changeLanguage()` method, resources should be added via the `i18n.addResources()` method. For example, before calling `i18n.changeLanguage('fr')`, we must call `i18n.addResources('fr', 'namespace', { key: 'value' })`.

WARNING:
if you do the language change, make sure `plural rules` (and `relative time format` if you need it) is loaded BEFORE you actually change the language:

```js
import { loadPluralRules, loadRelativeTimeFormat } from '@wix/wix-i18n-config/polyfills';

// only plural rules
loadPluralRules(newLocale).then(() => {
  i18n.changeLanguage();
});

// plural rules with relative date time format
Promise.all([
  loadPluralRules(newLocale),
  loadRelativeTimeFormat(newLocale),
]).then(() => {
  i18n.changeLanguage();
})

```

## Relative time formatting

If you need to format time relative to current time, use `createRtfFormatter` formatter.

```js
import { createRtfFormatter } from '@wix/wix-i18n-config';

const rtf = createRtfFormatter('en');
rtf(Date.now() - SECOND * 20); // '20 seconds ago'
rtf(Date.now() - SECOND * 59); // '59 seconds ago'
rtf(Date.now() - MINUTE * 6); // '6 minutes ago'
rtf(Date.now() - HOUR * 3); // '3 hours ago'


// floor the seconds
const rtf = createRtfFormatter('en', { floorSeconds: true });
rtf(Date.now() - SECOND * 20); // '1 second ago'
rtf(Date.now() - SECOND * 59); // '1 second ago'
rtf(Date.now() - SECOND * 61); // '1 minute ago'

// use extra short expression
const rtf = createRtfFormatter('en', {
  extraShortExpression: true,
}, {
  style: 'narrow' }
);
rtf(Date.now() - SECOND * 20); // '20s'
rtf(Date.now() - SECOND * 59); // '59s'
rtf(Date.now() - MINUTE * 6); // '6m'
rtf(Date.now() - HOUR * 3); // '3h'
```

## Migrating from `^5.0.0` to `^6.0.0`

Dropped all polyfills, since [min browsers that wix supports](https://support.wix.com/en/article/supported-browsers) have all the required features.

As a reasult following options are dropped:

* `polyfillRelativeTime`

## Migrating from `^4.0.0` to `^5.0.0`

You no longer need to load polyfills i browser by yourself, it's done automatically.

The following export was removed
```
- import { loadBrowserPolyfills } from '@wix/wix-i18n-config/polyfills';
```

Path to node polyfills was changed

```
- import { loadNodePolyfills } from '@wix/wix-i18n-config/polyfills';
+ import { loadNodePolyfills } from '@wix/wix-i18n-config/polyfills/node';
```

## Migrating from `^3.0.0` to `^4.0.0`

We are using [React with hooks](https://reactjs.org/docs/hooks-intro.html), and the latest versions of `i18next, react-i18next` as of version 4. If your React version is below 16.8, please use version 3.

## Migrating from `^2.0.0` to `^3.0.0`

The polyfill loaders are no longer exported through the main `index.js` file since it caused Webpack to create many irrelevant chunks even when the polyfills were not needed.

All the polyfill loaders are now available at `@wix/wix-i18n-config/polyfills`.

## Q & A:

Q: I want to migrate an existing project to use `wix-i18n-config`, is there something that I need to change with the translations?

A: You have to make sure that all of the formatting and interpolations are using the single curly brackets format, which is defined by ICU. For example, if you have a translation key with this value: `"Click this link: {{link}}"` you should manually transform it in all `messages_xx.json` files to look like this `"Click this link: {link}"`. Also note, that if the parameter that you're interpolating is a `number` or a `date` you should leverage the ICU formatting (e.g. `"Your apple count is: {appleCount, number}"`. You can find more formatting examples [here](https://apps.wix.com/translations-icu-examples-project).

---

Q: I want to remove the configuration boilerplate from the project's side and move to `wix-i18n-config`, but can't completely migrate to ICU format. Any options to disable ICU until I finish the migration?

A: Yes. You can use `initI18nWithoutICU` for the migration period.

----

Q: I'm migrating from the old version of those libs. Where is my `InjectedTranslatedProps` interface for React components?

A: Use `WithTranslation` instead, since `translate` HOC was renamed to `withTranslation`.

---

Q: My interpolation with numbers doesn’t work. What should I do?

A: Since new version of the lib uses ICU message format, make sure you are using *single curly brackets* for interpolation.

So instead of this:

`"some.key": "Knock {{count}} times"`

you could do this:

`"some.key": "Knock {count} times"`


## Further Reading

- [ICU Message Syntax](https://formatjs.io/docs/core-concepts/icu-syntax)
- [Translation @ FED Handbook](https://github.com/wix-private/fed-handbook/blob/master/TRANSLATION.md)
