I am trying to migrate from react-router-dom to next.js. I managed to make all major changes but now I am facing an issue with the internationalization of React-i18next. I changed the name of the file from i18n.js to next.config.js but I get this message 'Error: Text content does not match server-rendered HTML'
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
const Languages = ['ar', 'en', 'fr']
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next) // passes i18n down to react-i18next
.init({
// lng: 'en',
react: {
useSuspense: true,
},
// the translations
// (tip move them in a JSON file and import them,
// or even better, manage them via a UI: https://react.i18next.com/guides/multiple-translation-files#manage-your-translations-with-a-management-gui)
supported: ["en", "fr", "ar"],
fallbackLng: "en",
detection: {
order: ['localStorage', 'htmlTag','cookie' , 'subdomain'],
// order: ['path', 'cookie', 'htmlTag', 'localStorage', 'subdomain'],
caches: ['localStorage'],
},
debug: false,
whitelist: Languages,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
nsSeperator: false,
keySeperator: false,
backend: {
loadPath: '/static/locales/{{lng}}/{{ns}}.json',
},
});
export default i18n;
-app.js
import { Provider } from 'react-redux';
import { useStore } from '../store';
import '../styles/globals.css';
import Layout from '../hocs/Layout';
import '../next.config';
function App({Component, pageProps}) {
const store = useStore(pageProps.initialReduxState);
return (
<Provider store={store}>
<Layout>
<Component {...pageProps} />
</Layout>
</Provider>
);
};
export default App;
loginHeader.js
import React, { Fragment, useEffect } from 'react';
import styles from '../styles/LoginHeader.module.css';
import Link from 'next/link';
import { connect } from 'react-redux';
import { logout } from '../actions/auth';
import SortIcon from '@mui/icons-material/Sort';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import Alert from './Alert';
import Logo from '../assets/images/logoo.png';
import { useTranslation } from "react-i18next";
import { Select, MenuItem } from '@mui/material';
import LanguageIcon from '@mui/icons-material/Language';
import { Link as Scroll } from 'react-scroll';
import Image from 'next/image';
import i18next from 'i18next';
import cookies from 'js-cookie';
const languages = [
{
code: 'fr',
name: 'Français',
country_code: 'fr'
},
{
code: 'en',
name : 'English',
country_code: 'en'
},
{
code: 'ar',
name: 'العربية',
country_code: 'ly',
dir: 'rtl'
}
]
function LoginHeader({ logout, isAuthenticated }) {
const currentLanguageCode = cookies.get('i18next') || 'en';
const currentLanguage = languages.find(l => l.code === currentLanguageCode);
useEffect (() => {
document.body.dir = currentLanguage.dir || 'ltr'
// document.title = t('app_title')
},[currentLanguage]);
const { t } = useTranslation()
const guestLinks = () => (
<Fragment>
<div className={styles.loginHeader__right}>
<div className='middle__header__bx'>
<div className={styles.loginHeader__main__btns}>
<Link className={styles.loginHeader__loginButton} href='/login'><button className={styles.login__btn}>{t('header_login')}</button></Link>
<Link className={styles.loginHeader__signupButton} href='/signup'><button className={styles.signup__btn}>{t('header_signup')}</button></Link>
</div>
<div className={styles.loginHeader__services__dropdown}>
<Scroll offset={-100} to='services'><button className={styles['dropdown__btn']+' '+styles['dropdown__services']}>{t('header_services')}<ExpandMoreIcon className={styles.services__expand}/></button></Scroll>
<div className={styles['dropdown__content']+' '+styles['dropdown__services__content']}>
<Link className={styles.loginHeader__menuItem} href='/admission'>{t('services_addmissionOffers')}</Link>
{/* <Link className='loginHeader__menuItem' to='/application-form'>{t('services_forms')}</Link> */}
<Link className={styles.loginHeader__menuItem} href='/premuim-support'>{t('services_premium')}</Link>
<Link className={styles.loginHeader__menuItem} href='/visa-assist'>{t('services_visaAssist')}</Link>
</div>
</div>
{/* start of lang box */}
<Select
className={styles.loginHeader__select}
labelId='select-demo'
id='language-select'
disableUnderline
variant='standard'
IconComponent={LanguageIcon}
>
{languages.map(({code, name, country_code}) =>
<MenuItem
className={styles.loginHeader__select__menu}
key={country_code}
>
<button
onClick={() => i18next.changeLanguage(code)}
className='loginHeader__lang__btn'
>
{name}
</button>
</MenuItem>
)}
</Select>
{/* end of lang box */}
<div className={styles['loginHeader__services__dropdown']+' '+styles['sortIcon__bx']}>
<SortIcon className={styles['dropdown__btn']+' '+styles['loginHeader__sortIcon']}/>
<div className={styles['dropdown__content']+' '+styles['sortIcon__dropdown']}>
<Link className={styles.loginHeader__menuItem} href='/premuim-support'>{t('header_dropdown_prem')}</Link>
<Link className={styles.loginHeader__menuItem} href='/visa-assist'>{t('header_dropdown_visaAssist')}</Link>
<Link className={styles.loginHeader__menuItem} href='/admission'>{t('header_dropdown_admission')}</Link>
<Link className={styles.loginHeader__menuItem} href='/request-service'>{t('header_dropdown_requestService')}</Link>
<Link className={styles.loginHeader__menuItem} href='/contact'>{t('header_dropdown_contact')}</Link>
<Link className={styles.loginHeader__menuItem} href='/signup'>{t('header_signup')}</Link>
<Link className={styles.loginHeader__menuItem} href='/login'>{t('header_login')}</Link>
<Link className={styles.loginHeader__menuItem} href='/guid'>{t('header_dropdown_guide')}</Link>
</div>
</div>
</div>
</div>
</Fragment>
);
const authLinks = () => (
<Fragment>
<div className={styles.loginHeader__right}>
<div className={styles.middle__header__bx}>
<div className={styles.loginHeader__main__btns}>
<Link className={styles.loginHeader__loginButton} href='/login'><button className={styles.login__btn}>{t('header_login')}</button></Link>
<Link className={styles.loginHeader__signupButton} href='/signup'><button className={styles.singin__btn}>{t('header_signup')}</button></Link>
</div>
<div className={styles.loginHeader__services__dropdown}>
<Scroll offset={-100} to='services'><button className={styles['dropdown__btn']+' '+styles['dropdown__services']}>{t('header_services')}<ExpandMoreIcon className={styles.services__expand}/></button></Scroll>
<div className={styles['dropdown__content']+' '+styles['dropdown__services__content']}>
<Link className={styles.loginHeader__menuItem} href='/admission'>{t('services_addmissionOffers')}</Link>
{/* <Link className='loginHeader__menuItem' to='/application-form'>{t('services_forms')}</Link> */}
<Link className={styles.loginHeader__menuItem} href='/premuim-support'>{t('services_premium')}</Link>
<Link className={styles.loginHeader__menuItem} href='/visa-assist'>{t('services_visaAssist')}</Link>
</div>
</div>
{/* start of lang box */}
<Select
className={styles.loginHeader__select}
labelId='select-demo'
id='language-select'
disableUnderline
variant='standard'
IconComponent={LanguageIcon}
>
{languages.map(({code, name, country_code}) =>
<MenuItem
key={country_code}
>
<button
onClick={() => i18next.changeLanguage(code)}
className='loginHeader__lang__btn'
>
{name}
</button>
</MenuItem>
)}
</Select>
{/* end of lang box */}
<div className={styles['loginHeader__services__dropdown']+' '+styles['loggedin__icon__bx']}>
<button className={styles.dropdown__btn}><SortIcon className={styles['loginHeader__sortIcon']+' '+styles['logedin__sortIcon']}/></button>
<div className={styles['dropdown__content']+' '+styles['logged__sortIcon__dropdown']}>
<Link className={styles.loginHeader__menuItem} href='/premuim-support'>{t('header_dropdown_prem')}</Link>
<Link className={styles.loginHeader__menuItem} href='/admission'>{t('header_dropdown_admission')}</Link>
<Link className={styles.loginHeader__menuItem} href='/visa-assist'>{t('header_dropdown_visaAssist')}</Link>
<Link className={styles.loginHeader__menuItem} href='/request-service'>{t('header_dropdown_requestService')}</Link>
<Link className={styles.loginHeader__menuItem} href='/contact'>{t('header_dropdown_contact')}</Link>
<Link className={styles.loginHeader__menuItem} href='/guid'>{t('header_dropdown_guide')}</Link>
<button onClick={logout} className={styles.logout__btn}>{t('header_logout')}</button>
</div>
</div>
</div>
</div>
</Fragment>
);
return (
<div className={styles.loginHeader}>
<div className={styles.loginHeader__left}>
<Link href='/'><Image className={styles.logo} src={Logo} alt='logo'/></Link>
</div>
{isAuthenticated ? authLinks() : guestLinks()}
<Alert/>
</div>
)
};
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated
});
export default connect(mapStateToProps, { logout }) (LoginHeader);
I spent some days following many tutorials and reading. Here is a major note; place next.config.js & related files in the root directory not in src.