Paraglide.js in Practice: Modern React i18n Workflows for SaaS

Praxisleitfaden zu Paraglide.js – der modernen Compile-time-I18n-Bibliothek für React – mit Setup, Nachrichtensextraktion, Runtime-Switching, Pluralisierung, Datumsformatierung und TanStack-Start-Integration.

Sam Rivera
Sam Rivera
16. Juni 202612 min read
Auch verfügbar auf:English · 中文

TL;DR: Paraglide.js ist eine Compile-time-I18n-Bibliothek, die Nachrichten zur Build-Zeit extrahiert und optimierte Bundles pro Locale mit null Runtime-Overhead generiert. Im Gegensatz zu traditionellen i18n-Bibliotheken (react-intl, i18next), die alle Übersetzungen an den Client senden, liefert Paraglide nur das Locale aus, das der Benutzer benötigt. Dieser Leitfaden behandelt Produktionsmuster für mehrsprachige SaaS-Anwendungen, die Inhalte auf Englisch, Deutsch und Chinesisch bereitstellen.


Warum Paraglide?

FeatureParaglide.jsreact-intli18nextreact-i18next
Bundle-Größe~0 KB (kompiliert)~30 KB~35 KB~40 KB
Runtime-OverheadKeiner (Compile-time)NachrichtenparsingRuntime-LookupsRuntime-Lookups
Typsicherheit✅ Vollständig (generiertes TS)TeilweiseTeilweiseTeilweise
Tree-shakeable✅ (Dead Code Elimination)❌❌❌
Lazy LoadingIntegriert (per-Locale-Split)ManuellManuellManuell
ICU-Nachrichten✅✅TeilweiseTeilweise
SSR-kompatibel✅✅✅✅

Setup

bash
npm install @inlang/paraglide-js
npx paraglide-js init
typescript
// project.inlang/settings.json
{
  "sourceLanguageTag": "en",
  "languageTags": ["en", "zh", "de"],
  "modules": ["https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js"],
  "plugin.inlang.messageFormat": {
    "pathPattern": "./messages/{languageTag}.json"
  }
}

Nachrichtendateien

json
// messages/en.json
{
  "$schema": "https://inlang.com/schema/inlang-message-format",
  "app_title": "TanStack Ship",
  "nav_home": "Home",
  "nav_pricing": "Pricing",
  "nav_docs": "Docs",
  "hero_heading": "Build Your SaaS Faster",
  "hero_subheading": "A production-ready SaaS starter with auth, billing, and multi-language support.",
  "cta_start": "Get Started",
  "cta_learn": "Learn More",
  "footer_copyright": "© 2026 TanStack Ship. All rights reserved.",
  "pricing_monthly": "Monthly",
  "pricing_yearly": "Yearly",
  "pricing_save": "Save {discount}%",
  "welcome_back": "Welcome back, {name}!",
  "items_count": "{count} {count, plural, one {item} other {items}}"
}
json
// messages/zh.json
{
  "$schema": "https://inlang.com/schema/inlang-message-format",
  "app_title": "TanStack Ship",
  "nav_home": "首页",
  "nav_pricing": "定价",
  "nav_docs": "文档",
  "hero_heading": "更快地构建您的 SaaS",
  "hero_subheading": "一个生产就绪的 SaaS 启动套件,支持认证、计费和多媒体。",
  "cta_start": "开始使用",
  "cta_learn": "了解更多",
  "footer_copyright": "© 2026 TanStack Ship。保留所有权利。",
  "pricing_monthly": "月付",
  "pricing_yearly": "年付",
  "pricing_save": "节省 {discount}%",
  "welcome_back": "欢迎回来,{name}!",
  "items_count": "{count} 个{count, plural, other {项目}}"
}

Verwendung in React-Komponenten

Grundlegende Verwendung

typescript
// Von Paraglide zur Build-Zeit generiert
import * as m from "../paraglide/messages"

