react-redux-universal-hot-example icon indicating copy to clipboard operation
react-redux-universal-hot-example copied to clipboard

i18n support

Open zintaen opened this issue 7 years ago • 8 comments

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

zintaen avatar Dec 02 '17 07:12 zintaen

@cambeme Well. i'm using this lib https://react.i18next.com/ , and works perfectly. Even with switcher

firdausious avatar Dec 03 '17 18:12 firdausious

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 :(

zintaen avatar Dec 03 '17 18:12 zintaen

@firdausious Can you help me with this?

vermotr avatar Jul 25 '18 08:07 vermotr

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?

vermotr avatar Jul 31 '18 14:07 vermotr

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

bertho-zero avatar Jul 31 '18 15:07 bertho-zero

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?

vermotr avatar Aug 01 '18 15:08 vermotr

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.

bertho-zero avatar Aug 01 '18 15:08 bertho-zero

@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

hendrik-h-dev avatar Oct 26 '19 01:10 hendrik-h-dev