Next.js를 사용해서 다국어 지원하는 방법 - next-intl, next app router
Nextjs 13/14에서 다국어(localization)를 지원 할 수 있도록 설치 부터 어떻게 다른 언어를 사용 할 수 있는지 알아보겠습니다. 예시에서 사용될 next.js에 버전은 13/14이고 여기서 app 라우팅을 사용해서 알려드리려고 합니다. Localization을 지원하기위해서 사용한 라이브러리는 next-intl 입니다.
Internalization을 해야 하는 이유
한국어를 적용해서 만들어도 사이트에 많은 사람들이 들어올 수도 있지만 꼭 한국 사람들을 위한 서비스가 아니라면 전 세계를 대상으로 타게팅해서 사이트나 앱 유입을 높이는 것이 좋습니다. 한국 사람들만 사이트를 사용한다면 현재 기준으로 2천5백만 명에게 접근할 수 있습니다.
South Korea 25,300,000 하지만 영어도 같이 지원하게 되면 인구수가 많은 인도나 미국에서도 서비스를 사용할 수 있기 때문에 아래와 같이 엄청나게 많은 사람들에게 접근 할 수 있습니다.
India 469,000,000 United States 282,000,000
Nextjs app router와 next-intl을 설치하기
pnpm install next-intl
next-intl을 다 설치했다면 아래와 같이 파일 및 폴더를 다음과 같이 구성하면 됩니다.
├── messages (1)
│ ├── en.json
│ └── ...
├── i18n.js
├── middleware.ts (2)
└── app
└── [locale]
├── layout.tsx (3)
└── page.tsx (4)
messages 폴더는 언어 관련 json을 포함할 것이고 Middleware.ts는 자동으로 /en 또는 /kr로 라우팅 할 수 있게 해 줄 수 있습니다.
Nextjs에서 [] 안에 들어가는 폴더는 react router에서 나오는 id 부분처럼 사용되는 것입니다.
/:id/happy
middleware.js
import createMiddleware from 'next-intl/middleware'
export default createMiddleware({
// 지원 할 언어 리스트
locales: ['kr', 'en'],
// 기본 언어로 /kr 없이 사용가능. /about으로 가면 한국어로 보여지지만 영어로 보려면
// /en/about으로 가야한다.
defaultLocale: 'kr',
})
export const config = {
// Skip all paths that should not be internationalized. This example skips
// certain folders and all pathnames with a dot (e.g. favicon.ico)
matcher: ['/((?!api|_next|_vercel|.*\\..*).*)'],
}
먼저 middleware는 다음과 같이 설정해 주시면 됩니다. 다국어 지원하는 언어들을 locales 배열에 넣고 기본 언어도 적용해 줍니다.
i18n.js
import { getRequestConfig } from 'next-intl/server'
export default getRequestConfig(async ({ locale }) => {
return {
messages: (await import(`./messages/${locale}.json`)).default,
}
})
자동으로 언어별 json을 읽어와서 message로 저장해줍니다. 나중에 useMessage hook을 사용하면 message를 사용할 수 있게 됩니다. 이제 다음으로 app/layout.js에 가셔서 message를 NextIntlClientProvider를 통해서 제공해 줘야 합니다.
i18n에서 미리 locale에 따라 json을 미리 가져왔기 때문에 바로 NextIntlClientProvider에 넘겨주면 됩니다. 이렇게 하면 아래 모든 children들도 아래처럼 useTranslations hook을 통해서 사용할 수 있습니다. Object에 접근하는 것처럼 t('name.test.a')이런 식으로 사용이 가능합니다.
const t = useTranslations('word-counter')
Nextjs 13/14 next-intl 다국어 지원 사용 예시
이제 간단하게 제가 어떻게 서로 다른 언어를 적용했는지 예를 통해서 알아보도록 하겠습니다. 제가 아래 처럼 언어 파일이 있다면
// messages/en.json
{
"shared": {
"header": {
"text-tools": "Text Tools",
"text-compare": "Text compare",
"word-counter": "Word Counter"
}
}
}
// messages/kr.json
{
"shared": {
"header": {
"text-tools": "Text Tools",
"text-compare": "텍스트 비교",
"word-counter": "단어 수 세기"
}
}
}
제가 header에 다국어를 지원하려면 아래 처럼 사용하면 됩니다.
import React from 'react'
import Link from 'next/link'
import { useTranslations } from 'next-intl'
export const Header = () => {
const t = useTranslations('shared.header')
return (
<header className="flex justify-between px-3 py-3 border">
<div>
<Link href="/">{t('text-tools')}</Link>
</div>
<section className="flex gap-4">
<Link href="/text-counter">{t('word-counter')}</Link>
<Link href="/text-compare">{t('text-compare')}</Link>
</section>
</header>
)
}
위에 보이는 것 처럼 useTranslations에 Object key를.로 연결 해주면 prefix 형태로 사용할 수 있습니다.
그냥 useTranslations('shared')로 하고 t('header.text-tools')로 맞는 글을 찾아 주셔도 됩니다. 화면에 보이는 언어도 중요하지만 SEO관련 title, description 등 들도 언어에 맞게 변경해야 합니다. Next 13/14에서는 SEO metadata를 다이내믹하게 생성할 수도 있습니다.
위에 올라가면 layout 파일에 metadata를 설정할 수도 있지만 그러면 다른 언어를 지원할 수 없기 때문에 generateMetadata라는 함수를 정의해줘야 합니다. 먼저 generateMetadata는 Server component로 사용되기 때문에 useState과 같은 client 코드나 'use client' 같이 사용될 수 없습니다. 만약에 같이 사용한다면 다음과 같은 에러 메시지를 볼 겁니다.
You are attempting to export "generateMetadata" from a component marked with "use client"
page나 layout에 다음과 같이 generateMetadata를 정의해 줍니다. 앞에서 useTranslations를 사용했던 것 과 비슷하게 다음처럼 createTranslator를 사용해서 message를 가져와서 다음과 같이 SEO metadata를 정의해 주면 nextjs가 알아서 적용해 줍니다. 페이지에 가서 잘 적용되는지 확인해 보길 추천합니다.
import { createTranslator } from 'next-intl'
export async function generateMetadata({ params: { locale } }) {
// import path는 자기 프로젝트에 맞게 변경해주세요
const messages = (await import(`@/messages/${locale}.json`)).default
const t = createTranslator({ locale, messages: messages['word-counter'] })
return {
title: t('seo_text_section.title'),
description: t('seo_text_section.description'),
}
}
이상으로 Nextjs를 사용해서 Internalization을 적용하는 방법을 알아보았습니다. 개인적으로 간단한 툴링을 제공하는 웹사이트를 만들고 있는데 영어도 적용하는 게 좋을 것 같아서 적용해 보고 방법을 공유해보았습니다.