Getting Started internationalizing (i18n) with Intlayer and Next.js 15 App Router
Intlayer로 시작하기
Intlayer는 현대 웹 애플리케이션에서 다국어 지원을 간소화하기 위해 설계된 혁신적이고 오픈 소스인 국제화(i18n) 라이브러리입니다. Intlayer는 강력한 App Router를 포함한 최신 Next.js 15 프레임워크와 원활하게 통합됩니다. 서버 컴포넌트와의 효율적인 렌더링을 위해 최적화되어 있으며, Turbopack과 완벽히 호환됩니다.
Intlayer를 사용하면 다음을 수행할 수 있습니다:
- 컴포넌트 수준에서 선언적 사전을 사용하여 번역을 쉽게 관리합니다.
- 메타데이터, 경로 및 콘텐츠를 동적으로 지역화합니다.
- 클라이언트 측 및 서버 측 컴포넌트에서 번역에 액세스합니다.
- 자동 생성된 타입을 통해 TypeScript 지원을 보장하여 자동 완성과 오류 감지를 개선합니다.
- 동적 로케일 감지 및 전환과 같은 고급 기능을 활용합니다.
Intlayer는 Next.js 12, 13, 14, 15와 호환됩니다. Next.js 페이지 라우터를 사용하는 경우, 이 가이드를 참조하세요. Next.js 12, 13, 14에서 App Router를 사용하는 경우, 이 가이드를 참조하세요.
Next.js 애플리케이션에서 Intlayer 설정 단계별 가이드
1단계: 종속성 설치
npm을 사용하여 필요한 패키지를 설치합니다:
npm install intlayer next-intlayer
intlayer
구성 관리, 번역, 콘텐츠 선언, 트랜스파일링 및 CLI 명령을 위한 국제화 도구를 제공하는 핵심 패키지입니다.
next-intlayer
Intlayer를 Next.js와 통합하는 패키지입니다. Next.js 국제화를 위한 컨텍스트 제공자와 훅을 제공합니다. 또한, Intlayer를 Webpack 또는 Turbopack과 통합하기 위한 Next.js 플러그인과 사용자 선호 로케일을 감지하고, 쿠키를 관리하며, URL 리디렉션을 처리하는 미들웨어를 포함합니다.
2단계: 프로젝트 구성
애플리케이션의 언어를 구성하기 위한 설정 파일을 생성합니다:
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 로그 비활성화 등을 설정할 수 있습니다. 사용 가능한 매개변수의 전체 목록은 구성 문서를 참조하세요.
3단계: Next.js 구성에 Intlayer 통합
Intlayer를 사용하도록 Next.js 설정을 구성합니다:
import type { NextConfig } from "next";import { withIntlayer } from "next-intlayer/server";const nextConfig: NextConfig = { /* 구성 옵션 */};export default withIntlayer(nextConfig);
withIntlayer() Next.js 플러그인은 Intlayer를 Next.js와 통합하는 데 사용됩니다. 이는 콘텐츠 선언 파일의 빌드를 보장하고 개발 모드에서 이를 모니터링합니다. 또한, Webpack 또는 Turbopack 환경 내에서 Intlayer 환경 변수를 정의합니다. 성능 최적화를 위한 별칭을 제공하고 서버 컴포넌트와의 호환성을 보장합니다.
4단계: 동적 로케일 경로 정의
RootLayout에서 모든 내용을 제거하고 다음 코드를 추가합니다:
import type { PropsWithChildren, FC } from "react";import "./globals.css";const RootLayout: FC<PropsWithChildren> = ({ children }) => children;export default RootLayout;
동적 라우팅을 구현하려면 [locale] 디렉토리에 새 레이아웃을 추가하여 로케일 경로를 제공합니다:
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/about는 en-US를 참조하고 /fr/about는 fr을 참조합니다.
그런 다음, 애플리케이션 레이아웃에 generateStaticParams 함수를 구현합니다.
export { generateStaticParams } from "next-intlayer"; // 추가할 줄const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => { /*... 나머지 코드 */};export default LocaleLayout;
generateStaticParams는 애플리케이션이 모든 로케일에 필요한 페이지를 사전 빌드하도록 보장하여 런타임 계산을 줄이고 사용자 경험을 개선합니다. 자세한 내용은 Next.js의 generateStaticParams 문서를 참조하세요.
5단계: 콘텐츠 선언
번역을 저장하기 위해 콘텐츠 선언을 생성하고 관리합니다:
import { t, type Dictionary } from "intlayer";const pageContent = { key: "page", content: { getStarted: { main: t({ en: "Get started by editing", fr: "Commencez par éditer", es: "Comience por editar", ko: "편집을 시작하세요", }), pageLink: "src/app/page.tsx", }, },} satisfies Dictionary;export default pageContent;
콘텐츠 선언은 애플리케이션 어디에서나 정의할 수 있으며, contentDir 디렉토리(기본값: ./src)에 포함되고 콘텐츠 선언 파일 확장자(기본값: .content.{ts,tsx,js,jsx,mjs,cjs})와 일치해야 합니다.
자세한 내용은 콘텐츠 선언 문서를 참조하세요.
6단계: 코드에서 콘텐츠 활용
애플리케이션 전반에서 콘텐츠 사전에 액세스합니다:
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의 캐시 메커니즘을 통해)를 기반으로 하기 때문에 애플리케이션의 다른 세그먼트에 대해 각 "컨텍스트"가 다시 생성됩니다. 공유 레이아웃에 제공자를 배치하면 이 격리가 깨져 서버 컨텍스트 값이 서버 컴포넌트에 올바르게 전파되지 않습니다.
"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> );};
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 훅에 대한 자세한 내용은 문서를 참조하세요.
(선택 사항) 7단계: 로케일 감지를 위한 미들웨어 구성
사용자의 선호 로케일을 감지하기 위해 미들웨어를 설정합니다:
export { intlayerMiddleware as middleware } from "next-intlayer/middleware";export const config = { matcher: "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\..*|_next).*)",};
intlayerMiddleware는 사용자의 선호 로케일을 감지하고 구성에 지정된 대로 적절한 URL로 리디렉션하는 데 사용됩니다. 또한, 사용자의 선호 로케일을 쿠키에 저장할 수 있습니다.
(선택 사항) 8단계: 메타데이터의 국제화
페이지 제목과 같은 메타데이터를 국제화하려면 Next.js에서 제공하는 generateMetadata 함수를 사용할 수 있습니다. 함수 내부에서 getTranslation 함수를 사용하여 메타데이터를 번역합니다.
import { type IConfigLocales, getTranslation, getMultilingualUrls,} from "intlayer";import type { Metadata } from "next";import type { LocalPromiseParams } from "next-intlayer";export const generateMetadata = async ({ params,}: LocalPromiseParams): Promise<Metadata> => { const { locale } = await params; const t = <T>(content: IConfigLocales<T>) => getTranslation(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", ko: "내 제목", }), description: t({ en: "My description", fr: "Ma description", es: "Mi descripción", ko: "내 설명", }), alternates: { canonical: "/", languages: { ...multilingualUrls, "x-default": "/" }, }, openGraph: { url: multilingualUrls[locale], }, };};// ... 나머지 코드
메타데이터 최적화에 대한 자세한 내용은 공식 Next.js 문서를 참조하세요.
(선택 사항) 9단계: sitemap.xml 및 robots.txt의 국제화
sitemap.xml 및 robots.txt를 국제화하려면 Intlayer에서 제공하는 getMultilingualUrls 함수를 사용할 수 있습니다. 이 함수는 사이트맵에 다국어 URL을 생성하는 데 사용됩니다.
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;
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;
사이트맵 최적화에 대한 자세한 내용은 공식 Next.js 문서를 참조하세요. robots.txt 최적화에 대한 자세한 내용은 공식 Next.js 문서를 참조하세요.
(선택 사항) 10단계: 콘텐츠 언어 변경
콘텐츠의 언어를 변경하려면 useLocale 훅에서 제공하는 setLocale 함수를 사용할 수 있습니다. 이 함수는 애플리케이션의 로케일을 설정하고 콘텐츠를 업데이트합니다.
"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 ( <div> <button popoverTarget="localePopover">{getLocaleName(locale)}</button> <div id="localePopover" popover="auto"> {availableLocales.map((localeItem) => ( <Link href={getLocalizedUrl(pathWithoutLocale, localeItem)} hrefLang={localeItem} key={localeItem} aria-current={locale === localeItem ? "page" : undefined} onClick={(e) => { e.preventDefault(); setLocale(localeItem); }} > <span> {/* 로케일 - 예: FR */} {localeItem} </span> <span> {/* 로케일 자체 언어 - 예: Français */} {getLocaleName(localeItem, locale)} </span> <span dir={getHTMLTextDir(localeItem)} lang={localeItem}> {/* 현재 로케일 언어 - 예: Francés (현재 로케일이 Locales.SPANISH로 설정됨) */} {getLocaleName(localeItem)} </span> <span dir="ltr" lang={Locales.ENGLISH}> {/* 영어로 된 언어 - 예: French */} {getLocaleName(localeItem, Locales.ENGLISH)} </span> </Link> ))} </div> </div> );};
문서 참조:
(선택 사항) 11단계: 로컬화된 링크 컴포넌트 생성
애플리케이션의 탐색이 현재 로케일을 준수하도록 보장하려면 사용자 정의 Link 컴포넌트를 생성할 수 있습니다. 이 컴포넌트는 내부 URL에 현재 언어를 자동으로 접두사로 추가합니다. 예를 들어, 프랑스어 사용자가 "About" 페이지로 이동하는 링크를 클릭하면 /about 대신 /fr/about로 리디렉션됩니다.
이 동작은 여러 가지 이유로 유용합니다:
- SEO 및 사용자 경험: 로컬화된 URL은 검색 엔진이 언어별 페이지를 올바르게 색인화하고 사용자가 선호하는 언어로 콘텐츠를 제공하도록 돕습니다.
- 일관성: 애플리케이션 전반에서 로컬화된 링크를 사용하면 탐색이 현재 로케일 내에서 유지되며, 예상치 못한 언어 전환을 방지합니다.
- 유지 관리 용이성: 로컬화 논리를 단일 컴포넌트에 중앙 집중화하면 URL 관리를 단순화하고 애플리케이션이 성장함에 따라 코드베이스를 더 쉽게 유지 관리하고 확장할 수 있습니다.
아래는 TypeScript에서 로컬화된 Link 컴포넌트를 구현한 예입니다:
"use client";import { getLocalizedUrl } from "intlayer";import NextLink, { type LinkProps as NextLinkProps } from "next/link";import { useLocale } from "next-intlayer";import type { PropsWithChildren, FC } from "react";/** * 주어진 URL이 외부 URL인지 확인하는 유틸리티 함수입니다. * URL이 http:// 또는 https://로 시작하면 외부 URL로 간주됩니다. */export const checkIsExternalLink = (href?: string): boolean => /^https?:///.test(href ?? "");/** * 현재 로케일을 기반으로 href 속성을 조정하는 사용자 정의 Link 컴포넌트입니다. * 내부 링크의 경우 `getLocalizedUrl`을 사용하여 URL에 로케일을 접두사로 추가합니다(예: /fr/about). * 이를 통해 탐색이 동일한 로케일 컨텍스트 내에서 유지되도록 보장합니다. */export const Link: FC<PropsWithChildren<NextLinkProps>> = ({ href, children, ...props}) => { const { locale } = useLocale(); const isExternalLink = checkIsExternalLink(href.toString()); // 링크가 내부 링크이고 유효한 href가 제공된 경우 로컬화된 URL을 가져옵니다. const hrefI18n: NextLinkProps["href"] = href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href; return ( <NextLink href={hrefI18n} {...props}> {children} </NextLink> );};
작동 방식
외부 링크 감지:
헬퍼 함수 checkIsExternalLink는 URL이 외부인지 여부를 결정합니다. 외부 링크는 로컬화가 필요하지 않으므로 변경되지 않습니다.현재 로케일 검색:
useLocale 훅은 현재 로케일을 제공합니다(예: 프랑스어의 경우 fr).URL 로컬화:
내부 링크(즉, 외부가 아닌 경우)에 대해 getLocalizedUrl을 사용하여 URL에 현재 로케일을 자동으로 접두사로 추가합니다. 이를 통해 사용자가 프랑스어를 사용하는 경우 /about을 href로 전달하면 /fr/about으로 변환됩니다.링크 반환:
컴포넌트는 로컬화된 URL이 포함된 <a> 요소를 반환하여 탐색이 로케일과 일치하도록 보장합니다.
이 Link 컴포넌트를 애플리케이션 전반에 통합하면 일관되고 언어를 인식하는 사용자 경험을 유지하면서 SEO 및 사용성을 개선할 수 있습니다.
TypeScript 구성
Intlayer는 TypeScript의 모듈 확장을 사용하여 코드베이스를 더욱 강력하게 만듭니다.
자동 생성된 타입을 포함하도록 TypeScript 구성을 설정합니다.
{ // ... 기존 TypeScript 구성 "include": [ // ... 기존 TypeScript 구성 ".intlayer/**/*.ts", // 자동 생성된 타입 포함 ],}
Git 구성
Intlayer에서 생성된 파일을 무시하는 것이 좋습니다. 이를 통해 Git 저장소에 커밋하는 것을 방지할 수 있습니다.
이를 위해 .gitignore 파일에 다음 지침을 추가할 수 있습니다:
# Intlayer에서 생성된 파일 무시.intlayer
더 나아가기
이 문서를 개선할 아이디어가 있으시면 GitHub에 풀 리퀘스트를 제출하여 자유롭게 기여해 주세요.
문서에 대한 GitHub 링크