LiformBundle icon indicating copy to clipboard operation
LiformBundle copied to clipboard

translation problem - cannot handle TranslatableInterface

Open kor3k opened this issue 10 months ago • 0 comments

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);
    }
}

kor3k avatar Mar 14 '25 03:03 kor3k