← Learn

Troubleshooting

Every entry here is a failure mode someone actually hit. Each one lists the symptom, why it happens, and the fix — most come down to one of three things: the phrase isn't in the project, the SDK never initialized, or a placeholder was spelled for the wrong context.

Nothing translates — every phrase stays in the source language

When everything shows source text, the SDK almost certainly failed to initialize. It fails soft by design (your UI must never break), so the only loud signal is the browser console — check it first. The usual causes, in order of likelihood:

  1. CORS. The SDK's first request is authorize-project; if your API doesn't allowlist the app's origin, the browser blocks it and init dies right there. Remember an origin is the exact scheme + host + port — localhost:5173 and localhost:5174 are different origins, and dev servers drift ports when their usual one is taken. Allowlist every port you actually develop on.
  2. Wrong credentials. A missing or mistyped project id / API key fails the same authorize call. The console shows the failing request and URL.
  3. You're looking at the base locale. In the base locale the "translation" is the source text — nothing visibly changes. Switch to a target locale to test.
The CORS failure, and the fix
# The SDK's first call is an authorize-project request.
# If the browser blocks it, NOTHING translates. Check the console for:
Access to fetch at 'https://your-api/api/authorize-project/...'
from origin 'http://localhost:5173' has been blocked by CORS policy

# Fix: allowlist the app's EXACT origin (scheme + host + port) on the API.
CORS_ALLOWED_ORIGINS=http://localhost:5173,https://app.example.com

One phrase won't translate (but others do)

The phrase in your code is the lookup key — the phrase is the key. If a single phrase renders as source text while its neighbors translate, that key isn't in the project's catalog:

Why don't missing phrases throw an error?

By design. In production a translation system must degrade to readable source text, never to a broken page — so an unregistered phrase renders its source and the SDK stays quiet. The flip side: on a read-only key a missing phrase is silent. Nothing is logged, nothing is registered, and the first you hear of it is a user seeing English. The discipline that prevents it is the key split — develop with a write key so every phrase your UI renders gets discovered, then ship read-only.

Write key in dev, read-only in prod
# Development — write key: new phrases are discovered,
# registered, and machine-translated as you build.
VITE_LANGSYS_API_KEY=write-key

# Production — read-only key: fetches the finished catalog only.
# Unregistered phrases silently render their source text.
VITE_LANGSYS_API_KEY=read-only-key

My placeholder renders literally — "Hi %name%" or "Hi {name}"

It works, then breaks after I switch locales back and forth

Update the SDK. Real, fixed bugs in this area: returning to the initial/SSR locale left the previous catalog active (fixed in langsys-js-typescript 0.2.2), and content-block Translate/Phrase consumers could hang forever on a server-seeded catalog (fixed in 0.2.3). If you see stale-locale strings or blocks that never resolve, check your installed version against the base SDK's changelog before debugging your own code.

Server-side: the page 500s instead of degrading

The PHP SDK's client throws when constructed without credentials — so a Laravel (or plain PHP) app with an empty LANGSYS_API_KEY fails at container resolution, before any translation runs. Set the credentials (or, for an app that must boot without them, bind a stand-in client the way the demo apps do). Once credentials exist, runtime API failures do degrade: an unreachable or refusing API renders source text and reports the exception — it never takes the page down.

Server-side: the first request hangs, then everything is fast

The PHP SDK's HTTP timeout is 30 seconds, and an API instance waking from idle can approach it on the very first call. That request either completes slowly (and warms the catalog cache — everything after is served from cache) or times out and renders source text. If your first page load after a quiet period is slow, this is why; it's the API warming up, not your integration.

Server-side: I rendered with a write key but the phrase isn't in the dashboard

Discovered phrases are queued during the request and sent after the response goes out — Laravel's terminable middleware. Refresh the dashboard a moment after the page loads, not during. On long-lived worker runtimes (Octane, FrankenPHP, RoadRunner) make sure you're on the SDK's provided integration — the plain PHP shutdown hook never fires between worker requests.

Server rendering: translations from one request bleed into another

The JS base SDK holds one locale's catalog at a time — it's a module-level singleton. A Node server rendering different locales for concurrent requests will race. Fetch the catalog per request and seed the client from it, the pattern in the Next.js, Nuxt and SvelteKit guides, or run one locale per process.

Still stuck? Open an issue on the SDK's repo — typescript, react, vue, svelte, php, or laravel — with the console output (or server log) of the failing request. Or write to [email protected].

Search the docs & Learning Center