Skip to content
This repository has been archived by the owner on Jul 15, 2020. It is now read-only.

Add i18 support (e.g. i18next) #59

Open
iaincollins opened this issue Jan 6, 2018 · 1 comment
Open

Add i18 support (e.g. i18next) #59

iaincollins opened this issue Jan 6, 2018 · 1 comment

Comments

@iaincollins
Copy link
Owner

Implement i18next support to provide working basic implantation of support for internationalization/localization.

It doesn't need to be a complete translation of the site but should be useful enough to be a useful starting point.

iaincollins added a commit that referenced this issue Jan 6, 2018
This provides some support for issues rasied in #57 and #59.
iaincollins added a commit that referenced this issue Jan 12, 2018
This provides some support for issues rasied in #57 and #59.
@ghost
Copy link

ghost commented Mar 3, 2018

Suggestion (may not be the best, but it's what I did in one of my projects): Use a higher order component (HOC) which wraps a page to add the translation function to each page. This will incur the downsides associated with using HOCs (such as not being able to pass refs along HOC boundaries).

Example code:

PageWrapper.js

// import { compose, withHandlers, withProps, withState } from 'recompose'; // TODO: Use function composition?
import { I18nextProvider, translate } from 'react-i18next';
import { NextAuth } from 'next-auth/client';
import i18n from '../i18n';

// HOC
export default WrappedPage => {
	const TranslatedPage = translate(['common'], { i18n, wait: process.browser })(WrappedPage);

	return class extends React.Component {
		static async getInitialProps(ctx) {
			const {req} = ctx;

			let props = {};

			if (req && !process.browser)
				props = {...props,
					...i18n.getInitialProps(req, ['common']), // Pass initial translations. Use req.i18n instance on serverside to prevent overlapping requests from setting the language wrong
					t: i18n.t, // Pass translation function to wrapped component
				};

			props = {...props,
				session: await NextAuth.init({req}), // Pass session to all pages
				// lang: req.query ? req.query.language : 'en', // Add a lang property for accessibility
				// userAgent: req ? req.headers['user-agent'] : navigator.userAgent,
			};

			// Add props from wrapped component, if any
			if (TranslatedPage.getInitialProps)
				props = {...props, ...(await TranslatedPage.getInitialProps(ctx))};

			return props;
		}

		constructor(props) {
			super(props);
			this.i18n = i18n;
			this.t = props.t;
		}

		render() {
			return (
				<I18nextProvider i18n={this.i18n}>
					<Layout {...this.props}>
						<TranslatedPage {...this.props} />
					</Layout>
				</I18nextProvider>
			);
		}
	}
}

i18n.js

const i18n = require('i18next');
const LanguageDetector = require('i18next-browser-languagedetector');
const XHR = require('i18next-xhr-backend');

const options = {
	fallbackLng: 'en',
	load: 'languageOnly', // We only provide e.g. en, de -> no region specific locals like en-US, de-DE

	// Have a common namespace used around the full app
	ns: ['common'],
	defaultNS: 'common',

	// debug: true,
	saveMissing: true,

	keySeparator: '::', // Change the key separator from the default '.' so our JSON key names can contain periods https://stackoverflow.com/a/34037706

	interpolation: {
		escapeValue: false, // Not needed for React
		formatSeparator: ',',
		format: (value, format, lng) => {
			if (format === 'uppercase') return value.toUpperCase();
			return value;
		},
	},
};

// For browser use XHR backend to load translations and browser lng detector
if (process.browser) {
	i18n
		.use(XHR)
		// .use(Cache)
		.use(LanguageDetector)
}

// Initialize if not already initialized
if (!i18n.isInitialized) i18n.init(options);

// A simple helper to getInitialProps passed on loaded i18n data
i18n.getInitialProps = (req, namespaces) => {
	if (!namespaces)
		namespaces = i18n.options.defaultNS;
	if (typeof namespaces === 'string')
		namespaces = [namespaces];

	req.i18n.toJSON = () => null; // Do not serialize i18next instance and send to client

	const initialI18nStore = {};
	req.i18n.languages.forEach((l) => {
		initialI18nStore[l] = {};
		namespaces.forEach((ns) => {
			initialI18nStore[l][ns] = req.i18n.services.resourceStore.data[l][ns] || {};
		});
	});

	return {
		i18n: req.i18n, // Use the instance on req - fixed language on request (avoid issues in race conditions with lngs of different users)
		initialI18nStore,
		initialLanguage: req.i18n.language,
	};
}

module.exports = i18n;

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant