@m6web/react-i18n
Advanced tools
Comparing version 1.9.2 to 2.0.0-alpha.0
@@ -10,6 +10,7 @@ import React from 'react'; | ||
children = _ref.children, | ||
errorCallback = _ref.errorCallback; | ||
errorCallback = _ref.errorCallback, | ||
parseHTML = _ref.parseHTML; | ||
return React.createElement( | ||
Context.Provider, | ||
{ value: translate(lang, i18nNames, errorCallback) }, | ||
{ value: translate(lang, i18nNames, errorCallback, parseHTML) }, | ||
children | ||
@@ -22,4 +23,5 @@ ); | ||
lang: PropTypes.object.isRequired, | ||
parseHTML: PropTypes.bool, | ||
i18nNames: PropTypes.object, | ||
errorCallback: PropTypes.func | ||
}; |
@@ -7,7 +7,4 @@ import _has from 'lodash-es/has'; | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
import React from 'react'; | ||
import { sprintf } from 'sprintf-js'; | ||
import { interpolateHTMLTags } from './html.utils'; | ||
@@ -52,89 +49,6 @@ var pluralizeFunctions = { | ||
// find tags like : <Something>with content inside</Something> | ||
var JSX_TAG_WITH_CONTENT_REGEX = /(.*?)<([A-Z]\w+)>(.*)<\/\2+>(.*)/; | ||
// find tags like : <Something /> | ||
var SHORT_JSX_TAG_REGEX = /(.*?)<([A-Z]\w+) ?\/>(.*)/; | ||
var parseJSX = function parseJSX(content) { | ||
var regexResultTagWithContent = JSX_TAG_WITH_CONTENT_REGEX.exec(content); | ||
if (regexResultTagWithContent) { | ||
return { | ||
beforeTagContent: regexResultTagWithContent[1], | ||
componentTag: regexResultTagWithContent[2], | ||
insideTagContent: regexResultTagWithContent[3], | ||
afterTagContent: regexResultTagWithContent[4] | ||
}; | ||
} | ||
var regexResultShortTag = SHORT_JSX_TAG_REGEX.exec(content); | ||
if (regexResultShortTag) { | ||
return { | ||
beforeTagContent: regexResultShortTag[1], | ||
componentTag: regexResultShortTag[2], | ||
afterTagContent: regexResultShortTag[3] | ||
}; | ||
} | ||
return null; | ||
}; | ||
var createComponentInstance = function createComponentInstance(component, children) { | ||
if (!component) { | ||
return null; | ||
} | ||
return React.createElement.apply(React, [component, {}].concat(_toConsumableArray(Array.isArray(children) ? children : [children]))); | ||
}; | ||
var interpolateJSXInsideTranslation = function interpolateJSXInsideTranslation(translation, renderers) { | ||
var parsingResult = parseJSX(translation); | ||
if (!parsingResult) { | ||
return translation; | ||
} | ||
var beforeTagContent = parsingResult.beforeTagContent, | ||
componentTag = parsingResult.componentTag, | ||
insideTagContent = parsingResult.insideTagContent, | ||
afterTagContent = parsingResult.afterTagContent; | ||
var translationChildren = []; | ||
var interpolateAndAddToChildren = function interpolateAndAddToChildren(content) { | ||
var interpolatedContent = interpolateJSXInsideTranslation(content, renderers); | ||
if (typeof interpolatedContent === 'string') { | ||
translationChildren.push(interpolatedContent); | ||
} | ||
if (Array.isArray(interpolatedContent)) { | ||
translationChildren.push.apply(translationChildren, _toConsumableArray(interpolatedContent)); | ||
} | ||
}; | ||
if (beforeTagContent) { | ||
interpolateAndAddToChildren(beforeTagContent); | ||
} | ||
if (componentTag) { | ||
var interpolatedChildren = insideTagContent ? interpolateJSXInsideTranslation(insideTagContent, renderers) : null; | ||
var rendererToUse = renderers[componentTag]; | ||
var componentInstance = createComponentInstance(rendererToUse, interpolatedChildren); | ||
if (componentInstance) { | ||
translationChildren.push(componentInstance); | ||
} else { | ||
// eslint-disable-next-line no-console | ||
console.warn('No renderer provided for component "' + componentTag + '"'); | ||
} | ||
} | ||
if (afterTagContent) { | ||
interpolateAndAddToChildren(afterTagContent); | ||
} | ||
return translationChildren; | ||
}; | ||
export var translate = function translate(lang) { | ||
var i18nNames = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var errorCallback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _noop; | ||
var parseHTML = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; | ||
@@ -164,8 +78,8 @@ var pluralize = pluralizeFunctions[_get(lang, '_i18n.lang')] || pluralizeFunctions.fr; | ||
var translatedResult = sprintf(translation, _extends({}, data, i18nNames, { number: number })); | ||
if (renderers) { | ||
var JSXTranslated = interpolateJSXInsideTranslation(translatedResult, renderers); | ||
return createComponentInstance(React.Fragment, JSXTranslated); | ||
} | ||
if (!parseHTML) return translatedResult; | ||
var htmlTags = interpolateHTMLTags(translatedResult, renderers); | ||
if (htmlTags.length > 1) return htmlTags; | ||
return translatedResult; | ||
@@ -172,0 +86,0 @@ }; |
@@ -26,6 +26,7 @@ 'use strict'; | ||
children = _ref.children, | ||
errorCallback = _ref.errorCallback; | ||
errorCallback = _ref.errorCallback, | ||
parseHTML = _ref.parseHTML; | ||
return _react2.default.createElement( | ||
_i18n2.Context.Provider, | ||
{ value: (0, _i18n.translate)(lang, i18nNames, errorCallback) }, | ||
{ value: (0, _i18n.translate)(lang, i18nNames, errorCallback, parseHTML) }, | ||
children | ||
@@ -38,4 +39,5 @@ ); | ||
lang: _propTypes2.default.object.isRequired, | ||
parseHTML: _propTypes2.default.bool, | ||
i18nNames: _propTypes2.default.object, | ||
errorCallback: _propTypes2.default.func | ||
}; |
@@ -10,6 +10,2 @@ 'use strict'; | ||
var _react = require('react'); | ||
var _react2 = _interopRequireDefault(_react); | ||
var _lodash = require('lodash'); | ||
@@ -21,6 +17,6 @@ | ||
var _html = require('./html.utils'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
var pluralizeFunctions = { | ||
@@ -64,89 +60,6 @@ en: function en(number) { | ||
// find tags like : <Something>with content inside</Something> | ||
var JSX_TAG_WITH_CONTENT_REGEX = /(.*?)<([A-Z]\w+)>(.*)<\/\2+>(.*)/; | ||
// find tags like : <Something /> | ||
var SHORT_JSX_TAG_REGEX = /(.*?)<([A-Z]\w+) ?\/>(.*)/; | ||
var parseJSX = function parseJSX(content) { | ||
var regexResultTagWithContent = JSX_TAG_WITH_CONTENT_REGEX.exec(content); | ||
if (regexResultTagWithContent) { | ||
return { | ||
beforeTagContent: regexResultTagWithContent[1], | ||
componentTag: regexResultTagWithContent[2], | ||
insideTagContent: regexResultTagWithContent[3], | ||
afterTagContent: regexResultTagWithContent[4] | ||
}; | ||
} | ||
var regexResultShortTag = SHORT_JSX_TAG_REGEX.exec(content); | ||
if (regexResultShortTag) { | ||
return { | ||
beforeTagContent: regexResultShortTag[1], | ||
componentTag: regexResultShortTag[2], | ||
afterTagContent: regexResultShortTag[3] | ||
}; | ||
} | ||
return null; | ||
}; | ||
var createComponentInstance = function createComponentInstance(component, children) { | ||
if (!component) { | ||
return null; | ||
} | ||
return _react2.default.createElement.apply(_react2.default, [component, {}].concat(_toConsumableArray(Array.isArray(children) ? children : [children]))); | ||
}; | ||
var interpolateJSXInsideTranslation = function interpolateJSXInsideTranslation(translation, renderers) { | ||
var parsingResult = parseJSX(translation); | ||
if (!parsingResult) { | ||
return translation; | ||
} | ||
var beforeTagContent = parsingResult.beforeTagContent, | ||
componentTag = parsingResult.componentTag, | ||
insideTagContent = parsingResult.insideTagContent, | ||
afterTagContent = parsingResult.afterTagContent; | ||
var translationChildren = []; | ||
var interpolateAndAddToChildren = function interpolateAndAddToChildren(content) { | ||
var interpolatedContent = interpolateJSXInsideTranslation(content, renderers); | ||
if (typeof interpolatedContent === 'string') { | ||
translationChildren.push(interpolatedContent); | ||
} | ||
if (Array.isArray(interpolatedContent)) { | ||
translationChildren.push.apply(translationChildren, _toConsumableArray(interpolatedContent)); | ||
} | ||
}; | ||
if (beforeTagContent) { | ||
interpolateAndAddToChildren(beforeTagContent); | ||
} | ||
if (componentTag) { | ||
var interpolatedChildren = insideTagContent ? interpolateJSXInsideTranslation(insideTagContent, renderers) : null; | ||
var rendererToUse = renderers[componentTag]; | ||
var componentInstance = createComponentInstance(rendererToUse, interpolatedChildren); | ||
if (componentInstance) { | ||
translationChildren.push(componentInstance); | ||
} else { | ||
// eslint-disable-next-line no-console | ||
console.warn('No renderer provided for component "' + componentTag + '"'); | ||
} | ||
} | ||
if (afterTagContent) { | ||
interpolateAndAddToChildren(afterTagContent); | ||
} | ||
return translationChildren; | ||
}; | ||
var translate = exports.translate = function translate(lang) { | ||
var i18nNames = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var errorCallback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _lodash2.default.noop; | ||
var parseHTML = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; | ||
@@ -176,8 +89,8 @@ var pluralize = pluralizeFunctions[_lodash2.default.get(lang, '_i18n.lang')] || pluralizeFunctions.fr; | ||
var translatedResult = (0, _sprintfJs.sprintf)(translation, _extends({}, data, i18nNames, { number: number })); | ||
if (renderers) { | ||
var JSXTranslated = interpolateJSXInsideTranslation(translatedResult, renderers); | ||
return createComponentInstance(_react2.default.Fragment, JSXTranslated); | ||
} | ||
if (!parseHTML) return translatedResult; | ||
var htmlTags = (0, _html.interpolateHTMLTags)(translatedResult, renderers); | ||
if (htmlTags.length > 1) return htmlTags; | ||
return translatedResult; | ||
@@ -184,0 +97,0 @@ }; |
{ | ||
"name": "@m6web/react-i18n", | ||
"version": "1.9.2", | ||
"version": "2.0.0-alpha.0", | ||
"description": "Provider and utils for translation in a react app", | ||
@@ -54,4 +54,3 @@ "main": "lib/index.js", | ||
"release": "yarn lint && yarn test && yarn build && yarn version" | ||
}, | ||
"gitHead": "d4af2194bd9db341492239d0375e3cf4ab0e5738" | ||
} | ||
} |
141
README.md
@@ -51,3 +51,3 @@ # @m6web/react-i18n | ||
const Root = () => ( | ||
<I18nProvider lang={translations} i18nNames={i18nNames} errorCallback={errorCallback}> | ||
<I18nProvider lang={translations} i18nNames={i18nNames} errorCallback={errorCallback} parseHTML> | ||
<App /> | ||
@@ -60,2 +60,11 @@ </I18nProvider> | ||
### i18n Provider | ||
This component will provide the translation function to following components via the React.Context api. | ||
* **lang**: translation dictionary | ||
* **i18nNames**: static translation values for interpolation | ||
* **errorCallback**: callback triggered when an error happens during the execution of the translation function | ||
* **parseHTML**: activates parsing of HTML inside translation | ||
* **children**: your App main component | ||
### i18n String component | ||
@@ -72,3 +81,3 @@ | ||
export default const MyComponent = ({ nbExample, t }) => { | ||
export const MyComponent = ({ nbExample, t }) => { | ||
return ( | ||
@@ -102,3 +111,3 @@ <div class="foo"> | ||
export default const MyComponent = ({ nbExample, t }) => { | ||
export const MyComponent = ({ nbExample, t }) => { | ||
return ( | ||
@@ -223,4 +232,114 @@ <div class="foo"> | ||
### JSX Interpolation | ||
### 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 | ||
```json | ||
{ | ||
"foo": { | ||
"bar": "<a href=\"/page-%(number)s\">To page %(number)s</a>" | ||
} | ||
} | ||
``` | ||
- code | ||
```jsx | ||
import React from 'react'; | ||
import { useTranslate } from './useTranslate'; | ||
export const MyComponent = () => { | ||
const t = useTranslate(); | ||
return ( | ||
<div class="foo"> | ||
<p>{t('foo.bar', { number: 2 })}</p> | ||
</div> | ||
); | ||
} | ||
``` | ||
- result | ||
```jsx harmony | ||
<div> | ||
<p> | ||
<a href="/page-2">To page 2</a> | ||
</p> | ||
</div> | ||
``` | ||
#### excluded elements | ||
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 | ||
```js | ||
{ | ||
foo: { | ||
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>' | ||
} | ||
}; | ||
``` | ||
- result | ||
```jsx harmony | ||
<div> | ||
<h1 | ||
key="h1-0" | ||
> | ||
Test | ||
</h1> | ||
<p | ||
key="p-1" | ||
> | ||
This is not what we wanna do with this lib but we need to ensure it works anyway | ||
</p> | ||
<ul | ||
key="ul-2" | ||
> | ||
<li | ||
key="li-0" | ||
> | ||
simple link to | ||
<a | ||
href="https://github.com/M6Web/i18n-tools" | ||
key="a-1" | ||
target="_blank" | ||
> | ||
the package | ||
</a> | ||
. | ||
</li> | ||
<li | ||
key="li-1" | ||
> | ||
a disabled | ||
<button | ||
disabled={true} | ||
key="button-1" | ||
> | ||
button | ||
</button> | ||
</li> | ||
<li | ||
key="li-2" | ||
> | ||
and an auto closing br | ||
<br | ||
key="br-1" | ||
/> | ||
</li> | ||
</ul> | ||
</div> | ||
``` | ||
#### JSX Interpolation | ||
It is possible to interpolate JSX components inside translation, to do so you have to give `renderers` parameter or props. | ||
@@ -231,6 +350,7 @@ For example if you have in your translation : `foo <LinkToHome>bar</LinkToHome>` you should have a `LinkToHome` renderer. | ||
import React from 'react'; | ||
import { Link } from 'react-router-dom'; | ||
import { useTranslate } from '@m6web/react-i18n'; | ||
const renderers = { | ||
LinkToHome: ({ children }) => <a href="/">{children}</a>, | ||
LinkToHome: ({ children }) => <Link to="home">{children}</Link>, | ||
}; | ||
@@ -251,11 +371,4 @@ | ||
For the moment only the children props are used by the renderer. | ||
Attributes are also supported. | ||
```jsx harmony | ||
// Do | ||
<Link>Home</Link> | ||
<Link /> or <Link/> | ||
// Don't | ||
<Link href="/home">Home</Link> | ||
``` | ||
:warning: If the translation contains an unknown tag, the translation will be display without HTML parsing. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
47324
21
800
368
2