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를 채우세요.
다음 단계
- 구성 참조 — 모든 구성 옵션
- 사용자 정의 로케일 리졸버 — 로케일 감지 사용자 정의
- 수동 재정의 — 특정 번역 재정의
- 모범 사례 — 권장 패턴 및 워크플로