Skip to content
better-i18n.com
本页内容

Overview #

Code
┌─────────────────────────────────────────────────────────────┐
│                    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:

Code
https://cdn.better-i18n.com/{org}/{project}/{resource}

Resources #

ResourceURLDescription
Manifest/org/project/manifest.jsonAvailable languages
Messages/org/project/{locale}.jsonTranslations for a locale
Flags/flags/{code}.svgCountry flag images

Examples #

Code
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.svg

The Manifest #

Every project has a manifest.json that describes available languages:

manifest.json
{
  "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:

en.json
{
  "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:

Code
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.json

With namespaced delivery enabled, the manifest exposes two extra fields:

JSON
{
  "batch": true,
  "namespaces": ["common", "hero", "pricing", "footer"],
  "files": { "en": { "url": ".../translations.json", ... } }
}
  • batch: true — CDN supports /{locale}/batch.json?ns=a,b,c for multi-namespace requests in a single round-trip
  • namespaces: 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:

ResourceCache DurationInvalidation
manifest.json5 minutesOn publish
{locale}.json1 hourOn publish
Flag images1 yearNever (immutable)

Cache Headers #

Http
# 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, immutable

Publishing #

When you publish translations:

  1. New files are uploaded to origin storage
  2. CDN cache is purged globally
  3. New requests hit origin, then cache at edge
  4. Subsequent requests served from edge cache
Code
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:

LocaleURLNotes
English (default)/aboutClean URL for primary audience
Turkish/tr/aboutLocale prefix
German/de/aboutLocale prefix

Benefits #

Clean URLs for Primary Audience:

Code
https://example.com/pricing       ← Most users
https://example.com/tr/pricing    ← Turkish users

SEO Friendly:

  • Each locale has unique, indexable URLs
  • Search engines can crawl all versions
  • hreflang tags link translations together

SEO Considerations #

Hreflang Tags

Tell search engines about language alternatives:

HTML
<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:

HTML
<!-- 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:

XML
<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>

Custom CDN #

For enterprise deployments, you can use a custom CDN URL:

TSX
<BetterI18nProvider
  projectId="org/project"
  locale="en"
  cdnBaseUrl="https://cdn.your-company.com"
>
  <App />
</BetterI18nProvider>

Self-Hosting #

To self-host translations:

  1. Export JSON files from dashboard
  2. Host on your own CDN/server
  3. Configure cdnBaseUrl in provider
Code
https://cdn.your-company.com/org/project/manifest.json
https://cdn.your-company.com/org/project/en.json

Preloading #

For faster initial load, preload translations:

HTML
<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:

JavaScript
// 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:

  1. Check cache headers with browser DevTools
  2. Try hard refresh (Ctrl+Shift+R)
  3. Verify publish completed in dashboard

CORS Errors #

The CDN sets appropriate CORS headers:

Http
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:

  1. Use preload links
  2. Check edge location (use CDN debug headers)
  3. Consider server-side rendering