Autocomplete choice labels don't translate
Platform is Symfony 6.3, with UX Autocomplete 2.9
My issue
My issue: I have a Symfony UX Autocomplete form field, the choice_labels are stored in translated form in the database. I cannot get the choice_label translation to work. It always defaults to English (my default locale).
I have KnpLabs Translatable installed & working.
My current setup:
- FunctionProfile Entity
- FunctionProfileTranslation Entity -> contains a translated name field.
- TrainingPickerType Form -> includes FunctionProfileAutocompleteField
- FunctionProfileAutocompleteField -> included in TrainingPickerType
Just to make sure, the entities and the forms (removed unneccessary parts of the code):
<?php
//App\Entity\FunctionProfile.php
namespace App\Entity;
use App\Repository\FunctionProfileRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Contract\Entity\TranslatableInterface;
use Knp\DoctrineBehaviors\Model\Translatable\TranslatableTrait;
#[ORM\Entity(repositoryClass: FunctionProfileRepository::class)]
class FunctionProfile implements TranslatableInterface
{
use TranslatableTrait;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
//etc.
public function __toString(): string
{
return $this->translate(null, false)->getName();
}
//etc.
And it's friend:
<?php
namespace App\Entity;
//App\Entity\FunctionProfileTranslation.php
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Contract\Entity\TranslationInterface;
use Knp\DoctrineBehaviors\Model\Translatable\TranslationTrait;
#[ORM\Entity]
class FunctionProfileTranslation implements TranslationInterface
{
use TranslationTrait;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(length: 255)]
private ?string $name = null;
public function getId(): ?int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
}
Now for the form part, this main form includes the child form field that's not doing what I want it to do:
<?php
//App\Form\TrainingPickerType.php
namespace App\Form;
use App\Form\Field\FunctionProfileAutocompleteField;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class TrainingPickerType extends AbstractType
{
private TranslatorInterface $translator;
private UrlGeneratorInterface $router;
public function __construct(TranslatorInterface $translator, UrlGeneratorInterface $router)
{
$this->translator = $translator;
$this->router = $router;
}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$locale = $options['locale'];
$builder
->setAction($this->router->generate('app_api_get_training_pick_dialog'))
->add('functionProfile', FunctionProfileAutocompleteField::class, [
'label' => $this->translator->trans('Function Profiles', locale: $locale),
'attr' => ['class' => 'none', 'placeholder' => $this->translator->trans('Start typing or select an option...', locale: $locale),],
'label_attr' => ['class' => 'block text-sm font-medium leading-6 text-white'],
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'locale' => null
]);
}
}
The form hereabove behaves as it should, the locale is set to whatever it needs to be, labels are translated nicely, etc.
This included form field is the issue:
<?php
namespace App\Form\Field;
use App\Entity\FunctionProfile;
use App\Repository\FunctionProfileRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
use Symfony\UX\Autocomplete\Form\ParentEntityAutocompleteType;
#[AsEntityAutocompleteField]
class FunctionProfileAutocompleteField extends AbstractType
{
private RequestStack $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function configureOptions(OptionsResolver $resolver)
{
$request = $this->requestStack->getCurrentRequest();
$query = $request?->query->get('query');
$locale = $request ? $request->getLocale() : 'nl';
$resolver->setDefaults([
'class' => FunctionProfile::class,
'multiple' => true,
'choice_translation_domain' => true,
'choice_label' => function (FunctionProfile $functionProfile) use ($locale) {
$functionProfile->getTranslations();
return $functionProfile->translate($locale)->getName();
},
'query_builder' => function(FunctionProfileRepository $functionProfileRepository) use ($locale, $query) {
$qb = $functionProfileRepository->createQueryBuilder('functionProfile')
->join('functionProfile.translations', 't', 'WITH', 't.locale = :locale')
->setParameter('locale', $locale);
if (!empty($query)) {
$qb->andWhere('t.name LIKE :search')
->setParameter('search', '%' . $query . '%');
}
return $qb;
},
]);
}
public function buildView(FormView|\Symfony\Component\Form\FormView $view, FormInterface|\Symfony\Component\Form\FormInterface $form, array $options): void
{
$view->vars['attr']['class'] = ''; // Remove any existing classes
}
public function getParent(): string
{
return ParentEntityAutocompleteType::class;
}
}
When I enter a query, the search happens. The autocomplete options are presented. But they're always in English, even if I manually set a different locale like "nl", the outcome remains the same: all choice_labels are in English.
Any guidance or help you may be able to provide would be super welcome!
What I have tried
I have searched the KnpLabs translatable docs for solutions, but couldn't find any solutions.
I have verified that (outside of the form field), the translated versions of the entity appear; as an example;
Neatly returns Banana when I run:
$test = $functionProfileRepository->findOneBy(["id" => 187]);
dd($test->translate('en')->getName());
And Banaan when I run:
$test = $functionProfileRepository->findOneBy(["id" => 187]);
dd($test->translate('nl')->getName());
I tried retrieving the choice_labels in a separate function and then add those to the choice_label option, but to no avail.
public function getTranslatedLabel(FunctionProfile $functionProfile)
{
$request = $this->requestStack->getCurrentRequest();
$locale = $request ? $request->getLocale() : 'nl';
return $functionProfile->translate($locale)->getName();
}
I'm sorta out of idea's now.
I know about https://github.com/a2lix/TranslationFormBundle but my colleagues indicated not wanting to use it unless absolutely needed, citing bad experiences with earlier versions of the bundle. But if we must, we will use it.
Okay, so I was a bit stupid. My $request->getLocale() wasn't sticky yet. Everything works as it should implementing this EventSubscriber: https://symfony.com/doc/current/session.html#creating-a-localesubscriber