Skip to content

Translator

The Translator brings AI-powered translation to several places in the Sulu admin: individual text fields, full documents when copying a locale, categories, and media metadata.

Text translation

Editors can translate the content of text fields directly in the admin. The action is gated by the sulu.ai.translate security context (category Sulu AI).

Full content translation

When an editor copies content from another locale and chooses the translate option, the bundle translates the whole document in one pass instead of copying it verbatim.

Out of the box this works for:

  • Pages
  • Articles (when SuluArticleBundle is installed)
  • Snippets

on both Sulu 2.6 and Sulu 3.0.

What gets translated

The bundle walks the document's form metadata and translates:

  • All properties whose types are configured as plain-text field types (default: text_line, text_area)
  • All properties whose types are configured as HTML field types (default: text_editor) — translated with HTML handling so markup survives
  • Block content, including nested and global blocks, and image_map hotspot content
  • SEO extension data (title, description, keywords) and excerpt extension data (title, description, more)
  • Selected sub-properties of complex property types via property_type_translation_properties (default: the title of link properties and the title/description of teaser_selection items)

If your project uses custom property types that contain translatable text, add them to the configuration:

sulu_ai_platform:
    text_field_types: ['text_line', 'text_area', 'my_custom_text_type']
    html_field_types: ['text_editor', 'my_custom_editor_type']
    property_type_translation_properties:
        my_complex_type: ['items/*/label']

URL generation

After translating, the bundle regenerates the resource locator for the new locale from the translated content — using the fields tagged with sulu.rlp.part (falling back to the title) and respecting a route_schema option on the URL field. Homepage documents are excluded.

Enabled languages

The set of target languages offered to editors is a fixed list of DeepL-supported languages. Individual languages can be disabled:

sulu_ai_platform:
    translator:
        languages:
            BG: false   # hide Bulgarian as a target language

Note: The locale_map description field is not used for translations — it only provides language context for the generator experts.

Category translation

Category names and metadata can be translated into other locales from the category management in the admin.

Media metadata translation

Media titles and descriptions (alt text and caption) can be translated into the media's other locales — see Media Metadata Generator.

Translating custom entities

Full content translation is not available out of the box for custom entities (for example entities built on the content bundle). The supported path is to mirror what the bundle does for snippets — the snippet integration is deliberately the simplest reference implementation.

The pattern is the same on Sulu 2.6 and Sulu 3.0; the example below shows Sulu 2.6, on Sulu 3.0 use Sulu3FullContentTranslationSubscriber::onSnippetTranslationCopied() as the reference instead:

  1. Extend Sulu\Bundle\AiPlatformBundle\Features\FullContentTranslation\AbstractFullContentTranslationSubscriber.
  2. Subscribe to the event your entity dispatches when a translation is copied (the equivalent of SnippetTranslationCopiedEvent).
  3. Wrap the document and call handleContentTranslation() — exactly like Sulu26FullContentTranslationSubscriber::onSnippetTranslationCopied() does:
<?php

namespace App\EventSubscriber;

use App\Domain\Event\CustomEntityTranslationCopiedEvent;
use Sulu\Bundle\AiPlatformBundle\Compatibility\ContentPersister\Sulu26ContentDecorator;
use Sulu\Bundle\AiPlatformBundle\Features\FullContentTranslation\AbstractFullContentTranslationSubscriber;

final class CustomEntityFullContentTranslationSubscriber extends AbstractFullContentTranslationSubscriber
{
    public static function getSubscribedEvents(): iterable
    {
        yield CustomEntityTranslationCopiedEvent::class => 'onTranslationCopied';
    }

    public function onTranslationCopied(CustomEntityTranslationCopiedEvent $event): void
    {
        $document = $event->getDocument();

        $this->handleContentTranslation(
            'custom_entity_key',                    // form metadata key of your entity
            new Sulu26ContentDecorator($document),  // adapter around your document
            $event->getResourceLocale(),
            $event->getEventContext()['sourceLocale'] ?? null,
            $event->getEventPayload() ?? [],
            $document->getStructureType(),
        );
    }

    protected function generateUrl(array $parts, string $parentPath, string $urlFieldType, string $locale, ?string $routeSchema): string
    {
        // Only needed when your entity has a route/resource_locator field;
        // see Sulu26FullContentTranslationSubscriber for a route-generator-based implementation.
        return $parentPath . '/' . \implode('-', $parts);
    }
}
  1. Register the subscriber with the same dependencies as the built-in one:
// config/services.php
$services->set(CustomEntityFullContentTranslationSubscriber::class)
    ->args([
        service('sulu_ai_platform.translator'),
        service('sulu_admin.structure_form_metadata_loader'),
        service('sulu_admin.metadata_provider_registry'),
        service(ContentRepositoryInterface::class),
        service('request_stack'),
        service(SecurityCheckerInterface::class),
        param('sulu_ai_platform.property_type_translation_properties'),
        param('sulu_ai_platform.text_field_types'),
        param('sulu_ai_platform.html_field_types'),
    ])
    ->tag('kernel.event_subscriber');

Notes:

  • Sulu26ContentDecorator wraps PHPCR-based documents. If your custom entity is Doctrine-based (content bundle), implement the small ContentAdapterInterface for your entity instead and persist through your own repository.
  • The translation only runs when the admin request carries ?translate=true and the user holds the sulu.ai.translate permission — the abstract subscriber checks both for you.

Permission

All translation actions are gated by the sulu.ai.translate security context (category Sulu AI).