Skip to content
This repository was archived by the owner on Jan 20, 2026. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions packages/react-i18n/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,12 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline

**Note:** Version bump only for package @m6web/react-i18n





## [1.9.1](https://github.com/M6Web/i18n-tools/compare/@m6web/react-i18n@1.9.0...@m6web/react-i18n@1.9.1) (2021-03-09)

**Note:** Version bump only for package @m6web/react-i18n





# [1.9.0](https://github.com/M6Web/i18n-tools/compare/@m6web/react-i18n@1.9.0...@m6web/react-i18n@1.9.0) (2021-03-08)


### Bug Fixes

* **build:** fix build to make the lib importable ([97e59c5](https://github.com/M6Web/i18n-tools/commit/97e59c5232444c1ed46d07e676e8032a280e3209))
Expand All @@ -30,7 +21,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
* **react-i18n:** exports hook in index.js ([#18](https://github.com/M6Web/i18n-tools/issues/18)) ([c7c3fb8](https://github.com/M6Web/i18n-tools/commit/c7c3fb8434da037a43e39c4cbc0b061cd1fd1e15))
* **react-i18n:** remove unused contextType in component ([41d99e1](https://github.com/M6Web/i18n-tools/commit/41d99e17aa7d9692c795af2d373a258a54388178))


### Features

* **react-i18n:** add errorCallback for missing translations ([#54](https://github.com/M6Web/i18n-tools/issues/54)) ([56fac7d](https://github.com/M6Web/i18n-tools/commit/56fac7da38845b8aa4df4d20242b3db411607c9a))
Expand Down
36 changes: 24 additions & 12 deletions packages/react-i18n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
![GitHub last commit](https://img.shields.io/github/last-commit/M6Web/i18n-tools)
![NPM](https://img.shields.io/npm/l/@m6web/react-i18n)


This library brings internationalisation through a set of react components.

The translation function is in **[i18n.utils.js](./src/utils/i18n.utils.js)** file.
Expand Down Expand Up @@ -57,6 +56,7 @@ const Root = () => (
## Use translation components

### i18n Provider

This component will provide the translation function to following components via the React.Context api.

* **lang**: translation dictionary
Expand Down Expand Up @@ -226,20 +226,25 @@ The variable used in translation template string has to be `%(number)d`, and is
To use general form, you need to set 4th parameter of the translate function to `true`

### HTML Interpolation

Basic html tags are automatically interpolated in translation if the syntax is correct (opening tag should be close within the translation).

Attributes are supported too.

Basic textual interpolations are proceeded first, and the HTML comes in a second time.
- translation

* translation

```json
{
"foo": {
"bar": "<a href=\"/page-%(number)s\">To page %(number)s</a>"
}
}
```
- code

* code

```jsx
import React from 'react';
import { useTranslate } from './useTranslate';
Expand All @@ -252,9 +257,11 @@ export const MyComponent = () => {
<p>{t('foo.bar', { number: 2 })}</p>
</div>
);
}
};
```
- result

* result

```jsx harmony
<div>
<p>
Expand All @@ -264,26 +271,31 @@ export const MyComponent = () => {
```

#### excluded elements
For now `script` and `iframe` elements are ignored with all their children in the HTML tree.

For now `script` and `iframe` elements are ignored with all their children in the HTML tree.

#### keys

In case of arrays of component, keys will be automatically generated to please React.
- translation

* translation

```js
{
foo: {
bar:
'<h1>Test</h1>' +
bar: '<h1>Test</h1>' +
'<p>This is not what we wanna do with this lib but we need to ensure it works anyway</p>' +
'<ul>' +
'<li>simple link to <a href="https://github.com/M6Web/i18n-tools" target="_blank">the package</a>.</li>' +
'<li>a disabled <button disabled>button</button></li>' +
'<li>and an auto closing br <br /></li>' +
'</ul>'
'</ul>';
}
};
}
```
- result

* result

```jsx harmony
<div>
<h1
Expand Down
2 changes: 1 addition & 1 deletion packages/react-i18n/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@m6web/react-i18n",
"version": "2.0.0-alpha.0",
"version": "2.0.0-alpha.1",
"description": "Provider and utils for translation in a react app",
"main": "lib/index.js",
"module": "es/index.js",
Expand Down
15 changes: 11 additions & 4 deletions packages/react-i18n/src/components/i18n.provider.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import React from 'react';
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { translate } from '../utils/i18n.utils';
import { Context } from '../context/i18n.context';

export const I18nProvider = ({ lang, i18nNames, children, errorCallback, parseHTML }) => (
<Context.Provider value={translate(lang, i18nNames, errorCallback, parseHTML)}>{children}</Context.Provider>
);
export const I18nProvider = ({ lang, i18nNames, children, errorCallback, parseHTML }) => {
const t = useMemo(() => translate(lang, { i18nNames, errorCallback, parseHTML }), [
lang,
i18nNames,
errorCallback,
parseHTML,
]);

return <Context.Provider value={t}>{children}</Context.Provider>;
};

I18nProvider.propTypes = {
children: PropTypes.element.isRequired,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Context } from '../context/i18n.context';

export const HtmlTrans = ({ i18nKey, data, number, general, element: Element, renderers, ...props }) => (
<Context.Consumer>
{t => <Element {...props} dangerouslySetInnerHTML={{ __html: t(i18nKey, data, number, general, renderers) }} />}
{t => <Element {...props} dangerouslySetInnerHTML={{ __html: t(i18nKey, { data, number, general, renderers }) }} />}
</Context.Consumer>
);

Expand Down
2 changes: 1 addition & 1 deletion packages/react-i18n/src/components/i18nString.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { Context } from './../context/i18n.context';

export const Trans = ({ i18nKey, data, number, general, renderers }) => (
<Context.Consumer>{t => t(i18nKey, data, number, general, renderers)}</Context.Consumer>
<Context.Consumer>{t => t(i18nKey, { data, number, general, renderers })}</Context.Consumer>
);

Trans.defaultProps = {
Expand Down
6 changes: 3 additions & 3 deletions packages/react-i18n/src/hooks/__tests__/useTranslate.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ describe('useTranslate', () => {

it('should wrap the translation function', () => {
const wrapTranslate = useTranslate();
wrapTranslate('key', { data: {}, number: 'number', general: 'general', renderers: 'renderers' });
wrapTranslate('key', { data: {}, number: 2, general: true, renderers: [] });

expect(useContext).toHaveBeenCalledWith(Context);
expect(t).toHaveBeenCalledWith('key', {}, 'number', 'general', 'renderers');
expect(t).toHaveBeenCalledWith('key', { data: {}, number: 2, general: true, renderers: [] });
});

it('should wrap the translation function 2', () => {
const wrapTranslate = useTranslate();
wrapTranslate('key');

expect(useContext).toHaveBeenCalledWith(Context);
expect(t).toHaveBeenCalledWith('key', undefined, undefined, undefined, undefined);
expect(t).toHaveBeenCalledWith('key');
});
});
3 changes: 2 additions & 1 deletion packages/react-i18n/src/hooks/useTranslate.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useContext } from 'react';
import { Context } from '../context/i18n.context';

const emptyObject = {};
export const useTranslate = () => {
const t = useContext(Context);

return (key, { data, number, general, renderers } = {}) => t(key, data, number, general, renderers);
return (key, data = emptyObject) => t(key, data);
};
34 changes: 17 additions & 17 deletions packages/react-i18n/src/utils/__tests__/i18n.utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ describe('i18n translate function', () => {
},
};
const renderers = { Bold };
const t = translate(lang, undefined, undefined, true);
const t = translate(lang, { parseHTML: true });

const result = t('foo.bar', undefined, undefined, false, renderers);
const result = t('foo.bar', { renderers });
const wrapper = mount(<div>{result}</div>);

expect(wrapper).toMatchSnapshot();
Expand All @@ -199,9 +199,9 @@ describe('i18n translate function', () => {
},
};
const renderers = { Bold, Italic };
const t = translate(lang, undefined, undefined, true);
const t = translate(lang, { parseHTML: true });

const result = t('foo.bar', undefined, undefined, false, renderers);
const result = t('foo.bar', { renderers });
const wrapper = mount(<div>{result}</div>);

expect(wrapper).toMatchSnapshot();
Expand All @@ -214,9 +214,9 @@ describe('i18n translate function', () => {
},
};
const renderers = { Bold, Italic };
const t = translate(lang, undefined, undefined, true);
const t = translate(lang, { parseHTML: true });

const result = t('foo.bar', undefined, undefined, false, renderers);
const result = t('foo.bar', { renderers });
const wrapper = mount(<div>{result}</div>);

expect(wrapper).toMatchSnapshot();
Expand All @@ -229,9 +229,9 @@ describe('i18n translate function', () => {
},
};
const renderers = { LineBreak };
const t = translate(lang, undefined, undefined, true);
const t = translate(lang, { parseHTML: true });

const result = t('foo.bar', undefined, undefined, false, renderers);
const result = t('foo.bar', { renderers });
const wrapper = mount(<div>{result}</div>);

expect(wrapper).toMatchSnapshot();
Expand All @@ -244,9 +244,9 @@ describe('i18n translate function', () => {
},
};
const renderers = { LineBreak, Bold, Italic };
const t = translate(lang, undefined, undefined, true);
const t = translate(lang, { parseHTML: true });

const result = t('foo.bar', undefined, undefined, false, renderers);
const result = t('foo.bar', { renderers });
const wrapper = mount(<div>{result}</div>);

expect(wrapper).toMatchSnapshot();
Expand All @@ -258,7 +258,7 @@ describe('i18n translate function', () => {
bar: 'Hello <Bold><Italic>Moto</Italic></Bold> !',
},
};
const t = translate(lang, undefined, undefined, true);
const t = translate(lang, { parseHTML: true });

expect(t('foo.bar', undefined, undefined, false, { Bold })).toMatchSnapshot();
expect(t('foo.bar', undefined, undefined, false, { Italic })).toMatchSnapshot();
Expand All @@ -271,9 +271,9 @@ describe('i18n translate function', () => {
},
};
const renderers = {};
const t = translate(lang, undefined, undefined, true);
const t = translate(lang, { parseHTML: true });

const result = t('foo.bar', undefined, undefined, false, renderers);
const result = t('foo.bar', { renderers });
const wrapper = mount(<div>{result}</div>);

expect(wrapper).toMatchSnapshot();
Expand All @@ -287,9 +287,9 @@ describe('i18n translate function', () => {
};

const renderers = { Bold, Italic };
const t = translate(lang, undefined, undefined, true);
const t = translate(lang, { parseHTML: true });

const result = t('foo.bar', undefined, undefined, undefined, renderers);
const result = t('foo.bar', { renderers });
const wrapper = mount(<div>{result}</div>);

expect(wrapper).toMatchSnapshot();
Expand All @@ -311,8 +311,8 @@ describe('i18n translate function', () => {
},
};

const t = translate(lang, undefined, undefined, true);
const wrapper = mount(<div>{t('foo.bar', undefined, undefined, undefined, {})}</div>);
const t = translate(lang, { parseHTML: true });
const wrapper = mount(<div>{t('foo.bar', {})}</div>);

expect(wrapper).toMatchSnapshot();
});
Expand Down
6 changes: 4 additions & 2 deletions packages/react-i18n/src/utils/i18n.utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ const pluralizeFunctions = {
},
};

export const translate = (lang, i18nNames = {}, errorCallback = _.noop, parseHTML = false) => {
const defaultConfig = { i18nNames: {}, errorCallback: _.noop, parseHTML: false };
export const translate = (lang, config = {}) => {
const { i18nNames, errorCallback, parseHTML } = { ...defaultConfig, ...config };
const pluralize = pluralizeFunctions[_.get(lang, '_i18n.lang')] || pluralizeFunctions.fr;

return (key, data = {}, number, general, renderers) => {
return (key, { data = {}, number, general, renderers } = {}) => {
let combineKey = key;
// Pluralize
if (typeof number !== 'undefined') {
Expand Down