How It Works
本页内容
- Overview
- CDN URL Structure
- Resources
- Examples
- The Manifest
- Translation Files
- Namespaced Delivery
- Caching Strategy
- Cache Headers
- Publishing
- Purge Timing
- Edge Locations
- URL Strategy
- Default Pattern
- Benefits
- SEO Considerations
- Custom CDN
- Self-Hosting
- Preloading
- Offline Support
- Troubleshooting
- Stale Translations
- CORS Errors
- Slow Initial Load
- Related
Overview #
┌─────────────────────────────────────────────────────────────┐
│ Better i18n Platform │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Dashboard │ → │ Storage │ → │ CDN │ │
│ │ /GitHub │ │ (Origin) │ │ (Edge) │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ ↓ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────┐
│ Your Application │
│ (React, Next.js, etc.) │
└─────────────────────────────┘CDN URL Structure #
All resources follow a predictable URL pattern:
https://cdn.better-i18n.com/{org}/{project}/{resource}Resources #
| Resource | URL | Description |
|---|---|---|
| Manifest | /org/project/manifest.json | Available languages |
| Messages | /org/project/{locale}.json | Translations for a locale |
| Flags | /flags/{code}.svg | Country flag images |
Examples #
https://cdn.better-i18n.com/acme/webapp/manifest.json
https://cdn.better-i18n.com/acme/webapp/en.json
https://cdn.better-i18n.com/acme/webapp/tr.json
https://cdn.better-i18n.com/flags/us.svgThe Manifest #
Every project has a manifest.json that describes available languages:
{
"defaultLocale": "en",
"locales": ["en", "tr", "de", "es"],
"languages": [
{
"code": "en",
"name": "English",
"nativeName": "English",
"flagUrl": "https://cdn.better-i18n.com/flags/en.svg"
},
{
"code": "tr",
"name": "Turkish",
"nativeName": "Türkçe",
"flagUrl": "https://cdn.better-i18n.com/flags/tr.svg"
}
]
}The manifest powers:
useLanguages()hook for building language switchers- Automatic locale validation
- Default locale fallback
Translation Files #
Each locale has its own JSON file with namespaced translations:
{
"home": {
"title": "Welcome to our app",
"description": "The best app ever built"
},
"common": {
"save": "Save",
"cancel": "Cancel"
}
}Namespaced Delivery #
Projects with many translation files (marketing sites, documentation portals) can use namespaced delivery — one file per namespace per locale:
https://cdn.better-i18n.com/acme/landing/en/common.json
https://cdn.better-i18n.com/acme/landing/en/hero.json
https://cdn.better-i18n.com/acme/landing/en/pricing.jsonWith namespaced delivery enabled, the manifest exposes two extra fields:
{
"batch": true,
"namespaces": ["common", "hero", "pricing", "footer"],
"files": { "en": { "url": ".../translations.json", ... } }
}batch: true— CDN supports/{locale}/batch.json?ns=a,b,cfor multi-namespace requests in a single round-tripnamespaces: string[]— authoritative list for SDK validation
The SDK fetches only the namespaces a page needs instead of the entire bundle. See Selective Loading for configuration and performance details.
Caching Strategy #
The CDN uses aggressive caching with smart invalidation:
| Resource | Cache Duration | Invalidation |
|---|---|---|
manifest.json | 5 minutes | On publish |
{locale}.json | 1 hour | On publish |
| Flag images | 1 year | Never (immutable) |
Cache Headers #
# Manifest
Cache-Control: public, max-age=300, s-maxage=300
# Messages
Cache-Control: public, max-age=3600, s-maxage=3600
# Flags
Cache-Control: public, max-age=31536000, immutablePublishing #
When you publish translations:
- New files are uploaded to origin storage
- CDN cache is purged globally
- New requests hit origin, then cache at edge
- Subsequent requests served from edge cache
Publish → Purge Cache → First Request (Origin) → Cache at Edge → Subsequent Requests (Edge)Purge Timing #
Cache purge propagates globally within 5-10 seconds. During this window, some users may see old content.
Edge Locations #
Translations are served from edge locations worldwide:
- North America (US, Canada)
- Europe (UK, Germany, France, Netherlands)
- Asia Pacific (Japan, Singapore, Australia)
- South America (Brazil)
Users are routed to the nearest edge location automatically.
URL Strategy #
How to structure URLs for internationalized applications. Better i18n follows the "default locale without prefix" pattern.
Default Pattern #
The default locale has no prefix, other locales have prefixes:
| Locale | URL | Notes |
|---|---|---|
| English (default) | /about | Clean URL for primary audience |
| Turkish | /tr/about | Locale prefix |
| German | /de/about | Locale prefix |
Benefits #
Clean URLs for Primary Audience:
https://example.com/pricing ← Most users
https://example.com/tr/pricing ← Turkish usersSEO Friendly:
- Each locale has unique, indexable URLs
- Search engines can crawl all versions
hreflangtags link translations together
SEO Considerations #
Hreflang Tags
Tell search engines about language alternatives:
<link rel="alternate" hreflang="en" href="https://example.com/about" />
<link rel="alternate" hreflang="tr" href="https://example.com/tr/about" />
<link rel="alternate" hreflang="de" href="https://example.com/de/about" />
<link rel="alternate" hreflang="x-default" href="https://example.com/about" />x-default indicates the fallback for unmatched languages.
Canonical URLs
Each page should have a canonical URL:
<!-- On /about -->
<link rel="canonical" href="https://example.com/about" />
<!-- On /tr/about -->
<link rel="canonical" href="https://example.com/tr/about" />Sitemap
Include all locale variants in your sitemap:
<url>
<loc>https://example.com/about</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/about"/>
<xhtml:link rel="alternate" hreflang="tr" href="https://example.com/tr/about"/>
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/about"/>
</url><BetterI18nProvider
projectId="org/project"
locale="en"
cdnBaseUrl="https://cdn.your-company.com"
>
<App />
</BetterI18nProvider>Self-Hosting #
To self-host translations:
- Export JSON files from dashboard
- Host on your own CDN/server
- Configure
cdnBaseUrlin provider
https://cdn.your-company.com/org/project/manifest.json
https://cdn.your-company.com/org/project/en.json<head>
<link
rel="preload"
href="https://cdn.better-i18n.com/org/project/en.json"
as="fetch"
crossorigin
/>
</head>This starts the fetch before JavaScript loads.
Offline Support #
For offline-first apps, cache translations in a service worker:
// sw.js
const TRANSLATION_CACHE = 'translations-v1'
const TRANSLATION_URLS = [
'https://cdn.better-i18n.com/org/project/manifest.json',
'https://cdn.better-i18n.com/org/project/en.json',
'https://cdn.better-i18n.com/org/project/tr.json',
]
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(TRANSLATION_CACHE).then((cache) => {
return cache.addAll(TRANSLATION_URLS)
})
)
})
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('cdn.better-i18n.com')) {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request)
})
)
}
})Troubleshooting #
Stale Translations #
If translations aren't updating:
- Check cache headers with browser DevTools
- Try hard refresh (Ctrl+Shift+R)
- Verify publish completed in dashboard
CORS Errors #
The CDN sets appropriate CORS headers:
Access-Control-Allow-Origin: *If you see CORS errors, check if you're using a custom CDN without proper headers.
Slow Initial Load #
If translations load slowly:
- Use preload links
- Check edge location (use CDN debug headers)
- Consider server-side rendering
Related #
- Provider - Client-side setup
- Server Utilities - Server-side fetching
- TanStack SSR - SSR integration
Better I18N