|
Documentación
Reservar una demoPlataforma
PlataformaMCPCLIAPIFlujos de trabajo
Guías
Registro de cambios

Localización

  • Resumen
  • API de traducción
  • Localización de aplicaciones web
  • Localización de apps móviles
  • iOS con catálogos de cadenas
  • Android con strings.xml
  • Localización de emails
  • Contenido estático (p. ej., .md, .json)
  • Next.js con Markdoc
  • Rails con i18n

Flujos de trabajo

  • Configuración del motor con MCP
  • Triaje de Jira
  • CI/CD

Localización de Next.js App Router con Markdoc

La CLI de Lingo.dev traduce archivos de Markdoc y catálogos JSON de cadenas de interfaz mediante un motor de localización configurado. Markdoc es un formato de autoría basado en Markdown con etiquetas personalizadas tipadas y respaldadas por React, ideal para sitios con Next.js App Router que combinan contenido extenso con componentes interactivos.

Esta guía te acompaña de principio a fin en la localización de un sitio con Next.js App Router: configurar la CLI, organizar el contenido por idioma, renderizar Markdoc en rutas dinámicas y automatizar las traducciones con GitHub Actions.

Repositorio de demostración

Clona o haz un fork de lingodotdev/markdoc-nextjs-localization-example para seguir el tutorial. El repositorio incluye una aplicación funcional de Next.js App Router con contenido en Markdoc, una configuración de la CLI de Lingo.dev y un flujo de trabajo de GitHub Actions.

Cómo funciona la localización con Next.js + Markdoc#

La mayoría de los sitios con Next.js App Router dividen el contenido localizado en dos capas:

CapaQué incluyeArchivo de ejemplo
Contenido extensoPáginas de marketing, documentación y artículos del blogsrc/content/[locale]/pages/home.md
Cadenas de interfazEtiquetas de la barra de navegación, CTA y estados de botonessrc/content/[locale]/ui.json

Las rutas viven en src/app/[lang]/ y leen los archivos del idioma correspondiente en tiempo de ejecución. Un middleware selecciona un idioma predeterminado a partir de la cabecera Accept-Language del navegador y redirige rutas sin prefijo como / a /en (o a la mejor coincidencia).

El bucket markdoc de la CLI procesa archivos de Markdoc manteniendo intactos el frontmatter y las etiquetas personalizadas, y el bucket json se encarga del catálogo de cadenas de interfaz. Ambos traducen el delta mediante tu motor de localización y escriben archivos por idioma junto al original.

Requisitos previos#

1

Crea un motor de localización

Cada ejecución de la CLI envía el contenido a través de un motor de localización: la configuración que determina qué modelo LLM, glosario, voz de marca e instrucciones se aplican. Crea uno en el panel de Lingo.dev y genera una API key.

2

Comprueba Node.js

La CLI requiere Node.js 18 o superior:

bash
node -v
3

Prepara tu proyecto de Next.js

Tu proyecto necesita App Router (src/app/) y un directorio de contenido por idioma. El repositorio de demostración usa src/content/[locale]/ con dos subcarpetas (pages/ y blog/) más un archivo ui.json. Consulta Next.js internationalization para ver los conceptos básicos del enrutado.

Organiza el contenido#

Divide el contenido según su función. Las páginas y publicaciones largas se redactan en Markdoc; las cadenas cortas de la interfaz se guardan en JSON para que los componentes puedan cargarlas directamente.

text
src/content/
  en/                  # Source locale
    pages/home.md      # Long-form Markdoc
    blog/hello.md
    ui.json            # UI strings (navbar, CTAs, button states)
  es/                  # Target locales – generated by Lingo.dev
  fr/
  de/

Los archivos de Markdoc admiten frontmatter para metadatos por página (título, descripción, fecha y autor) y etiquetas personalizadas que se renderizan como componentes de React. Una página mínima tiene este aspecto:

markdown
---
title: Author once in Markdoc, ship in every language.
description: An example Next.js App Router app that localizes Markdoc with Lingo.
---

{% inline-callout type="info" %}
This page is authored in Markdoc and translated by Lingo.dev.
{% /inline-callout %}

## Built from three pieces

Markdoc custom tags render as React components – even interactive ones.

Configura la CLI#

Crea un archivo i18n.json en la raíz del proyecto. Declara dos buckets: uno para el contenido en Markdoc y otro para el catálogo de cadenas de interfaz:

json
{
  "$schema": "https://lingo.dev/schema/i18n.json",
  "version": "1.15",
  "locale": {
    "source": "en",
    "targets": ["es", "fr", "de"]
  },
  "buckets": {
    "markdoc": {
      "include": [
        "src/content/[locale]/pages/*.md",
        "src/content/[locale]/blog/*.md"
      ]
    },
    "json": {
      "include": ["src/content/[locale]/ui.json"]
    }
  }
}

