Next.js 14 - How to integrate i18n with client and server components?

Hi Friends,

I am trying to integrate internationalization (i18n) for a Next.js 14 (app directory) project. However I get a lot of errors related to client and server components. Actually I followed the Next.js 14 documentation for internationalization.

At the moment I am not using an additional library for internationalization. All I am doing is pass down “lang” to the child component to use it there. Would you recommend to install an additional library?

At the moment I have a translation file (for German and English) and all I want to do is get the data and display it in the frontend. However I always get the error:

TypeError: Cannot read properties of undefined (reading ‘lang’)

I dont understand how “lang” can be undefined? The translation file is locally hosted in my application., so it should be always available. Btw: I am using TypeScript.

Furthermore there are different methods for client and server components. Here the method I am using for client components:

'use client';

import React, { useState, useEffect } from 'react';
import FaqData from '../../../../data/FaqData.json';
import { AiOutlinePlus, AiOutlineMinus } from 'react-icons/ai';

import { Locale } from '../../../../i18n.config';
import { getlocales } from '../../../actions';

import { FAQSectionTranslation } from '../../../../types';

export default function FaqSection({ lang = 'de' }: { lang: Locale }) {
  const [activeQuestion, setActiveQuestion] = useState<number | null>(null);
  const [faqSection, setFaqSection] = useState<FAQSectionTranslation | null>(
    null
  );

  useEffect(() => {
    console.log('Current language:', lang);

    async function fetchData() {
      try {
        const data = await getlocales(lang);
        setFaqSection(data.sections.faqSection);
      } catch (error) {
        console.error('Error fetching tools data:', error);
        try {
          const defaultData = await getlocales('de');
          setFaqSection(defaultData.sections.faqSection);
        } catch (defaultError) {
          console.error('Error fetching default language data:', defaultError);
        }
      }
    }

    fetchData();
  }, [lang]);

  const toggleQuestion = (index: number) => {
    setActiveQuestion(activeQuestion === index ? null : index);
  };

  return (
    <div className="container" id="faq-section__container">
      <div className="wrapper" id="faq-section__wrapper">
        <h1 className="main-heading">{faqSection?.mainHeading}</h1>
        <h4 className="sub-heading">{faqSection?.subHeading}</h4>
        <div className="faq-container">
          {FaqData.map((item, index) => (
            <div key={index} className="faq-item">
              <h5
                className={`faq-question ${
                  activeQuestion === index ? 'active' : ''
                }`}
                onClick={() => toggleQuestion(index)}
              >
                {item.question[lang]}
                <span className="faq-icon">
                  {activeQuestion === index ? (
                    <AiOutlineMinus />
                  ) : (
                    <AiOutlinePlus />
                  )}
                </span>
              </h5>
              <div
                className={`faq-answer ${
                  activeQuestion === index ? 'active' : ''
                }`}
              >
                <p>{item.answer[lang]}</p>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

This is the method I am using for server components:

import Image from 'next/image';
import Link from 'next/link';
import Button from '../Layout/BtnComponent';
import HardwareDataTable from '../Tables/HardwareDataTable';
import SoftwareDataTable from '../Tables/SoftwareDataTable';

import { Locale } from '../../../../i18n.config';
import { getlocales } from '../../../actions';

const ProductSection = async ({ lang }: { lang: Locale }) => {
  const { sections } = await getlocales(lang);

  return (
    <>
      <div className="container" id="product-section__container">
        <div className="wrapper" id="product-section__wrapper">
          <h1 className="main-heading">
            {sections.productSection.mainHeading}
          </h1>
          <h4 className="sub-heading">{sections.productSection.subHeading}</h4>

          <div className="product-section__grid">
            {/* <div className="sticky-element">
            <p>Test</p>
          </div> */}
            <div className="product-section__text">
              <h3>{sections.productSection.speamBoxDetailsHeading}</h3>
              <ul className="product-section__data-list">
                <li>{sections.productSection.speamBoxDetails1}</li>
                <li>{sections.productSection.speamBoxDetails2}</li>
                <li>{sections.productSection.speamBoxDetails3}</li>
                <li>{sections.productSection.speamBoxDetails4}</li>
                <li>{sections.productSection.speamBoxDetails5}</li>
                <li>{sections.productSection.speamBoxDetails6}</li>
                <li>{sections.productSection.speamBoxDetails7}</li>
                <li>{sections.productSection.speamBoxDetails8}</li>
                <li>{sections.productSection.speamBoxDetails9}</li>
              </ul>

              <div className="download-btn__wrapper">
                <Link
                  href="/pdfs/Box_Data_Sheet_DE.pdf"
                  passHref
                  target="_blank"
                >
                  <Button
                    className="btn--primary"
                    variant="primary"
                    size="small"
                    type="button"
                  >
                    <Image
                      src="/icons/icon__download.svg"
                      alt="Download"
                      width={20}
                      height={20}
                    />
                    {sections.productSection.btnDownloadDataSheet}
                  </Button>
                </Link>
                <Link
                  href="/pdfs/Box_Info_Sheet_DE.pdf"
                  passHref
                  target="_blank"
                >
                  <Button
                    className="btn--primary"
                    variant="secondary"
                    size="small"
                    type="button"
                  >
                    <Image
                      src="/icons/icon__download.svg"
                      alt="Download"
                      width={20}
                      height={20}
                    />
                    {sections.productSection.btnDownloadInfoSheet}
                  </Button>
                </Link>
              </div>
            </div>
            <div className="product-section__image-wrapper-wrapper">
              <div className="product-section__image-wrapper">
                <div className="product-section__image">
                  <Image
                    src="/images/speambox-connections.webp"
                    alt="Speambox"
                    layout="fill"
                    objectFit="contain"
                  />
                </div>
              </div>

              {/* <div className="product-section__image-wrapper">
                <div className="product-section__image">
                  <Image
                    src="/images/speambox-connections-02.png"
                    alt="Speambox"
                    layout="fill"
                    objectFit="contain"
                  />
                </div>
              </div> */}
            </div>
          </div>

          <div className="data-table__wrapper">
            <hr />
            <HardwareDataTable lang={lang} />
            <hr />
            <SoftwareDataTable lang={lang} />
          </div>
        </div>
      </div>
    </>
  );
};

export default ProductSection;

I get the following build error:

TypeError: Cannot read properties of undefined (reading 'lang')
> Export encountered errors on following paths:
        /[lang]/agb/page: /de/agb
        /[lang]/agb/page: /en/agb
        /[lang]/danke/page: /de/danke
        /[lang]/danke/page: /en/danke
        /[lang]/datenschutz/page: /de/datenschutz
        /[lang]/datenschutz/page: /en/datenschutz
        /[lang]/impressum/page: /de/impressum
        /[lang]/impressum/page: /en/impressum
        /[lang]/page: /de
        /[lang]/page: /en

Can anyone let me know what I am doing wrong? I would highly apprechiate if someone could guide me in the right direction or if someone can let me know another method to use i18n within Next.js 14 :slight_smile:

Actually I wanted to create a languageProvider which will wrap all the components to pass doen the “lang” to the children. However I dont want the index page to be a client component and always when I wrap the components inside a languageProvider the entire page component will become a client component.

How can I fix this issue? :thinking:

Thank you very much for answer!

Hello, about in this instance is going to go at the end of a URL, something like http://localhost:3000/about should give more feedback. This is what was on stack overflow. Good luck

1 Like

Thanks, but this is not related to my error…

1 Like

Hello!

Know that your frustration and feeling “that should actually work” is growing in the Next community. The current lack of developer feedback isn’t helping either. You can watch this closely on r/Next with some reporting that their mistake is actually a bug in the framework.

I love Next, but I will stay away for now with new projects. Some have abandonded Next for using React/Vite and adding Remix for SSR. If your problems keep growing, maybe consider a rewrite in the tech stack above or try SvelteKit.

If it is a dynamic route shouldn’t it be params in the server component?


Post your repo.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.