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?
| Feature | Paraglide.js | react-intl | i18next | react-i18next |
|---|---|---|---|---|
| Bundle-Größe | ~0 KB (kompiliert) | ~30 KB | ~35 KB | ~40 KB |
| Runtime-Overhead | Keiner (Compile-time) | Nachrichtenparsing | Runtime-Lookups | Runtime-Lookups |
| Typsicherheit | ✅ Vollständig (generiertes TS) | Teilweise | Teilweise | Teilweise |
| Tree-shakeable | ✅ (Dead Code Elimination) | ❌ | ❌ | ❌ |
| Lazy Loading | Integriert (per-Locale-Split) | Manuell | Manuell | Manuell |
| ICU-Nachrichten | ✅ | ✅ | Teilweise | Teilweise |
| SSR-kompatibel | ✅ | ✅ | ✅ | ✅ |
Setup
npm install @inlang/paraglide-js
npx paraglide-js init
// 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
// 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}}"
}
// 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
// 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
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
// 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
// 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
// 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
// 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:
# 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:
// Dynamischer Import basierend auf dem Locale
// Dies wird von Paraglide zur Build-Zeit automatisch gehandhabt
import(`../paraglide/${locale}.js`)
Zahlen- und Datumsformatierung
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:
// 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
# 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:
- Keine Runtime-Kosten — Übersetzungen werden ins Bundle kompiliert
- Typsicherheit — fehlende Nachrichten werden zur Build-Zeit erkannt, nicht zur Runtime
- Optimierte Auslieferung — Benutzer laden nur die Nachrichten ihres Locales herunter
- 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.