From 78b54e7a976b31a8d76ef267f18956496d74b58f Mon Sep 17 00:00:00 2001 From: Adrien Abraham Date: Tue, 21 Jan 2025 16:11:33 +0100 Subject: [PATCH 1/3] Support TranslatableMessage as a label value --- src/Service/BreadcrumbItemProcessor.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 { From 3ea03c8934a0ed0c7639eccb02aaf3a68b6d8abb Mon Sep 17 00:00:00 2001 From: Adrien Abraham Date: Wed, 29 Oct 2025 13:53:02 +0100 Subject: [PATCH 2/3] Add tests for TranslatableMessage usage --- .../Service/BreadcrumbItemProcessorTest.php | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) 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(); From 2b5907d0aaa088a7772c2f346c172b4b642c272c Mon Sep 17 00:00:00 2001 From: Adrien Abraham Date: Wed, 29 Oct 2025 14:02:13 +0100 Subject: [PATCH 3/3] Add TranslatableMessage usage in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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