为什么约束消息的自动转换在twig / symfony中失败?

First of all: I'm new to Symfony4 and Twig. Yet I hope I have read the docs as far as my problem is concerned. :)

Introduction

Ok, my aim is to integrate the Symfony Form component along with other suitable Symfony components into an existing PHP application. The form I have created also relies on the Translation, Validator and Twig-Bridge component. I have setup the code as suggested in the official docs. Everything works as expected except for the translation of the constraint messages, albeit form labels are translated properly.

Details

The Translator is instanciated with the 'de' locale. Fallback locales are set to ['de']. The German default translations (xlf) from the Form and Validator component are loaded first, with my custom translations (yaml) following after and overriding the former if needed. I have setup two translation domains: messages and validators.

I have checked that the catalogue contains all German default messages including the constraint translations. That means the translation files are loaded correctly. Manually translating a constraint message in the controller or Twig template works fine. But the automatic translation in the template fails.

I fear it's a simple configuration problem but I have no clue what else I could try to make the translation work. Building each form field individually within the Twig template seems no option to me.

Since there is no translation cache, caching can't be the problem.

(validators.de.yaml)

// creating the custom constraint message

entity.name.not_blank: 'Bitte einen Namen eingeben!'

(Controller.php)

// creating the translator

$translator = new Translator('de');
$translator->setFallbackLocales(['de']);
$translator->addLoader('xlf', new XliffFileLoader());
$translator->addLoader('yml', new YamlFileLoader());

// adding default translations
$translator->addResource('xlf', "path/to/vendor/symfony/form/Resources/translations/validators.de.xlf", 'de', 'validators');
$translator->addResource('xlf', "path/to/validator/Resources/translations/validators.de.xlf", 'de', 'validators');

// adding custom translations
$translator->addResource('yml', "path/to/messages.de.yaml", 'de');
$translator->addResource('yml', "path/to/validators.de.yaml", 'de', 'validators');

…

// setting up Twig

$defaultFormTheme = 'bootstrap_4_layout.html.twig';
$appVariableReflection = new \ReflectionClass('\Symfony\Bridge\Twig\AppVariable');

$twig = new Environment(new FilesystemLoader(['/path/to/views', '/path/to/Resources/views/Form']));

$formEngine = new TwigRendererEngine([$defaultFormTheme], $twig);

$twig->addRuntimeLoader(
  new FactoryRuntimeLoader([
    FormRenderer::class => function () use($formEngine, $csrfManager) {
      return new FormRenderer($formEngine, $csrfManager);
    }
  ])
);

$twig->addExtension(new FormExtension());
$twig->addExtension(new TranslationExtension($translator));

…

// creating the form factory adding the Validator extension

$validator = Validation::createValidator();
$formFactory = Forms::createFormFactoryBuilder()
    ->addExtension(new ValidatorExtension($validator))
    ->getFormFactory();

…

// rendering the form using Twig

echo $this->getTwig()->render('form.html.twig', [
        'form' => $form->createView()
    ]);

(Entity.php)

// setting up the EntityType form

public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder
      ->add('name', TextType::class, [
          'constraints' => [
              new NotBlank(['message' => 'entity.name.not_blank'])
          ]
      ]);
}

Tests and Results

The constraint message should translate to "Bitte einen Namen eingeben!", instead the message key "entity.name.not_blank" is shown. Setting no message key on the constraint results in "This value should not be blank." being shown instead of "Dieser Wert sollte nicht leer sein.".

Yet, the following works:

  • translating the message key manually with {% trans from 'validators' %}entity.name.not_blank{% endtrans %} or {% trans from 'validators' %}This value should not be blank.{% endtrans %} respectively
  • translating the error tag that precedes the constraint message ("ERROR" is correctly substituted with "FEHLER")
  • translating form labels

What I have tried so far but doesn't help:

  • {{ form_widget(form, {'translation_domain': 'validators'}) }}
  • {% trans_default_domain 'validators' %}
  • $session->set('_locale', 'de')
  • $this->getRequest()->setLocale('de')

Fun fact: Testing a comparable setup with the symfony framework is successful. Yet unfortunately, the framework is no option for me.

Thanks for reading! Any helpful comment is highly appreciated. :)

[…] I fear it's a simple configuration problem but I have no clue what else I could try to make the translation work. […]

An assumption to the point and not a millimeter off the mark! :-)

Of course the ValidatorExtension can't know about the Translator (and thus the missing translation messages) unless the former is injected into the Validator as follows:

$validatorBuilder = Validation::createValidatorBuilder();

// the essential part: coupling the ValidatorBuilder with the Translator
$validatorBuilder->setTranslator($translator);
$validatorBuilder->setTranslationDomain('validators');

$validator = $validatorBuilder->getValidator();

$formFactory = Forms::createFormFactoryBuilder()
    ->addExtension(new ValidatorExtension($validator))
    ->getFormFactory();

Unfortunately the docs didn't help much finding the solution. What brought me closer was Loading Resources: Using Multiple Loaders and, eventually, reading the source code of ValidatorBuilderInterface.