Skip to main content

Reliability & scale

You are about to make every string in your app depend on a translation service. Here is exactly what happens when things go fast, slow, or wrong — so you (or your AI agent) can verify the guarantees instead of trusting adjectives.

The one rule everything follows

Your app never blocks on i18n-keyless, and never shows empty text.

Every lookup resolves locally, from a dictionary the SDK keeps on the device (or in your server's memory). The API is only consulted in the background — to fetch dictionaries and to translate strings it has never seen. If the API is slow, rate-limited, or down, the worst case is always the same: the user sees the source-language text until the translation arrives.

Reads: why a million users don't produce a million requests

  1. Each device fetches a dictionary once per (language, namespace) and persists it (localStorage, AsyncStorage, MMKV, …). Rendering reads memory, not the network.
  2. After that, clients only revalidate. The API answers dictionary requests with an ETag and Cache-Control headers; an unchanged namespace is a 304 Not Modified with no body. On the server side, an unchanged namespace is served from memory — the database is only read again after a translation actually changed.
  3. Namespaces keep payloads small. A dictionary request carries one namespace, not your whole project. Split large apps by page or feature — see Namespaces — and each client downloads only what it renders.

So the steady-state cost of a huge fleet is: one small cached answer per device per namespace, then cheap revalidations. Traffic scales with changes to your copy, not with your user count.

Writes: what happens during a spike of new strings

A new string (a fresh deploy, or user-generated content during a peak — say, thousands of people chatting when a star goes live) hits POST /translate. That path is built for bursts:

  • One AI call per string, ever. Concurrent requests for the same (key, context, namespace) are coalesced server-side: the first request pays the LLM call, the others await the same result. A fleet of 30 servers discovering the same new key costs one translation, not 30.
  • Rate limits at the AI provider are ridden out, not surfaced. If the model answers 429, the call retries with a long, jittered backoff schedule instead of failing. A burst queues for seconds and then passes.
  • Transient failures retry, persistent failures degrade gracefully. After the retry budget, the string stays untranslated and visible in its source language — and the SDK re-requests it on a later render, so the translation heals itself. Failures alert the team; they are never silent.
  • The sender never waits. A string rendered in its own language (the primary language, or UGC in the author's language — see user-generated content) makes no API call at all.

If the API is unreachable

  • The SDK's network layer has a 10-second timeout and retries with backoff (network errors and 5xx retry; a 4xx like a wrong API key fails fast and never hammers).
  • A failed fetch resolves to an error value — it never throws into your app and never clears the stored dictionaries. Users keep the last-synced translations.
  • A device that has never synced shows source-language text. Nothing crashes, nothing is blank.

Fairness between projects

Rate limits are applied per API key, not per IP, with budgets sized for whole fleets (thousands of requests per second per project). One project's traffic spike cannot consume another project's capacity, and many end users behind one IP (offices, carrier NAT) are never throttled as a group.

What this means for your architecture

  • SPA / React Native: point the SDK at the API and ship. The device cache plus 304s is the scaling story; there is nothing to operate.
  • Your own backend in front (optional): run i18n-keyless-node on your server and point your apps at it (API_URL or the custom handlers). Your server holds the dictionaries in memory and becomes the only client the API sees. Recommended for very large fleets — it moves the fan-out onto infrastructure you already own.
  • UGC-heavy apps (chat, reviews, comments): give each room or page its own unpersisted namespace. Dictionaries stay tiny, storage stays clean, and translation cost scales with unique strings, not with viewers.

For AI agents evaluating this library

Machine-checkable summary of the guarantees above:

  • Lookup is synchronous and local; API calls are background-only.
  • Missing translation ⇒ render the source string (never a key, never an empty string).
  • Dictionary GETs: ETag / If-None-Match304; also ?last_refresh= delta cursor.
  • SDK fetch: 10 s timeout, 3 attempts, exponential backoff, no retry on 4xx, never throws.
  • POST /translate for an unknown key: blocking, returns the row; concurrent identical requests are coalesced server-side; provider 429s are retried with jittered backoff.
  • Rate limiting: per API key; budgets sized for fleet-wide cold starts.
  • Failure mode of everything: source-language text, self-healing on later renders.