Next.js 集成

@lingo.dev/compiler 通过异步配置包装器与 Next.js App Router 集成,支持 Webpack 和 Turbopack。

设置

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 组件和 Client 组件。

React Server 组件

该编译器完全支持 React Server 组件(RSC)。Server 组件会在构建时被翻译,翻译内容嵌入到服务器输出中。

// 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 组件中的翻译文本不会添加任何客户端 JavaScript。

Client 组件

对于 Client 组件,请使用 "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 组件会接收优化后的翻译包,仅包含该组件实际使用的翻译内容。

语言环境检测

默认情况下,语言环境存储在 cookie (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",
};

常见问题

“找不到模块 '@lingo.dev/compiler/react'” 请确保已安装该包:pnpm install @lingo.dev/compiler

添加 LingoProvider 后 HMR 不工作 请确保 LingoProvider 正确放置在根布局中,而不是嵌套布局或页面中。

生产环境构建中未显示翻译 请检查是否正在使用 buildMode: "cache-only",并且 .lingo/metadata.json 包含所有语言环境的翻译。

“缺少语言环境 X 的翻译” 请使用 usePseudotranslator: false 启动开发服务器以生成真实翻译,或运行 CI 构建以填充 .lingo/metadata.json

下一步