react-redux-universal-hot-example
react-redux-universal-hot-example copied to clipboard
i18n support
Would you kindy advice me how to support i18n in your boilerplate @bertho-zero, i'm new to redux/duck and trying to adapt them :) I tried to follow some guides from erikas project
https://github.com/erikras/react-redux-universal-hot-example/issues/140
but stuck at Locale switcher part, hope u can help me out
@cambeme Well. i'm using this lib https://react.i18next.com/ , and works perfectly. Even with switcher
Did you appied i18next into bertho's boilerplate @firdausious? Can you guide me what i should modify? I'm still struggling the whole last week :(
@firdausious Can you help me with this?
So, based on the fork of @simpleblack, I have this:
packagage.json:
"dependencies": {
…
"i18next": "^11.5.0",
"i18next-express-middleware": "^1.2.0",
"i18next-node-fs-backend": "^1.2.1",
…
"react-i18next": "^7.10.1",
…
}
src/i18n-server.js:
import i18n from 'i18next';
import Backend from 'i18next-node-fs-backend';
import { LanguageDetector } from 'i18next-express-middleware';
i18n
.use(Backend)
.use(LanguageDetector)
.init({
fallbackLng: 'fr',
// have a common namespace used around the full app
ns: ['common'],
defaultNS: 'common',
debug: true,
interpolation: {
escapeValue: false // not needed for react!!
},
backend: {
loadPath: 'src/locales/{{lng}}/{{ns}}.json',
jsonIndent: 2
}
})
.loadLanguages(['fr', 'en']);
export default i18n;
src/i18n-client.js:
import i18n from 'i18next';
i18n
.init({
whitelist: ['fr', 'en'],
fallbackLng: 'fr',
// have a common namespace used around the full app
ns: ['common'],
defaultNS: 'common',
debug: true,
interpolation: {
escapeValue: false, // not needed for react!!
}
});
export default i18n;
src/server.js:
import { I18nextProvider } from 'react-i18next';
import i18nMiddleware from 'i18next-express-middleware';
import i18n from './i18n-server';
app
.use(…)
…
.use(i18nMiddleware.handle(i18n));
app.use(async (req, res) => {
…
const locale = req.language;
const resources = i18n.getResourceBundle(locale, 'common');
const i18nClient = { locale, resources };
const i18nServer = i18n.cloneInstance();
i18nServer.changeLanguage(locale);
function hydrate() {
res.write('<!doctype html>');
ReactDOM.renderToNodeStream(<Html
assets={webpackIsomorphicTools.assets()}
store={store}
i18n={i18nClient} // Here
/>).pipe(res);
}
const component = (
…
<I18nextProvider i18n={i18nServer}>
…
</I18nextProvider>
…
)
…
const html = (
<Html
assets={webpackIsomorphicTools.assets()}
bundles={bundles}
content={content}
store={store}
i18n={i18nClient} // Here
/>
);
}
src/client.js:
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n-client';
const hydrate = async _routes => {
i18n.changeLanguage(window.__i18n.locale);
i18n.addResourceBundle(window.__i18n.locale, 'common', window.__i18n.resources, true);
…
ReactDOM.hydrate(
<HotEnabler>
<I18nextProvider i18n={i18n} >
…
</I18nextProvider>
…
helpers/Html.js:
<script dangerouslySetInnerHTML={{__html: `window.__i18n=${serialize(i18n)};`}} charSet="UTF-8" />
And the locales files:
src
├── locales
│ ├── en
│ │ └── common.json
│ └── fr
│ └── common.json
With this configuration, everything works well, but I would like to add the language parameter in the route path. Currently, we can change the language by adding this parameter: /?lng=en
or /?lng=fr
, but I prefer something like this: /en/
or /fr/
.
Can you help me with this?
I would rather have used https://github.com/yahoo/react-intl. But in any case I have not yet studied the subject.
For your problem you should edit routes and use the react-router params (https://reacttraining.com/react-router/web/example/url-params):
Example (pseudo-code):
const routes = [
{
component: App,
routes: [
{ path: '/(:lang/)', exact: true, component: Home },
{ path: '/(:lang/)about', component: About },
{ path: '/(:lang/)chat', component: Chat },
{ path: '/(:lang/)login', component: Login },
{ path: '/(:lang/)login-success', component: isAuthenticated(LoginSuccess) },
{ path: '/(:lang/)register', component: isNotAuthenticated(Register) },
{ component: NotFound }
]
}
];
then use params.lang
from:
https://github.com/bertho-zero/react-redux-universal-hot-example/blob/98a9386aca0998f4906aaec8a40174194735fcfe/src/components/ReduxAsyncConnect/ReduxAsyncConnect.js#L36
https://github.com/bertho-zero/react-redux-universal-hot-example/blob/66f8418cd3ab9ea02d02f54039ce1786786c2401/src/server.js#L148
Thanks and I solved my problem by adding theses lines in src/i18n-client.js:
i18n
.use(Backend)
.use(LanguageDetector)
.init({
…
detection: {
// order and from where user language should be detected
order: ['path'],
// keys or params to lookup language from
lookupPath: 'lng',
lookupFromPathIndex: 0
}
});
And here the routes.js file:
const routes = [
{
component: App,
routes: [
{ path: '/:lng(fr|en)?/', exact: true, component: Home },
{ path: '/:lng?/about', component: About },
{ path: '/:lng?/chat', component: Chat },
{ path: '/:lng?/login', component: Login },
{ path: '/:lng?/login-success', component: isAuthenticated(LoginSuccess) },
{ path: '/:lng?/register', component: isNotAuthenticated(Register) },
{ component: NotFound }
]
}
];
@bertho-zero do you want a PR to add this feature or do you prefer to use https://github.com/yahoo/react-intl?
If it is not too much work you can do a PR, I would do one with react-intl and I could then choose. And then those who want to change libraries will only have to look at your PR.
@vermotr Thank you for your work, I actually try to implement i18next but with your changes, but it's not working. I read the react-i18n documentation but wasn't able to solve the issue. Would it be possible to guide me little bit through the integration or make a pull PR of you changes, that I can try to figure out what are the differences in my code or possible issues. Thanks in advance