번역 로드하기

프로바이더를 사용하여 메시지 관리하기

문제

애플리케이션의 컴포넌트에 'Hello World'와 같은 텍스트를 직접 하드코딩하면 콘텐츠가 코드와 결합됩니다. 다른 언어를 표시하려면 개발자는 컴포넌트를 복제하거나 if/else 로직을 추가해야 하므로 번역이 확장 불가능해지고 새로운 텍스트마다 전체 코드 변경이 필요합니다.

해결 방법

react-intlIntlProvider를 사용하여 번역을 제공하세요. 루트 레이아웃에서 서버에 번역 메시지를 로드하고, 클라이언트 측 프로바이더 컴포넌트에 전달한 다음, useIntl 훅을 사용하여 다른 클라이언트 컴포넌트에서 사용하세요.

단계

1. react-intl 설치

먼저 프로젝트에 react-intl를 종속성으로 추가하세요.

npm install react-intl

2. 플랫 번역 파일 생성

dictionaries 폴더를 생성하세요. 내부에 각 언어에 대한 JSON 파일을 추가하세요. react-intl는 플랫 키-값 구조에서 가장 잘 작동합니다.

// dictionaries/en.json
{
  "home.title": "Home Page",
  "home.welcome": "Hello, welcome to our site!",
  "about.title": "About Us"
}
// dictionaries/es.json
{
  "home.title": "Página de Inicio",
  "home.welcome": "¡Hola, bienvenido a nuestro sitio!",
  "about.title": "Sobre Nosotros"
}

3. 딕셔너리를 로드하는 함수 생성

lang 매개변수를 기반으로 서버에서 올바른 사전 파일을 로드하는 헬퍼 함수를 생성하세요.

// app/get-dictionary.ts
import 'server-only';

// Define the type for our flat message object
type Messages = Record<string, string>;

const dictionaries: { [key: string]: () => Promise<Messages> } = {
  en: () => import('@/dictionaries/en.json').then((module) => module.default),
  es: () => import('@/dictionaries/es.json').then((module) => module.default),
  // fr: () => import('@/dictionaries/fr.json').then((module) => module.default),
};

export const getDictionary = async (lang: string) => {
  const load = dictionaries[lang];
  if (load) {
    return load();
  }
  // Fallback to English
  return dictionaries.en();
};

4. 클라이언트 측 프로바이더 생성

IntlProvider는 React의 Context를 사용하는 클라이언트 컴포넌트입니다. 서버에서 로드된 메시지를 받을 수 있는 래퍼를 생성해야 합니다.

// app/components/IntlClientProvider.tsx
'use client';

import { IntlProvider } from 'react-intl';

type Props = {
  children: React.ReactNode;
  locale: string;
  messages: Record<string, string>; // Flat messages object
};

export default function IntlClientProvider({
  children,
  locale,
  messages,
}: Props) {
  return (
    <IntlProvider messages={messages} locale={locale} defaultLocale="en">
      {children}
    </IntlProvider>
  );
}

5. 루트 레이아웃 업데이트

app/[lang]/layout.tsxasync 컴포넌트로 수정하세요. 메시지를 로드하고 IntlClientProvider에 전달합니다.

// app/[lang]/layout.tsx
import { getDictionary } from '@/app/get-dictionary';
import IntlClientProvider from '@/app/components/IntlClientProvider';

export async function generateStaticParams() {
  return [{ lang: 'en' }, { lang: 'es' }];
}

export default async function RootLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: { lang: string };
}) {
  // Load messages on the server
  const messages = await getDictionary(params.lang);

  return (
    <html lang={params.lang}>
      <body>
        {/* Pass messages to the client provider */}
        <IntlClientProvider locale={params.lang} messages={messages}>
          {children}
        </IntlClientProvider>
      </body>
    </html>
  );
}

6. 클라이언트 컴포넌트에서 번역 사용

이제 모든 클라이언트 컴포넌트에서 useIntl 훅을 사용할 수 있습니다. 서버 컴포넌트는 이 훅을 사용할 수 없습니다.

번역된 텍스트를 표시할 새 클라이언트 컴포넌트를 생성하세요:

// app/components/HomePageContent.tsx
'use client';

import { useIntl } from 'react-intl';

export default function HomePageContent() {
  const intl = useIntl();

  return (
    <div>
      <h1>{intl.formatMessage({ id: 'home.title' })}</h1>
      <p>{intl.formatMessage({ id: 'home.welcome' })}</p>
    </div>
  );
}

7. 페이지에 컴포넌트 추가하기

마지막으로, 새로 만든 클라이언트 컴포넌트를 페이지에 추가하세요.

// app/[lang]/page.tsx
import HomePageContent from '@/app/components/HomePageContent';

export default function Home() {
  // This page is a Server Component
  return (
    <div>
      {/* It renders the Client Component */}
      <HomePageContent />
    </div>
  );
}