Nextjs14 다국어 지원하기(feat.next-international)

April·2023년 11월 21일
3

Nextjs🚀

목록 보기
13/14
post-thumbnail

들어가며,

이번에 회사에서 서비스 외에 새로운 프로젝트를 진행하게 되었는데
레포부터 새로 생성하는 거라 새로운, 안써본, 트렌디한 스택으로 진행하기로 했다
그 중 next14로 시작하면서, 결제, 다국어 지원을 내가 맡아 적용하기로 했다

결제는 경험이 있어 크게 어렵지 않았는데,
다국어 지원은 생각보다 레퍼런스가 별로 없어 공식문서에 의지하며 적용한 내용을 기록해본다!!

(진짜.. 3일만에 만들어서 QA 하고 운영배포 하고..😩 그 다음주인 2주차에 글로벌 작업 2일만에 하고.. QA 준비를 하고 있다..😫 빡신 일정이었지만.. 계획대로 진행되어 다행.. 과정에 많은 빡침🤬이 있었지만.. 뭐.. 그래..)



폴더구조

├── locales
│       ├── client.ts // 클라이언트에서 동작하도록 셋팅
│       ├── server.ts // 서버에서 동작하도록 셋팅
│       ├── en.ts
│       └── ko.ts
├── middleware.ts // defaultLocale, locales 설정 등
└── app
    └── [locale] 
        ├── layout.tsx
        └── page.tsx

다국어 설정

  • middleware.ts
    import { createI18nMiddleware } from 'next-international/middleware';
    import type { NextRequest } from 'next/server';
    
    const I18nMiddleware = createI18nMiddleware({
      locales: ['ko', 'en'],
      defaultLocale: 'ko',
    });
    
    export function middleware(request: NextRequest) {
      return I18nMiddleware(request);
    }
    
    export const config = {
      matcher: ['/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)'],
    };

  • locales/server.ts
    import { createI18nServer } from 'next-international/server';
    
    export const { getI18n, getScopedI18n, getCurrentLocale, getStaticParams } =
      createI18nServer(
        {
          ko: () => import('./ko'),
          en: () => import('./en'),
        },
        {
          // Uncomment to use custom segment name
          // segmentName: 'locale',
          // Uncomment to set fallback locale
          // fallbackLocale: en,
        },
      );
  • locales/client.ts
    import { createI18nClient } from 'next-international/client';
    
    export const {
      useI18n,
      useScopedI18n,
      I18nProviderClient,
      useChangeLocale,
      defineLocale,
      useCurrentLocale,
    } = createI18nClient(
      {
          ko: () => import('./ko'),
          en: () => import('./en'),
      },
      {
        // Uncomment to set base path
        // basePath: '/base',
        // Uncomment to use custom segment name
        // segmentName: 'locale',
        // Uncomment to set fallback locale
        // fallbackLocale: en,
      },
    );
  • locales/ko.ts
    console.log('Loaded KO');
    
    export default {
      hello: '안녕',
      welcome: '안녕 {name}!',
      'detail.title': '헬로우봇 AI 프로필 생성기',
    } as const;
  • locales/en.ts
    console.log('Loaded EN');
    
    export default {
      hello: 'Hello',
      welcome: 'Hello {name}!',
      'detail.title': 'hello ai profile',
    } as const;
  • app/[locale]/provider.ts
    'use client';
    
    import type { ReactNode } from 'react';
    import { I18nProviderClient } from '../../locales/client';
    
    type ProviderProps = {
      locale: string;
      children: ReactNode;
    };
    
    export function Provider({ locale, children }: ProviderProps) {
      return (
        <I18nProviderClient locale={locale} fallback={<p>Loading...</p>}>
          {children}
        </I18nProviderClient>
      );
    }

사용법

import { getI18n, getScopedI18n, getCurrentLocale } from '../../../../locales/server';
import { useChangeLocale, useCurrentLocale } from '../../../../locales/client';  // 'use client'; 에서 적용
import { Provider } from '@/app/[locale]/provider.ts';

export default function MyComponent () {
  const t = await getI18n(); // 단일 사용
  const t2 = await getScopedI18n('detail'); // 묶음으로 사용
  const currentLocale = getCurrentLocale(); // 현재 값 가져오기
  const locale = useCurrentLocale() // 현재 값 가져오기, 'use client'; 에서 적용

  const changeLocale = useChangeLocale(); // 언어 변경하기

  const onClick = () => {
  // 'use client'; 에서 적용
    changeLocale('en');
  };

 return (
      <>
        <h1>{t('title')}</h1>
        <p>{t2('title')}</p> {/* detail.title 값이 적용 됨 */} 
        <p>{t('welcome', { name: 'yurim' })}</p>  {/* 변수 사용 가능 */} 
        <button type="button" onClick={onClick}>언어 변경</button>
     
		<Provider locale={locale}> {/* 가장 하위의 컴포넌트에 적용할 것! */} 
	        <ClientComponent /> {/* client component인 경우는 Provider로 감싸야 적용 됨 */} 
        </Provider>
      </>
  );
}


관련 링크




profile
🚀 내가 보려고 쓰는 기술블로그

1개의 댓글

comment-user-thumbnail
2024년 1월 24일

포스팅 잘 봤습니다.
첫 문단에서 깊은 분노가 느껴졌지만... ㅎ
덕분에 next-international 을 알게되었네요 :)

답글 달기