How to Build Seamless Multilingual Websites with Next.js App Directory & i18n
In this article, we will learn how to build a multilingual website together and try to make it as simple as possible and make the process smooth for you.
To succeed in our mission we will use next-intl library. In a few words it is just a possibility to change the language in our app.
Once our next.js project is launched done, we will follow the step by step guide on the next-intl website.
To add the dependency to our project we use the command npm install next-intl .
With that, we finished step one and now can set up and use it.
and with that, we finished step one and now can set up and use it.
Step number two is to make a messages folder, where we will save json files, each for every different language. In my case its English and French.
In each file, we will make an objects with the description. For example, now were having a title and the description for the Homepage, if we would have other pages, like about page, or contact page, we would have other objects inside this component.
Let me add it as well, in order to give you more clear picture.
Now these would be the objects for other pages.
We are doing the same with other json files and we write the content in the other languages. And on the screenshot below is my french version for the homepage
There is one important thing we should take in mind, we should have the keys the same, for every language, meaning: “Homepage”, “title” and “description” are the same. Only the content of it changes.
This was the second step, we created messages folder, where all the translations happens.
Now we need to set up the whole thing. For that, we need to import something into our next.config file. It will look like this after changes.
import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default withNextIntl(nextConfig);
Next, we need to create i18n.js file and paste this code.
import { notFound } from "next/navigation";
import { getRequestConfig } from "next-intl/server";
// Can be imported from a shared config
export const locales = ["en", "fr"];
export default getRequestConfig(async ({ locale }) => {
// Validate that the incoming `locale` parameter is valid
if (!locales.includes(locale)) notFound();
return {
messages: (await import(`/messages/${locale}.json`)).default,
};
});
To dive into details, we have an locales array const locales = [“en”, “fr”]; with the languages were going to have for our app.
One more thing to keep in mind is to keep the i18n and messages folder on the same level in the folders hierarchy.
The next thing we need is the middleware file on the same level, where we paste other piece of code from the library guide.
import createMiddleware from "next-intl/middleware";
export default createMiddleware({
// A list of all locales that are supported
locales: ["en", "fr"],
// Used when no locale matches
defaultLocale: "en",
});
export const config = {
// Match only internationalized pathnames
matcher: ["/", "/(fr|en)/:path*"],
};
Here as well, locales: [“en”, “fr”], is for the languages we use and then we choose the default language to be on the app. Its English now and I suggest keeping it that way.
And Last thing we need to change is to wrap our main page code with the
NextIntlClientProvider
Step number two is the matching code for our project. We need to apply some code in our _app.js component.
import {NextIntlClientProvider} from 'next-intl';
import {useRouter} from 'next/router';
export default function App({Component, pageProps}) {
const router = useRouter();
return (
<NextIntlClientProvider
locale={router.locale}
timeZone="Europe/Vienna"
messages={pageProps.messages}
>
<Component {...pageProps} />
</NextIntlClientProvider>
);
}
To be exact, we added routing from next/router and NextIntlClientProvider from next-intl.
Now we need to create a folder named locale with the square brackets around and put our main files there.
It will look like this. It is necessary step and needed for routing, because the pathname changes on every language.