diff --git a/README.md b/README.md index 3b9a75a..8091d74 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Furthermore, in case you need to know whether the breadcrumb has items or not, y Under the hood, this is the business logic involved, for each item, in the breadcrumb generation: * `label` will be the printed text. It can be either: * A "static" string (the translator will attempt to translate it by using it as a translation key) - * A special string, prepended with `$`. In this case, the breadcrumb label will be extracted from the variable passed to the template. Property paths can be used, e.g.: `$variable.property.path` + * A special string, prepended with `$`. In this case, the breadcrumb label will be extracted from the variable passed to the template. Property paths can be used, e.g.: `$variable.property.path`. The variable can resolve to a TranslatableMessage instead of a string; in that case, the translated string will be used. * `route` will be used to generate the url for the item anchor (if provided). If not provided, the item will not be clickable. * `params` will be used to generate the url related to the provided route. It's an associative array where each value can be either: * A "static" string diff --git a/src/Service/BreadcrumbItemProcessor.php b/src/Service/BreadcrumbItemProcessor.php index 0f96588..8e7005c 100644 --- a/src/Service/BreadcrumbItemProcessor.php +++ b/src/Service/BreadcrumbItemProcessor.php @@ -7,6 +7,7 @@ use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Translation\TranslatableMessage; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -78,7 +79,12 @@ private function processItem(BreadcrumbItem $item, array $variables): ProcessedB { // Process the label if ($item->label && \str_starts_with($item->label, '$')) { - $processedLabel = $this->parseValue($item->label, $variables); + $labelValue = $this->parseValue($item->label, $variables); + if ($labelValue instanceof TranslatableMessage) { + $processedLabel = $labelValue->trans($this->translator); + } else { + $processedLabel = $labelValue; + } } elseif (!$item->label || $item->translationDomain === false) { $processedLabel = $item->label; } else { diff --git a/tests/Unit/Service/BreadcrumbItemProcessorTest.php b/tests/Unit/Service/BreadcrumbItemProcessorTest.php index 2c769e0..bf178e0 100644 --- a/tests/Unit/Service/BreadcrumbItemProcessorTest.php +++ b/tests/Unit/Service/BreadcrumbItemProcessorTest.php @@ -14,6 +14,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Translation\TranslatableMessage; use Symfony\Contracts\Translation\TranslatorInterface; #[CoversClass(BreadcrumbItemProcessor::class)] @@ -111,6 +112,40 @@ public function test_process_item_with_label_to_be_translated_with_specific_tran $this->assertSame('Translated label', $processedItems[0]->translatedLabel); } + public function test_process_item_with_label_as_translatable_message_with_default_transition_domain() + { + $item = new BreadcrumbItem('$variableName.property'); + + $translatable = new TranslatableMessage('translatable_key'); + $object = (object) ['getProperty' => fn () => $translatable]; + + $this->propertyAccessor->expects('getValue')->with($object, 'property') + ->andReturn($translatable); + $this->translator->expects('trans')->with('translatable_key', [], null, null) + ->andReturn('Translated label'); + + $processedItems = $this->SUT->process([$item], ['variableName' => $object]); + + $this->assertSame('Translated label', $processedItems[0]->translatedLabel); + } + + public function test_process_item_with_label_as_translatable_message_with_custom_transition_domain() + { + $item = new BreadcrumbItem('$variableName.property', translationDomain: 'custom_domain'); + + $translatable = new TranslatableMessage('translatable_key', domain: 'custom_domain'); + $object = (object) ['getProperty' => fn () => $translatable]; + + $this->propertyAccessor->expects('getValue')->with($object, 'property') + ->andReturn($translatable); + $this->translator->expects('trans')->with('translatable_key', [], 'custom_domain', null) + ->andReturn('Translated label'); + + $processedItems = $this->SUT->process([$item], ['variableName' => $object]); + + $this->assertSame('Translated label', $processedItems[0]->translatedLabel); + } + public function test_process_item_with_null_label() { $item = new BreadcrumbItem();