function HeroSection() {
  return (
    <section className="hero">
      <h1>{m.hero_heading()}</h1>
      <p>{m.hero_subheading()}</p>
      <div className="ctas">
        <Button>{m.cta_start()}</Button>
        <Button variant="secondary">{m.cta_learn()}</Button>
      </div>
    </section>
  )
}

Parameter und Pluralisierung

typescript
function WelcomeBanner({ userName, itemCount }: { userName: string; itemCount: number }) {
  return (
    <div className="welcome">
      <h2>{m.welcome_back({ name: userName })}</h2>
      <p>{m.items_count({ count: itemCount })}</p>
      {/* Englisch: "Welcome back, Alice!" / "5 items" */}
      {/* Chinesisch: "欢迎回来,Alice!" / "5 个项目" */}
    </div>
  )
}

Locale-Switching

Mit TanStack-Router-Suchparametern

typescript
// src/routes/__root.tsx
import { useRouter } from "@tanstack/react-router"

function LocaleSwitcher() {
  const router = useRouter()

  const switchLocale = async (locale: "en" | "zh" | "de") => {
    // Locale für Paraglide setzen
    await setLocale(locale)

    // URL aktualisieren, ohne die Seite neu zu laden
    router.navigate({
      to: router.state.location.pathname,
      search: { locale },
      replace: true,
    })
  }

  return (
    <select onChange={(e) => switchLocale(e.target.value as any)}>
      <option value="en">English</option>
      <option value="zh">中文</option>
      <option value="de">Deutsch</option>
    </select>
  )
}

Serverseitige Locale-Erkennung

typescript
// server/middleware/locale.ts
export const resolveLocale = createServerFn({ method: "GET" }).handler(
  async ({ request, context }) => {
    // Priorität: URL-Parameter > Cookie > Accept-Language-Header > Standard
    const urlParam = new URL(request.url).searchParams.get("locale")
    if (urlParam && ["en", "zh", "de"].includes(urlParam)) {
      return urlParam
    }

    const cookie = request.headers.get("Cookie")
    const cookieLocale = cookie?.match(/locale=(\w+)/)?.[1]
    if (cookieLocale && ["en", "zh", "de"].includes(cookieLocale)) {
      return cookieLocale
    }

    const acceptLanguage = request.headers.get("Accept-Language")
    if (acceptLanguage?.startsWith("zh")) return "zh"
    if (acceptLanguage?.startsWith("de")) return "de"

    return "en" // Standard
  }
)

SEO-Integration

hreflang-Tags

typescript
// Aus Paraglides Sprach-Tags generiert
function HreflangTags() {
  const locales = ["en", "zh", "de"]

  return (
    <>
      {locales.map((locale) => (
        <link
          key={locale}
          rel="alternate"
          hrefLang={locale}
          href={`https://tanstackship.com/${locale}`}
        />
      ))}
      <link rel="alternate" hrefLang="x-default" href="https://tanstackship.com" />
    </>
  )
}

Locale-spezifische kanonische URLs

typescript
// TanStack-Router-Routenkonfiguration
export const Route = createFileRoute("/pricing")({
  loader: async ({ context }) => {
    const locale = context.locale ?? "en"
    const messages = await loadMessages(locale)

    return {
      meta: {
        title: messages.page_title_pricing,
        description: messages.page_desc_pricing,
        canonical: `https://tanstackship.com/${locale}/pricing`,
        hreflang: ["en", "zh", "de"].map((l) => ({
          lang: l,
          url: `https://tanstackship.com/${l}/pricing`,
        })),
      },
    }
  },
})

Optimiertes Bundle-Splitting

Paraglides Compile-time-Ansatz generiert separate Bundles pro Locale:

bash
# Build-Ausgabe — jedes Locale ist unabhängig cachebar
dist/
  paraglide/
    en.js       # 3.2 KB — Englische Nachrichten
    zh.js       # 3.5 KB — Chinesische Nachrichten
    de.js       # 3.4 KB — Deutsche Nachrichten

