Zurück zum Blog
Terminal output showing a translation script writing de.json and fr.json, alongside a folder tree and a browser rendering the German locale route.

Wie man eine Next.js-App mit PolyLingo in weniger als 30 Minuten übersetzt

By Robert

Wie man eine Next.js-App mit PolyLingo in unter 30 Minuten übersetzt

Am Ende dieses Tutorials hast du ein funktionierendes mehrsprachiges Next.js App Router Projekt: Strings extrahiert in messages/en.json, übersetzte Locale-Dateien für jede benötigte Sprache, next-intl liefert die richtige Datei pro Route und ein einzelnes Node-Skript, das du bei Änderungen am Inhalt erneut ausführen kannst.

Keine Anmeldung bei einer Übersetzungsplattform. Keine pauschalen Gebühren pro Sprache. Ein API-Aufruf erledigt alle Zielsprachen auf einmal.

Was du brauchst:

  • Ein Next.js-Projekt mit App Router (Next.js 14 oder 15)
  • Node.js 18 oder neuer
  • Ein kostenloses PolyLingo-Konto und API-Schlüssel

Schritt 1: Hol dir deinen PolyLingo API-Schlüssel (5 Minuten)

Erstelle ein kostenloses Konto auf usepolylingo.com. Der kostenlose Tarif beinhaltet 100.000 Tokens pro Monat, was ausreicht, um eine mittelgroße Locale-Datei mehrmals in 10+ Sprachen zu übersetzen.

Sobald du eingeloggt bist, gehe im Dashboard zu API keys und erstelle einen Schlüssel. Du siehst den vollständigen Wert nur einmal, also kopiere ihn sofort.

Füge ihn als Umgebungsvariable zu deinem Projekt hinzu. Committe ihn niemals in die Versionskontrolle und zeige ihn niemals im Client-Code an:

# .env.local
POLYLINGO_API_KEY="pl_dein_schluessel_hier"

Überprüfe, ob die API erreichbar ist, bevor du weitermachst:

curl -sS "https://api.usepolylingo.com/v1/health"

Du solltest eine kleine JSON-Antwort mit "status": "ok" erhalten.


Schritt 2: Installiere next-intl und richte Routing ein (10 Minuten)

Installiere die Bibliothek:

npm install next-intl

Erstelle eine i18n.ts Datei im Projektstamm. Diese teilt next-intl mit, welche Locales du unterstützt und wie die richtige Nachrichten-Datei für jede Anfrage geladen wird:

// i18n.ts
import { getRequestConfig } from 'next-intl/server'

export const locales = ['en', 'de', 'fr'] as const
export type Locale = (typeof locales)[number]
export const defaultLocale: Locale = 'en'

export default getRequestConfig(async ({ requestLocale }) => {
  let locale = await requestLocale
  if (!locale || !locales.includes(locale as Locale)) {
    locale = defaultLocale
  }
  return {
    locale,
    messages: (await import(`./messages/${locale}.json`)).default,
  }
})

Füge Middleware hinzu, um Routen mit dem Locale-Präfix zu versehen:

// middleware.ts
import createMiddleware from 'next-intl/middleware'
import { locales, defaultLocale } from './i18n'

export default createMiddleware({
  locales: [...locales],
  defaultLocale,
  localePrefix: 'as-needed',
})

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

Verschiebe deine Seiten-Dateien unter app/[locale]/. Aktualisiere dein Root-Layout, um den Locale-Parameter zu empfangen und die Kinder mit NextIntlClientProvider zu umschließen:

// app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl'
import { getMessages } from 'next-intl/server'

export default async function LocaleLayout({
  children,
  params,
}: {
  children: React.ReactNode
  params: Promise<{ locale: string }>
}) {
  const { locale } = await params
  const messages = await getMessages()
  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  )
}

Schritt 3: Extrahiere deine Strings in eine JSON-Nachrichtendatei (10 Minuten)

Erstelle einen messages/ Ordner im Projektstamm. Füge eine en.json Datei mit deinen Quellstrings hinzu. next-intl verwendet eine verschachtelte Schlüsselstruktur:

{
  "Home": {
    "title": "Willkommen",
    "cta": "Loslegen"
  }
}

Aktualisiere deine Seiten, um useTranslations anstelle von fest codierten Strings zu verwenden:

// app/[locale]/page.tsx
import { useTranslations } from 'next-intl'

