Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions code_samples/translations_management/config/services.yaml
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

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

Class App\TranslationsManagement\ImageAltTextTransformer implements unknown interface Ibexa\Contracts\TranslationsManagement\AutoTranslate\Transformer\Field\FieldValueTransformerInterface.

Check failure on line 12 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

Class App\TranslationsManagement\ImageAltTextTransformer implements unknown interface Ibexa\Contracts\TranslationsManagement\AutoTranslate\Transformer\Field\FieldValueTransformerInterface.
{
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

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

Method App\TranslationsManagement\ImageAltTextTransformer::encode() has invalid return type Ibexa\Contracts\TranslationsManagement\AutoTranslate\Transformer\Field\EncodedFieldValue.

Check failure on line 19 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

Method App\TranslationsManagement\ImageAltTextTransformer::encode() has invalid return type Ibexa\Contracts\TranslationsManagement\AutoTranslate\Transformer\Field\EncodedFieldValue.
{
return new EncodedFieldValue($field->getValue()->alternativeText ?? '');

Check failure on line 21 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

Instantiated class Ibexa\Contracts\TranslationsManagement\AutoTranslate\Transformer\Field\EncodedFieldValue not found.

Check failure on line 21 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

Instantiated class Ibexa\Contracts\TranslationsManagement\AutoTranslate\Transformer\Field\EncodedFieldValue not found.
}

public function decode(string $value, mixed $previousFieldValue, array $metadata): Value

Check failure on line 24 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

App\TranslationsManagement\ImageAltTextTransformer must not depend on Ibexa\Core\FieldType\Value (CodeSamples on IbexaNotAllowed)

Check failure on line 24 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

Method App\TranslationsManagement\ImageAltTextTransformer::decode() has parameter $metadata with no value type specified in iterable type array.

Check failure on line 24 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

App\TranslationsManagement\ImageAltTextTransformer must not depend on Ibexa\Core\FieldType\Value (CodeSamples on IbexaNotAllowed)

Check failure on line 24 in code_samples/translations_management/src/TranslationsManagement/ImageAltTextTransformer.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

Method App\TranslationsManagement\ImageAltTextTransformer::decode() has parameter $metadata with no value type specified in iterable type array.
{
$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

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

Class App\TranslationsManagement\MyCustomExclusionRule implements unknown interface Ibexa\Contracts\TranslationsManagement\SideBySide\Service\SideBySideExclusionRuleInterface.

Check failure on line 10 in code_samples/translations_management/src/TranslationsManagement/MyCustomExclusionRule.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

Class App\TranslationsManagement\MyCustomExclusionRule implements unknown interface Ibexa\Contracts\TranslationsManagement\SideBySide\Service\SideBySideExclusionRuleInterface.
{
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

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

Class App\TranslationsManagement\MyCustomProvider implements unknown interface Ibexa\Contracts\TranslationsManagement\AutoTranslate\Provider\TranslationProviderInterface.

Check failure on line 10 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

Class App\TranslationsManagement\MyCustomProvider implements unknown interface Ibexa\Contracts\TranslationsManagement\AutoTranslate\Provider\TranslationProviderInterface.
{
public function __construct(
private MyApiClient $apiClient,

Check failure on line 13 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

Property App\TranslationsManagement\MyCustomProvider::$apiClient has unknown class App\TranslationsManagement\MyApiClient as its type.

Check failure on line 13 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

Parameter $apiClient of method App\TranslationsManagement\MyCustomProvider::__construct() has invalid type App\TranslationsManagement\MyApiClient.

Check failure on line 13 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

Property App\TranslationsManagement\MyCustomProvider::$apiClient has unknown class App\TranslationsManagement\MyApiClient as its type.

Check failure on line 13 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

Parameter $apiClient of method App\TranslationsManagement\MyCustomProvider::__construct() has invalid type App\TranslationsManagement\MyApiClient.
) {
}

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

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

Parameter $translationData of method App\TranslationsManagement\MyCustomProvider::translate() has invalid type Ibexa\Contracts\TranslationsManagement\AutoTranslate\TranslationDataInterface.

Check failure on line 32 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

Parameter $translationData of method App\TranslationsManagement\MyCustomProvider::translate() has invalid type Ibexa\Contracts\TranslationsManagement\AutoTranslate\TranslationDataInterface.
{
return $this->apiClient->translate(

Check failure on line 34 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

Call to method translate() on an unknown class App\TranslationsManagement\MyApiClient.

Check failure on line 34 in code_samples/translations_management/src/TranslationsManagement/MyCustomProvider.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

Call to method translate() on an unknown class App\TranslationsManagement\MyApiClient.
$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

View workflow job for this annotation

GitHub Actions / Validate code samples (8.3)

MyTranslationAddExtension must not depend on Ibexa\AdminUi\Form\Type\Content\Translation\TranslationAddType (CodeSamples on IbexaNotAllowed)

Check failure on line 11 in code_samples/translations_management/src/TranslationsManagement/MyTranslationAddExtension.php

View workflow job for this annotation

GitHub Actions / Validate code samples (8.4)

MyTranslationAddExtension must not depend on Ibexa\AdminUi\Form\Type\Content\Translation\TranslationAddType (CodeSamples on IbexaNotAllowed)
}

public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('my_custom_field'/* ... */);
}
}
1 change: 1 addition & 0 deletions docs/api/event_reference/event_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
29 changes: 29 additions & 0 deletions docs/api/event_reference/translations_management_events.md
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

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/api/event_reference/translations_management_events.md#L2

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/api/event_reference/translations_management_events.md", "range": {"start": {"line": 2, "column": 26}}}, "severity": "INFO"}
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

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/api/event_reference/translations_management_events.md#L13

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/api/event_reference/translations_management_events.md", "range": {"start": {"line": 13, "column": 20}}}, "severity": "INFO"}
They are used for logging, analytics, and observability.

Check notice on line 14 in docs/api/event_reference/translations_management_events.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/api/event_reference/translations_management_events.md#L14

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/api/event_reference/translations_management_events.md", "range": {"start": {"line": 14, "column": 6}}}, "severity": "INFO"}
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

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/api/event_reference/translations_management_events.md#L17

[Ibexa.EOLWhitespace] Remove whitespace characters from the end of the line.
Raw output
{"message": "[Ibexa.EOLWhitespace] Remove whitespace characters from the end of the line.", "location": {"path": "docs/api/event_reference/translations_management_events.md", "range": {"start": {"line": 17, "column": 57}}}, "severity": "WARNING"}
|---|---|---|----|
| [`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

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/api/event_reference/translations_management_events.md#L19

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/api/event_reference/translations_management_events.md", "range": {"start": {"line": 19, "column": 207}}}, "severity": "INFO"}
| [`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

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/api/event_reference/translations_management_events.md#L20

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/api/event_reference/translations_management_events.md", "range": {"start": {"line": 20, "column": 195}}}, "severity": "INFO"}

## 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

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/api/event_reference/translations_management_events.md#L24

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/api/event_reference/translations_management_events.md", "range": {"start": {"line": 24, "column": 30}}}, "severity": "INFO"}

Check notice on line 24 in docs/api/event_reference/translations_management_events.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/api/event_reference/translations_management_events.md#L24

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/api/event_reference/translations_management_events.md", "range": {"start": {"line": 24, "column": 77}}}, "severity": "INFO"}

| 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

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/api/event_reference/translations_management_events.md#L28

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/api/event_reference/translations_management_events.md", "range": {"start": {"line": 28, "column": 286}}}, "severity": "INFO"}
| [`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

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/api/event_reference/translations_management_events.md#L29

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/api/event_reference/translations_management_events.md", "range": {"start": {"line": 29, "column": 281}}}, "severity": "INFO"}
1 change: 1 addition & 0 deletions docs/ibexa_products/editions.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,4 @@ The features brought by LTS Updates become standard parts of the next LTS releas
| [Integrated help](integrated_help.md) | &#10004; | &#10004; | &#10004; |
| [MCP servers](mcp_guide.md) | &#10004; | &#10004; | &#10004; |
| [Shopping list](shopping_list_guide.md) | | | &#10004; |
| [Translations management](translations_management_guide.md) | &#10004; | &#10004; | &#10004; |
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>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading