I have a simple bean defining user credentials on signup registration form model:
public class UserCreds {
@Length(min=1, max=50)
@NotEmpty
private String username;
@Email
private String email;
@NotEmpty
@Length(min=1, max=50)
private String password;
private String passwordConfirm;
... only obvious getters and setters below this line ...
}
I pass it's instance under "userCreds" model attribute, so i have a following localization bundle:
NotEmpty.userCreds.username=Username is required
NotEmpty.userCreds.password=Passsword is required
Length.userCreds.username=Username length must be between {2} and {1} symbols
Length.userCreds.password=Password length must be between {2} and {1} symbols
Email.userCreds.email=Ill-formed e-mail address
And it works!
The problem here: i don't want use this cryptic {N} placeholders, which may change their places occasionally (in bundle above 'max' is binded to {1} and 'min' is binded to {2}, which is at least non-obvious positioning) i wish to use full names of annotaions, as in
Length.userCreds.username=Username length must be between {min} and {max} symbols
(which is not working - when i try it - am getting java.lang.IllegalArgumentException: can't parse argument number min
exception)
As i could when define message in code (the following example also works):
@Length(min=1, max=50, message = "Username length must be between {min} and {max} symbols")
@NotEmpty
private String username;
But when i define it in code i loose my ability to localize the message for different languages.
What do?
UPDATE 1
After a few hours of poking around debugger in my IDE i found that using the following class:
public class BakaMessageSource extends ReloadableResourceBundleMessageSource {
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
/* original implementation *
for (String basename : basenames) {
List<String> filenames = calculateAllFilenames(basename, locale);
for (String filename : filenames) {
PropertiesHolder propHolder = getProperties(filename);
MessageFormat result = propHolder.getMessageFormat(code, locale);
if (result != null) {
return result;
}
}
}
*/
return null;
}
}
With the following spring-servlet.xml declaration:
<bean id="messageSource" class="org.eientei.radiorandom.framework.crutchy.BakaMessageSource"
p:defaultEncoding="UTF-8"
p:alwaysUseMessageFormat="false"
>
<property name="basenames">
<list>
<!-- my application-specific localization bundles -->
<value>classpath:messages/root/errorMessages</value>
<value>classpath:messages/root/langMessages</value>
<value>classpath:messages/user/signupMessages</value>
</list>
</property>
</bean>
instead of original ReloadableResourceBundleMessageSource actually helps to get localized messages with named parameters.
But i guess swapping classes around at that point is not a proper solution. Which one would be?
UPDATE 2
I am such an idiot. I forgot in-code message and thought it got interpolated properly. Geez. So, alphabetical ordering... guess i can't ask for more.
I think that the actual order is alphabetical ("max" is lesser than "min") starting from Spring 3.0.4.
From SPR-6730 discussion (Juergen Hoeller post)
For the first option, Spring-managed field error arguments include the actual constraint annotation attributes in alphabetical order now. So for @Size(min=1,max=5), it'll include the field name as {0} , the max value as {1} , and the min value as {2}