From ca0e7c2def9e836d3d26f84d169030bf1e9aedd6 Mon Sep 17 00:00:00 2001 From: Jan Muehlemann Date: Mon, 13 Jun 2016 09:24:00 +0200 Subject: [PATCH] adds wait flag --- CHANGELOG.md | 3 + README.md | 33 +++++- example/app/components/AnotherComponent.js | 2 +- example/app/components/View.js | 2 +- react-i18next.js | 31 +++--- react-i18next.min.js | 2 +- src/translate.js | 119 +++++++++++---------- 7 files changed, 112 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fd7fa4d..a06820e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### 1.5.0 +- adds wait option, which delays initial rendering until translations are loaded + ### 1.4.2 - possible fix for HRM issues diff --git a/README.md b/README.md index 15681635..aaa92664 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,30 @@ export default translate(['defaultNamespace', 'anotherNamespace'])(TranslatableV ``` -__getWrappedInstance()__: allows you to access to the component instance, wrapped into `translate()`. +You can set options.wait to true if you want to delay rendering until translation files are loaded: + +```javascript +import React from 'react'; +import { translate } from 'react-i18next'; + +function TranslatableView(props) { + const { t } = props; + + return ( +
+

{t('keyFromDefault')}

+

{t('anotherNamespace:key.from.another.namespace', { /* options t options */ })}

