-
Notifications
You must be signed in to change notification settings - Fork 81
IBX-10684: Document translation management #3249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 5.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| services: | ||
| App\TranslationsManagement\MyCustomProvider: | ||
| tags: | ||
| - name: 'ibexa.translations_management.auto_translate.provider' | ||
| identifier: 'my_custom_provider' | ||
| validation_profile: 'ai_generic' | ||
| App\TranslationsManagement\MyProviderValidator: | ||
| tags: | ||
| - name: 'ibexa.translations_management.auto_translate.provider.validator' | ||
| profile: 'my_custom_profile' | ||
| App\TranslationsManagement\MyTranslationAddExtension: | ||
| tags: | ||
| - { name: form.type_extension } | ||
| App\TranslationsManagement\ImageAltTextTransformer: | ||
| tags: | ||
| - name: 'ibexa.translations_management.auto_translate.field_value_transformer' | ||
| field_type_identifier: 'ibexa_image' | ||
| App\TranslationsManagement\MyCustomExclusionRule: | ||
| tags: | ||
| - { name: 'ibexa.translations_management.side_by_side.exclusion_rule' } | ||
| app.translations_management.exclusion_rule.custom_field_types: | ||
| class: Ibexa\TranslationsManagement\SideBySide\Service\UnsupportedFieldTypeExclusionRule | ||
| arguments: | ||
| $excludedFieldTypeIdentifiers: ['custom_blog_post', 'custom_landing_page'] | ||
| tags: | ||
| - { name: 'ibexa.translations_management.side_by_side.exclusion_rule' } | ||
| App\TranslationsManagement\TwigComponent\MyTranslationModalFooter: | ||
| tags: | ||
| - name: ibexa.twig.component | ||
| group: 'admin-ui-content-translation-modal-footer' | ||
| priority: 10 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| <?php declare(strict_types=1); | ||
|
|
||
| namespace App\TranslationsManagement\EventSubscriber; | ||
|
|
||
| use Ibexa\Contracts\AdminUi\Event\ContentProxyTranslateEvent; | ||
| use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
| use Symfony\Component\HttpFoundation\RedirectResponse; | ||
| use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
|
|
||
| final readonly class ContentProxyTranslateSubscriber implements EventSubscriberInterface | ||
| { | ||
| public function __construct( | ||
| private UrlGeneratorInterface $urlGenerator, | ||
| ) { | ||
| } | ||
|
|
||
| public static function getSubscribedEvents(): array | ||
| { | ||
| return [ | ||
| ContentProxyTranslateEvent::class => ['onProxyTranslate', 200], | ||
| ]; | ||
| } | ||
|
|
||
| public function onProxyTranslate(ContentProxyTranslateEvent $event): void | ||
| { | ||
| // Read the translation context: | ||
| $event->getContentId(); | ||
| $event->getFromLanguageCode(); // ?string — null when no source language exists | ||
| $event->getToLanguageCode(); | ||
| $event->getLocationId(); // ?int — null when no location context is available | ||
|
|
||
| $url = $this->urlGenerator->generate('your_custom_route', [ | ||
| 'contentId' => $event->getContentId(), | ||
| ]); | ||
|
|
||
| $event->setResponse(new RedirectResponse($url)); | ||
| $event->stopPropagation(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\TranslationsManagement; | ||
|
|
||
| use Ibexa\Contracts\Core\Repository\Values\Content\Field; | ||
| use Ibexa\Contracts\TranslationsManagement\AutoTranslate\Transformer\Field\EncodedFieldValue; | ||
| use Ibexa\Contracts\TranslationsManagement\AutoTranslate\Transformer\Field\FieldValueTransformerInterface; | ||
| use Ibexa\Core\FieldType\Value; | ||
|
|
||
| final class ImageAltTextTransformer implements FieldValueTransformerInterface | ||
|
Check failure on line 12 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php
|
||
| { | ||
| public function getFieldTypeIdentifier(): string | ||
| { | ||
| return 'ibexa_image'; | ||
| } | ||
|
|
||
| public function encode(Field $field): EncodedFieldValue | ||
|
Check failure on line 19 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php
|
||
| { | ||
| return new EncodedFieldValue($field->getValue()->alternativeText ?? ''); | ||
|
Check failure on line 21 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php
|
||
| } | ||
|
|
||
| public function decode(string $value, mixed $previousFieldValue, array $metadata): Value | ||
|
Check failure on line 24 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php
|
||
| { | ||
| $previousFieldValue->alternativeText = $value; | ||
|
|
||
| return $previousFieldValue; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\TranslationsManagement; | ||
|
|
||
| use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; | ||
| use Ibexa\Contracts\TranslationsManagement\SideBySide\Service\SideBySideExclusionRuleInterface; | ||
|
|
||
| final class MyCustomExclusionRule implements SideBySideExclusionRuleInterface | ||
|
Check failure on line 10 in code_samples/translations_management/src/TranslationsManagement/MyCustomExclusionRule.php
|
||
| { | ||
| public function isExcluded(ContentInfo $contentInfo): bool | ||
| { | ||
| return $contentInfo->getContentType()->identifier === 'my_excluded_type'; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\TranslationsManagement; | ||
|
|
||
| use Ibexa\Contracts\TranslationsManagement\AutoTranslate\Provider\TranslationProviderInterface; | ||
| use Ibexa\Contracts\TranslationsManagement\AutoTranslate\TranslationDataInterface; | ||
|
|
||
| final readonly class MyCustomProvider implements TranslationProviderInterface | ||
|
Check failure on line 10 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php
|
||
| { | ||
| public function __construct( | ||
| private MyApiClient $apiClient, | ||
|
Check failure on line 13 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php
|
||
| ) { | ||
| } | ||
|
|
||
| public function getIdentifier(): string | ||
| { | ||
| return 'my_custom_provider'; | ||
| } | ||
|
|
||
| public function getName(): string | ||
| { | ||
| return 'My Translation Service'; | ||
| } | ||
|
|
||
| public function getVendorName(): string | ||
| { | ||
| return 'My Company Ltd'; | ||
| } | ||
|
|
||
| public function translate(TranslationDataInterface $translationData): string | ||
|
Check failure on line 32 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php
|
||
| { | ||
| return $this->apiClient->translate( | ||
|
Check failure on line 34 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php
|
||
| $translationData->getText(), | ||
| $translationData->getSourceLanguage(), | ||
| $translationData->getTargetLanguage() | ||
| ); | ||
| } | ||
|
|
||
| /** @return array<string> */ | ||
| public function getSupportedLanguageCodes(): array | ||
| { | ||
| return ['en_GB', 'de_DE', 'fr_FR']; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <?php declare(strict_types=1); | ||
|
|
||
| use Ibexa\AdminUi\Form\Type\Content\Translation\TranslationAddType; | ||
| use Symfony\Component\Form\AbstractTypeExtension; | ||
| use Symfony\Component\Form\FormBuilderInterface; | ||
|
|
||
| final class MyTranslationAddExtension extends AbstractTypeExtension | ||
| { | ||
| public static function getExtendedTypes(): iterable | ||
| { | ||
| return [TranslationAddType::class]; | ||
|
Check failure on line 11 in code_samples/translations_management/src/TranslationsManagement/MyTranslationAddExtension.php
|
||
| } | ||
|
|
||
| public function buildForm(FormBuilderInterface $builder, array $options): void | ||
| { | ||
| $builder->add('my_custom_field'/* ... */); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| --- | ||
| description: Events that are triggered when working with translations management. | ||
|
Check notice on line 2 in docs/api/event_reference/translations_management_events.md
|
||
| edition: lts-update | ||
| page_type: reference | ||
| --- | ||
|
|
||
| # Translations management events | ||
|
|
||
| The [Translations management](configure_translations_management.md) package dispatches events at two levels. | ||
|
|
||
| ## Translation events | ||
|
|
||
| Translation events are thrown once per field value per translation operation. | ||
|
Check notice on line 13 in docs/api/event_reference/translations_management_events.md
|
||
| They are used for logging, analytics, and observability. | ||
|
Check notice on line 14 in docs/api/event_reference/translations_management_events.md
|
||
| Both events are read-only, you can't use them to override the translation result. | ||
|
|
||
| | Event | Dispatched by | Dispatched when | Properties | | ||
|
Check warning on line 17 in docs/api/event_reference/translations_management_events.md
|
||
| |---|---|---|----| | ||
| | [`BeforeTranslateEvent`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Event-BeforeTranslateEvent.html) | `TranslationService` | Before a translation request is sent to the provider | `TranslationProviderInterface $provider`</br>`string $text`</br>`string $sourceLanguage`</br>`string $targetLanguage` | | ||
|
Check notice on line 19 in docs/api/event_reference/translations_management_events.md
|
||
| | [`TranslateEvent`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Event-TranslateEvent.html) | `TranslationService` | After a translation response is received | `string $result`</br>`TranslationProviderInterface $provider`</br>`string $text`</br>`string $sourceLanguage`</br>`string $targetLanguage` | | ||
|
Check notice on line 20 in docs/api/event_reference/translations_management_events.md
|
||
|
|
||
| ## Side-by-side creation events | ||
|
|
||
| Side-by-side creation events are dispatched when a new translation draft is being prepared. | ||
|
Check notice on line 24 in docs/api/event_reference/translations_management_events.md
|
||
|
|
||
| | Event | Dispatched by | Dispatched when | Properties | | ||
| |---|---|---|---| | ||
| | [`OnContentSideBySideTranslationCreateEvent`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-SideBySide-Event-OnContentSideBySideTranslationCreateEvent.html) | `SideBySideTranslationService` | When a draft side-by-side translation of a content item is being created | `Request $request`</br>`Content $sourceContent`</br>`string $sourceLanguageCode`</br>`string $targetLanguageCode`</br>`?Content $targetDraft` | | ||
|
Check notice on line 28 in docs/api/event_reference/translations_management_events.md
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "Dispatched by" column is inaccurate for the side-by-side events. In the current implementation, |
||
| | [`OnProductSideBySideTranslationCreateEvent`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-SideBySide-Event-OnProductSideBySideTranslationCreateEvent.html) | `SideBySideTranslationService` | When a draft side-by-side translation of a product is being created | `Request $request`</br>`ContentAwareProductInterface $sourceProduct`</br>`ContentAwareProductInterface $targetProduct`</br>`string $sourceLanguageCode`</br>`string $targetLanguageCode`</br>`?ProductUpdateData $productUpdateData` | | ||
|
Check notice on line 29 in docs/api/event_reference/translations_management_events.md
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| <mxfile host="Electron" modified="2026-06-16T10:51:05.860Z" agent="5.0 (Macintosh; Intel Mac OS X 26_3_1) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" etag="j-W4lXxHCJBOFpcNCibg" version="14.6.13" type="device"><diagram id="1kQWOgmGZ1G1xJNYzsRM" name="Page-1">5Zptc6M2EIB/DTPtB2d4MRh/tB3Hcc5pPfW0l/uUkUEG9WTkCuGX+/WVQNiAyJn0iJ1cZzxjWEmw++xqWQk0a7TeTyjYhI/Eh1gzdX+vWbeaaRqGaWrip/sHKbFtJ5MEFPlSdhIs0DcohbqUJsiHcakjIwQztCkLPRJF0GMlGaCU7MrdVgSX77oBAVQECw9gVfoZ+SzMDdP1U8M9REEob+3asmEN8s5SEIfAJ7uCyBpr1ogSwrKj9X4EsaCXc8nG3b3QelSMwog1GTDIBmwBTqRtA3+NIqEZZDH/SzYCLgVRjAFDRLRsKNly/jSWJrBDzoWSJPKhuLShWcNdiBhcbIAnWnc8FLgsZGssm1cI4xHBhKZjLR9Ad+Vxecwo+QoLLY7nwuWKt6jGSXu3kDK4L4iksRNI1pDRA+8iW007GyEjr2PkIbUr+NGVsrDgQkfKgAyd4HjpE11+IAHXwx4qsMc+YtxMU6fwnwTGKfEI7qrI28RsQ9fv1mF2zaXlOC1h7l+T80jh/AeMCd7CuBC9mulgfs/hUhwF4ugXDKIg4VO3swFIOEXckM+AWHTm8wHzVMLd8Wur/litVqZXG/a+s3TslvxhdPWKP1R3HHNS0R29Ftxxp4b9noe3l0Z7HudgyWnyVIwg9ttNLJchXEks/QuG+0Thu4CRn8IlLybvj4i4fz3G9zUpxYMoSyk5YU5PIKUoCj5iCBvlHGFahpoj9DfiO1X4jijkRFO8gAaQdfLsLApHClbsAxI23Ssifvhe9bFFcBenj7zqUxFEIqrXxEc8N6fuCOGHr0+MbjmVWOYFHfFJzddgC7OLvkVsXwhpObS7NUQNu4ao3QLRmUo05LbE6e353fMIb/mxdyGwPfs82d4bkX1UyM6TJUZxCFte/V0EpeU0CNK6MrgNlLBmcS5FMUmol6+/M1H20NOK6xnol3YlVCspFAl5W96k+CGVzUYqD9+TylYjlUeqyndtq5wOHVAKDoUOG4IiFheuPBeC4jqiPNtdvWJ9dsETi6NmzfB0G+G5U/FMruZRW8lB2sjUBr1GlrSudr3bjlt/x8qukjEylHLUyfjXhofllsPD6Ff2987oVen/4/HkNPLCvRpP06vFU7PAmaoqP7yLDGFVFhF57dqaS91GfB5UPp+u5tL+f1V5djWV83r59To/XktnFPT8ER3iu0n09LsxGuyecNLJi7BCdq4WhnEINuLQSyg+DCnwvgpTzlWI5XJyhdHmXh5r9WVfDcsXK0G7slzpHMu+0gpQUyrBrtlCKVgL0vopQFp63Upa5Wj0+m/EsatwnM/+nEx/U2i+ClsbqKwyKkeNuNrFRxt7DrWkHIXUYno77gy/dMQ/b/lrOv787rDxRdyNfWVy7vm5KmxGHsAzsIR4TmKUbpVZt0vCGFlzNHmHAUaBaGCkQjKf7et9IN6j3yxBjLyb7nNaIDzH/JHwzLEN0/fq+o3bDu3jZD2+m3cU1k4N6jZeFNWi7v9/UNckhLqofrMnUJ7KC2hFkbGQp4SykAQkAnh8khbmu87PTn1mREBO6f4NGTvIrzhAwkiZPdwj9pRyteXZl0LL7V5eOT05FE7mkCJuN6RSptVVRWfrq+9N72LR9XK/qxVeDSqvn2RmHLfi258Z/PT0tU22Wjp9tGSN/wU=</diagram></mxfile> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| --- | ||
| description: Configure and extend translations management, including side-by-side translation view, and AI-based translation providers. | ||
| edition: lts-update | ||
| month_change: true | ||
| --- | ||
|
|
||
| # Translations management | ||
|
|
||
| `ibexa/translations-management` extends [[= product_name =]]'s content translation capabilities represented by the built-in language management tools. | ||
|
|
||
| ## Install | ||
|
|
||
| ```bash | ||
| composer require ibexa/translations-management | ||
| ``` | ||
|
|
||
| After installation, run the Ibexa data migrations to complete the setup. | ||
|
Check failure on line 17 in docs/multisite/translations_management/configure_translations_management.md
|
||
| This creates the database records and default configuration the package requires. | ||
|
|
||
| ## Configure translation providers | ||
|
|
||
|
|
||
| ### Provider configuration | ||
|
|
||
|
|
||
| ### Provider types | ||
|
|
||
|
|
||
| ### Provider options | ||
|
|
||
|
|
||
| ### Built-in AI providers | ||
|
Check notice on line 32 in docs/multisite/translations_management/configure_translations_management.md
|
||
|
|
||
|
|
||
| ### Plugin disabled state | ||
|
|
||
|
|
||
| ### Error handling | ||
|
|
||
|
|
||
| ## Manage language pairs | ||
|
|
||
| ## User settings | ||
|
|
||
|
|
||
| ### Always use automatic translation | ||
|
|
||
|
|
||
| ### Three-state provider selection | ||
|
|
||
|
|
||
| ### Multiple provider rendering | ||
|
|
||
|
|
||
| ## Side-by-side translation view | ||
|
|
||
| ### Side-by-side view functions | ||
|
|
||
| ### Architecture | ||
|
|
||
| ## Translate content items with CLI | ||
|
|
||
| ### CLI command options | ||
|
Check notice on line 63 in docs/multisite/translations_management/configure_translations_management.md
|
||
|
|
||
| ## Design system assets | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this sample the
App\TranslationsManagementnamespace is missing. The YAML registersApp\TranslationsManagement\MyTranslationAddExtension. Could we add the namespace so the sample can actually be wired into the Add translation modal? (thedeclare(strict_types=1);is missing too)