Lingo.dev CLI překládá soubory Markdoc i JSON katalogy řetězců uživatelského rozhraní přes nakonfigurovaný lokalizační engine. Markdoc je autorský formát postavený na Markdownu s typovanými vlastními tagy napojenými na React – skvěle se proto hodí pro weby na Next.js App Routeru, které kombinují dlouhý obsah s interaktivními komponentami.
Tento průvodce vás krok za krokem provede lokalizací webu v Next.js App Routeru: od nastavení CLI přes organizaci obsahu pro jednotlivé jazyky a renderování Markdocu v dynamických routách až po automatizaci překladů pomocí Lingo.dev GitHub App.
Ukázkový repozitář
Naklonujte si nebo forkněte lingodotdev/markdoc-nextjs-localization-example a postupujte podle návodu. Repozitář obsahuje funkční aplikaci v Next.js App Routeru s obsahem z Markdocu, konfigurací CLI pro Lingo.dev a CI workflow.
Jak funguje lokalizace v Next.js + Markdoc#
Většina webů na Next.js App Routeru rozděluje lokalizovaný obsah do dvou vrstev:
| Vrstva | Co sem patří | Ukázkový soubor |
|---|---|---|
| Dlouhý obsah | Marketingové stránky, dokumentace, blogové příspěvky | src/content/en/pages/home.md |
| Řetězce uživatelského rozhraní | Popisky navigace, CTA, stavy tlačítek | src/content/en/ui.json |
Routy žijí pod src/app/[lang]/ a při zpracování požadavku načítají soubory pro odpovídající jazyk. Middleware vybere výchozí jazyk z hlavičky prohlížeče Accept-Language a přesměruje základní cesty jako / na /en (nebo na nejbližší shodu).
CLI zpracuje soubory Markdocu se zachovaným frontmatterem i vlastními tagy a katalog UI textů obslouží jako JSON. V obou případech přeloží jen změny přes váš lokalizační engine a zapíše soubory pro jednotlivé jazyky vedle zdrojových souborů.
Předpoklady#
Vytvořte lokalizační engine
Při každém spuštění CLI se obsah odesílá přes lokalizační engine — konfiguraci, která určuje, jaký model LLM, glosář, hlas značky a pravidla se použijí. Vytvořte ho v Lingo.dev dashboardu a pro CI vygenerujte API key.
Ověřte verzi Node.js
CLI vyžaduje Node.js 22 nebo novější:
node -vPřipravte projekt v Next.js
Projekt potřebuje App Router (src/app/) a adresář s obsahem pro každý jazyk. Demo repozitář používá pod src/content/ jeden adresář pro každý jazyk (například src/content/en/), ve kterém jsou dvě podsložky (pages/ a blog/) plus soubor ui.json. Základy routování najdete v Next.js internationalization.
Uspořádejte obsah#
Rozdělte obsah podle jeho role. Dlouhé stránky a příspěvky se píšou v Markdoc, zatímco krátké řetězce uživatelského rozhraní žijí v JSON, aby je komponenty mohly načítat přímo.
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/Soubory Markdoc podporují frontmatter pro metadata jednotlivých stránek (title, description, date, author) a vlastní tagy, které se vykreslují jako React komponenty. Minimální stránka vypadá takto:
---
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.Nakonfigurujte CLI#
Nainstalujte CLI a přihlaste se:
npm install -g @lingo.dev/cli
lingo loginPak vygenerujte konfiguraci a propojte ji se svým enginem:
lingo init
lingo linklingo init vytvoří .lingo/config.json s vaším zdrojovým a cílovými jazyky i se vzory souborů k překladu; lingo link přidá vaše orgId a engineId. Commitněte .lingo/config.json, aby celý tým i všechny běhy v CI používaly stejnou konfiguraci.
V tomto projektu konfigurace definuje dva vzory souborů – jeden pro obsah Markdocu a druhý pro katalog UI textů:
{
"orgId": "org_...",
"engineId": "eng_...",
"sourceLocale": "en",
"targetLocales": ["es", "fr", "de"],
"files": [
{ "pattern": "src/content/en/pages/*.md" },
{ "pattern": "src/content/en/blog/*.md" },
{ "pattern": "src/content/en/ui.json" }
]
}Segment jazyka v každé cestě se nahrazuje podle cílového jazyka: src/content/en/pages/home.md se změní na src/content/es/pages/home.md a src/content/en/ui.json na src/content/de/ui.json. Zdrojová cesta musí obsahovat kód jazyka. Formáty se rozpoznávají automaticky podle přípony souboru, takže soubory Markdocu (.md) i JSON (.json) nepotřebují explicitně uvedený typ. Podrobnosti najdete v Configuration a Formats.
Katalogy v jednom souboru
Nové CLI očekává jeden soubor pro každý jazyk, přičemž kód jazyka je součástí cesty (jak je uvedeno výše). Pokud máte UI texty v jednom vícejazyčném JSON souboru, toto rozložení (dřívější bucket json-per-locale) nové CLI zatím nepodporuje – ponechte ho na legacy CLI a sledujte changelog, kdy podpora dorazí. Doporučená cesta je rozdělit obsah do jednoho souboru pro každý jazyk.
Vykreslení Markdoc v App Routeru#
Typická dynamická route načte dokument a vykreslí transformovaný strom. Ukázkový repozitář k tomu přidává malý helper:
// 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 };
}Stránka v App Routeru je jen tenká vrstva, která propojí dokument s řetězci uživatelského rozhraní pro daný jazyk:
// 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>
);
}Vlastní tagy Markdoc (callout, bento, blog-hero atd.) jsou deklarované v markdoc.schema.ts a napojené na React komponenty v src/components/markdoc/. Kompletní API najdete v Markdoc schema docs.
Detekce jazyka v middleware#
Middleware v Next.js kontroluje požadavek ještě před vykreslením routy. Použijte ho k přesměrování základních cest na nejlépe odpovídající jazyk podle hlavičky Accept-Language:
// 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|.*\\..*).*)", ],
};Návštěvníci se tak dostanou na /en, /es, /fr nebo /de, aniž by prefix kdy sami psali.
Překládejte lokálně#
Po lingo login spusťte push. Při prvním spuštění – nebo po přidání nového cílového jazyka – nejdřív doplňte vše:
lingo push --backfill-missingPři dalších spuštěních už jen pushněte změny:
lingo pushlingo push načte všechny soubory odpovídající vašim vzorům, pomocí lock file (.lingo/lock.json, commitnutého v repozitáři) identifikuje nepřeložené položky, přeloží změny přes váš lokalizační engine, počká na dokončení a zapíše výsledky do adresáře každého cílového jazyka. Klíče frontmatteru, vlastní tagy Markdocu i struktura JSON zůstávají zachované – mění se jen přeložitelný text. Pokud chcete stáhnout překlady vytvořené jinde (například v CI), spusťte lingo pull.
Pokud chcete běh omezit jen na konkrétní soubory, předejte glob:
lingo push "src/content/en/blog/*.md"Automatizace v CI#
Nainstalujte Lingo.dev GitHub App a propojte ji se svým repozitářem. Na straně serveru čte .lingo/config.json i propojený engineId a při každé změně zdrojového obsahu otevře pull request s překlady – bez workflow souboru, bez runneru, bez secretu s API key a bez ruční práce s lock file.
Ověřte vše před nasazením#
Použijte lingo check jako kontrolu před nasazením, abyste měli jistotu, že se do produkce nedostane žádný nepřeložený obsah. Pokud některé položky stále čekají na překlad, příkaz skončí s nenulovým stavovým kódem:
lingo checkPřidejte tento krok jako samostatnou CI fázi před buildem Next.js:
- name: Verify translations
run: lingo check
env:
LINGO_API_KEY: ${{ secrets.LINGO_API_KEY }}
- name: Build
run: pnpm build