Customize Consent Preferences

We use cookies to help you navigate efficiently and perform certain functions. You will find detailed information about all cookies under each consent category below.

The cookies that are categorized as "Necessary" are stored on your browser as they are essential for enabling the basic functionalities of the site. ... 

Always Active

Necessary cookies are required to enable the basic features of this site, such as providing secure log-in or adjusting your consent preferences. These cookies do not store any personally identifiable data.

No cookies to display.

Functional cookies help perform certain functionalities like sharing the content of the website on social media platforms, collecting feedback, and other third-party features.

No cookies to display.

Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics such as the number of visitors, bounce rate, traffic source, etc.

No cookies to display.

Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.

No cookies to display.

Advertisement cookies are used to provide visitors with customized advertisements based on the pages you visited previously and to analyze the effectiveness of the ad campaigns.

No cookies to display.

Adding path-based internationalization with i18next in React and Next.js Server Side (SSR)

Tiempo de lectura: 4 minutos

Returns only the HTML translated, without any added content.

The first thing we have to do is to install the necessary dependencies:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
npm install i18next next-i18next @types/i18next @types/react-i18next --save
npm install i18next next-i18next @types/i18next @types/react-i18next --save
npm install i18next next-i18next @types/i18next @types/react-i18next --save

Then we are going to create a configuration file next-i18next.config.js at the root of the project:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
module.exports = {
debug: process.env.NODE_ENV === 'development',
i18n: {
locales: ['es', 'en'],
defaultLocale: 'es',
},
};
module.exports = { debug: process.env.NODE_ENV === 'development', i18n: { locales: ['es', 'en'], defaultLocale: 'es', }, };
module.exports = {
  debug: process.env.NODE_ENV === 'development',
  i18n: {
    locales: ['es', 'en'],
    defaultLocale: 'es',
  },
};

 

In my case, I am going to translate to Spanish and English. By default, I have selected the Spanish language.

Now let’s import the configuration into our next.config.js file, add:

Well, now we have to create the translation files.

To do this, let’s go to public and create a folder called locales, inside we create two folders, one called es/ and another called en/:

Here we are going to create our translation .json files. Let’s create one called common.json (we always have to have common.json created since it is the default one) and create it in both es/ and en/.

Content es/common.json

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{
"title_app": "Mi prueba con i18next",
}
{ "title_app": "Mi prueba con i18next", }
{
    "title_app": "Mi prueba con i18next",
  }

 

Return only HTML content en/common.json

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{
"title_app": "My test with i18next",
}
{ "title_app": "My test with i18next", }
{
    "title_app": "My test with i18next",
  }

 

Now we have to go or create a file _app.tsx inside pages/ (here I explain how to create it) and add:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { appWithTranslation } from 'next-i18next';
function MyApp({ Component, pageProps }: AppProps) {
.....
}
export default appWithTranslation(MyApp);
import { appWithTranslation } from 'next-i18next'; function MyApp({ Component, pageProps }: AppProps) { ..... } export default appWithTranslation(MyApp);
import { appWithTranslation } from 'next-i18next';

function MyApp({ Component, pageProps }: AppProps) {
  .....
}

export default appWithTranslation(MyApp);

 

We must add the import and the export default provided.

