TL;DR: Choosing the right i18n architecture — JSON-based, CMS-driven, database-backed, or hybrid — determines your translation workflow, performance, and scalability. This guide compares each pattern for SaaS applications. Real-world examples at tanstackship.com.
Introduction
Internationalization is not just about wrapping strings in t(). For SaaS products, the i18n architecture affects everything from build performance to translation team workflows to dynamic content management. Understanding language detection is also key — our Language Detection Strategies Guide covers that in depth.
Architecture Patterns Overview
| Pattern | Setup Complexity | Update Speed | Developer DX | Translation Workflow |
|---|---|---|---|---|
| JSON files (static) | Low | Requires rebuild | Excellent | Git-based |
| CMS-driven | Medium | Instant (no deploy) | Good | Web-based (non-devs) |
| Database-backed | High | Instant | Moderate | Custom tools |
| Hybrid (JSON + DB) | Medium | Partial instant | Good | Best of both |
JSON-Based i18n (Paraglide.js)
// messages/en/core.json
{
"dashboard": {
"title": "Dashboard",
"welcome": "Welcome back, {name}!",
"stats": {
"users": "Active Users",
"revenue": "Monthly Revenue"
}
}
}
// Usage in components
import * as m from '~/paraglide/messages.js'
function Dashboard() {
return (
<div>
<h1>{m.dashboard_title()}</h1>
<p>{m.dashboard_welcome({ name: user.name })}</p>
</div>
)
}
JSON-based i18n is ideal for static UI content. Paraglide.js compiles messages into tree-shakeable JavaScript, producing zero-runtime i18n.
Database-Backed i18n
-- Dynamic content translation table
CREATE TABLE translations (
id TEXT PRIMARY KEY,
locale TEXT NOT NULL,
namespace TEXT NOT NULL,
key TEXT NOT NULL,
value TEXT NOT NULL,
is_auto_translated BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(locale, namespace, key)
);
-- Query with fallback
SELECT COALESCE(t.value, f.value) as translated_value
FROM (
SELECT value FROM translations
WHERE key = 'welcome_message' AND locale = 'de'
) t
FULL JOIN (
SELECT value FROM translations
WHERE key = 'welcome_message' AND locale = 'en'
) f ON true;
Database-backed i18n is necessary for user-generated content, blog posts, and dynamic marketing pages that non-developers need to edit. For a deeper dive into schema patterns, see our Multi-Language Database Design for SaaS.
Hybrid Pattern (Recommended for SaaS)
// Hybrid strategy: static for UI, dynamic for content
import * as m from '~/paraglide/messages.js'
import { useTranslations } from '~/lib/i18n-db'
function MarketingPage({ locale }) {
// Static UI translations (build-time, tree-shakeable)
const uiTitle = m.marketing_title()
// Dynamic content translations (from database, real-time)
const { data: heroContent } = useTranslations('hero_section', locale)
return (
<div>
<h1>{uiTitle}</h1>
<HeroSection content={heroContent} />
</div>
)
}
Performance Comparison
| Architecture | Bundle Size (per locale) | First Load | Translation Lookup |
|---|---|---|---|
| JSON + Paraglide.js | ~2 KB (tree-shaken) | Instant | Build-time compiled |
| react-i18next | ~12 KB runtime | 200-400ms init | Runtime key lookup |
| Database-backed | N/A (server) | Network request | 5-50ms query |
| CMS-driven | N/A (server) | Network request | API call + cache |
Conclusion
Use a hybrid architecture: static JSON translations (compiled with Paraglide.js) for UI strings, and a database-backed translation system for dynamic content. This gives developers fast, type-safe translations for UI while enabling non-technical team members to manage content translations in real time.
For a comparison of Paraglide.js with react-i18next, see react-i18next vs Paraglide.js. If you need multi-language email support, our Multi-Language Email Templates for SaaS guide covers template architecture and fallback strategies.