
In thinking about how to increase traffic to my blog, I decided to try setting up multilingual support.
I explored various methods, but the first step is routing differently based on language.
I decided to try using next-intl.
1. Installation
yarn add next-intl2. Configuration
It is somewhat tricky to apply.
Simply follow the steps below.
Put all folder routing inside the app folder into a [locale] folder.
Create an i18n folder and set up config.ts, request.ts, ko.json, and en.json files inside.
Configure middleware.ts and next.config.ts
Wrap children with params and provider in the layout
Fetch messages in the components
3. Trying It Out
1) [locale] Folder

The internal folder structure of my app is as above.
Here, I plan to wrap all folders under the app, excluding api and uploads, into [locale].
When moving folders with .next present, a permission error appears, so remove it with sudo rm -r .next and then move the folders.
Note that if there are any calls using relative paths due to the folder structure change, they must be changed.

2) Creating the i18n Folder
If you have a src folder, create it under src; otherwise, you can create it at the root folder.
I created an i18n folder with config.ts and request.ts, and within a message folder, ko.json and en.json.

Then, set the config.ts as follows.
I configured the default path "/" to be in Korean.
export const locales = ['ko', 'en'] as const;
export type Locale = (typeof locales)[number];
export const defaultLocale: Locale = 'ko'; // Set default path to Korean
export const pathnames = {
'/': '/',
'/about': {
ko: '/about',
en: '/about',
},
'/post/[slug]': {
ko: '/post/[slug]',
en: '/post/[slug]',
},
} as const;And set the request.ts as follows.
It's a method that calls locale information and returns it to the server.
import { getRequestConfig } from 'next-intl/server';
import { defaultLocale } from './config';
export default getRequestConfig(async ({ requestLocale }: { requestLocale: Promise<string | undefined> }) => {
const locale = await requestLocale || defaultLocale;
return {
locale,
messages: (await import(`./message/${locale}.json`)).default,
};
});3) Configuring middleware.ts and next.config.ts
First, set middleware.ts as below.
Just think of it as adding createMiddleware at the start as an export.
import createMiddleware from 'next-intl/middleware';
import { locales, defaultLocale } from './i18n/config';
export createMiddleware({
locales,
defaultLocale,
localePrefix: 'as-needed', // Default (ko) has no prefix in paths
});
//...existing settings...//
export default withAuth(
function middleware(req) {
//...existing settings...//
});Then, create createNextIntlPlugin in next.config.ts as below, and wrap the existing settings.
import type { NextConfig } from "next";
import createNextIntlPlugin from 'next-intl/plugin';
const withNextIntl = createNextIntlPlugin()
const withNextIntlConfig: NextConfig = withNextIntl({
existing next.config settings...
});
export default withNextIntlConfig;4) Wrapping in Layout
Now, after fetching the locale with getMessage in the layout, wrap it with the provider so it can be used in the child components.
import { NextIntlClientProvider } from "next-intl";
import { getMessages, getLocale } from "next-intl/server";
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const messages = await getMessages();
const locale = await getLocale();
return (
<html
lang={locale}
>
<NextIntlClientProvider locale={locale} messages={messages}>
<body>
{children}
</body>
</NextIntlClientProvider>
</html>
);
}5) Trying Out Messages
First, function names differ when rendering on the server vs. the client side.
import {useTranslations, useLocale, useFormatter} from 'next-intl'; // Call in 'use client' component
import {getTranslations, getLocale, getFormatter} from 'next-intl/server'; // Call in server componentCategory | Client Component | Server Component / Server Function | Description |
|---|---|---|---|
Translations |
|
| Fetch translation messages like |
Locale |
|
| Returns the current active locale ( |
Formatter |
|
| Formats dates, times, numbers, etc., according to the locale |
Messages | — |
| The entire translation message object for the current request (usually used for Provider in layout) |
Request Locale Setup | — |
| Ensures locale consistency during SSG·SSR caching, used in layout |
I decided to change /about first.
My current /about component is hardcoded, so it seems easy to change.
import {
HeroSection,
JourneySection,
ProjectsSection,
} from "@/components/about";
const baseUrl = process.env.NEXT_PUBLIC_URL||"";
export default function AboutPage() {
...
const heroData = {
...
};
const journeyData = {
title: "My Journey",
items: [
.....
]
};
const projectsData = {
title: "Featured Projects",
projects: [
{
title: "University Entrance Analysis",
...
},
...
],
};
return (
<div className="min-h-screen bg-gray-900 ">
<main className="rounded-lg container relative mx-auto scroll-my-12 overflow-auto">
{/* Language Selector */}
<HeroSection {...heroData} />
<JourneySection {...journeyData} />
<ProjectsSection {...projectsData} />
</main>
</div>
);
}
Now, set up ko.json and en.json and use getTranslations to insert text in the component.

However, the problem is that what is fetched with getTranslations comes out as a string structure only.
When I tried to change it as shown below, an error occurred.
export default async function AboutPage() {
...
const t = await getTranslations("about");
const heroData = JSON.parse(JSON.stringify(t("hero")));
const journeyData = JSON.parse(JSON.stringify(t("journey")));
const projectsData = JSON.parse(JSON.stringify(t("projects")));
...
}It was so cumbersome that I got help from Curi.

That way, I passed everything to the subcomponents as props.
Then checked after accessing in en.

You can see that it’s applied well.
4. Conclusion
Until now, I thought I had to manually translate all blog posts into English, but with AI and many automated tools, it seems this can be done easily.
The remaining task is to translate all blog posts and put them into the database.
Then, adding locale to the database and returning posts by searching with locale should work.
I should try this soon.
댓글을 불러오는 중...