Reputation: 1081
I'm using symfony 3.4.19 and have translations for 2 languages: English en Dutch. I'm using this symfony guide to translate validation messages
Here is the part my UserDTO with assertions and message translations:
class UserDTO
{
/**
* @var string
*
* @Assert\NotNull(message="security.registration.firstname.required", groups={"createUser", "updateUser"})
* @Assert\NotBlank(message="security.registration.firstname.required", groups={"createUser", "updateUser"})
*/
private $firstname;
/**
* @var string
*
* @Assert\NotNull(message="security.registration.lastname.required", groups={"createUser", "updateUser"})
* @Assert\NotBlank(message="security.registration.lastname.required", groups={"createUser", "updateUser"})
*/
private $lastname;
/**
* @var string
*
* @Assert\NotNull(message="security.registration.email.required", groups={"createUser", "updateUser"})
* @Assert\NotBlank(message="security.registration.email.required", groups={"createUser", "updateUser"})
* @Assert\Email(message="security.registration.email.format", groups={"createUser", "updateUser"})
*/
private $email;
/**
* @var string
*
* @Assert\NotNull(message="security.registration.password.required", groups={"createUser", "updateUser"})
* @Assert\NotBlank(message="security.registration.password.required", groups={"createUser", "updateUser"})
*/
private $plainPassword;
}
So I've 2 translation files: security.en.yml
"Username could not be found.": "Wrong email or password!"
"Invalid credentials.": "Wrong email or password!"
security:
login:
email: 'Email'
password: 'Password'
registration:
firstname:
title: 'Firstname'
required: 'A firstname is required'
lastname:
title: 'Lastname'
required: 'A lastname is required'
email:
title: 'Email'
required: 'An email is required'
format: 'Wrong email format'
password:
title: 'Password'
required: 'A password is required'
and security.nl.yml:
"Username could not be found.": "Verkeerd email of wachtwoord!"
"Invalid credentials.": "Verkeerd email of wachtwoord!"
security:
login:
email: 'Email'
password: 'Wachtwoord'
registration:
firstname:
title: 'Voornaam'
required: 'Voornaam is verplicht'
lastname:
title: 'Achternaam'
required: 'Achternaam is verplicht'
email:
title: 'Email'
required: 'Email is verplicht'
format: 'Verkeerd email formaat'
password:
title: 'Wachtwoord'
required: 'Wachtwoord is verplicht'
Also I don't use symfony form builder so I just submit the form that I create within the twig template:
{% trans_default_domain 'security' %}
{% extends 'base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<link href="{{ asset('css/project/registration.css') }}" rel="stylesheet"/>
{% endblock %}
{% block javascripts %}
{{ parent() }}
{% endblock %}
{% block body %}
{{ include('component/main-navbar.html.twig') }}
<div>
<h2>Create new account</h2>
{% if errors is defined %}
<div class="alert alert-danger">
<ul>
{% for error in errors %}
<li>{{ error|trans }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<form action="/register" method="post">
<input type="text" name="firstname" placeholder="{{ 'security.registration.firstname.title'|trans }}">
<input type="text" name="lastname" placeholder="{{ 'security.registration.lastname.title'|trans }}">
<input type="text" name="email" placeholder="{{ 'security.registration.email.title'|trans }}">
<input type="text" name="plainPassword" placeholder="{{ 'security.registration.password.title'|trans }}">
<button type="submit" class="btn btn-success btn-sm float-right">Login <span class="fa fa-lock"></span>
</button>
</form>
</div>
{{ include('component/footer.html.twig') }}
{% endblock %}
As you can see on top of the template I explicitly set the translation domain ie. 'security'.
In controller I've 2 methods. The 1st one is just to render the view, and the other is to submit the form.
public function registrationPageAction(Request $request)
{
return new Response(
$this->twigService->render(
'security/register.html.twig',
[
'error' => []
]
),
200
);
}
Submitting the form:
public function registrationSubmitAction(Request $request)
{
$submittedFormData = $request->request->all();
$userDTO = new UserDTO();
$userDTO->setFirstname($submittedFormData['firstname'])
->setLastname($submittedFormData['lastname'])
->setEmail($submittedFormData['email'])
->setPlainPassword($submittedFormData['plainPassword']);
$fieldErrors = $this->validator->validate($userDTO, null, ['createUser']);
$errors = [];
if (count($fieldErrors) > 0)
{
foreach ($fieldErrors as $error)
{
$errors[] = $error->getMessage();
}
return new Response(
$this->twigService->render(
'security/register.html.twig',
[
'errors' => $errors
]
),
200
);
}
}
Currently I don't store submitted data anywhere, I just want to be sure that the translations work fine.
So when I go to /register I see the form with TRANSLATED fields as expected. And in the debug bar there are no notifications concerning the missing translations:
After the submitting form I've got also the form with translated fields, but this time in debug bar I also have the missing translations notification:
Note that all the translations (fields and messages) are working fine. In config.yml I've fallback locale set to 'en' and the current locale is set to 'nl'.
I really don't understand why I keep seeing those notifications. Any ideas?
UPDATE:
Now I've got rid of security.en.yml and security.nl.yml and created for test purpose only the app/Resources/translations/validators.en.yml with this content:
firstname: 'Firstname is needed'
lastname: 'Lastname is needed'
email: 'Email is needed'
emailformat: 'Wrong email format'
password: 'Password is needed'
Further I've modified validation messages in the UserDTO class:
class UserDTO
{
/**
* @var string
*
* @Assert\NotNull(message="firstname", groups={"createUser", "updateUser"})
* @Assert\NotBlank(message="firstname", groups={"createUser", "updateUser"})
*/
private $firstname;
/**
* @var string
*
* @Assert\NotNull(message="lastname", groups={"createUser", "updateUser"})
* @Assert\NotBlank(message="lastname", groups={"createUser", "updateUser"})
*/
private $lastname;
/**
* @var string
*
* @Assert\NotNull(message="email", groups={"createUser", "updateUser"})
* @Assert\NotBlank(message="email", groups={"createUser", "updateUser"})
* @Assert\Email(message="emailformat", groups={"createUser", "updateUser"})
*/
private $email;
/**
* @var string
*
* @Assert\NotNull(message="password", groups={"createUser", "updateUser"})
* @Assert\NotBlank(message="password", groups={"createUser", "updateUser"})
*/
private $plainPassword;
}
In register.html.twig I've removed {% trans_default_domain 'security' %} and made the form without field translations:
<form action="/register" method="post">
<input type="text" name="firstname" placeholder="Firstname">
<input type="text" name="lastname" placeholder="Lastname">
<input type="text" name="email" placeholder="Email">
<input type="text" name="plainPassword" placeholder="Password">
<button type="submit" class="btn btn-success btn-sm float-right">Login <span class="fa fa-lock"></span>
</button>
</form>
And again after submitting the form I get those annoying notifications:
What am I doing wrong? I just follow step by step the official symfony guide :(
Upvotes: 2
Views: 1187
Reputation: 17166
The problem is a bit hidden in your last screenshot. The validation constraints will not use the default translation domain from your template. These messages are pulled from the validators
domain by default.
An option could be to duplicate the message keys in a new file for validators-domain our you could change the default domain for validation messages. As far as I know, this is done by overwriting the parameter %validator.translation_domain%
e.g. in your services.yaml or in your config.yaml:
# app/config/config.yaml
parameters:
validator.translation_domain: security
framework:
...
Since those are really validation errors I think it would make more sense to keep those messages inside the validators domain and just use this domain selectively in your template:
<input type="text" name="firstname" placeholder="{{ 'security.registration.firstname.title'|trans(domain="validators") }}">
or split up the placeholders and the validation messages into separate translation domains (and files).
edit for clarification: First the validator will translate your messages, not find a translation and so the translation is the original key. This "translation" is then passed into your error handling in your template, where it is translated with your current domain. So basically, your problem is a somewhat hidden attempt at doubly translating the validation messages.
Upvotes: 3