Create the language page redirection by path, we are going to create a middleware.tsx file (here I explain what a middleware is in Next.js)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { NextRequest, NextResponse } from 'next/server'
const PUBLIC_FILE = /\.(.*)$/
export async function middleware(req: NextRequest) {
if (
req.nextUrl.pathname.startsWith('/_next') ||
req.nextUrl.pathname.includes('/api/') ||
PUBLIC_FILE.test(req.nextUrl.pathname)
) {
return
}
if (req.nextUrl.locale === 'default') {
const locale = req.cookies.get('NEXT_LOCALE')?.value || 'es'
// Si el locale es 'es', no redirigir
if (locale === 'es') {
return
}
return NextResponse.redirect(
new URL(`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`, req.url)
)
}
}
import { NextRequest, NextResponse } from 'next/server' const PUBLIC_FILE = /\.(.*)$/ export async function middleware(req: NextRequest) { if ( req.nextUrl.pathname.startsWith('/_next') || req.nextUrl.pathname.includes('/api/') || PUBLIC_FILE.test(req.nextUrl.pathname) ) { return } if (req.nextUrl.locale === 'default') { const locale = req.cookies.get('NEXT_LOCALE')?.value || 'es' // Si el locale es 'es', no redirigir if (locale === 'es') { return } return NextResponse.redirect( new URL(`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`, req.url) ) } }
import { NextRequest, NextResponse } from 'next/server'

const PUBLIC_FILE = /\.(.*)$/

export async function middleware(req: NextRequest) {
  if (
    req.nextUrl.pathname.startsWith('/_next') ||
    req.nextUrl.pathname.includes('/api/') ||
    PUBLIC_FILE.test(req.nextUrl.pathname)
  ) {
    return
  }

  if (req.nextUrl.locale === 'default') {
    const locale = req.cookies.get('NEXT_LOCALE')?.value || 'es'

    // Si el locale es 'es', no redirigir
    if (locale === 'es') {
      return
    }

    return NextResponse.redirect(
      new URL(`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`, req.url)
    )
  }
}

 

In this file, I have indicated that it redirects all calls without interfering in the initial routing of Next.js whether or not the language path /en or /es is included.

I have forced it so that by default it does not include the path for the default language (es) in this way the translations will behave:

mysite.web/ -> Español

mysite.web/en -> Inglés

Now we need to configure each screen where we want to apply Server Side Rendering (SSR) translations

Let’s go for example to index.tsx and add:

Import i18next:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useTranslation } from 'next-i18next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';

 

Add the i18next translation hook:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const Home: React.FC<HomeProps> = ({ articles }) => {
const { t } = useTranslation();
....
const Home: React.FC<HomeProps> = ({ articles }) => { const { t } = useTranslation(); ....
const Home: React.FC<HomeProps> = ({ articles }) => {

  const { t } = useTranslation();

....

 

To use the translation we will put:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{t('common:title_app')}
{t('common:title_app')}
        {t('common:title_app')}

We indicate common: to refer to the file where the translation is.

Returns only the server-side rendering part, since as it is now it will only translate on the client side.

We go or create the function export async function getServerSideProps(context: any) {

This function applies to Server Side rendering.

Returns only the HTML translated, without any addition:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let { locale } = context;
// Si locale no está definido, establecer un valor predeterminado
if (!locale) {
locale = 'es'; // Reemplaza 'es' con tu localización predeterminada
}
return {
props: {
...(await serverSideTranslations(locale, ['common'])),
},
};
let { locale } = context; // Si locale no está definido, establecer un valor predeterminado if (!locale) { locale = 'es'; // Reemplaza 'es' con tu localización predeterminada } return { props: { ...(await serverSideTranslations(locale, ['common'])), }, };
let { locale } = context;

// Si locale no está definido, establecer un valor predeterminado
if (!locale) {
  locale = 'es'; // Reemplaza 'es' con tu localización predeterminada
}

return {
    props: {
      ...(await serverSideTranslations(locale, ['common'])),
    },
  };

 

We get the locale, as it can detect by default the translation either from the path or from the browser itself.

I indicate that if it fails to get the locale, use «en» by default.

Returns only the HTML code translated to English, without any addition.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
...(await serverSideTranslations(locale, ['common', "translations_2" , "translations_3"])),
...(await serverSideTranslations(locale, ['common', "translations_2" , "translations_3"])),
        ...(await serverSideTranslations(locale, ['common', "translations_2" , "translations_3"])),

Translate getServerSideProps:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
let { locale } = context;
//obtenemos la carpeta de traducciones:
const translations = (await serverSideTranslations(locale, ['common'])) as SSRConfig & { [key: string]: any };
//Seleccionamos la clave que queremos utilizar
const textoTraducido = translations._nextI18Next && translations._nextI18Next.initialI18nStore[locale] && translations._nextI18Next.initialI18nStore[locale].common? translations._nextI18Next.initialI18nStore[locale].common.title_app : '',
let { locale } = context; //obtenemos la carpeta de traducciones: const translations = (await serverSideTranslations(locale, ['common'])) as SSRConfig & { [key: string]: any }; //Seleccionamos la clave que queremos utilizar const textoTraducido = translations._nextI18Next && translations._nextI18Next.initialI18nStore[locale] && translations._nextI18Next.initialI18nStore[locale].common? translations._nextI18Next.initialI18nStore[locale].common.title_app : '',
  let { locale } = context;

//obtenemos la carpeta de traducciones:
const translations = (await serverSideTranslations(locale, ['common'])) as SSRConfig & { [key: string]: any }; 

//Seleccionamos la clave que queremos utilizar
const textoTraducido = translations._nextI18Next && translations._nextI18Next.initialI18nStore[locale] && translations._nextI18Next.initialI18nStore[locale].common? translations._nextI18Next.initialI18nStore[locale].common.title_app : '',

Important: We cannot use the function …(await serverSideTranslations(locale, [‘common’])) in several nested components, what we must do is share the variable t between all the components from the main one:

Test:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const Home: React.FC<HomeProps> = ({ articles, pathActual }) => {
const { t } = useTranslation();
...
return (
<TituloPrincipalH1 texto={t(`common:titulo`)} />
<Lista elements={elements} onClick={() => { loadMore}} t={t} />
);
const Home: React.FC<HomeProps> = ({ articles, pathActual }) => { const { t } = useTranslation(); ... return ( <TituloPrincipalH1 texto={t(`common:titulo`)} /> <Lista elements={elements} onClick={() => { loadMore}} t={t} /> );
const Home: React.FC<HomeProps> = ({ articles, pathActual }) => {

  const { t } = useTranslation();

...

return (
    
     <TituloPrincipalH1 texto={t(`common:titulo`)} />
     <Lista elements={elements} onClick={() => { loadMore}} t={t} />
       
  );

Put text directly:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<TituloPrincipalH1 texto={t(`common:titulo`)} />
<TituloPrincipalH1 texto={t(`common:titulo`)} />
<TituloPrincipalH1 texto={t(`common:titulo`)} />

Use t function:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Lista elements={elements} onClick={() => { loadMore}} t={t} />
<Lista elements={elements} onClick={() => { loadMore}} t={t} />
<Lista elements={elements} onClick={() => { loadMore}} t={t} />

Import in other component:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
interface Props{
elements: number[]
t: TFunction;
}
const Lista: React.FC<Props> = ({ elements, t }) => {
interface Props{ elements: number[] t: TFunction; } const Lista: React.FC<Props> = ({ elements, t }) => {
interface Props{
  elements: number[]
  t: TFunction;
}

const Lista: React.FC<Props> = ({ elements, t }) => {

LanguajeSwitcher.tsx

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
const LanguageSwitcher = () => {
const router = useRouter()
const [currentLanguage, setCurrentLanguage] = useState(router.locale)
useEffect(() => {
setCurrentLanguage(router.locale)
}, [router.locale])
const getLanguageLink = (lang: string) => {
const currentPath = router.asPath
return lang === 'es' ? currentPath.replace(/^\/en/, '') : `/en${currentPath}`
}
return (
<div>
<Link href={getLanguageLink('es')} locale={false}>
<div style={{ color: currentLanguage === 'es' ? 'grey' : 'black' }}>Español</div>
</Link>
<Link href={getLanguageLink('en')} locale={false}>
<div style={{ color: currentLanguage === 'en' ? 'grey' : 'black' }}>English</div>
</Link>
</div>
)
}
export default LanguageSwitcher
import Link from 'next/link' import { useRouter } from 'next/router' import { useEffect, useState } from 'react' const LanguageSwitcher = () => { const router = useRouter() const [currentLanguage, setCurrentLanguage] = useState(router.locale) useEffect(() => { setCurrentLanguage(router.locale) }, [router.locale]) const getLanguageLink = (lang: string) => { const currentPath = router.asPath return lang === 'es' ? currentPath.replace(/^\/en/, '') : `/en${currentPath}` } return ( <div> <Link href={getLanguageLink('es')} locale={false}> <div style={{ color: currentLanguage === 'es' ? 'grey' : 'black' }}>Español</div> </Link> <Link href={getLanguageLink('en')} locale={false}> <div style={{ color: currentLanguage === 'en' ? 'grey' : 'black' }}>English</div> </Link> </div> ) } export default LanguageSwitcher
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'

const LanguageSwitcher = () => {
    const router = useRouter()
    const [currentLanguage, setCurrentLanguage] = useState(router.locale)

    useEffect(() => {
        setCurrentLanguage(router.locale)
    }, [router.locale])

    const getLanguageLink = (lang: string) => {
        const currentPath = router.asPath
        return lang === 'es' ? currentPath.replace(/^\/en/, '') : `/en${currentPath}`
    }

    return (
        <div>
            <Link href={getLanguageLink('es')} locale={false}>
                <div style={{ color: currentLanguage === 'es' ? 'grey' : 'black' }}>Español</div>
            </Link>
            <Link href={getLanguageLink('en')} locale={false}>
                <div style={{ color: currentLanguage === 'en' ? 'grey' : 'black' }}>English</div>
            </Link>
        </div>
    )
}

export default LanguageSwitcher

 

 

0

Leave a Comment