Getting Started 국제화 (i18n) with Intlayer and Next.js 15 App Router

    What is Intlayer?

    Intlayer는 현대 웹 애플리케이션에서 다국어 지원을 간소화하기 위해 설계된 혁신적인 오픈소스 국제화 (i18n) 라이브러리입니다. Intlayer는 최신 Next.js 15 프레임워크와 원활하게 통합되며, 강력한 App Router를 포함합니다. 이는 효율적인 렌더링을 위해 서버 컴포넌트와 함께 작업하도록 최적화되어 있으며, Turbopack과 완전히 호환됩니다.

    Intlayer를 사용하면:

    • 구성 요소 수준에서 선언형 사전을 사용하여 쉽게 번역을 관리할 수 있습니다.
    • 메타데이터, 경로 및 콘텐츠를 동적으로 로컬화할 수 있습니다.
    • 클라이언트 측 및 서버 측 컴포넌트에서 번역에 액세스할 수 있습니다.
    • 자동 생성된 타입으로 TypeScript 지원을 보장하여 자동 완성과 오류 감지를 개선합니다.
    • 동적 로케일 감지 및 전환과 같은 고급 기능의 이점을 누릴 수 있습니다.

    Intlayer는 Next.js 12, 13, 14 및 15와 호환됩니다. Next.js 페이지 라우터를 사용하고 있는 경우 이 가이드를 참조할 수 있습니다. Next.js 12, 13, 14와 App Router의 경우 이 가이드를 참조하십시오.


    Step-by-Step Guide to Set Up Intlayer in a Next.js Application

    Step 1: Install Dependencies

    필요한 패키지를 npm을 사용하여 설치합니다:

    bash
    npm install intlayer next-intlayer
    • intlayer

      구성 관리, 번역, 콘텐츠 선언, 변환 및 CLI 명령을 위한 국제화 도구를 제공하는 핵심 패키지입니다.

    • next-intlayer

      Intlayer와 Next.js를 통합하는 패키지입니다. Next.js의 국제화를 위한 컨텍스트 제공자 및 훅을 제공합니다. 또한 Webpack 또는 Turbopack과 함께 Intlayer를 통합하기 위한 Next.js 플러그인을 포함하며, 사용자의 선호 로케일 감지, 쿠키 관리, URL 리디렉션 처리를 위한 미들웨어도 포함되어 있습니다.

    Step 2: Configure Your Project

    응용 프로그램의 언어를 구성하기 위해 구성 파일을 만듭니다:

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  internationalization: {    locales: [      Locales.ENGLISH,      Locales.FRENCH,      Locales.SPANISH,      // 다른 로케일 추가    ],    defaultLocale: Locales.ENGLISH,  },};export default config;

    이 구성 파일을 통해 로컬화된 URL, 미들웨어 리디렉션, 쿠키 이름, 콘텐츠 선언의 위치 및 확장, Intlayer 로그를 콘솔에서 비활성화하는 등의 설정을 할 수 있습니다. 사용 가능한 매개변수의 전체 목록은 구성 문서를 참조하십시오.

    Step 3: Integrate Intlayer in Your Next.js Configuration

    Next.js 설정을 구성하여 Intlayer를 사용합니다:

    next.config.mjs
    import { withIntlayer } from "next-intlayer/server";/** @type {import('next').NextConfig} */const nextConfig = {};export default withIntlayer(nextConfig);

    withIntlayer() Next.js 플러그인은 Intlayer를 Next.js와 통합하는 데 사용됩니다. 콘텐츠 선언 파일을 빌드하도록 하며, 개발 모드에서 이를 모니터링합니다. Webpack 또는 Turbopack 환경 내에서 Intlayer 환경 변수를 정의합니다. 또한 성능을 최적화하고 서버 컴포넌트와의 호환성을 보장하기 위해 별칭을 제공합니다.

    Step 4: Configure Middleware for Locale Detection

    사용자의 선호 로케일을 감지하기 위한 미들웨어를 설정합니다:

    src/middleware.ts
    export { intlayerMiddleware as middleware } from "next-intlayer/middleware";export const config = {  matcher:    "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",};

    intlayerMiddleware는 사용자의 선호 로케일을 감지하고 구성에서 지정된 대로 적절한 URL로 리디렉션하는 데 사용됩니다. 또한 사용자의 선호 로케일을 쿠키에 저장할 수 있도록 합니다.

    Step 5: Define Dynamic Locale Routes

    RootLayout의 모든 내용을 제거하고 다음 코드를 추가합니다:

    src/app/layout.tsx
    import type { PropsWithChildren, FC } from "react";import "./globals.css";const RootLayout: FC<PropsWithChildren> = ({ children }) => children;export default RootLayout;

    RootLayout 컴포넌트를 비워두면 <html> 태그에 langdir 속성을 설정할 수 있습니다.

    동적 라우팅을 구현하기 위해 [locale] 디렉토리에 새로운 레이아웃을 추가하여 로케일 경로를 제공합니다:

    src/app/[locale]/layout.tsx
    import type { NextLayoutIntlayer } from "next-intlayer";import { Inter } from "next/font/google";import { getHTMLTextDir } from "intlayer";const inter = Inter({ subsets: ["latin"] });const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {  const { locale } = await params;  return (    <html lang={locale} dir={getHTMLTextDir(locale)}>      <body className={inter.className}>{children}</body>    </html>  );};export default LocaleLayout;

    [locale] 경로 세그먼트는 로케일을 정의하는 데 사용됩니다. 예: /en-US/abouten-US를 참조하며 /fr/aboutfr을 참조합니다.

    그런 다음 애플리케이션 레이아웃에서 generateStaticParams 함수를 구현합니다.

    src/app/[locale]/layout.tsx
    export { generateStaticParams } from "next-intlayer"; // 삽입할 줄const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {  /*... 나머지 코드 */};export default LocaleLayout;

    generateStaticParams는 애플리케이션이 모든 로케일에 필요한 페이지를 사전 구축하여 런타임 계산을 줄이고 사용자 경험을 개선하도록 보장합니다. 자세한 내용은 Next.js 문서의 generateStaticParams를 참조하십시오.

    Step 6: Declare Your Content

    번역을 저장하기 위해 콘텐츠 선언을 생성하고 관리합니다:

    src/app/[locale]/page.content.ts
    import { t, type DeclarationContent } from "intlayer";const pageContent = {  key: "page",  content: {    getStarted: {      main: t({        en: "Get started by editing",        fr: "Commencez par éditer",        es: "Comience por editar",      }),      pageLink: "src/app/page.tsx",    },  },} satisfies DeclarationContent;export default pageContent;

    콘텐츠 선언은 contentDir 디렉토리(기본적으로 ./src)에 포함될 때까지 애플리케이션의 어디에든 정의될 수 있습니다. 콘텐츠 선언 파일 확장자(기본적으로 .content.{ts,tsx,js,jsx,mjs,cjs})와 일치해야 합니다. 자세한 내용은 콘텐츠 선언 문서를 참조하십시오.

    Step 7: Utilize Content in Your Code

    전체 애플리케이션에서 콘텐츠 사전에 액세스합니다:

    src/app/[locale]/page.tsx
    import type { FC } from "react";import { ClientComponentExample } from "@components/ClientComponentExample";import { ServerComponentExample } from "@components/ServerComponentExample";import { type NextPageIntlayer, IntlayerClientProvider } from "next-intlayer";import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";const PageContent: FC = () => {  const { title, content } = useIntlayer("page");  return (    <>      <p>{content.getStarted.main}</p>      <code>{content.getStarted.pageLink}</code>    </>  );};const Page: NextPageIntlayer = async ({ params }) => {  const { locale } = await params;  return (    <>      <IntlayerServerProvider locale={locale}>        <PageContent />        <ServerComponentExample />        <IntlayerClientProvider locale={locale}>          <ClientComponentExample />        </IntlayerClientProvider>      </IntlayerServerProvider>    </>  );};export default Page;
    • IntlayerClientProvider는 클라이언트 측 구성 요소에 로케일을 제공하는 데 사용됩니다. 부모 구성 요소 어디에든 배치할 수 있지만 레이아웃에 두는 것이 좋습니다. Next.js는 여러 페이지에 걸쳐 레이아웃 코드를 공유하므로 더 효율적입니다. 레이아웃에서 IntlayerClientProvider를 사용하면 각 페이지에 대해 이를 재초기화하지 않아 성능이 향상되고 애플리케이션 전반에 걸쳐 일관된 로컬화 컨텍스트를 유지할 수 있습니다.
    • IntlayerServerProvider는 서버 자녀에게 로케일을 제공하는 데 사용됩니다. 레이아웃 내에 설정할 수 없습니다.

    레이아웃과 페이지는 일반 서버 컨텍스트를 공유할 수 없습니다. 이는 서버 컨텍스트 시스템이 요청별 데이터 저장소(React의 캐시 메커니즘을 통해)를 기반으로 하기 때문입니다. 따라서 각 애플리케이션 세그먼트에 대해 서로 다른 “컨텍스트”가 재생성됩니다. 제공자를 공유 레이아웃에 배치하면 이 격리가 깨지고 서버 구성 요소에 대한 서버 컨텍스트 값의 올바른 전파를 방해합니다.

    src/components/ClientComponentExample.tsx
    "use client";import type { FC } from "react";import { useIntlayer } from "next-intlayer";export const ClientComponentExample: FC = () => {  const content = useIntlayer("client-component-example"); // 관련 콘텐츠 선언 생성  return (    <div>      <h2>{content.title} </h2>      <p>{content.content}</p>    </div>  );};
    src/components/ServerComponentExample.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer/server";export const ServerComponentExample: FC = () => {  const content = useIntlayer("server-component-example"); // 관련 콘텐츠 선언 생성  return (    <div>      <h2>{content.title} </h2>      <p>{content.content}</p>    </div>  );};

    콘텐츠를 alt, title, href, aria-label 등과 같은 string 속성에서 사용하려면 함수 값을 호출해야 합니다. 예:

    jsx
    <img src={content.image.src.value} alt={content.image.value} />

    useIntlayer 훅에 대해 자세히 알아보려면 문서를 참조하십시오.

    (Optional) Step 8: Internationalization of your metadata

    메타데이터, 즉 페이지 제목을 국제화하려면 Next.js에서 제공하는 generateMetadata 함수를 사용할 수 있습니다. 함수 내부에서 getTranslationContent 함수를 사용하여 메타데이터를 번역합니다.

    src/app/[locale]/layout.tsx or src/app/[locale]/page.tsx
    import {  type IConfigLocales,  getTranslationContent,  getMultilingualUrls,} from "intlayer";import type { Metadata } from "next";import type { LocalPromiseParams } from "next-intlayer";export const generateMetadata = async ({  params: { locale },}: LocalPromiseParams): Metadata => {  const { locale } = await params;  const t = <T>(content: IConfigLocales<T>) =>    getTranslationContent(content, locale);  /**   * 각 로케일에 대한 모든 URL를 포함하는 객체를 생성합니다.   *   * 예시:   * ```ts   *  getMultilingualUrls('/about');   *   *  // 반환값   *  // {   *  //   en: '/about',   *  //   fr: '/fr/about',   *  //   es: '/es/about',   *  // }   * ```   */  const multilingualUrls = getMultilingualUrls("/");  return {    title: t<string>({      en: "My title",      fr: "Mon titre",      es: "Mi título",    }),    description: t({      en: "My description",      fr: "Ma description",      es: "Mi descripción",    }),    alternates: {      canonical: "/",      languages: { ...multilingualUrls, "x-default": "/" },    },    openGraph: {      url: multilingualUrls[locale],    },  };};// ... 나머지 코드

    메타데이터 최적화에 대한 자세한 내용은 공식 Next.js 문서를 참조하십시오.

    (Optional) Step 9: Internationalization of your sitemap.xml and robots.txt

    sitemap.xmlrobots.txt를 국제화하려면 Intlayer에서 제공하는 getMultilingualUrls 함수를 사용할 수 있습니다. 이 함수는 Sitemap을 위한 다국어 URL을 생성할 수 있습니다.

    src/app/sitemap.ts
    import { getMultilingualUrls } from "intlayer";import type { MetadataRoute } from "next";const sitemap = (): MetadataRoute.Sitemap => [  {    url: "https://example.com",    alternates: {      languages: getMultilingualUrls("https://example.com"),    },  },  {    url: "https://example.com/login",    alternates: {      languages: getMultilingualUrls("https://example.com/login"),    },  },  {    url: "https://example.com/register",    alternates: {      languages: getMultilingualUrls("https://example.com/register"),    },  },];export default sitemap;
    src/app/robots.ts
    import type { MetadataRoute } from "next";import { getMultilingualUrls } from "intlayer";const getAllMultilingualUrls = (urls: string[]) =>  urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);const robots = (): MetadataRoute.Robots => ({  rules: {    userAgent: "*",    allow: ["/"],    disallow: getAllMultilingualUrls(["/login", "/register"]),  },  host: "https://example.com",  sitemap: `https://example.com/sitemap.xml`,});export default robots;

    Sitemap 최적화에 대한 자세한 내용은 공식 Next.js 문서를 참조하십시오. robots.txt 최적화에 대한 자세한 내용은 공식 Next.js 문서를 참조하십시오.

    (Optional) Step 10: Change the language of your content

    콘텐츠의 언어를 변경하려면 useLocale 훅에서 제공하는 setLocale 함수를 사용할 수 있습니다. 이 함수는 애플리케이션의 로케일을 설정하고 내용을 업데이트할 수 있습니다.

    src/components/LocaleSwitcher.tsx
    "use client";import type { FC } from "react";import {  Locales,  getHTMLTextDir,  getLocaleName,  getLocalizedUrl,} from "intlayer";import { useLocale } from "next-intlayer";import Link from "next/link";export const LocaleSwitcher: FC = () => {  const { locale, pathWithoutLocale, availableLocales, setLocale } =    useLocale();  return (    <ol>      {availableLocales.map((localeItem) => (        <li key={localeItem}>          <Link            href={getLocalizedUrl(pathWithoutLocale, localeItem)}            hrefLang={localeItem}            aria-current={locale === localeItem ? "page" : undefined}            onClick={(e) => {              e.preventDefault();              setLocale(localeItem);            }}          >            <span>              {/* 해당 로케일의 언어 - 예: Français */}              {getLocaleName(localeItem, locale)}            </span>            <span dir={getHTMLTextDir(localeItem)} lang={localeItem}>              {/* 현재 로케일에서의 언어 - 예: 현재 로케일이 Locales.SPANISH으로 설정된 경우 Francés */}              {getLocaleName(localeItem)}            </span>            <span dir="ltr" lang={Locales.ENGLISH}>              {/* 영어로 된 언어 - 예: French */}              {getLocaleName(localeItem, Locales.ENGLISH)}            </span>            <span>              {/* 해당 로케일의 언어 코드 - 예: FR */}              {localeItem}            </span>          </Link>        </li>      ))}    </ol>  );};

    문서 참조:

    Configure TypeScript

    Intlayer는 모듈 증분을 사용하여 TypeScript의 혜택을 누리고 코드베이스를 강화합니다.

    alt text

    alt text

    TypeScript 구성에 자동 생성된 타입이 포함되어 있는지 확인합니다.

    tsconfig.json
    {  // ... 기존 TypeScript 구성  "include": [    // ... 기존 TypeScript 구성    "types", // 자동 생성된 타입 포함  ],}

    Git Configuration

    Intlayer에서 생성된 파일을 무시하는 것이 좋습니다. 이로 인해 Git 리포지토리에 이를 커밋하는 것을 피할 수 있습니다.

    이를 위해 .gitignore 파일에 다음 지침을 추가할 수 있습니다:

    .gitignore
    # Intlayer에서 생성된 파일 무시.intlayer

    이 문서를 개선할 아이디어가 있으시면 GitHub에 풀 리퀘스트를 제출하여 자유롭게 기여해 주세요.

    문서에 대한 GitHub 링크