Skip to content
Nav

Full page translation

composer require langsys/langsys-php
Packagist version Monthly downloads License PHP 7.4+

translatePage() translates an entire HTML document in one pass. It automatically:

  • Sets the lang attribute on <html>
  • Ensures <meta charset="utf-8"> exists
  • Translates <title>, meta description, OpenGraph tags, Twitter cards
  • Extracts and translates body content (phrases and content blocks)
  • Registers new phrases automatically (if write key)
  • Falls back to original content when translations are missing
ob_start();
// ... your application generates the HTML page ...
$html = ob_get_contents();
ob_end_clean();
$client = new Client();
$client->setLocale('es-es');
echo $client->translatePage($html, 'homepage');
$client->setLocale('fr-ca');
$locale = $client->getLocale(); // detected from HTTP_ACCEPT_LANGUAGE, or project's base_locale
echo (new Client())->setLocale('de-de')->translatePage($html, 'contact');
  1. Single-phrase blocks are registered as simple phrases (<p>Hello</p> → phrase "Hello"; inline formatting on a single phrase is preserved)
  2. Multi-phrase blocks are registered as content blocks (<nav><a>Home</a><a>About</a></nav>)
  3. Head section is processed for translatable meta tags
  4. translate="no" is respected to skip elements
  5. Script/style tags are never processed

The key distinction: if a block element contains exactly one phrase that matches its text content, it’s a simple phrase. Multiple phrases (from text nodes or attributes) = content block.

With a read-only API key, translatePage() translates content that has existing translations, silently skips registration of new phrases, and falls back to original content for untranslated items.

Per-section categories with data-langsys-category

Section titled “Per-section categories with data-langsys-category”

By default, all phrases on a page use the category passed to translatePage(). Override per section:

<header data-langsys-category="header">
<h1>Welcome</h1> <!-- category: header -->
</header>
<main>
<p>Hello World</p> <!-- category: homepage (default) -->
<section data-langsys-category="widgets/contact">
<h2>Contact Us</h2> <!-- category: widgets/contact -->
</section>
</main>

Categories are inherited — child elements use their parent’s category unless they specify their own. Empty string (data-langsys-category="") falls back to __uncategorized__.

echo $client->translatePage($html, 'homepage', [
'nav li' => ['category' => 'Navigation'],
'.action, .btn' => ['category' => 'UI Elements'],
'section.contact' => [
'category' => 'Contact Form',
'overrideParentElementCategory' => true,
],
]);

Supported selectors: tag, class, ID, tag+class, attribute, descendant, child, and comma-separated lists.

Priority order (highest to lowest):

  1. Selector with overrideParentElementCategory: true
  2. Element’s data-langsys-category attribute
  3. Inherited category from parent
  4. Selector with overrideParentElementCategory: false
  5. Default category parameter
  6. __uncategorized__

overrideParentElementCategory defaults to false so HTML attributes win unless you opt in.

Forcing a content block with data-langsys-contentblock

Section titled “Forcing a content block with data-langsys-contentblock”
<section data-langsys-contentblock="true">
<h2>Contact Us</h2>
<p>Fill out the form below</p>
</section>

Truthy values: "true", "1", "yes", or any non-empty string (but not "false", "0", or ""). Use it for complex widgets that should be translated as a unit.

See also keeping a run together and excluding content.

Feedback