Translate a phrase
The translate() method both translates and automatically queues new phrases for registration:
$client->setLocale('es-es');
$text = $client->translate('Home');$text = $client->translate('Home', 'es-es');$text = $client->translate('Home', 'es-es', 'UI');$text = $client->translate('Home', null, 'UI');$text = $client->translate('Menu Item', 'es-es', 'Navigation', 'content-block-id');$text = $client->translate('Hello, {name}!', null, 'UI', null, ['name' => 'Sarah']);If the phrase doesn’t exist in translations, it will be:
- Queued for registration (if write key)
- Returned as-is (fallback to original)
- Added to in-memory cache to prevent re-queueing in the same request
Placeholder interpolation
Section titled “Placeholder interpolation”Pass dynamic values via the $params argument rather than building the string yourself. Placeholders use {name}, the same canonical form the Langsys JS SDKs store, so a shared catalog renders identically from a PHP backend and a JS frontend.
Placeholders work in phrases, content blocks and whole pages:
$client->translate('Hello, {name}!', null, 'UI', null, ['name' => 'Sarah']);$client->translateContentBlock('<p>Welcome back, {name}</p>', 'homepage', ['name' => 'Sarah']);$client->translatePage($html, 'homepage', [], ['name' => 'Sarah']);In translateContentBlock(), placeholders resolve in text nodes and in translatable attributes (placeholder, alt, title, …).
| Case | Result |
|---|---|
| Unknown key | Left verbatim ({name} stays {name}) |
Key present but null | Also left verbatim |
{ name } | Whitespace tolerated |
| String value | Passed through untouched — the opt-out from number formatting (IDs, codes) |
| Int / float | Formatted for the locale (1234.5 → 1.234,5 in de-DE) |
| Bool | "true" / "false" |
DateTimeInterface | Locale-formatted, medium date style |
| Phrase not translated yet | Fallback text still interpolates, so users never see raw {name} |
The phrase registered with Langsys always keeps its placeholders — interpolation only affects what your code receives.
Plurals (ICU MessageFormat)
Section titled “Plurals (ICU MessageFormat)”Full ICU is supported, so plural categories are correct per language — Russian’s four, Arabic’s six:
$client->translate( '{n, plural, one {# item} other {# items}}', 'ru-RU', null, null, ['n' => 21]);Requires the intl extension. If intl is disabled at runtime the SDK logs a warning naming the phrase and falls back to simple {name} substitution rather than failing the render. Malformed ICU also falls back rather than throwing.
Excluding content from translation
Section titled “Excluding content from translation”Two spellings, both author-facing and equivalent:
<code translate="no">rm -rf /</code><div data-notrans>Internal build id: 4f2a91</div>translate="no" is the standard HTML attribute. data-notrans is an alias for hosts whose templating strips unknown bare attributes, or where translate collides with another tool.
For data-notrans, presence alone is intent — the bare attribute works like any boolean HTML attribute — and only an explicit "false" or "0" opts out (case-insensitive). Excluded subtrees are never extracted, never registered, and never rendered over.
This is also the right way to tell translatePage() that a subtree is already translated. Without it, page translation looks up the already-translated string as a source phrase, misses, and registers it, putting translated text into the catalog as a new source phrase.
Keeping a run together with data-langsys-phrase
Section titled “Keeping a run together with data-langsys-phrase”By default, page translation splits at tag boundaries, so this becomes two catalog entries — "Based on {n}" and "reviews":
<p>Based on {n} <strong>reviews</strong></p>That puts the count in a different phrase from the noun it inflects, and no plural rule can reach across the boundary. Mark the element to register the whole run as one phrase:
<p data-langsys-phrase>Based on {n} <strong>reviews</strong></p>The catalog then receives a single entry with markup tokens:
Based on {n} {m0o}reviews{m0c}{m0o} / {m0c} are the opening and closing positions of the first element. Translators may move them, and the markup is rebuilt where the tokens end up. If a translation drops, unbalances or misnumbers the tokens, the text is rendered without the markup rather than failing.
This is the same wire format as the JS SDK’s <Phrase> component, so entries registered by either SDK are usable by the other.