Next.js 통합

@lingo.dev/compiler는 Webpack과 Turbopack을 모두 지원하는 비동기 구성 래퍼를 통해 Next.js App Router와 통합됩니다.

설정

1. 패키지 설치

pnpm install @lingo.dev/compiler

2. Next.js 구성

next.config.ts를 업데이트하여 비동기 withLingo() 래퍼를 사용하세요:

import type { NextConfig } from "next";
import { withLingo } from "@lingo.dev/compiler/next";

const nextConfig: NextConfig = {
  // Your existing Next.js config
};

export default async function (): Promise<NextConfig> {
  return await withLingo(nextConfig, {
    sourceRoot: "./app",
    sourceLocale: "en",
    targetLocales: ["es", "de", "fr"],
    models: "lingo.dev",
    dev: {
      usePseudotranslator: true,
    },
  });
}

왜 비동기인가요? 래퍼는 플러그인을 지연 로드하고 구성을 동적으로 해결합니다. 이를 통해 빌드 속도를 빠르게 유지하고 조건부 플러그인 로딩이 가능합니다.

3. Provider 추가

루트 레이아웃에서 LingoProvider로 앱을 래핑하세요:

// app/layout.tsx
import { LingoProvider } from "@lingo.dev/compiler/react";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <LingoProvider>
      <html>
        <body>{children}</body>
      </html>
    </LingoProvider>
  );
}

중요: LingoProvider<html> 내부에 배치하되 모든 콘텐츠를 래핑하세요. Server Component와 Client Component 모두에서 작동합니다.

React Server Components

컴파일러는 React Server Components(RSC)를 완벽하게 지원합니다. Server Component는 빌드 시점에 번역되며, 번역본은 서버 출력에 포함됩니다.

// app/page.tsx (Server Component)
export default function Page() {
  return (
    <div>
      <h1>Welcome to our app</h1>
      <p>This is a server component—translated at build time</p>
    </div>
  );
}

Server Component의 번역된 텍스트에는 클라이언트 측 JavaScript가 추가되지 않습니다.

Client Components

Client Component의 경우 "use client" 지시문을 사용하세요:

"use client";

import { useLingoContext } from "@lingo.dev/compiler/react";

export function LanguageSwitcher() {
  const { locale, setLocale } = useLingoContext();

  return (
    <select value={locale} onChange={(e) => setLocale(e.target.value)}>
      <option value="en">English</option>
      <option value="es">Español</option>
      <option value="de">Deutsch</option>
    </select>
  );
}

Client Component는 최적화된 번역 번들을 받습니다. 해당 컴포넌트에서 사용되는 번역만 포함됩니다.

로케일 감지

기본적으로 로케일은 쿠키(locale)에 저장됩니다. 컴파일러는 로케일 감지 및 지속성을 자동으로 처리합니다.

커스텀 서버 로케일 감지

커스텀 로직(데이터베이스, 헤더, 서브도메인)의 경우 .lingo/locale-resolver.server.ts를 생성하세요:

// .lingo/locale-resolver.server.ts
import { headers } from "next/headers";

export async function getServerLocale(): Promise<string> {
  const headersList = await headers();
  const acceptLanguage = headersList.get("accept-language");

  // Parse accept-language header
  const locale = acceptLanguage?.split(",")[0]?.split("-")[0] || "en";

  return locale;
}

이 함수는 모든 요청에 대해 서버에서 호출됩니다. 로케일 코드(예: "en", "es")를 반환해야 합니다.

커스텀 클라이언트 로케일 지속성

커스텀 클라이언트 측 로직(localStorage, URL 파라미터)의 경우 .lingo/locale-resolver.client.ts를 생성하세요:

// .lingo/locale-resolver.client.ts
export function getClientLocale(): string {
  // Check URL parameter first
  const params = new URLSearchParams(window.location.search);
  const urlLocale = params.get("lang");
  if (urlLocale) return urlLocale;

  // Fall back to localStorage
  return localStorage.getItem("locale") || "en";
}

export function persistLocale(locale: string): void {
  localStorage.setItem("locale", locale);

  // Optionally update URL
  const url = new URL(window.location.href);
  url.searchParams.set("lang", locale);
  window.history.replaceState({}, "", url.toString());
}

자세한 내용은 커스텀 로케일 리졸버를 참조하세요.

로케일 라우팅을 위한 미들웨어

로케일 기반 라우팅(/en/about, /es/about)을 원하는 경우 Next.js 미들웨어를 구현하세요:

// middleware.ts
import { NextRequest, NextResponse } from "next/server";

const locales = ["en", "es", "de", "fr"];
const defaultLocale = "en";

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // Check if pathname already has a locale
  const pathnameHasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  );

  if (pathnameHasLocale) return;

  // Get locale from cookie or accept-language header
  const localeCookie = request.cookies.get("locale")?.value;
  const acceptLanguage = request.headers.get("accept-language");
  const locale =
    localeCookie ||
    acceptLanguage?.split(",")[0]?.split("-")[0] ||
    defaultLocale;

  // Redirect to localized pathname
  request.nextUrl.pathname = `/${locale}${pathname}`;
  return NextResponse.redirect(request.nextUrl);
}

export const config = {
  matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
};

[locale] 동적 세그먼트를 사용하도록 라우트를 업데이트하세요:

app/
  [locale]/
    page.tsx
    about/
      page.tsx
    layout.tsx

빌드 구성

개발 빌드

{
  dev: {
    usePseudotranslator: true, // Fast fake translations
  }
}

npm run dev를 실행하여 즉시 의사 번역이 적용된 개발 서버를 시작하세요.

프로덕션 빌드

{
  buildMode: "cache-only", // Use pre-generated translations
}

npm run build를 실행하여 프로덕션용으로 빌드하세요. API 키가 필요 없으며 번역은 .lingo/metadata.json에서 가져옵니다.

권장 사항: 프로덕션 빌드 전에 CI에서 실제 번역을 생성하세요. 권장 워크플로는 빌드 모드를 참조하세요.

Turbopack 지원

컴파일러는 Webpack과 Turbopack(Next.js 15+) 모두에서 작동합니다.

개발 환경에서 Turbopack을 사용하려면:

next dev --turbo

컴파일러는 적절한 번들러를 자동으로 감지하고 구성합니다.

TypeScript

컴파일러는 완전히 타입이 지정되어 있습니다. @lingo.dev/compiler에서 타입을 가져오세요:

import type { LingoConfig } from "@lingo.dev/compiler";

const config: LingoConfig = {
  sourceRoot: "./app",
  sourceLocale: "en",
  targetLocales: ["es", "de"],
  models: "lingo.dev",
};

일반적인 문제

"Cannot find module '@lingo.dev/compiler/react'" 패키지가 설치되어 있는지 확인하세요: pnpm install @lingo.dev/compiler

LingoProvider 추가 후 HMR이 작동하지 않음 LingoProvider가 중첩된 레이아웃이나 페이지가 아닌 루트 레이아웃에 올바르게 배치되어 있는지 확인하세요.

프로덕션 빌드에서 번역이 표시되지 않음 buildMode: "cache-only"를 사용하고 있는지, 그리고 .lingo/metadata.json에 모든 로케일에 대한 번역이 포함되어 있는지 확인하세요.

"Missing translations for locale X" 실제 번역을 생성하려면 usePseudotranslator: false로 개발 서버를 실행하거나, CI 빌드를 실행하여 .lingo/metadata.json를 채우세요.

다음 단계