iTranslated by AI
Building a Multilingual Site with Next.js Was Incredibly Easy
Background of attempting multi-language support in Next.js
Previously, as a hobby, I developed a service called Blog Friends. By registering your blog's RSS, you can display your posting frequency in a GitHub-style contribution heatmap and compete with other users for consistency.
I decided to implement English support to submit this service to ProductHunt (a site where you can post web services and apps you've created for an international audience).
Here is the Japanese site:
And here is the English site:
The result of submitting to ProductHunt was 12 Upvotes—a somewhat underwhelming result—but I have no regrets because I learned a lot about building multi-language sites.
How to create a multi-language site with Next.js
Understanding Routing
As stated on the official website, there are two routing methods for creating multi-language sites in Next.js:
- Domain Routing
- Sub-path Routing
Domain Routing
Routing that switches languages based on the domain. For example:
Showing Japanese at example.com/blog and English at example.en/blog.
You can set up the domain for each language in next.config.js as follows:
// next.config.js
module.exports = {
i18n: {
locales: ['en', 'ja'],
defaultLocale: 'ja',
},
domains: [
{
domain: 'example.com',
defaultLocale: 'ja',
},
{
domain: 'example.en',
defaultLocale: 'en',
},
]
}
Sub-path Routing
A method where the language is switched by the path instead of the domain.
For example, it displays English under example.com/en.
In this project, I didn't have the budget to prepare extra domains, so I adopted Sub-path Routing.
For Sub-path Routing, you just need to configure next.config.js as follows:
// next.config.js
module.exports = {
i18n: {
locales: ['en', 'ja'],
defaultLocale: 'ja',
},
}
With just this, the locale is automatically determined as en when someone accesses anything under /en.
Creating hooks that return language sets per locale
Once you understand routing, let's try creating hooks that return the language set for each locale.
This article was very helpful:
It didn't take exactly 10 minutes, but thanks to it, I was able to make the site English-compatible in a few hours.
Create a hook that extracts locale from useRouter and returns a translation set according to each locale as follows:
import { useRouter } from 'next/router'
import en from "../locales/en";
import ja from "../locales/ja";
export const useLocale = () => {
const { locale } = useRouter();
const t = locale === "en" ? en : ja;
return { locale, t };
}
export default {
RANKED_HIGH_CONDITION: "Among all bloggers, the users who are doing their best to update the blog are ranked high.",
UNCATEGORIZED_RANKED_HIGH_CONDITION: "Among Uncategorized bloggers, the users who are doing their best to update the blog are ranked high.",
SEE_ALL_BLOGGERS: "See all bloggers",
...
}
export default {
RANKED_HIGH_CONDITION: "全ブロガーの中で頑張ってブログを更新しているユーザーを上位表示します",
UNCATEGORIZED_RANKED_HIGH_CONDITION: "未分類のブロガーの中で頑張ってブログを更新しているユーザーを上位表示します",
SEE_ALL_BLOGGERS: "全ブロガーをもっとみる",
...
}
Thanks to this, I could easily translate into each language using the hook like this:
const SignupButton = () => {
const { t } = useLocale()
return (<Button>{t.SIGNUP}</Button>)
}
Measures for ISR and SSG
When using getStaticProps, you need to be careful about returning additional paths as follows:
// pages/blog/[slug].js
export const getStaticPaths = ({ locales }) => {
return {
paths: [
// if no `locale` is provided only the defaultLocale will be generated
{ params: { slug: 'post-1' }, locale: 'en' },
{ params: { slug: 'post-1' }, locale: 'ja' },
],
fallback: true,
}
}
It is easy and recommended to first create paths for one language and then duplicate them for other languages based on that, as shown below:
export const getStaticPaths: GetStaticPaths = async () => {
const paths = categories.map(c => ({
params: {
slug: c.slug as string,
},
locale: 'ja',
}))
// Create paths for English as well
paths.push(...paths.map(p => ({ ...p, locale: 'en' })))
return {
paths,
fallback: false
}
}
Language Switching
I also prepared a language switching menu like the one below so that English users who accidentally visit the Japanese environment can switch languages smoothly.

Language switching can be easily done by specifying locale in next/link as follows:
<Link href="/" locale="en" passHref>...</Link>
For example, the link above redirects to /en.
<Link href="/categories/all" locale="en" passHref>...</Link>
Also, in the case above, it redirects to /en/categories/all.
Link without a specified locale will transition considering the current locale.
For example, if a user in the en locale clicks a link like the one below:
<Link href="/categories/all" passHref>...</Link>
It will redirect to /en/categories/all.
In other words, you normally don't need to be aware of the locale in Link, and you specify the locale in Link when you want to perform a screen transition across locales.
Summary
Next.js is designed with i18n in mind even without introducing special plugins or libraries, making multi-language support very easy.
If you ever need to make a site built with Next.js multi-lingual, I would be happy if you remember this article.
Discussion