From f29202daff4dddf05a6c94d06d7a09ec0235aa9c Mon Sep 17 00:00:00 2001 From: Zack Stayman Date: Mon, 24 Jul 2017 13:36:42 -0400 Subject: [PATCH 1/2] create wrapped in span --- src/component/component.js | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/component/component.js b/src/component/component.js index 5a59e69..e1f37cc 100644 --- a/src/component/component.js +++ b/src/component/component.js @@ -4,7 +4,6 @@ */ import React from 'react' -import ReactDOMServer from 'react-dom/server' import {PropTypes} from 'prop-types' import deepForceUpdate from 'react-deep-force-update' import {setForceRefresh, setLanguage} from '../actions' @@ -18,23 +17,20 @@ class I18n extends React.Component { // It check if text have params params(text, params) { - if (params !== undefined) { - for (let k in params) { - let reg = new RegExp('\{' + k + '\}', 'g') - let param = params[k]; - - // Escape possible '$' in params to prevent unexpected behavior with .replace() - // especially important for IE11, which misinterprets '$0' as a regex command - if (typeof param === 'string') { - param = param.replace(/\$/g, '$$$$'); - } else if (typeof param === 'object' && param !== null) { - param = ReactDOMServer.renderToStaticMarkup(param) - } - - text = text.replace(reg, param) - } + // if params don't exist, just return the string + if (!params) { + return text; } - return text + + const children = text.split(/({[^}]+})/g) + .map((child) => { + const match = /{(.+)}/g.exec(child)[1]; + return params[match] || child; + }); + + // When React 16 is released, change the span to an identity function for array children, + // removing the extra dom node + return React.createElement('span', null, ...children); } // Main method for translating texts From 52f31a6cc0b670774040ea0b2e7080a8139a37c3 Mon Sep 17 00:00:00 2001 From: Zack Stayman Date: Mon, 24 Jul 2017 14:17:13 -0400 Subject: [PATCH 2/2] translate now accepts react components --- dist/component/component.js | 2 +- src/component/component.js | 19 ++++++++++++++----- test/component.immutable.spec.js | 2 +- test/component.spec.js | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/dist/component/component.js b/dist/component/component.js index 35a1218..7c56bad 100644 --- a/dist/component/component.js +++ b/dist/component/component.js @@ -1 +1 @@ -'use strict';Object.defineProperty(exports,'__esModule',{value:true});var _typeof=typeof Symbol==='function'&&typeof Symbol.iterator==='symbol'?function(obj){return typeof obj}:function(obj){return obj&&typeof Symbol==='function'&&obj.constructor===Symbol&&obj!==Symbol.prototype?'symbol':typeof obj};var _createClass=function(){function defineProperties(target,props){for(var i=0;i-1){langMessages=translations[this.props.lang.split('-')[0]]}if(langMessages===undefined){return this.params(textKey,params)}var message=langMessages[textKey];if(message===undefined||message===''){if(this.props.fallbackLang&&translations[this.props.fallbackLang]){var literal=translations[this.props.fallbackLang][textKey];if(literal!==undefined&&literal!==''){return this.params(literal,params)}}return this.params(textKey,params)}return this.params(message,params)}},{key:'getChildContext',value:function getChildContext(){return{t:this.trans}}},{key:'shouldComponentUpdate',value:function shouldComponentUpdate(nextProps,nextState){if(this.props.forceRefresh&&!nextProps.forceRefresh){return false}return true}},{key:'componentDidUpdate',value:function componentDidUpdate(prevProps,prevState){if(prevProps.lang!==this.props.lang||!prevProps.forceRefresh&&this.props.forceRefresh){(0,_reactDeepForceUpdate2.default)(this);if(this.props.forceRefresh){this.props.dispatch((0,_actions.setForceRefresh)(false))}}}},{key:'componentWillMount',value:function componentWillMount(){this.props.dispatch((0,_actions.setLanguage)(this.props.initialLang))}},{key:'render',value:function render(){return this.props.children}}]);return I18n}(_react2.default.Component);I18n.childContextTypes={t:_propTypes.PropTypes.func.isRequired};I18n.propTypes={translations:_propTypes.PropTypes.object.isRequired,useReducer:_propTypes.PropTypes.bool,initialLang:_propTypes.PropTypes.string,fallbackLang:_propTypes.PropTypes.string};I18n.defaultProps={useReducer:false,initialLang:'en',fallbackLang:null};exports.default=I18n; \ No newline at end of file +'use strict';Object.defineProperty(exports,'__esModule',{value:true});var _typeof=typeof Symbol==='function'&&typeof Symbol.iterator==='symbol'?function(obj){return typeof obj}:function(obj){return obj&&typeof Symbol==='function'&&obj.constructor===Symbol&&obj!==Symbol.prototype?'symbol':typeof obj};var _createClass=function(){function defineProperties(target,props){for(var i=0;i-1){langMessages=translations[this.props.lang.split('-')[0]]}if(langMessages===undefined){return this.params(textKey,params)}var message=langMessages[textKey];if(message===undefined||message===''){if(this.props.fallbackLang&&translations[this.props.fallbackLang]){var literal=translations[this.props.fallbackLang][textKey];if(literal!==undefined&&literal!==''){return this.params(literal,params)}}return this.params(textKey,params)}return this.params(message,params)}},{key:'getChildContext',value:function getChildContext(){return{t:this.trans}}},{key:'shouldComponentUpdate',value:function shouldComponentUpdate(nextProps,nextState){if(this.props.forceRefresh&&!nextProps.forceRefresh){return false}return true}},{key:'componentDidUpdate',value:function componentDidUpdate(prevProps,prevState){if(prevProps.lang!==this.props.lang||!prevProps.forceRefresh&&this.props.forceRefresh){(0,_reactDeepForceUpdate2.default)(this);if(this.props.forceRefresh){this.props.dispatch((0,_actions.setForceRefresh)(false))}}}},{key:'componentWillMount',value:function componentWillMount(){this.props.dispatch((0,_actions.setLanguage)(this.props.initialLang))}},{key:'render',value:function render(){return this.props.children}}]);return I18n}(_react2.default.Component);I18n.childContextTypes={t:_propTypes.PropTypes.func.isRequired};I18n.propTypes={translations:_propTypes.PropTypes.object.isRequired,useReducer:_propTypes.PropTypes.bool,initialLang:_propTypes.PropTypes.string,fallbackLang:_propTypes.PropTypes.string};I18n.defaultProps={useReducer:false,initialLang:'en',fallbackLang:null};exports.default=I18n; \ No newline at end of file diff --git a/src/component/component.js b/src/component/component.js index e1f37cc..2526567 100644 --- a/src/component/component.js +++ b/src/component/component.js @@ -24,13 +24,22 @@ class I18n extends React.Component { const children = text.split(/({[^}]+})/g) .map((child) => { - const match = /{(.+)}/g.exec(child)[1]; - return params[match] || child; + const match = /{(.+)}/g.exec(child); + if (match) { + const param = params[match[1]]; + return param ? param : String(param) + } + + return child; }); - // When React 16 is released, change the span to an identity function for array children, - // removing the extra dom node - return React.createElement('span', null, ...children); + // if any children are objects (i.e. react components), wrap in a span, otherwise return as string + // ignore anything that is falsy, bypassing null, etc + return children.some(child => child && typeof child === 'object') + // When React 16 is released, change the span to an identity function for array children, + // removing the extra dom node + ? React.createElement('span', null, ...children) + : children.join(''); } // Main method for translating texts diff --git a/test/component.immutable.spec.js b/test/component.immutable.spec.js index 01a326f..def434d 100644 --- a/test/component.immutable.spec.js +++ b/test/component.immutable.spec.js @@ -169,7 +169,7 @@ describe('immutable component test', function() { }) it('object as param', function() { - expect(this.objAsParam.textContent).toEqual('Hello Cesc') + expect(this.objAsParam.textContent).toEqual('Hello Cesc') }) }) diff --git a/test/component.spec.js b/test/component.spec.js index b733ddc..cdc6767 100644 --- a/test/component.spec.js +++ b/test/component.spec.js @@ -181,7 +181,7 @@ describe('component test', function() { }) it('object as param', function() { - expect(this.objAsParam.textContent).toEqual('Hello Cesc') + expect(this.objAsParam.textContent).toEqual('Hello Cesc') }) })