diff --git a/code_samples/translations_management/config/services.yaml b/code_samples/translations_management/config/services.yaml new file mode 100644 index 0000000000..b198c91ba6 --- /dev/null +++ b/code_samples/translations_management/config/services.yaml @@ -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 diff --git a/code_samples/translations_management/src/TranslationsManagement/ContentProxyTranslateSubscriber.php b/code_samples/translations_management/src/TranslationsManagement/ContentProxyTranslateSubscriber.php new file mode 100644 index 0000000000..e9442a3115 --- /dev/null +++ b/code_samples/translations_management/src/TranslationsManagement/ContentProxyTranslateSubscriber.php @@ -0,0 +1,39 @@ + ['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(); + } +} diff --git a/code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php b/code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php new file mode 100644 index 0000000000..b977aef677 --- /dev/null +++ b/code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php @@ -0,0 +1,30 @@ +getValue()->alternativeText ?? ''); + } + + public function decode(string $value, mixed $previousFieldValue, array $metadata): Value + { + $previousFieldValue->alternativeText = $value; + + return $previousFieldValue; + } +} diff --git a/code_samples/translations_management/src/TranslationsManagement/MyCustomExclusionRule.php b/code_samples/translations_management/src/TranslationsManagement/MyCustomExclusionRule.php new file mode 100644 index 0000000000..48a55e68e0 --- /dev/null +++ b/code_samples/translations_management/src/TranslationsManagement/MyCustomExclusionRule.php @@ -0,0 +1,16 @@ +getContentType()->identifier === 'my_excluded_type'; + } +} diff --git a/code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php b/code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php new file mode 100644 index 0000000000..fa77ae4595 --- /dev/null +++ b/code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php @@ -0,0 +1,46 @@ +apiClient->translate( + $translationData->getText(), + $translationData->getSourceLanguage(), + $translationData->getTargetLanguage() + ); + } + + /** @return array */ + public function getSupportedLanguageCodes(): array + { + return ['en_GB', 'de_DE', 'fr_FR']; + } +} diff --git a/code_samples/translations_management/src/TranslationsManagement/MyTranslationAddExtension.php b/code_samples/translations_management/src/TranslationsManagement/MyTranslationAddExtension.php new file mode 100644 index 0000000000..47608fd81e --- /dev/null +++ b/code_samples/translations_management/src/TranslationsManagement/MyTranslationAddExtension.php @@ -0,0 +1,18 @@ +add('my_custom_field'/* ... */); + } +} diff --git a/docs/api/event_reference/event_reference.md b/docs/api/event_reference/event_reference.md index 6cc5792d69..49396a4264 100644 --- a/docs/api/event_reference/event_reference.md +++ b/docs/api/event_reference/event_reference.md @@ -37,6 +37,7 @@ For example, copying a content item is connected with two events: `BeforeCopyCon "api/event_reference/segmentation_events", "api/event_reference/site_events", "api/event_reference/taxonomy_events", + "api/event_reference/translations_management_events", "api/event_reference/trash_events", "api/event_reference/twig_component_events", "api/event_reference/url_events", diff --git a/docs/api/event_reference/translations_management_events.md b/docs/api/event_reference/translations_management_events.md new file mode 100644 index 0000000000..01c69e1b63 --- /dev/null +++ b/docs/api/event_reference/translations_management_events.md @@ -0,0 +1,29 @@ +--- +description: Events that are triggered when working with translations management. +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. +They are used for logging, analytics, and observability. +Both events are read-only, you can't use them to override the translation result. + +| Event | Dispatched by | Dispatched when | Properties | +|---|---|---|----| +| [`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`
`string $text`
`string $sourceLanguage`
`string $targetLanguage` | +| [`TranslateEvent`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Event-TranslateEvent.html) | `TranslationService` | After a translation response is received | `string $result`
`TranslationProviderInterface $provider`
`string $text`
`string $sourceLanguage`
`string $targetLanguage` | + +## Side-by-side creation events + +Side-by-side creation events are dispatched when a new translation draft is being prepared. + +| 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`
`Content $sourceContent`
`string $sourceLanguageCode`
`string $targetLanguageCode`
`?Content $targetDraft` | +| [`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`
`ContentAwareProductInterface $sourceProduct`
`ContentAwareProductInterface $targetProduct`
`string $sourceLanguageCode`
`string $targetLanguageCode`
`?ProductUpdateData $productUpdateData` | diff --git a/docs/ibexa_products/editions.md b/docs/ibexa_products/editions.md index fc5296196e..5bc7c73bf9 100644 --- a/docs/ibexa_products/editions.md +++ b/docs/ibexa_products/editions.md @@ -71,3 +71,4 @@ The features brought by LTS Updates become standard parts of the next LTS releas | [Integrated help](integrated_help.md) | ✔ | ✔ | ✔ | | [MCP servers](mcp_guide.md) | ✔ | ✔ | ✔ | | [Shopping list](shopping_list_guide.md) | | | ✔ | +| [Translations management](translations_management_guide.md) | ✔ | ✔ | ✔ | diff --git a/docs/multisite/img/diagram_source/translations_management_flow.drawio b/docs/multisite/img/diagram_source/translations_management_flow.drawio new file mode 100644 index 0000000000..e289d5a54e --- /dev/null +++ b/docs/multisite/img/diagram_source/translations_management_flow.drawio @@ -0,0 +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= \ No newline at end of file diff --git a/docs/multisite/img/managing_translations_sxs_view.png b/docs/multisite/img/managing_translations_sxs_view.png new file mode 100644 index 0000000000..8a27800397 Binary files /dev/null and b/docs/multisite/img/managing_translations_sxs_view.png differ diff --git a/docs/multisite/img/translations_management_flow.png b/docs/multisite/img/translations_management_flow.png new file mode 100644 index 0000000000..61a7d04471 Binary files /dev/null and b/docs/multisite/img/translations_management_flow.png differ diff --git a/docs/multisite/translations_management/extend_translations_management.md b/docs/multisite/translations_management/extend_translations_management.md new file mode 100644 index 0000000000..2a467abc03 --- /dev/null +++ b/docs/multisite/translations_management/extend_translations_management.md @@ -0,0 +1,194 @@ +--- +description: Extend translations management - add custom classes, exclude custom content types and intercept the flow. +edition: lts-update +month_change: true +--- + +# Extend translations management + +By extending [Translations management](translations_management_guide.md), you can build custom translation workflows and adapt the feature set's behavior to your specific requirements. +The package is designed to be extended in multiple ways. +You can create custom [translation providers](configure_translations_management.md#configure-translation-providers), field type transformers, exclusion rules, and UI components. +In all cases you follow the same pattern: implement an interface or extend a base class, then register the service with a service tag. +The package discovers and registers tagged services automatically. + +## Add custom translation provider + +Before you build a custom translation provider, make sure that the `ibexa/connector-ai` package has been installed in your system. + +To connect a translation service that is not built into the package, implement [`TranslationProviderInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Provider-TranslationProviderInterface.html). +The `translate()` method receives a `TranslationDataInterface` object that carries the text to translate along with the source and target language codes: + +``` php hl_lines="31-44" +[[= include_code('code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php') =]] +``` + +Register the provider with the `ibexa.translations_management.auto_translate.provider` tag. +Both `identifier` and `validation_profile` are required attributes. + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 6) =]] +``` + +The `validation_profile` attribute links the provider to a validator that checks language codes and payload size before each call. +By default, three profiles are available: + +| Profile | Used by | +|---|---| +| `google` | Google Translate provider | +| `deepl` | DeepL provider | +| `ai_generic` | All built-in AI providers. Suitable for custom AI providers. | + +To define a custom validation profile, implement [`ProviderValidatorInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Validator-ProviderValidatorInterface.html) and register it: + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 7, 10) =]] +``` + +The [`DefaultProviderValidator`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Validator-DefaultProviderValidator.html) class is available as a reusable base with configurable maximum payload size and language code regex patterns. + +The package also provides several specialized interfaces for providers with specific requirements: + +| Interface | Purpose | +|---|---| +| [`ConfigurableProviderInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Provider-ConfigurableProviderInterface.html) | Extends `TranslationProviderInterface`. Adds `getConfiguration()` and `isConfigured()` for providers that store API keys and other settings | +| [`AiTranslationProviderInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Provider-AiTranslationProviderInterface.html) | Extends `ConfigurableProviderInterface`. Used as a type marker for AI-based providers, it inherits the configuration methods | +| [`TranslationHttpClientInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Http-TranslationHttpClientInterface.html) | For HTTP-based providers that use a REST API pattern | + +## Add support for custom field types + +The translation engine works by extracting translatable text from fields, sending it to the provider, and writing the translated text back. +This encode/decode cycle is handled by field value transformers, one per field type. +The package includes transformers for standard text and RichText fields. +To add support for a custom or non-standard field type, implement [`FieldValueTransformerInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Transformer-Field-FieldValueTransformerInterface.html): + +- `getFieldTypeIdentifier()` - returns the field type identifier this transformer handles +- `encode(Field $field): EncodedFieldValue` - extracts the translatable string from the field and wraps it in an [`EncodedFieldValue`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Transformer-Field-EncodedFieldValue.html). The constructor takes the extracted string as its first argument and an optional metadata array as its second. +- `decode(string $value, mixed $previousFieldValue, array $metadata): Value` - receives the translated string, the previous field value, and any metadata, and returns the updated field value + + +``` php hl_lines="19 24" +[[= include_code('code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php') =]] +``` + +Register the transformer with the `ibexa.translations_management.auto_translate.field_value_transformer` tag. +The `field_type_identifier` attribute is required and must match the value returned by `getFieldTypeIdentifier()`: + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 14, 17) =]] +``` + +For field types that require metadata, for example, RichText fields with embedded objects that must be preserved after translation, implement [`MetadataAwareFieldValueTransformerInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-AutoTranslate-Transformer-Field-MetadataAwareFieldValueTransformerInterface.html) instead. + +## Define custom exclusion rules + +Content types that should not use the side-by-side view are identified by exclusion rules. +The Translations management package ships with one rule that excludes content types that contain `ibexa_landing_page` or `ibexa_form` fields. + +### Exclude with custom class + +To exclude additional content types, for example, content types with fields that are known to behave poorly in the side-by-side layout, implement [`SideBySideExclusionRuleInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-TranslationsManagement-SideBySide-Service-SideBySideExclusionRuleInterface.html). +The `isExcluded()` method receives a [`ContentInfo`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-Values-Content-ContentInfo.html) object and returns `true` if the content item should be excluded. +Classes that implement this interface are automatically tagged via autoconfigure: + +``` php +[[= include_code('code_samples/translations_management/src/TranslationsManagement/MyCustomExclusionRule.php') =]] +``` + +If autoconfigure is not available, register the tag explicitly: + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 18, 20) =]] +``` + +### Exclude with existing class + +`MyCustomExclusionRule` targets one specific content type by name. +To exclude any content type that contain specific field types without the need to write a custom class, register an additional instance of the built-in [`UnsupportedFieldTypeExclusionRule`](https://github.com/ibexa/translations-management/blob/main/src/lib/SideBySide/Service/UnsupportedFieldTypeExclusionRule.php). +Because this registers a second instance of the service with different arguments, you can't use the class name as the service ID. +Use an arbitrary string ID instead to avoid overwriting the package's own registration: + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 21, 26) =]] +``` + +## Use Twig component extension points + +Two Twig component groups allow you to inject custom UI elements into the translation interface without the need to override their templates. +Such custom elements could be: + +- buttons that allow the editor to create a new translation either in the side-by-side view or the standard single-panel editor +- a disclaimer or policy notice that the editor must acknowledge before a translation is created + + +| Component group | Location | Variables available | +|---|---|---| +| `admin-ui-content-translation-modal-footer` | Footer of the **Add translation** modal | `form`, `content_id`, `location`, `allow_placeholder` | +| `admin-ui-content-edit-translation-select-footer` | Footer of the **Select translation** panel on the content edit screen | `form`, `content_id`, `main_language_code` | + +Both groups follow the same pattern: if any component renders a non-empty output into the group, the default footer buttons are replaced entirely by the component output. +Therefore, if your component template replaces the defaults, make sure it includes its own action buttons. + +Register a component with the `ibexa.twig.component` tag: + + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 27, 31) =]] +``` + +!!! note + + The `admin-ui-content-translation-modal-footer` group receives a `location` variable that may be `null` when the modal is rendered outside a location context. + Always check for `null` before you access location properties in your component template. + +## Extend the modal + +If injecting custom UI elements is not sufficient, you can extend the modal itself. +To add a field to the **Add translation** modal, for example, to let the editor choose a custom workflow or pass extra parameters along with the translation request, extend [`TranslationAddType`](https://github.com/ibexa/admin-ui/blob/main/src/lib/Form/Type/Content/Translation/TranslationAddType.php) with a [Symfony's Form Type extension](https://symfony.com/doc/current/form/create_form_type_extension.html). +It's the same mechanism the translations management package uses internally to inject its provider selector into the modal. + +Create a class that extends [`AbstractTypeExtension`](https://symfony.com/doc/current/reference/forms/types/form.html) and declare the extended type: + +``` php +[[= include_code('code_samples/translations_management/src/TranslationsManagement/MyTranslationAddExtension.php') =]] +``` + +Register it as a service: + +``` yaml +[[= include_code('code_samples/translations_management/config/services.yaml', 1, 1) =]] +[[= include_code('code_samples/translations_management/config/services.yaml', 11, 13) =]] +``` + +The extra field is then available in the submitted form data, which the standard `admin-ui` controller passes through the translation flow. +Use this approach when you need to read extra input from the editor, not to redirect or replace the response. + +## Intercept translation flow + +The `BeforeTranslateEvent` and `TranslateEvent` [events](translations_management_events.md#translation-events) operate at the field-value level and cannot redirect the HTTP flow. +To intercept the "Add translation" action at the HTTP level, for example, to trigger auto-translation and redirect to a custom view, or to bypass the default flow entirely, subscribe to `admin-ui`'s `ContentProxyTranslateEvent`. + +The `translations-management` package listens to this event at priority `100`. +Subscribe at a higher priority to act before the package does: + +``` php hl_lines="35 36" +[[= include_code('code_samples/translations_management/src/TranslationsManagement/ContentProxyTranslateSubscriber.php') =]] +``` + +Both highlighted calls are required: + +- `setResponse()` alone does not prevent the translations management listener at priority 100 from running and overwriting the response. +- `stopPropagation()` stops all lower-priority listeners from executing. + +When a response is set on the event, `admin-ui` uses it and doesn't proceed with the standard translation editor. + +!!! caution "Internal `ContentProxyTranslateEvent`" + + `ContentProxyTranslateEvent` is marked `@internal` in `ibexa/admin-ui`. + While it functions as an extension point in practice, its name and signature may change. + It may even be removed entirely without a deprecation notice. diff --git a/docs/multisite/translations_management/translations_management.md b/docs/multisite/translations_management/translations_management.md new file mode 100644 index 0000000000..468165d855 --- /dev/null +++ b/docs/multisite/translations_management/translations_management.md @@ -0,0 +1,16 @@ +--- +description: Translations management brings multiple features that help managers, developers and localization teams automated multilingual content delivery. +edition: lts-update +page_type: landing_page +--- + +# Translations management + +Translations management helps [[= product_name =]] developers and users deliver automated content item, product and product catalog translations. + +[[= cards([ + "multisite/translations_management/translations_management_guide", + "multisite/translations_management/configure_translations_management", + "multisite/translations_management/extend_translations_management", + "api/event_reference/translations_management_events", +], columns=3) =]] diff --git a/docs/multisite/translations_management/translations_management_guide.md b/docs/multisite/translations_management/translations_management_guide.md new file mode 100644 index 0000000000..cc17a779ce --- /dev/null +++ b/docs/multisite/translations_management/translations_management_guide.md @@ -0,0 +1,89 @@ +--- +description: Translations management helps managers, developers and localization teams with multilingual content delivery. +edition: lts-update +month_change: true +--- + +# Translations management product guide + +## What is Translations management + +Content managers, translators and proofreaders who work with multilingual content in [[= product_name =]] often face a common set of challenges: + +- context is lost when the source text isn't visible alongside the translation +- translation of long and complex content items is time consuming +- quality assurance is slow and error-prone without a direct comparison view +- switching between tools or tabs to cross-reference languages disrupts focus and slows down publishing + +Translations management package addresses these pain points through a side-by-side view, machine translation and ability to invite reviewers to collaborate on content item or product translation. + +The package integrates with the [AI Actions framework](ai_actions.md) to support machine translation providers such as Google Translate and DeepL, and AI powered translation services like OpenAI, Anthropic, and Google Gemini. + +Administrators can manage providers and configure default provider-to-language-pair mappings directly in [[= product_name =]]'s user interface, while editors can trigger machine translation from the content editing interface. + +!!! note + + Translations management is a standalone set of features. + Although some views are similar to those delivered by the [Automated translations](automated_translations.md) opt-in package, Translations management does not require the `ibexa/automated-translation` package to run. + These two packages use different namespaces, service tags, and provider interfaces. + +## Availability + +Translations management is an [LTS Update](editions.md#lts-updates) available in all [[= product_name =]] editions. + +## How it works + +Before the translation flow can happen, an administrator sets up the translation providers and assigns language pairs to them. +Then, when an editor opens a content item and requests a new machine translation, the plugin resolves which provider to use. +It falls back from a language-pair rule to the user's manual selection if necessary. +The plugin then extracts the translatable fields from the source language version of a content item and sends them to the configured provider's API. +The translated strings are written into a target-language draft version of a content item, which opens in a side-by-side view for the editor to review and refine. +The editor can save the result as draft, share it with a reviewer or publish it. + +![Translations management flow](translations_management_flow.png "Translations management flow") + +## Capabilities + +### Translation provider management + +Administrators can manage translation providers and configure [language pair defaults](configure_translations_management.md#manage-language-pairs). +This way they can define which provider handles which language combination. +Editors see the configured default pre-selected when creating a new translation, but can override the selection if needed. + +Translations management supports several built-in translation providers, for example Google Translate, which is accessed through its REST API, or OpenAI, accessed through AI Actions. + +### Side-by-side translation view + +Translations management introduces a [side-by-side translation view]([[= user_doc =]]/content_management/translate_content/#side-by-side-translation-view) that displays the source language read-only next to an editable target language form. +In this view, editors can provide and review translations in context, without having to leave the content editing interface. + +![Side-by-side translation view](managing_translations_sxs_view.png "Side-by-side translation view") + +Editors can: + +- access the side-by-side view when creating a new translation, reviewing an existing one, or editing a draft +- compare source and target content field by field while editing +- copy all content from the source column to the target column with a single action +- provide localized versions of media assets +- use the distraction-free mode for focused editing of individual fields, with AI actions available inline +- choose whether the source column appears on the left or right in user settings + +!!! note "Excluded content types" + + Content types that are editable in Page builder or Form builder are excluded from side-by-side editing. + + Products are editable in the side-by-side view, but product attributes are not translatable. + +### CLI translation + +Translations management package exposes a [console command](configure_translations_management.md#translate-content-items-with-cli) for translating content items from the command line, +useful for batch processing or CI/CD workflows. + +### Extensibility + +Developers can [extend the translations management](extend_translations_management.md) package: + +- create custom translation providers +- add support for custom fields +- add custom content type exclusion rules +- tap into the translation lifecycle with [events](translations_management_events.md) diff --git a/mkdocs.yml b/mkdocs.yml index 6ecfc86c8c..7d32355408 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -105,6 +105,7 @@ nav: - Discounts events: api/event_reference/discounts_events.md - Collaboration events: api/event_reference/collaboration_events.md - Integrated help events: api/event_reference/integrated_help_events.md + - Translations management events: api/event_reference/translations_management_events.md - Other events: api/event_reference/other_events.md - Notification channels: api/notification_channels.md - Administration: @@ -479,6 +480,11 @@ nav: - Language API: multisite/languages/language_api.md - Back office translations: multisite/languages/back_office_translations.md - Automated content translation: multisite/languages/automated_translations.md + - Translations management: + - Translations management: multisite/translations_management/translations_management.md + - Translations management product guide: multisite/translations_management/translations_management_guide.md + - Configure translations management: multisite/translations_management/configure_translations_management.md + - Extend translations management: multisite/translations_management/extend_translations_management.md - Permissions: - Permissions: permissions/permissions.md - Permission overview: permissions/permission_overview.md