Skip to content

CDN Deployment

The CDN is Comvi’s production read path. When you publish a project, translations become JSON files at predictable URLs on Cloudflare’s global edge — this is where your apps should fetch them in production.

Every published bundle is one JSON file per (language, namespace) pair. The default namespace is served at the project root; all other namespaces sit under a folder named after the namespace:

https://cdn.comvi.io/<distribution-token>/<language>.json # default namespace
https://cdn.comvi.io/<distribution-token>/<namespace>/<language>.json # any other namespace

Examples for a project with token abc123:

https://cdn.comvi.io/abc123/en.json # default namespace, English
https://cdn.comvi.io/abc123/checkout/fr.json # checkout namespace, French
https://cdn.comvi.io/abc123/emails/pt-BR.json # emails namespace, Brazilian Portuguese

The distribution token is project-scoped. Find it under Settings → Content Deploy in the dashboard.

Each file is a flat JSON map from key to value:

{
"greeting": "Hello, {name}!",
"nav.home": "Home",
"nav.settings": "Settings"
}

Responses are served compact (no pretty-printing), gzip/brotli compressed, with Content-Type: application/json. Any i18n library that can load a JSON resource from a URL can consume them — see Consume Translations.

Clicking Publish (or writing the API equivalent) triggers a worker job that:

  1. Builds a manifest — one file per selected (language, namespace), filtered by your configured status filter
  2. Uploads the files to object storage (Cloudflare R2), in parallel with retries
  3. Deletes stale files that were in the previous snapshot but are not in this one
  4. Invalidates the Cloudflare edge cache for every affected URL
  5. Updates the deployment record and notifies connected dashboard clients

The whole thing is typically done in a few seconds. You do not need to manage cache headers, URLs, or versions — the platform owns the full lifecycle.

sequenceDiagram
  participant U as User
  participant P as Platform API
  participant S as R2 Storage
  participant C as CDN Edge
  participant D as Dashboard

  U->>P: Click Publish
  Note over P: Build per-namespace per-language bundles
  P->>S: Upload bundles
  P->>C: Invalidate cache keys
  P->>D: Send deploy status update
  P-->>U: Publish complete
Publish is atomic from the user's perspective but fans out to storage, CDN, and the live dashboard status.

Comvi publishes in two modes, both writing to the same CDN:

ModeTriggerDelay
ManualYou click PublishRuns immediately
Auto (if enabled)Any translation edit, import, or status changeDebounced: 30 s after the last edit, capped at 5 min total

Auto-deploy batches a burst of edits into one publish. Every new edit pushes the scheduled deploy forward by 30 seconds, up to a hard ceiling of 5 minutes from the first edit in the burst. This means a single typo fix doesn’t redeploy the world, but a long edit session also can’t starve the deploy forever.

Manual publish always wins: if you click Publish while an auto-deploy is pending, the auto-deploy is cancelled and the immediate job runs instead.

Under Settings → Content Deploy you control what is deployed:

SettingEffect
EnabledMaster switch. If off, publish is a no-op (the dashboard still accepts the click).
LanguagesSubset of project languages to include. Default: all.
NamespacesSubset of namespaces to include. Default: all.
Status filterWhich translation statuses to include. Default: all statuses if nothing is selected. Select only Translated to keep drafts out of production.
FormatDefault json. Other formats (e.g. i18next-v4 compatible shape) available for compatibility with third-party loaders.

The status filter is the most important setting in practice. See Translation Lifecycle for what each status means.

Each publish records a result you can see in the dashboard and via the REST API:

  • Statussuccess, partial (some files failed but below 5%), or failed (aborted)
  • Files uploaded / failed
  • Duration
  • Warnings — e.g. ICU messages that couldn’t be converted to a legacy format

The last deploy timestamp and result are persisted on the project, so you can tell exactly what is currently on the edge without re-running anything.

Publish invalidates every file it touches. On the next request to any affected URL, users get the fresh bundle. For typical bundle sizes and edge traffic, propagation is a few seconds globally.

If you are debugging a stale read:

  • Confirm the CDN setting is Enabled
  • Confirm the language and namespace you are fetching are in the CDN settings’ allow-list
  • Confirm the value’s status matches the status filter. If no statuses are selected, all statuses are included.
  • Bypass your own client cache (the SDK, the browser, and any intermediate CDN in front of your own site will all cache)

There is no first-class “rollback to previous snapshot” button today. Because publish rebuilds the manifest from the current database state, the way to roll back is:

  1. Revert the offending translation values in the dashboard (use the editor’s history to find the previous value)
  2. Click Publish

This gives you a fully auditable rollback — every change still shows up in history. If you need versioned snapshots for a specific compliance reason, file a request.

  • 5% failure threshold — a deployment with more than 5% of files failing is aborted; the previous snapshot remains live
  • Debounce ceiling — auto-deploy waits at most 5 minutes from the first edit in a burst before it runs, regardless of continued edits
  • One in-flight deploy per project — a new manual publish is ignored while another is already running

Start from the top of the Cache invalidation checklist. The most common cause is the status filter excluding a value that is still Not reviewed.

A partial deployment means under 5% of files failed, so the publish went through. Open the deploy result in the dashboard to see which files, and why. Typical causes are transient R2 errors — a re-publish usually clears them.

Above 5% of files failed, and the deploy was aborted. The previous snapshot is still live. Open the deploy result to see error messages. If errors are uniform (e.g. network issues) just retry. If they are format conversion errors (e.g. ICU → i18next-v4), fix the source values and re-publish.

Public project CDN URLs are unauthenticated — the token in the URL is the access identifier. Treat tokens as secrets only if your translations themselves are sensitive. For genuinely private content, contact us.