Once you’ve published a distribution, consuming it is
plain HTTPS. There is no SDK, no authentication and no handshake — just a
manifest and the files it points at.
This page covers the client side. For the manifest’s exact shape and cache
headers, see the
Content delivery manifest reference.
Quick start
That’s the whole contract.
Fetch one language, not all of themThe manifest lists every language the distribution publishes, but a screen
renders one. Fetching them all costs N requests and N× the bytes to display the
same page — on a 12-language project that’s 12× the payload for nothing.Take the language from your app’s locale, not from iterating manifest.files.
You never construct a file URL yourself — take files[].url from the manifest.
It’s absolute, so a distribution published to your own bucket or CDN needs no
client change.
Web apps
i18next
Register a backend so i18next requests exactly the language it needs, when it
needs it. Do not preload every file into resources.
Split by module (i18next namespaces)
If the distribution has Split by module enabled, it publishes one file per
(language, module) instead of one per language, laid out as
<language>/<module>.json. Match on both:
Then consume a namespace where you need it:
Keys resolve within their namespaceA key stored in TextSetu as checkout.title ships inside checkout.json as
title. So it’s t("title") from the checkout namespace, or
t("checkout:title") from anywhere else — not t("checkout.title").That’s required for i18next to resolve it at all, which is why the prefix is
stripped rather than kept.
To avoid a loading flash on a route transition, warm the namespace first:
Official i18next documentation:
Plain JSON, without a framework
languageDetails carries the label, flag and text direction, so the picker
renders from the manifest you already fetched:
Always keep that fallback: languageDetails is optional, and releases published
before the field existed don’t carry it.
Mobile apps
Packaged resources — strings.xml, Localizable.strings, .arb — are resolved
at build time and cannot be replaced at runtime. So the pattern is: fetch,
cache to disk, parse yourself, and keep the bundled files as the fallback for
a first launch with no network.
The same shape works on iOS and Android. Two things worth doing:
- Key the disk cache on
contentHash. It changes exactly when the content
changes, so you never need to guess whether your copy is stale.
- Load at launch, apply at next launch if your UI can’t re-render mid-session.
Fetching in the background and swapping on the next cold start avoids strings
changing under the user.
Server-side rendering
Fetch the manifest at boot, then refresh on an interval or on a webhook.
Loading every language is right here — a server renders all of them. This is
the one case where the “fetch one language” rule doesn’t apply.
Better than polling: subscribe to the distribution.published
webhook and call refresh() when it arrives.
Pulling in CI
For interchange formats, or to commit translations into your repo:
To fail a build when a language is missing:
Handling errors
Your client should handle four cases. See the
status code reference
for the full list.
Always have a local fallback. Ship a baseline copy of your source language
with the app and treat the remote files as an upgrade. A network failure then
degrades to slightly stale strings instead of an empty screen:
Detecting a new release
Two options, no polling of the file contents required:
X-TextSetu-Release — the manifest response carries the release version
as a header, so you can detect a change without parsing the body.
- Webhooks — subscribe to
distribution.published for a push the moment a
release lands. See the Webhooks guide.