COYO-LOG:D
BLOG

nextjs-intl: Next.js 환경에서 다국어 지원하기

2024/8/6

nextjs로 새로운 프로젝트를 진행하는 중에 다국어를 미리 지원해두면 편할 것 같아서 다국어 지원 라이브러리를 적용하면서 그 방법을 정리해두려고 합니다.

nextjs로 다국어 지원은 처음이라 다양한 라이브러리를 찾아보았는데 그 중에서도 app router를 지원하는 라이브러리는 next-intl이 가장 깔끔해서 해당 라이브러리를 선택하였습니다.

next-intl에서 제공하는 example공식 docs를 따라 진행하였습니다.

설치

// npm을 사용하는 경우
npm i next-intl
 
// yarn을 사용하는 경우
yarn add next-intl
 
// pnpm을 사용하는 경우
pnpm i next-intl

다음과 같은 두 가지 경우에 해당하는 경우 i18n routing을 설정할 필요가 없습니다.

  1. 사용자 설정 등에 따라 언어 설정을 제공하는 경우
  2. 하나의 언어만 지원하는 경우

저희 서비스는 사용자가 서비스 안에서 자유롭게 설정할 수 있도록 만들기 위해 [next-intl]App Router: App Router setup with i18n routing 도움말에 따라 진행하겠습니다.

폴더 구조 설정

├── messages
│   ├── en.json (1)
│   └── ...
├── next.config.mjs (2)
└── src
    ├── i18n
    │   ├── routing.ts (3)
    │   └── request.ts (5)
    ├── middleware.ts (4)
    └── app
        └── [locale]
            ├── layout.tsx (6)
            └── page.tsx (7)

messages 폴더 생성

next.config.[mjs/js]가 있는 디렉토리에 messages 폴더를 생성하여 json 파일을 생성합니다.

// en.json
{
  "SignUpPage": {
    "title": {...}
  }
}
 
// ko.json
{
  "SignUpPage": {
    "title": {
      "verify": "이메일 인증",
      "register": "회원 정보 입력"
    }
  }
}

next config 설정

next.config.[mjs/js]
import createNextIntlPlugin from 'next-intl/plugin';
 
const withNextIntl = createNextIntlPlugin();
 
/** @type {import('next').NextConfig} */
const nextConfig = {};
 
export default withNextIntl(nextConfig);

routing.ts 생성

24년 9월 10일에 추가된 내용입니다.

routing.ts
  import {defineRouting} from 'next-intl/routing';
 
  export const routing = defineRouting({
    locales: ['en', 'ko'],
    defaultLocale: 'ko'
  });

middleware.ts 생성

middleware.ts
import createMiddleware from 'next-intl/middleware';
import {routing} from "@i18n/routing";
 
// export default createMiddleware({ => 업데이트: routing으로 변경
// 	// A list of all locales that are supported
// 	locales: ['en', 'ko'],
//
// 	// Used when no locale matches
// 	defaultLocale: 'ko'
// });
 
export default createMiddleware(routing);
 
export const config = {
	// Match only internationalized pathnames
	matcher: ['/', '/(ko|en)/:path*']
};

request.ts 생성

i18n.ts 에서 i18n/request.ts으로 변경되었습니다.

i18n/request.ts
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';
import {routing} from "@i18n/routing";
 
// Can be imported from a shared config
// export const locales = ['en', 'ko'];  => 업데이트: routing으로 변경
 
export default getRequestConfig(async ({locale}) => {
	// Validate that the incoming `locale` parameter is valid
	if (!routing.locales.includes(locale as any)) notFound();
 
	return {
		messages: (await import(`../messages/${locale}.json`)).default
	};
});

app/[locale]/layout.tsx 설정

import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';
 
export default async function LocaleLayout({
  children,
  params: {locale}
}: {
  children: React.ReactNode;
  params: {locale: string};
}) {
  // Providing all messages to the client
  // side is the easiest way to get started
  const messages = await getMessages();
 
  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}

사용하기

export default function RegisterPage() {
	const t = useTranslations("SignUpPage");
 
	return (
		<>
			<h1 className={"title"}>{t("title.register")}</h1>
		</>
	);
}

추가 설정

다음과 같이 설정해놓고 사용한다면 pathname에 locale을 입력하지 않고 사용 가능합니다.

routing.ts

import {defineRouting} from 'next-intl/routing';
import {createSharedPathnamesNavigation} from 'next-intl/navigation';
 
...
 
export const {Link, redirect, usePathname, useRouter} =
	createSharedPathnamesNavigation(routing);

만일 사용언어에 따라 다른 path를 쓴다면 createLocalizedPathnamesNavigation 사용하면 됩니다. 자세한 내용은 공식 문서 참고 바랍니다.

참조