LiformBundle
LiformBundle copied to clipboard
translation problem - cannot handle TranslatableInterface
liform cannot handle instances of Symfony\Contracts\Translation\TranslatableInterface for label , help and choice_label.
Symfony\Component\Translation\DataCollectorTranslator::trans(): Argument #1 ($id) must be of type ?string, Symfony\Component\Translation\TranslatableMessage given, called in /app/vendor/survos/liform/src/Limenius/Liform/Transformer/ChoiceTransformer.php on line 39
also ignores form options like help_translation_parameters, label_translation_parameters, choice_translation_parameters, choice_translation_domain.
it calls $translator->trans() on few places in Limenius\Liform\Transformer\AbstractTransformer and \Limenius\Liform\Transformer\ChoiceTransformer.
imho it would be better to remove the translator & it's calls completely, and leave the translation to an extension. that way one does not have to override the transformers.
abstract class AbstractTransformer implements TransformerInterface
{
public function __construct(protected ?FormTypeGuesserInterface $validatorGuesser = null) // remove translator
{
}
protected function addLabel(FormInterface $form, array $schema): array
{
if ($label = $form->getConfig()->getOption('label')) {
$schema['title'] = $label; // remove $translator->trans()
} else {
$schema['title'] = $form->getName(); // remove $translator->trans()
}
return $schema;
}
protected function addDescription(FormInterface $form, array $schema): array
{
$formConfig = $form->getConfig();
if ($help = $formConfig->getOption('help', '')) {
$schema['description'] = $help; // remove $translator->trans()
}
if ($liform = $formConfig->getOption('liform')) {
if (isset($liform['description']) && $description = $liform['description']) {
$schema['description'] = $description; // remove $translator->trans()
}
}
return $schema;
}
}
class ChoiceTransformer extends AbstractTransformer
{
public function transform(FormInterface $form, array $extensions = [], $widget = null): array
{
$formView = $form->createView();
$choices = [];
$titles = [];
foreach ($formView->vars['choices'] as $choiceView) {
if ($choiceView instanceof ChoiceGroupView) {
foreach ($choiceView->choices as $choiceItem) {
$choices[] = $choiceItem->value;
$titles[] = $choiceItem->label; // remove $translator->trans()
}
} else {
$choices[] = $choiceView->value;
$titles[] = $choiceView->label; // remove $translator->trans()
}
}
if ($formView->vars['multiple']) {
$schema = $this->transformMultiple($form, $choices, $titles);
} else {
$schema = $this->transformSingle($form, $choices, $titles);
}
$schema = $this->addCommonSpecs($form, $schema, $extensions, $widget);
return $schema;
}
}
// do translation in dedicated extension & cover TranslatableInterface and form options
class FormTranslationExtension implements ExtensionInterface
{
public function __construct(private TranslatorInterface $translator)
{
}
public function apply(FormInterface $form, array $schema): array
{
$schema = $this->addLabel($form, $schema);
$schema = $this->addDescription($form, $schema);
$schema = $this->addChoices($form, $schema);
return $schema;
}
protected function addChoices(FormInterface $form, array $schema): array
{
if (!FormUtil::isTypeInAncestry($form, 'choice')) {
return $schema;
}
$getChoiceDomain = static function ($choiceView) use ($form): null|false|string {
$domain = $form->getConfig()->getOption('choice_translation_domain');
if (false === $domain) {
return false;
}
if (null === $domain) {
return $form->getConfig()->getOption('translation_domain');
}
return $domain;
};
$trans = $this->trans(...);
$transChoiceView = static function ($choiceView) use ($getChoiceDomain, $trans): string {
$domain = $getChoiceDomain($choiceView);
if (false === $domain) {
return (string) $choiceView->label;
} else {
return $trans(
$choiceView->label,
$choiceView->labelTranslationParameters,
$domain,
);
}
};
$titles = [];
foreach ($form->createView()->vars['choices'] as $choiceView) {
if ($choiceView instanceof ChoiceGroupView) {
foreach ($choiceView->choices as $choiceItem) {
$titles[] = $transChoiceView($choiceItem);
}
} else {
$titles[] = $transChoiceView($choiceView);
}
}
$schema['enum_titles'] = $titles;
$schema['options']['enum_titles'] = $titles;
return $schema;
}
protected function addLabel(FormInterface $form, array $schema): array
{
$label = $form->getConfig()->getOption('label') ?: $form->getName();
$schema['title'] = $this->trans(
$label,
$form->getConfig()->getOption('label_translation_parameters', []),
$form->getConfig()->getOption('translation_domain'),
);
return $schema;
}
protected function addDescription(FormInterface $form, array $schema): array
{
$formConfig = $form->getConfig();
if ($liform = $formConfig->getOption('liform')) {
if (isset($liform['description']) && $description = $liform['description']) {
$schema['description'] = $this->trans(
$description,
[],
$form->getConfig()->getOption('translation_domain'),
);
}
} elseif ($help = $formConfig->getOption('help')) {
$schema['description'] = $this->trans(
$help,
$form->getConfig()->getOption('help_translation_parameters', []),
$form->getConfig()->getOption('translation_domain'),
);
}
return $schema;
}
protected function trans(string|\Stringable|TranslatableInterface $item, array $parameters = [], ?string $domain = null): string
{
if ($item instanceof TranslatableInterface) {
return $item->trans($this->translator);
}
$item = (string) $item;
if (empty($item)) {
return '';
}
return $this->translator->trans($item, $parameters, $domain);
}
}