Skip to content
Open
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion src/Service/BreadcrumbItemProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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 {
Expand Down
35 changes: 35 additions & 0 deletions tests/Unit/Service/BreadcrumbItemProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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();
Expand Down