+
+ ) +} + +export default translate(['defaultNamespace', 'anotherNamespace'], { wait: true })(TranslatableView); + +``` + + + +__getWrappedInstance()__: allows you to access to the component instance, wrapped into `translate()`. Only available if you pass `{ withRef: true }` to the `translate()` options. ```javascript @@ -81,14 +104,14 @@ import React, { Component } from 'react'; import { translate } from 'react-i18next'; class TranslatableView extends Component { - + foo() { // do something important } - + render() { const { t } = this.props; - + return (

{t('keyFromDefault')}

@@ -198,7 +221,7 @@ app.use((req, res) => { ); - + // render as desired now ... }); ``` diff --git a/example/app/components/AnotherComponent.js b/example/app/components/AnotherComponent.js index 7ce6c01c..d07c19b2 100644 --- a/example/app/components/AnotherComponent.js +++ b/example/app/components/AnotherComponent.js @@ -5,4 +5,4 @@ function Component({t}) { return

{t('content.text', { /* options t options */ })}

} -export default translate('view')(Component); +export default translate('view', { wait: true })(Component); diff --git a/example/app/components/View.js b/example/app/components/View.js index b81c4512..8530a329 100755 --- a/example/app/components/View.js +++ b/example/app/components/View.js @@ -4,7 +4,7 @@ import AnotherComponent from './AnotherComponent'; import YetAnotherComponent from './YetAnotherComponent'; -@translate(['view', 'nav']) +@translate(['view', 'nav'], { wait: true }) class TranslatableView extends React.Component { render() { const { t } = this.props; diff --git a/react-i18next.js b/react-i18next.js index c56d1c88..68afdfc2 100644 --- a/react-i18next.js +++ b/react-i18next.js @@ -123,6 +123,8 @@ var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var _options$withRef = options.withRef; var withRef = _options$withRef === undefined ? false : _options$withRef; + var _options$wait = options.wait; + var wait = _options$wait === undefined ? false : _options$wait; return function Wrapper(WrappedComponent) { @@ -140,8 +142,11 @@ namespaces = namespaces || i18n.options.defaultNS; _this.state = { - i18nLoadedAt: null + i18nLoadedAt: null, + ready: false }; + + _this.onI18nChanged = _this.onI18nChanged.bind(_this); return _this; } @@ -153,21 +158,17 @@ }, { key: 'componentWillMount', value: function componentWillMount() { + var _this2 = this; + this.mounted = true; - i18n.loadNamespaces(namespaces); + i18n.loadNamespaces(namespaces, function () { + _this2.setState({ ready: true }); + }); this.t = i18n.getFixedT(null, namespaces); } }, { key: 'componentDidMount', value: function componentDidMount() { - var _this2 = this; - - this.onI18nChanged = function () { - if (!_this2.mounted) return; - - _this2.setState({ i18nLoadedAt: new Date() }); - }; - i18n.on('languageChanged loaded', this.onI18nChanged); } }, { @@ -180,8 +181,8 @@ } } }, { - key: 'onI18nChange', - value: function onI18nChange() { + key: 'onI18nChanged', + value: function onI18nChanged() { if (!this.mounted) return; this.setState({ i18nLoadedAt: new Date() }); @@ -199,7 +200,9 @@ }, { key: 'render', value: function render() { - var i18nLoadedAt = this.state.i18nLoadedAt; + var _state = this.state; + var i18nLoadedAt = _state.i18nLoadedAt; + var ready = _state.ready; var extraProps = { i18nLoadedAt: i18nLoadedAt, t: this.t }; @@ -207,6 +210,8 @@ extraProps.ref = 'wrappedInstance'; } + if (!ready && wait) return null; + return React__default.createElement(WrappedComponent, babelHelpers.extends({}, this.props, extraProps)); } }]); diff --git a/react-i18next.min.js b/react-i18next.min.js index 9b8e349e..da6fbed1 100644 --- a/react-i18next.min.js +++ b/react-i18next.min.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define("reactI18next",["exports","react"],t):t(e.reactI18next=e.reactI18next||{},e.React)}(this,function(e,t){"use strict";function n(e){return e.displayName||e.name||"Component"}function r(e){var r=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],o=r.withRef,i=void 0===o?!1:o;return function(r){var o=void 0,a=function(t){function n(t,r){c.classCallCheck(this,n);var i=c.possibleConstructorReturn(this,Object.getPrototypeOf(n).call(this,t,r));return o=r.i18n,e=e||o.options.defaultNS,i.state={i18nLoadedAt:null},i}return c.inherits(n,t),c.createClass(n,[{key:"getChildContext",value:function(){return{t:this.t}}},{key:"componentWillMount",value:function(){this.mounted=!0,o.loadNamespaces(e),this.t=o.getFixedT(null,e)}},{key:"componentDidMount",value:function(){var e=this;this.onI18nChanged=function(){e.mounted&&e.setState({i18nLoadedAt:new Date})},o.on("languageChanged loaded",this.onI18nChanged)}},{key:"componentWillUnmount",value:function(){this.mounted=!1,this.onI18nChanged&&(o.off("languageChanged",this.onI18nChanged),o.off("loaded",this.onI18nChanged))}},{key:"onI18nChange",value:function(){this.mounted&&this.setState({i18nLoadedAt:new Date})}},{key:"getWrappedInstance",value:function(){return i||console.error("To access the wrapped instance, you need to specify { withRef: true } as the second argument of the translate() call."),this.refs.wrappedInstance}},{key:"render",value:function(){var e=this.state.i18nLoadedAt,t={i18nLoadedAt:e,t:this.t};return i&&(t.ref="wrappedInstance"),s.createElement(r,c["extends"]({},this.props,t))}}]),n}(t.Component);return a.WrappedComponent=r,a.contextTypes={i18n:t.PropTypes.object.isRequired},a.childContextTypes={t:t.PropTypes.func.isRequired},a.displayName="Translate["+n(r)+"]",a.namespaces=e,a}}function o(e,t){for(var n=0,r=e.length;r>n;n++)if("object"===c["typeof"](e[n])){var o=!0,i=!1,a=void 0;try{for(var s,u=Object.entries(e[n])[Symbol.iterator]();!(o=(s=u.next()).done);o=!0){var l=c.slicedToArray(s.value,2),p=l[0],f=l[1];t(f,n,p)}}catch(h){i=!0,a=h}finally{try{!o&&u["return"]&&u["return"]()}finally{if(i)throw a}}}else t(e[n],n)}function i(e){var t=[];return o(e,function(e){e&&e.namespaces&&e.namespaces.forEach(function(e){-1===t.indexOf(e)&&t.push(e)})}),t}function a(e){var t=e.components,n=e.i18n,r=i(t);return new Promise(function(e){n.loadNamespaces(r,e)})}var s="default"in t?t["default"]:t,c={};c["typeof"]="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},c.classCallCheck=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},c.createClass=function(){function e(e,t){for(var n=0;nn;n++)if("object"===c["typeof"](e[n])){var o=!0,i=!1,a=void 0;try{for(var s,u=Object.entries(e[n])[Symbol.iterator]();!(o=(s=u.next()).done);o=!0){var l=c.slicedToArray(s.value,2),p=l[0],f=l[1];t(f,n,p)}}catch(d){i=!0,a=d}finally{try{!o&&u["return"]&&u["return"]()}finally{if(i)throw a}}}else t(e[n],n)}function i(e){var t=[];return o(e,function(e){e&&e.namespaces&&e.namespaces.forEach(function(e){-1===t.indexOf(e)&&t.push(e)})}),t}function a(e){var t=e.components,n=e.i18n,r=i(t);return new Promise(function(e){n.loadNamespaces(r,e)})}var s="default"in t?t["default"]:t,c={};c["typeof"]="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},c.classCallCheck=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},c.createClass=function(){function e(e,t){for(var n=0;n { + this.setState({ ready: true }); + }); + this.t = i18n.getFixedT(null, namespaces); + } + + componentDidMount() { + i18n.on('languageChanged loaded', this.onI18nChanged); + } + + componentWillUnmount() { + this.mounted = false; + if (this.onI18nChanged) { + i18n.off('languageChanged', this.onI18nChanged); + i18n.off('loaded', this.onI18nChanged); } + } - getChildContext() { - return { t: this.t }; - } - - componentWillMount() { - this.mounted = true; - i18n.loadNamespaces(namespaces); - this.t = i18n.getFixedT(null, namespaces); - } + onI18nChanged() { + if (!this.mounted) return; - componentDidMount() { - this.onI18nChanged = () => { - if (!this.mounted) return; - - this.setState({ i18nLoadedAt: new Date() }); - }; - - i18n.on('languageChanged loaded', this.onI18nChanged); - } + this.setState({ i18nLoadedAt: new Date() }); + } - componentWillUnmount() { - this.mounted = false; - if (this.onI18nChanged) { - i18n.off('languageChanged', this.onI18nChanged); - i18n.off('loaded', this.onI18nChanged); - } + getWrappedInstance() { + if (!withRef) { + // eslint-disable-next-line no-console + console.error( + 'To access the wrapped instance, you need to specify ' + + '{ withRef: true } as the second argument of the translate() call.' + ); } - onI18nChange() { - if (!this.mounted) return; - - this.setState({ i18nLoadedAt: new Date() }); - } + return this.refs.wrappedInstance; + } - getWrappedInstance() { - if (!withRef) { - // eslint-disable-next-line no-console - console.error( - 'To access the wrapped instance, you need to specify ' + - '{ withRef: true } as the second argument of the translate() call.' - ); - } + render() { + const { i18nLoadedAt, ready } = this.state; + const extraProps = { i18nLoadedAt, t: this.t }; - return this.refs.wrappedInstance; + if (withRef) { + extraProps.ref = 'wrappedInstance'; } - render() { - const { i18nLoadedAt } = this.state; - const extraProps = { i18nLoadedAt, t: this.t }; + if (!ready && wait) return null; - if (withRef) { - extraProps.ref = 'wrappedInstance'; - } - - return React.createElement( - WrappedComponent, - { ...this.props, ...extraProps } - ); - } + return React.createElement( + WrappedComponent, + { ...this.props, ...extraProps } + ); + } } Translate.WrappedComponent = WrappedComponent;