In Part 1 we created the foundation of our Next.js 14 project. In Part 2 we created the frontend with Contentful (GraphQL) as a headless CMS, Tailwindcss for styling, and Typescript for the coding. In Part 3 we added a header and a footer component, as well as the so-called draft/preview functionality offered by Contentful, and last but not least the nowadays mandatory dark mode toggle. In Part 4 we are going to add a custom 404 page, a loading UI (SVG Spinner) and we add some extra colors to our tailwind.config.ts. In Part 5, we will add multilanguage support with the package i18next and a language switcher. We will show how you can use the internationalization feature for Client and Server components and of course in combination with Contentful and the GraphQL API.
Here already a spoiler to the final result of Part 5 -> https://nextjs14-internationalization-with-contentful-i18next.vercel.app/

Let’s add new NPM packages for the language handling
Now we have the needed packages ready and we can start with the real implementation.
Edge Middleware — Request handling
We will create the middleware.ts file directly under src. Edge Middleware (Functions) runs code that executes before a request is processed on a site, to provide speed and personalization to your users. Functions using the Edge runtime execute in the data center is closest to the user.
In our case the fallbackLng is “en-US” and therefore this URL segment will be removed from the path with a redirect, so that /en-US/about becomes /about
If the locale is missing we are doing a rewrite, so that our blog receives the locale (fallbackLng).
Now we add the folder i18n under src/app. This folder has a subfolder called locales, where we create a new subfolder for every locale that we would like to handle.

In this example, we add “de-DE”. In the file common.json, we add the translation for labels, buttons, etc., while the rest is handled directly in Contentful.

for en-USThere are three additional files in the folder “i18n”:
client.ts
Translation handling for client components.
server.ts
Translation handling for server components
settings.ts
(general options needed for client and server)
We import "useTranslation" for the client components and "createTranslation" for the server components.
or
common refers to the namespace "common" -> common.json file under src/app/i18n/locales/de-DE/
Adding a new locale (de-DE) on Contentful
Now we move to Contentful because we have to create a second locale.




Now we can open the content item “TestBlogPost” and enable the second locale de-DE.


If not yet done during the creation of the content type, you have to enable the localization of the field that needs it, like the title, subtitle, content, etc.


Please check your content items add the needed translated content and republish them, so that we have the content items in both languages ready.
New/modified/deleted components/pages
Now we will add a new folder [locale] under src/app. Move your folder [slug] and the files page.tsx and layout.tsx into this new folder and recreate a new layout.tsx under src/app.
Let’s take a look at the moved layout.tsx, now in the folder src/app/[locale]
We added the import
and we are passing a new type “LayoutProps” with our children and locale in it. We then use this new type in our Layout function because we have to pass the locale prop to our functions/components.
Our GraphQL files were already prepared with the variable “locale”
so that we can query Contentful for the right content.
As a last step, we have to modify our pages/components, so that the language handling works as expected.
Here are all the files that were modified (added, deleted, moved)

Language Switcher
We add new code to our navbar.component.tsx for the language switcher.On the top, we add these new imports:
Under “export default function Navbar…..” we add these new “const” and the async function for handling the click on the language switcher.
In the section “Desktop View” we have to add the new “locale” for our links.
And of course the language dropdown with the needed tailwind styles
Tipp: If you are asking where you can get these nice flags? Well, you can use this link -> https://emojipedia.org/
You can use these icons within Contentful as well. Edit the content type you like and copy & paste it to the field -> save.


As always, you will find the complete code in my Github Repo listed below. Stay tuned for the next steps where we will add:
On-site search with Algolia (Extended data sync between Contentful and Algolia via an Azure Logic App)
Adding new page content type in addition to GraphQL deep dive
Registration/Login/User role handling with Next-Auth and Azure Entra ID
and lot more. As always we will only use services in the free plan
If you like what you see, then please support me with a clap or follow me on medium.com.
Github Repo with full code
https://github.com/cloudapp-dev/nextjs14-typescript-app-router-contentful/tree/nextjs14-part5