Es wird nur das Locale des Benutzers geladen:

typescript
// Dynamischer Import basierend auf dem Locale
// Dies wird von Paraglide zur Build-Zeit automatisch gehandhabt
import(`../paraglide/${locale}.js`)

Zahlen- und Datumsformatierung

typescript
function PriceDisplay({ amount, locale }: { amount: number; locale: string }) {
  const formatter = new Intl.NumberFormat(locale, {
    style: "currency",
    currency: "USD",
  })

  return <span>{formatter.format(amount)}</span>
  // en: "$29.00" | zh: "US$29.00" | de: "29,00 $"
}

function DateDisplay({ date, locale }: { date: Date; locale: string }) {
  return (
    <time dateTime={date.toISOString()}>
      {new Intl.DateTimeFormat(locale, {
        dateStyle: "long",
      }).format(date)}
    </time>
    // en: "June 16, 2026" | zh: "2026年6月16日" | de: "16. Juni 2026"
  )
}

Arbeit mit dem Übersetzungsmanagement

Inlang-IDE-Erweiterung

Verwenden Sie die Inlang-VS-Code-Erweiterung für die Inline-Nachrichtenbearbeitung:

typescript
// In VS Code: Bewegen Sie den Mauszeiger über m.hero_heading(), um Übersetzungen zu sehen
// Klicken Sie, um direkt in der IDE zu bearbeiten

function HeroSection() {
  return (
    <h1>{m.hero_heading()}</h1>
    // Drücken Sie Cmd+., um den Übersetzungseditor zu öffnen
  )
}

Export/Import für Übersetzer

bash
# Alle Nachrichten als CSV für Übersetzer exportieren
npx paraglide-js export --format csv

# Übersetzte Nachrichten importieren
npx paraglide-js import translated_messages.csv

Produktions-Checkliste

  • Alle benutzerorientierten Zeichenketten verwenden Paraglide-Nachrichtenfunktionen
  • Locale-Wechsel löst keinen vollständigen Seitenreload aus
  • Locale-Präferenz in Cookie und Datenbank gespeichert
  • hreflang-Tags für alle unterstützten Locales gesetzt
  • Kanonische URLs enthalten Locale-Präfix
  • Serverseitige Locale-Erkennung verarbeitet Accept-Language-Header
  • Zahlen- und Datumsformatierung verwenden Intl-APIs (nicht hartcodiert)
  • Übersetzungsdateien haben einen Review-Workflow
  • Fehlende Übersetzungen fallen auf die Ausgangssprache (Englisch) zurück
  • RTL-Unterstützung geprüft, falls zutreffend (Arabisch, Hebräisch)

Fazit

Paraglide.js steht für die nächste Generation der Internationalisierung für React-Anwendungen. Sein Compile-time-Ansatz eliminiert den Runtime-Overhead und bietet gleichzeitig vollständige Typsicherheit sowie optimiertes Bundle-Splitting. In Kombination mit TanStack Start fügen sich Locale-Erkennung und -Wechsel nahtlos in die Server-Funktionen und Routing-Architektur ein. Für eine breitere Perspektive zum Eintritt in globale Märkte lesen Sie SaaS globalization strategy.

Die wesentlichen Vorteile für SaaS:

  1. Keine Runtime-Kosten — Übersetzungen werden ins Bundle kompiliert
  2. Typsicherheit — fehlende Nachrichten werden zur Build-Zeit erkannt, nicht zur Runtime
  3. Optimierte Auslieferung — Benutzer laden nur die Nachrichten ihres Locales herunter
  4. SEO-bereit — hreflang, kanonische URLs und Sitemap-Integration

Für eine vollständige multilinguale SEO-Strategie mit hreflang, kanonischen URLs und Inhaltslokalisierung über verschiedene Märkte hinweg lesen Sie Multilingual SEO complete strategy und das hreflang tags ultimate guide.