Custom formatter and I18N

153 views
Skip to first unread message

Florian Pradines

unread,
Aug 23, 2017, 11:53:58 AM8/23/17
to Play Framework

Hi,

Following this documentation : https://www.playframework.com/documentation/2.6.x/JavaForms I registered a custom DataBinder.
This DataBinder is here to bind PhoneNumber (from the library: https://github.com/googlei18n/libphonenumber) to my form.

My problem is for internationalization, in the documentation there are these lines:

When the binding fails an array of errors keys is created, the first one defined in the messages file will be used. This array will generally contain:

["error.invalid.<fieldName>", "error.invalid.<type>", "error.invalid"]

The errors keys are created by Spring DefaultMessageCodesResolver, the root “typeMismatch” is replaced by “error.invalid”.


Okay, so I guess I should set in my messages and messages.xx files the field name of the form or the type of the field. I tried the following lines:


error.invalid.phone=A valid phone number is required
error.invalid.PhoneNumber=A valid phone number is required
error.invalid.phonenumber=A valid phone number is required
error.invalid.PhoneNumber.PhoneNumber=A valid phone number is required
error.invalid.PhoneNumber.phoneNumber=A valid phone number is required
error.invalid.phonenumber.phonenumber=A valid phone number is required
error.invalid.com.google.i18n.phonenumbers.invalid.PhoneNumber.PhoneNumber=A valid phone number is required
error.invalid.com.google.i18n.phonenumbers.invalid.PhoneNumber.Phonenumber=A valid phone number is required
error.invalid.com.google.i18n.phonenumbers.invalid.phonenumber.phonenumber=A valid phone number is required

error.phone=A valid phone number is required
error.PhoneNumber=A valid phone number is required
error.phonenumber=A valid phone number is required
error.PhoneNumber.PhoneNumber=A valid phone number is required
error.PhoneNumber.phoneNumber=A valid phone number is required
error.phonenumber.phonenumber=A valid phone number is required
error.com.google.i18n.phonenumbers.invalid.PhoneNumber.PhoneNumber=A valid phone number is required
error.com.google.i18n.phonenumbers.invalid.PhoneNumber.Phonenumber=A valid phone number is required
error.com.google.i18n.phonenumbers.invalid.phonenumber.phonenumber=A valid phone number is required

But I am still unsuccessful, it's always the default "error.invalid" message which is displayed...

Do you have any ideas?

Thanks,

Matthias Kurz

unread,
Aug 23, 2017, 12:49:06 PM8/23/17
to Play Framework
Could you please post the source code of your custom DataBinder?
Thanks!

Florian Pradines

unread,
Aug 23, 2017, 12:55:14 PM8/23/17
to Play Framework
Hi Matthias,

Here it is:

package modules.formatter;

import java.text.ParseException;
import java.util.Locale;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;

import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import play.data.format.Formatters;
import play.data.format.Formatters.SimpleFormatter;
import play.i18n.MessagesApi;


@Singleton
public class FormattersProvider implements Provider<Formatters> {

   
private final MessagesApi messagesApi;

   
@Inject
   
public FormattersProvider(MessagesApi messagesApi) {
       
this.messagesApi = messagesApi;
   
}

   
@Override
   
public Formatters get() {
       
Formatters formatters = new Formatters(messagesApi);
        formatters
.register(Phonenumber.PhoneNumber.class, getPhoneNumberFormatter());

       
return formatters;
   
}

   
private SimpleFormatter<Phonenumber.PhoneNumber> getPhoneNumberFormatter() {
       
return new SimpleFormatter<Phonenumber.PhoneNumber>() {
           
@Override
           
public Phonenumber.PhoneNumber parse(String input, Locale l) throws ParseException {
               
try {
                   
Phonenumber.PhoneNumber phoneNumber = PhoneNumberUtil.getInstance().parse(input, "FR");
                   
if (!PhoneNumberUtil.getInstance().isValidNumberForRegion(phoneNumber, "FR")) {
                       
throw new ParseException("", 0);
                   
}

                   
return phoneNumber;
               
} catch (NumberParseException e) {
                   
throw new ParseException("", 0);
               
}
           
}

           
@Override
           
public String print(Phonenumber.PhoneNumber phoneNumber, Locale l) {
               
return PhoneNumberUtil.getInstance().format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
           
}
       
};
   
}
}

I tested if the ParseException is thrown with a log.

Thanks,

Matthias Kurz

unread,
Aug 23, 2017, 1:10:06 PM8/23/17
to Play Framework
Did you bind the provider like described in the docs? Please also post that code.


After defining the provider you have to bind it:

import com.google.inject.AbstractModule;

import play.data.format.Formatters;

public class FormattersModule extends AbstractModule {

    @Override
    protected void configure() {

        bind(Formatters.class).toProvider(FormattersProvider.class);

    }
}

Finally you have to disable Play’s default FormattersModule and instead enable your module in application.conf:

play.modules.enabled += "com.example.FormattersModule"
play.modules.disabled += "play.data.format.FormattersModule"

Florian Pradines

unread,
Aug 23, 2017, 1:14:59 PM8/23/17
to Play Framework

Yes I already did that. Actually the binding works perfectly and my PhoneNumber is parsed in my form.

My problem is the error displayed. I want to customize the error shown by the form When I throw a ParseException. The message is always "error.invalid" which is then translated with I18n in "Invalid value" :(


--
You received this message because you are subscribed to a topic in the Google Groups "Play Framework" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/play-framework/5XVJ0U52MFg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to play-framewor...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/b84c576f-7e2a-47ef-ab23-151a6aceb48d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Matthias Kurz

unread,
Aug 24, 2017, 4:53:56 AM8/24/17
to Play Framework
Can you please also post your form class?

Florian Pradines

unread,
Aug 24, 2017, 5:45:09 AM8/24/17
to Play Framework
Yes

package forms.authorization;

import com.google.i18n.phonenumbers.Phonenumber;
import play.data.validation.Constraints;

public class CreateOrganizationForm {

   
@Constraints.Required
   
//@Constraints.Pattern(value = "^\\+33 [1-9] [0-9]{2} [0-9]{2} [0-9]{2} [0-9]{2}$", message = "error.phoneNumber")
   
private Phonenumber.PhoneNumber phone;

   
// other fields

   
public Phonenumber.PhoneNumber getPhone() {
       
return phone;
   
}

   
public void setPhone(Phonenumber.PhoneNumber phone) {
       
this.phone = phone;
   
}

   
// other getters & setters
}

When I was parsing the number from a String, I was able to define a custom error message if the regex failed.
Now, since the parsing is done "magically" by the formatter, I don't know how to customize the error message :(

Matthias Kurz

unread,
Aug 25, 2017, 6:17:36 AM8/25/17
to Play Framework
What should work is
error.invalid.phone=Invalid phone number
because the field is named phone. Please let me know if this works. (Maybe stop your app, remove the target folder and try again).

What also SHOULD work is
error.invalid.com.google.i18n.phonenumbers.Phonenumber$PhoneNumber=Invalid phone number
which applies to all Phonenumber.PhoneNumber fields, no matter of what the field is called and in which form class it is used.
Using the dollar sign is necassary because Phonenumber.PhoneNumber is a nested class.
However right now in Play it is not possible to use a dollar sign in a message key - so you won't be able to use that key right now.
I created a pull request to fix this problem:

Florian Pradines

unread,
Aug 25, 2017, 7:13:50 AM8/25/17
to Play Framework
I already have the key error.invalid.phone :(
I removed all my target folders, but the result is still the same :

{"phone":["Invalid value"]}

Is this a bug with play? Should I create an issue?

Also, thanks for your explanation about the dollar for nested classes! I hope your pull request will be merged in the 2.6.4 release.

Florian Pradines

unread,
Aug 25, 2017, 7:37:51 AM8/25/17
to Play Framework
Reply all
Reply to author
Forward
0 new messages