Nextjs 다국어 처리
2024.05.21
라이브러리 선정하기
- 검색 nextjs 13 app router를 구성할때 다국어 지원 라이브러리로 검색을 하였고 두가지 정도의 라이브러리를 뽑았다
- next-18next
- next-intl
- NPM Trend 비교
1번에서 선정한 이 두가지 npm trend를 봤을때 nextjs가 pages router일때는 next-18next
가 매우 높았지만 next-intl
이 점점 따라가는 추세처럼 보였다.
- 최종 선정
i18next
의 공식 페이지 https://next.i18next.com/ 에서 https://locize.com/blog/next-app-dir-i18n/ 를 보면i18next
라이브러리 자체가 nextjs를 위한 라이브러리가 아니기때문에 기존에는 next에서 사용할때에 SSR이나 SSG를 사용하기 위해서 i18next및 여러 라이브러리를 사용하여 직접 세팅을 하여 구성을 한다. (hook, middleware 등)
next-18next의 참고 코드 : https://github.com/hotsunchip/nextjs-app-router
복잡한 서비스를 구성을 하는 거면 이 라이브러리를 사용했겠지만, 그렇지 않기 때문에 next전용 라이브러리로 개발되고 13버전부터 변경된 app-router를 지원하며 초기 세팅이 편한 next-intl을 사용하기로 했다.
next-intl 사용하기
-
설치
pnpm isntall next-intl
-
i18n.ts
i18n.tsimport { notFound } from 'next/navigation'; import { getRequestConfig } from 'next-intl/server'; const locales = ['ko', 'en']; export default getRequestConfig(async ({ locale }) => { if (!locales.includes(locale as any)) notFound(); return { messages: (await import(`../../locale/${locale}.json`)).default, }; });
-
next.config.mjs
next.config.mjsimport createNextIntlPlugin from 'next-intl/plugin'; const withNextIntl = createNextIntlPlugin('./src/utils/server/i18n.ts'); /** @type {import('next').NextConfig} */ const nextConfig = {}; export default withNextIntl(nextConfig);
→ i18n 세팅 파일 경로를 지정해준다.
-
locale 파일생성
- i18n.ts에서 지정한 [local].json 형식으로 만든다.
-
middleware.ts
middleware.tsimport createMiddleware from 'next-intl/middleware'; export default createMiddleware({ locales: ['ko', 'en'], defaultLocale: 'ko', }); export const config = { matcher: ['/', '/(de|en)/:path*'] };
matcher는 항상 locale경로로 이동하게 해준다.
이때
matcher: ['/((?!api|_next/static|_next/image|assets/imgs|favicon.ico).*)']
로설정하면 next/image의 경우 image 파일을 ssr로 최적화해서 화면에 보여주어서 경로가 _next/image로 보내주게 되는데 이때는 locale경로를 사용하지 않고, api또한 마찬가지로 이때에는 local경로를 사용하지 않게 한다.같은 이유로 이미지 파일이 있는 경로인
assets/imgs
도 추가한다. -
router 세팅
├── app ├── [locale] │ ├── layout.tsx │ └── page.tsx └── api
기존 파일들을 다이나믹 라우팅으로 [locale]로 만들어서 넣는다.
-
provider
-
Root
layout.tsx
: server 컴포넌트layout.tsximport { Providers } from '@/system'; import '@/styles/globals.css'; import { getMessages } from 'next-intl/server'; export default async function RootLayout({ children, params: { locale }, }: Readonly<{ children: React.ReactNode; params: { locale: string }; }>) { const messages = await getMessages(); return ( <html lang={locale}> <body> <Providers locale={locale} messages={messages}> {children} </Providers> </body> </html> ); }
-
providers.tsx
: client 컴포넌트providers.tsx'use client'; import { NextIntlClientProvider, useMessages } from 'next-intl'; interface ProvidersProps { children: React.ReactNode; locale: string; messages: ReturnType<typeof useMessages>; } export function Providers({ children, locale, messages }: ProvidersProps) { return ( <NextIntlClientProvider locale={locale} messages={messages}> {children} </NextIntlClientProvider> ); }
-
-
사용하기
-
locale 파일
ko.json{ "home": { "title": "i18n 예제" } }
en.json{ "home": { "title": "i18n Example" } }
page.tsximport { useTranslations } from 'next-intl'; export interface HomeProps {} export const Home = ({}: HomeProps) => { const { data: session, status } = useSession(); const t = useTranslations('home'); console.log('t', t('title')); return ... }
다음과 같이 출력되게 된다.
- localhost:3000/ko : “i18n 예제”
- localhost:3000/en : “i18n Example”
-