export default function HomePage() {
  const t = useTranslations('Home')
  return (
    <main>
      <h1>{t('title')}</h1>
      <button type="button">{t('cta')}</button>
    </main>
  )
}

Schreibe nun das Übersetzungsskript. Dieses liest messages/en.json, sendet es an die PolyLingo API mit format: "json" und schreibt eine Ausgabedatei pro Ziel-Locale. Das Flag format: "json" sagt der API, die Schlüsselstruktur zu erhalten und nur Stringwerte zu übersetzen – verschachtelte Schlüssel, Arrays und Nicht-String-Typen bleiben unverändert.

// scripts/translate-messages.mjs
// Ausführen mit: node scripts/translate-messages.mjs

import fs from 'node:fs'
import path from 'node:path'

const API_KEY = process.env.POLYLINGO_API_KEY
const API_URL = (process.env.POLYLINGO_API_URL || 'https://api.usepolylingo.com/v1').replace(/\/$/, '')
const TARGETS = ['de', 'fr'] // erweitere dieses Array, um weitere Locales hinzuzufügen

const enPath = path.join('messages', 'en.json')
const en = JSON.parse(fs.readFileSync(enPath, 'utf8'))

const res = await fetch(`${API_URL}/translate`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    content: JSON.stringify(en),
    format: 'json',
    targets: TARGETS,
    model: 'standard',
  }),
})

if (!res.ok) {
  const err = await res.text()
  throw new Error(`PolyLingo ${res.status}: ${err}`)
}

const { translations } = await res.json()

for (const locale of TARGETS) {
  const out = path.join('messages', `${locale}.json`)
  fs.writeFileSync(out, JSON.stringify(JSON.parse(translations[locale]), null, 2) + '\n')
  console.log('Wrote', out)
}

Führe es aus:

node scripts/translate-messages.mjs

Du solltest eine Ausgabe wie diese sehen:

Wrote messages/de.json
Wrote messages/fr.json

Öffne diese Dateien und überprüfe sie. Deine Schlüssel sind identisch zu en.json. Nur die Stringwerte wurden geändert.


Schritt 4: Teste die Routen (5 Minuten)

Starte deinen Dev-Server:

npm run dev

Besuche http://localhost:3000 und http://localhost:3000/de. Die Überschrift und der Button sollten auf Englisch bzw. Deutsch angezeigt werden. Füge weitere Locales hinzu, indem du das TARGETS Array im Skript und das locales Array in i18n.ts erweiterst, und führe das Skript erneut aus.

Überprüfe deinen Token-Verbrauch im PolyLingo-Dashboard unter Usage. Für eine kleine Locale-Datei, die in zwei Sprachen übersetzt wurde, hast du nur ein paar hundert Tokens deines monatlichen Kontingents verbraucht.


Wie es weitergeht

Füge weitere Locales hinzu. Das Skript sendet eine Anfrage, egal wie viele Einträge in TARGETS sind. Japanisch, Spanisch und Arabisch kosten nur einen API-Aufruf, nicht drei.

Integriere es in CI. Füge POLYLINGO_API_KEY als Repository-Secret in GitHub Actions hinzu und führe das Skript als Teil deiner Build-Pipeline aus. Deine Locale-Dateien bleiben automatisch synchron, sobald sich en.json ändert.

Übersetze andere Formate. Das gleiche Skriptmuster funktioniert für Markdown-Dokumentationsseiten (format: "markdown") und HTML-E-Mail-Vorlagen (format: "html"). Die API erhält die Struktur in allen Fällen.

Nutze den Batch-Endpunkt für größere Projekte. Wenn du mehrere separate JSON-Dateien hast (z.B. eine pro Feature-Bereich), akzeptiert POST /translate/batch bis zu 100 Items in einer Anfrage, jeweils mit eigener id und format.


Kostenlos ausprobieren

Der kostenlose Tarif von PolyLingo beinhaltet 100.000 Tokens pro Monat. Keine Kreditkarte erforderlich.

curl -sS -X POST "https://api.usepolylingo.com/v1/translate" \
  -H "Authorization: Bearer $POLYLINGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "{\"Home\":{\"title\":\"Willkommen\",\"cta\":\"Loslegen\"}}",
    "format": "json",
    "targets": ["de", "fr", "es", "ja"]
  }'

Hole deinen API-Schlüssel