El marcador de posición [locale] se resuelve con cada código de idioma configurado. Con source: "en", la CLI lee desde src/content/en/ y escribe los archivos traducidos en src/content/es/, src/content/fr/ y src/content/de/.

Catálogos de un solo archivo

Si tus cadenas de interfaz están en un único archivo JSON multiidioma en lugar de un archivo por idioma, usa el tipo de bucket json-per-locale. Consulta Static Content Localization para ver la lista completa de tipos de bucket.

Renderiza Markdoc en App Router#

Una ruta dinámica típica carga un documento y renderiza el árbol transformado. El repositorio de demostración incluye un pequeño helper:

ts
// src/lib/markdoc.ts
export async function loadDoc(
  locale: Locale,
  collection: "pages" | "blog",
  slug: string,
) {
  const raw = await fs.readFile(
    path.join(process.cwd(), "src/content", locale, collection, `${slug}.md`),
    "utf8",
  );
  const ast = Markdoc.parse(raw);
  const frontmatter = ast.attributes.frontmatter
    ? parseFrontmatter(ast.attributes.frontmatter)
    : {};
  const content = Markdoc.transform(ast, { ...schema, variables: { frontmatter } });
  return { frontmatter, content };
}

La página de App Router es un contenedor ligero que combina el documento con cadenas de interfaz específicas de cada idioma:

tsx
// src/app/[lang]/page.tsx
export default async function Home({ params }: PageProps<"/[lang]">) {
  const { lang } = await params;
  const doc = await loadDoc(lang, "pages", "home");
  const { home } = await getMessages(lang);

  return (
    <main>
      <h1>{doc.frontmatter.title}</h1>
      {renderMarkdoc(doc.content)}
    </main>
  );
}

Las etiquetas personalizadas de Markdoc (callout, bento, blog-hero, etc.) se declaran en markdoc.schema.ts y se conectan a componentes de React en src/components/markdoc/. Consulta la documentación del esquema de Markdoc para ver la API completa.

Detecta el idioma en el middleware#

El middleware de Next.js inspecciona la solicitud antes de que se renderice una ruta. Úsalo para redirigir rutas sin prefijo al idioma que mejor encaje según la cabecera Accept-Language:

ts
// src/middleware.ts
export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  const hasLocale = locales.some(
    (locale) => pathname === `/${locale}` || pathname.startsWith(`/${locale}/`),
  );
  if (hasLocale) return;

  const locale = pickLocale(request); // parses Accept-Language
  const url = request.nextUrl.clone();
  url.pathname = `/${locale}${pathname === "/" ? "" : pathname}`;
  return NextResponse.redirect(url);
}

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

Los visitantes llegan a /en, /es, /fr o /de sin tener que escribir nunca el prefijo.

Traduce en local#

Configura tu API key y ejecuta la CLI:

bash
export LINGO_API_KEY="your-api-key"
npx lingo.dev@latest run

La CLI lee todos los archivos que coinciden con los patrones de tus buckets, identifica las entradas sin traducir mediante el lockfile, traduce el delta con tu motor de localización y escribe los resultados en el directorio de cada idioma de destino. Se conservan las claves del frontmatter, las etiquetas personalizadas de Markdoc y la estructura del JSON: solo cambia el texto traducible.

Para dirigirte a un idioma concreto durante el desarrollo:

bash
npx lingo.dev@latest run --target-locale es

Automatiza con GitHub Actions#

Añade un archivo de flujo de trabajo en .github/workflows/translate.yml para traducir con cada push:

Las traducciones se confirman directamente en main: cero fricción, ideal para equipos pequeños:

yaml
name: Translate
on:
  push:
    branches: [main]
permissions:
  contents: write
jobs:
  translate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Lingo.dev
        uses: lingodotdev/lingo.dev@main
        with:
          api-key: ${{ secrets.LINGODOTDEV_API_KEY }}

Guarda tu API key como LINGODOTDEV_API_KEY en Settings > Secrets and variables > Actions dentro de tu repositorio de GitHub.

Verifica antes de desplegar#

Usa la opción --frozen como control previo al despliegue para asegurarte de que no llegue contenido sin traducir a producción. La CLI finaliza con un estado distinto de cero si alguna entrada necesita traducción:

bash
npx lingo.dev@latest run --frozen

Añádelo como un paso de CI independiente antes de tu build de Next.js:

yaml
- name: Verify translations
  run: npx lingo.dev@latest run --frozen
- name: Build
  run: pnpm build

Siguientes pasos#

Static Content Localization
Markdown, MDX, JSON, YAML y más tipos de bucket
Localización de aplicaciones web
Patrones de cadenas de interfaz en los frameworks web más habituales
Flujos de trabajo de CI/CD
Patrones para GitHub Actions, GitLab CI y Bitbucket Pipelines
Glosarios
Protege nombres de marca y términos técnicos para que no se traduzcan

¿Te ha resultado útil esta página?

Max PrilutskiyMax Prilutskiy·Actualizado hace 4 días·6 min de lectura