Integración con Next.js

@lingo.dev/compiler se integra con Next.js App Router a través de un wrapper de configuración asíncrono que soporta tanto Webpack como Turbopack.

Configuración

1. Instalar paquete

pnpm install @lingo.dev/compiler

2. Configurar Next.js

Actualiza tu next.config.ts para usar el wrapper asíncrono 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,
    },
  });
}

¿Por qué asíncrono? El wrapper carga los plugins de forma diferida y resuelve la configuración dinámicamente. Esto mantiene tu compilación rápida y permite la carga condicional de plugins.

3. Añadir proveedor

Envuelve tu aplicación con LingoProvider en tu layout raíz:

// 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>
  );
}

Importante: Coloca LingoProvider dentro de <html> pero envolviendo todo el contenido. Funciona tanto con componentes de servidor como de cliente.

Componentes de servidor de React

El compilador soporta completamente los componentes de servidor de React (RSC). Los componentes de servidor se traducen en tiempo de compilación, y las traducciones se incrustan en la salida del servidor.

// 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>
  );
}

No se añade JavaScript del lado del cliente para el texto traducido en componentes de servidor.

Componentes de cliente

Para componentes de cliente, usa la directiva "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>
  );
}

Los componentes de cliente reciben paquetes de traducción optimizados. Solo se incluyen las traducciones utilizadas en ese componente.

Detección de idioma

Por defecto, el idioma se almacena en una cookie (locale). El compilador maneja automáticamente la detección y persistencia del idioma.

Detección personalizada de idioma en el servidor

Para lógica personalizada (base de datos, encabezados, subdominio), crea .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;
}

Esta función se llama en el servidor para cada solicitud. Debe devolver el código de idioma (por ejemplo, "en", "es").

Persistencia personalizada de idioma en el cliente

Para lógica personalizada del lado del cliente (localStorage, parámetros de URL), crea .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());
}

Consulta Resolvedores de idioma personalizados para más detalles.

Middleware para enrutamiento de idiomas

Si deseas enrutamiento basado en idiomas (/en/about, /es/about), implementa el middleware de 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|.*\\..*).*)"],
};

Actualiza tus rutas para usar el segmento dinámico [locale]:

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

Configuración de compilación

Compilación de desarrollo

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

Ejecuta npm run dev para iniciar el servidor de desarrollo con pseudotraducciones instantáneas.

Compilación de producción

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

Ejecuta npm run build para compilar para producción. No se necesitan claves de API: las traducciones provienen de .lingo/metadata.json.

Buena práctica: Genera traducciones reales en CI antes de compilar para producción. Consulta Modos de compilación para el flujo de trabajo recomendado.

Compatibilidad con Turbopack

El compilador funciona tanto con Webpack como con Turbopack (Next.js 15+).

Para usar Turbopack en desarrollo:

next dev --turbo

El compilador detecta y configura automáticamente el empaquetador apropiado.

TypeScript

El compilador está completamente tipado. Importa los tipos desde @lingo.dev/compiler:

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

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

Problemas comunes

"Cannot find module '@lingo.dev/compiler/react'" Asegúrate de haber instalado el paquete: pnpm install @lingo.dev/compiler

HMR no funciona después de añadir LingoProvider Asegúrate de que LingoProvider esté colocado correctamente en tu layout raíz, no en un layout o página anidados.

Las traducciones no se muestran en la compilación de producción Verifica que estés usando buildMode: "cache-only" y que .lingo/metadata.json contenga traducciones para todos los locales.

"Missing translations for locale X" Ejecuta tu servidor de desarrollo con usePseudotranslator: false para generar traducciones reales, o ejecuta una compilación de CI para poblar .lingo/metadata.json.

Próximos pasos