Next.js는 여러 언어를 지원하기 위해 라우팅 및 콘텐츠 렌더링을 구성할 수 있게 해준다. 여러 locale에 대응하는 사이트를 만들기 위해서는 번역된 콘텐츠(localization)와 internationalized routes가 포함된다.
Middleware (/src/middleware.ts)
미들웨어는 클라이언트의 요청에서 locale 을 추출하고, 이에 따라 적절한 리디렉션 또는 리라이트를 수행한다. 만약 클라이언트가 /about에 대한 요청을 하고 locale이 en으로 설정되어 있다면, 미들웨어는 이를 /en/about으로 리디렉션하거나 리라이트할 것이다.
이러한 처리를 통해 사용자는 적절한 언어로 번역된 페이지로 리디렉션되거나 리라이트되어 효과적으로 국제화된 콘텐츠를 제공받을 수 있다. 이것이 국제화를 지원하는 미들웨어의 주요 역할 중 하나다.
import createMiddleware from 'next-intl/middleware'
import { NextRequest } from 'next/server'
import { locales } from './navigation'
/**
* 국제화 (i18n)
* ko-KR : Korean (Korea)
* en-US : English (United States)
* zh-CN : Chinese (S)
* zh-TW : Chinese (T)
* ja-JP : Japanese (Japan)
*/
export default async function middleware(request: NextRequest) {
const acceptLanguage =
request.headers.get('accept-language') || process.env.DEFAULT_LOCALE
const defaultLocales = locales.filter((locale) => {
if (acceptLanguage.startsWith(locale)) return true
else return false
})
const defaultLocale = defaultLocales[0] || process.env.DEFAULT_LOCALE
const handleI18nRouting = createMiddleware({
locales,
defaultLocale,
localePrefix: process.env.LOCALE_PREFIX || ('always' as any),
localeDetection: true
})
const response = handleI18nRouting(request)
if (response.cookies.get('NEXT_LOCALE' as any)) {
response.cookies.delete('NEXT_LOCALE' as any)
}
return response
}
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)']
}
message(/src/data/i18n)
메시지는 로컬에서 제공하거나 원격 데이터 소스에서 로드할 수 있다. 가장 간단한 옵션은 locale을 기반으로 한 JSON 파일을 프로젝트에 추가하는 것이다.
// Json 파일 예시
{
"common-page": {
"loggedIn": "Logged In",
"loggedOut": "Logged Out",
"login": "Sign In",
"logout": "Log Out",
"Settings": "Settings",
"title": "Account"
}
}
다국어 Json파일은 i18n폴더에 언어별 폴더에 json 파일을 넣으면 되며, 각 언어와 json 파일을 매핑하기 위해서는 i18n파일에 해당 json 경로를 명시해주면 된다.
/src/i18n.ts
import { getRequestConfig } from 'next-intl/server'
export default getRequestConfig(async ({ locale }) => {
// messages json 파일들 경로를 명시
const combinedMessages = {
...(await import(`/src/data/i18n/${locale}/common.json`)),
...(await import(`/src/data/i18n/${locale}/display.json`)),
...(await import(`/src/data/i18n/${locale}/event.json`)),
...(await import(`/src/data/i18n/${locale}/goods.json`)),
...(await import(`/src/data/i18n/${locale}/member.json`)),
...(await import(`/src/data/i18n/${locale}/order.json`)),
...(await import(`/src/data/i18n/${locale}/promotion.json`)),
...(await import(`/src/data/i18n/${locale}/search.json`))
}
return {
messages: combinedMessages
}
})
/src/data
message를 사용하기 위해 server와 client단에서 제공해주는 함수가 다른데 공통된 함수로 사용하기 위해 분기처리하여 해당하는 값을 retrun해 준다.
import { getTranslations } from 'next-intl/server'
import { useTranslations } from 'next-intl';
import { isNull } from '@/lib/x2bee-core';
export function getMessage(namespace: string): any {
if (isNull(namespace)) {
throw new Error('Message Client namespace value is not null.');
}
const isServerComponent: () => (boolean) = () => {
return typeof window === 'undefined' ? true : false;
};
if (isServerComponent()) {
let localeTranslations = {}
try {
localeTranslations = getTranslations(namespace);
} catch(error) {
localeTranslations = useTranslations(namespace);
}
return localeTranslations;
} else {
return useTranslations(namespace);
}
}
Server에서 사용 방법
import { getMessage } from '@/lib/common/plugins/messageClient'
const TestPage = async () => {
const t = await getMessage('account-page')
console.log(t)
return (
<>
<h1>{t('loggedIn')}</h1>
</>
)
}
export default TestPage
Client에서 사용 방법
'use client'
import { getMessage } from '@/lib/common/plugins/messageClient'
const TestPage = () => {
const t = getMessage('account-page')
console.log(t)
return (
<>
<h1>{t('loggedIn')}</h1>
</>
)
}
export default TestPage
Navigation
next-intl은 사용자 locale을 자동으로 처리하는 공통 Next.js 네비게이션 API에 대한 솔루션을 제공한다.
import {
createLocalizedPathnamesNavigation,
Pathnames
} from 'next-intl/navigation'
export const locales = ['ko', 'en'] as const
export const localePrefix = 'always' // Default
export const pathnames = {} satisfies Pathnames<typeof locales>
export const { Link, redirect, usePathname, useRouter, getPathname } =
createLocalizedPathnamesNavigation({ locales, localePrefix, pathnames })
Navigation을 사용하여 locale 변경 및 페이지 이동을 구현할 수 있다. 아래 예시 소스는 위에 설정한 navegation에서 route와 link를 사용하여 locale 변경 및 페이지 이동하는 소스이다.
'use client'
import { getMessage } from '@/lib/common/plugins/messageClient'
// next-intl navigation 가져오기
import { Link, useRouter, usePathname } from '@/navigation'
const TestPage = () => {
const t = getMessage('common-page')
const pathname = usePathname()
const router = useRouter()
//router를 사용한 방식
const handleChange = (event) => {
router.replace(pathname, { locale: event.target.value })
}
return (
<>
<h1>{t('loggedIn')}</h1>
<select onChange={handleChange}>
<option>ko</option>
<option>en</option>
</select>
//Link를 사용한 방식
<Link href="/goods" locale="en">
Switch to German
</Link>
</>
)
}
export default TestPage
출력 예시
ko로 설정 시
zh로 설정 시
변경될 경로 예시
/src/app/[locale]/(root)
/src/app/[locale]/[...notFound]
/src/app/[locale]/fo
/src/app/[locale]/goods
/src/app/[locale]/ui
'Next.js 개발 가이드 > 02. 코딩 가이드 및 필수 패키지' 카테고리의 다른 글
7. Parallel Routes, Intercepting Routes (0) | 2024.01.26 |
---|---|
6. Middleware (0) | 2024.01.26 |
4. loading, Suspense, error boundary, zod, dynamic routes, searchParams 사용 예시 (0) | 2024.01.21 |
3. tailwind-merge + clsx (0) | 2023.12.16 |
2. Json schema validator: Zod (0) | 2023.12.15 |