diff --git a/packages/react-i18n/CHANGELOG.md b/packages/react-i18n/CHANGELOG.md index 24122e7..0b33cff 100644 --- a/packages/react-i18n/CHANGELOG.md +++ b/packages/react-i18n/CHANGELOG.md @@ -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)) @@ -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)) diff --git a/packages/react-i18n/README.md b/packages/react-i18n/README.md index 9cdbd81..1f335de 100644 --- a/packages/react-i18n/README.md +++ b/packages/react-i18n/README.md @@ -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. @@ -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 @@ -226,12 +226,15 @@ 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": { @@ -239,7 +242,9 @@ Basic textual interpolations are proceeded first, and the HTML comes in a second } } ``` -- code + +* code + ```jsx import React from 'react'; import { useTranslate } from './useTranslate'; @@ -252,9 +257,11 @@ export const MyComponent = () => {

{t('foo.bar', { number: 2 })}

); -} +}; ``` -- result + +* result + ```jsx harmony

@@ -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: - '

Test

' + + bar: '

Test

' + '

This is not what we wanna do with this lib but we need to ensure it works anyway

' + '' + ''; } -}; +} ``` -- result + +* result + ```jsx harmony

( - {children} -); +export const I18nProvider = ({ lang, i18nNames, children, errorCallback, parseHTML }) => { + const t = useMemo(() => translate(lang, { i18nNames, errorCallback, parseHTML }), [ + lang, + i18nNames, + errorCallback, + parseHTML, + ]); + + return {children}; +}; I18nProvider.propTypes = { children: PropTypes.element.isRequired, diff --git a/packages/react-i18n/src/components/i18nElement.component.js b/packages/react-i18n/src/components/i18nElement.component.js index bfba717..3d69cc6 100644 --- a/packages/react-i18n/src/components/i18nElement.component.js +++ b/packages/react-i18n/src/components/i18nElement.component.js @@ -4,7 +4,7 @@ import { Context } from '../context/i18n.context'; export const HtmlTrans = ({ i18nKey, data, number, general, element: Element, renderers, ...props }) => ( - {t => } + {t => } ); diff --git a/packages/react-i18n/src/components/i18nString.component.js b/packages/react-i18n/src/components/i18nString.component.js index 4563510..0971758 100644 --- a/packages/react-i18n/src/components/i18nString.component.js +++ b/packages/react-i18n/src/components/i18nString.component.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { Context } from './../context/i18n.context'; export const Trans = ({ i18nKey, data, number, general, renderers }) => ( - {t => t(i18nKey, data, number, general, renderers)} + {t => t(i18nKey, { data, number, general, renderers })} ); Trans.defaultProps = { diff --git a/packages/react-i18n/src/hooks/__tests__/useTranslate.spec.js b/packages/react-i18n/src/hooks/__tests__/useTranslate.spec.js index 35eb9b8..176346b 100644 --- a/packages/react-i18n/src/hooks/__tests__/useTranslate.spec.js +++ b/packages/react-i18n/src/hooks/__tests__/useTranslate.spec.js @@ -14,10 +14,10 @@ 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', () => { @@ -25,6 +25,6 @@ describe('useTranslate', () => { wrapTranslate('key'); expect(useContext).toHaveBeenCalledWith(Context); - expect(t).toHaveBeenCalledWith('key', undefined, undefined, undefined, undefined); + expect(t).toHaveBeenCalledWith('key'); }); }); diff --git a/packages/react-i18n/src/hooks/useTranslate.js b/packages/react-i18n/src/hooks/useTranslate.js index af54f32..a8c019f 100644 --- a/packages/react-i18n/src/hooks/useTranslate.js +++ b/packages/react-i18n/src/hooks/useTranslate.js @@ -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); }; diff --git a/packages/react-i18n/src/utils/__tests__/i18n.utils.spec.js b/packages/react-i18n/src/utils/__tests__/i18n.utils.spec.js index a81d1b4..7594f87 100644 --- a/packages/react-i18n/src/utils/__tests__/i18n.utils.spec.js +++ b/packages/react-i18n/src/utils/__tests__/i18n.utils.spec.js @@ -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(
{result}
); expect(wrapper).toMatchSnapshot(); @@ -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(
{result}
); expect(wrapper).toMatchSnapshot(); @@ -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(
{result}
); expect(wrapper).toMatchSnapshot(); @@ -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(
{result}
); expect(wrapper).toMatchSnapshot(); @@ -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(
{result}
); expect(wrapper).toMatchSnapshot(); @@ -258,7 +258,7 @@ describe('i18n translate function', () => { bar: 'Hello Moto !', }, }; - 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(); @@ -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(
{result}
); expect(wrapper).toMatchSnapshot(); @@ -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(
{result}
); expect(wrapper).toMatchSnapshot(); @@ -311,8 +311,8 @@ describe('i18n translate function', () => { }, }; - const t = translate(lang, undefined, undefined, true); - const wrapper = mount(
{t('foo.bar', undefined, undefined, undefined, {})}
); + const t = translate(lang, { parseHTML: true }); + const wrapper = mount(
{t('foo.bar', {})}
); expect(wrapper).toMatchSnapshot(); }); diff --git a/packages/react-i18n/src/utils/i18n.utils.js b/packages/react-i18n/src/utils/i18n.utils.js index f8c634d..d4a4bf4 100644 --- a/packages/react-i18n/src/utils/i18n.utils.js +++ b/packages/react-i18n/src/utils/i18n.utils.js @@ -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') {