Skip to content

Commit a9c46f4

Browse files
committed
HV-1748 Improve localization support
- Make the default locale configurable - Always initialize the default locale for the predefined scope VF
1 parent 1b17b69 commit a9c46f4

File tree

9 files changed

+252
-56
lines changed

9 files changed

+252
-56
lines changed

engine/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
<properties>
2424
<hibernate-validator-parent.path>..</hibernate-validator-parent.path>
25+
<surefire.jvm.args.additional>-Duser.language=en</surefire.jvm.args.additional>
2526
</properties>
2627

2728
<distributionManagement>

engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.hibernate.validator;
88

99
import java.time.Duration;
10+
import java.util.Locale;
1011
import java.util.Set;
1112

1213
import javax.validation.Configuration;
@@ -360,4 +361,14 @@ public interface BaseHibernateValidatorConfiguration<S extends BaseHibernateVali
360361
*/
361362
@Incubating
362363
S propertyNodeNameProvider(PropertyNodeNameProvider propertyNodeNameProvider);
364+
365+
/**
366+
* Allows setting the default locale used to interpolate the constraint violation messages.
367+
* <p>
368+
* If not set, defaults to the system locale obtained via {@link Locale#getDefault()}.
369+
*
370+
* @since 6.1.1
371+
*/
372+
@Incubating
373+
S defaultLocale(Locale defaultLocale);
363374
}

engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ public abstract class AbstractConfigurationImpl<T extends BaseHibernateValidator
113113
private Object constraintValidatorPayload;
114114
private GetterPropertySelectionStrategy getterPropertySelectionStrategy;
115115

116-
// locales to initialize eagerly
117-
private Set<Locale> localesToInitialize = Collections.emptySet();
116+
// the default locale
117+
private Locale defaultLocale = Locale.getDefault();
118118

119119
protected AbstractConfigurationImpl(BootstrapState state) {
120120
this();
@@ -325,6 +325,14 @@ public T getterPropertySelectionStrategy(GetterPropertySelectionStrategy getterP
325325
return thisAsT();
326326
}
327327

328+
@Override
329+
public T defaultLocale(Locale defaultLocale) {
330+
Contracts.assertNotNull( defaultLocale, MESSAGES.parameterMustNotBeNull( "defaultLocale" ) );
331+
332+
this.defaultLocale = defaultLocale;
333+
return thisAsT();
334+
}
335+
328336
public boolean isAllowParallelMethodsDefineParameterConstraints() {
329337
return this.methodValidationConfigurationBuilder.isAllowParallelMethodsDefineParameterConstraints();
330338
}
@@ -518,7 +526,8 @@ public ClassLoader getExternalClassLoader() {
518526
@Override
519527
public final MessageInterpolator getDefaultMessageInterpolator() {
520528
if ( defaultMessageInterpolator == null ) {
521-
defaultMessageInterpolator = new ResourceBundleMessageInterpolator( getDefaultResourceBundleLocator(), localesToInitialize );
529+
defaultMessageInterpolator = new ResourceBundleMessageInterpolator( getDefaultResourceBundleLocator(), getAllLocalesToInitialize(),
530+
defaultLocale );
522531
}
523532

524533
return defaultMessageInterpolator;
@@ -538,7 +547,7 @@ public final ConstraintValidatorFactory getDefaultConstraintValidatorFactory() {
538547
public final ResourceBundleLocator getDefaultResourceBundleLocator() {
539548
if ( defaultResourceBundleLocator == null ) {
540549
defaultResourceBundleLocator = new PlatformResourceBundleLocator(
541-
ResourceBundleMessageInterpolator.USER_VALIDATION_MESSAGES, localesToInitialize );
550+
ResourceBundleMessageInterpolator.USER_VALIDATION_MESSAGES, getAllLocalesToInitialize() );
542551
}
543552

544553
return defaultResourceBundleLocator;
@@ -688,12 +697,12 @@ private MessageInterpolator getDefaultMessageInterpolatorConfiguredWithClassLoad
688697
if ( externalClassLoader != null ) {
689698
PlatformResourceBundleLocator userResourceBundleLocator = new PlatformResourceBundleLocator(
690699
ResourceBundleMessageInterpolator.USER_VALIDATION_MESSAGES,
691-
localesToInitialize,
700+
getAllLocalesToInitialize(),
692701
externalClassLoader
693702
);
694703
PlatformResourceBundleLocator contributorResourceBundleLocator = new PlatformResourceBundleLocator(
695704
ResourceBundleMessageInterpolator.CONTRIBUTOR_VALIDATION_MESSAGES,
696-
localesToInitialize,
705+
getAllLocalesToInitialize(),
697706
externalClassLoader,
698707
true
699708
);
@@ -707,7 +716,8 @@ private MessageInterpolator getDefaultMessageInterpolatorConfiguredWithClassLoad
707716
return new ResourceBundleMessageInterpolator(
708717
userResourceBundleLocator,
709718
contributorResourceBundleLocator,
710-
localesToInitialize
719+
getAllLocalesToInitialize(),
720+
defaultLocale
711721
);
712722
}
713723
finally {
@@ -719,8 +729,13 @@ private MessageInterpolator getDefaultMessageInterpolatorConfiguredWithClassLoad
719729
}
720730
}
721731

722-
protected void setLocalesToInitialize(Set<Locale> localesToInitialize) {
723-
this.localesToInitialize = localesToInitialize;
732+
protected final Locale getDefaultLocale() {
733+
return defaultLocale;
734+
}
735+
736+
protected Set<Locale> getAllLocalesToInitialize() {
737+
// By default, we return an empty set meaning that we will dynamically initialize the locales.
738+
return Collections.emptySet();
724739
}
725740

726741
@SuppressWarnings("unchecked")

engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeConfigurationImpl.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
*/
77
package org.hibernate.validator.internal.engine;
88

9+
import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;
10+
11+
import java.util.Collections;
912
import java.util.Locale;
1013
import java.util.Set;
1114

@@ -15,6 +18,7 @@
1518

1619
import org.hibernate.validator.PredefinedScopeHibernateValidatorConfiguration;
1720
import org.hibernate.validator.internal.util.CollectionHelper;
21+
import org.hibernate.validator.internal.util.Contracts;
1822
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;
1923

2024
/**
@@ -27,6 +31,13 @@ public class PredefinedScopeConfigurationImpl extends AbstractConfigurationImpl<
2731

2832
private BeanMetaDataClassNormalizer beanMetaDataClassNormalizer;
2933

34+
/**
35+
* Locales to initialize eagerly.
36+
* <p>
37+
* We will always include the default locale in the final list.
38+
*/
39+
private Set<Locale> localesToInitialize = Collections.emptySet();
40+
3041
public PredefinedScopeConfigurationImpl(BootstrapState state) {
3142
super( state );
3243
}
@@ -46,8 +57,9 @@ public Set<Class<?>> getBeanClassesToInitialize() {
4657
}
4758

4859
@Override
49-
public PredefinedScopeHibernateValidatorConfiguration initializeLocales(Set<Locale> locales) {
50-
setLocalesToInitialize( CollectionHelper.toImmutableSet( locales ) );
60+
public PredefinedScopeHibernateValidatorConfiguration initializeLocales(Set<Locale> localesToInitialize) {
61+
Contracts.assertNotNull( localesToInitialize, MESSAGES.parameterMustNotBeNull( "localesToInitialize" ) );
62+
this.localesToInitialize = localesToInitialize;
5163
return thisAsT();
5264
}
5365

@@ -60,4 +72,16 @@ public PredefinedScopeHibernateValidatorConfiguration beanMetaDataClassNormalize
6072
public BeanMetaDataClassNormalizer getBeanMetaDataClassNormalizer() {
6173
return beanMetaDataClassNormalizer;
6274
}
75+
76+
@Override
77+
protected Set<Locale> getAllLocalesToInitialize() {
78+
if ( localesToInitialize.isEmpty() ) {
79+
return Collections.singleton( getDefaultLocale() );
80+
}
81+
82+
Set<Locale> allLocales = CollectionHelper.newHashSet( localesToInitialize.size() + 1 );
83+
allLocales.addAll( localesToInitialize );
84+
allLocales.add( getDefaultLocale() );
85+
return Collections.unmodifiableSet( allLocales );
86+
}
6387
}

engine/src/main/java/org/hibernate/validator/messageinterpolation/AbstractMessageInterpolator.java

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.hibernate.validator.messageinterpolation;
88

99
import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.ReferenceType.SOFT;
10+
import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;
1011

1112
import java.lang.invoke.MethodHandles;
1213
import java.util.Collections;
@@ -20,15 +21,16 @@
2021
import java.util.regex.Pattern;
2122

2223
import javax.validation.MessageInterpolator;
23-
import javax.validation.ValidationException;
2424

25+
import org.hibernate.validator.Incubating;
2526
import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTermType;
2627
import org.hibernate.validator.internal.engine.messageinterpolation.LocalizedMessage;
2728
import org.hibernate.validator.internal.engine.messageinterpolation.parser.MessageDescriptorFormatException;
2829
import org.hibernate.validator.internal.engine.messageinterpolation.parser.Token;
2930
import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenCollector;
3031
import org.hibernate.validator.internal.engine.messageinterpolation.parser.TokenIterator;
3132
import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap;
33+
import org.hibernate.validator.internal.util.Contracts;
3234
import org.hibernate.validator.internal.util.logging.Log;
3335
import org.hibernate.validator.internal.util.logging.LoggerFactory;
3436
import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator;
@@ -131,7 +133,7 @@ public abstract class AbstractMessageInterpolator implements MessageInterpolator
131133
* {@code MessageInterpolator} using the default resource bundle locators.
132134
*/
133135
public AbstractMessageInterpolator() {
134-
this( Collections.emptySet() );
136+
this( Collections.emptySet(), Locale.getDefault() );
135137
}
136138

137139
/**
@@ -140,7 +142,7 @@ public AbstractMessageInterpolator() {
140142
* @param userResourceBundleLocator {@code ResourceBundleLocator} used to load user provided resource bundle
141143
*/
142144
public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator) {
143-
this( userResourceBundleLocator, Collections.emptySet() );
145+
this( userResourceBundleLocator, Collections.emptySet(), Locale.getDefault() );
144146
}
145147

146148
/**
@@ -152,7 +154,7 @@ public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocat
152154
*/
153155
public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator,
154156
ResourceBundleLocator contributorResourceBundleLocator) {
155-
this( userResourceBundleLocator, contributorResourceBundleLocator, Collections.emptySet() );
157+
this( userResourceBundleLocator, contributorResourceBundleLocator, Collections.emptySet(), Locale.getDefault() );
156158
}
157159

158160
/**
@@ -166,61 +168,74 @@ public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocat
166168
public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator,
167169
ResourceBundleLocator contributorResourceBundleLocator,
168170
boolean cacheMessages) {
169-
this( userResourceBundleLocator, contributorResourceBundleLocator, Collections.emptySet(), cacheMessages );
171+
this( userResourceBundleLocator, contributorResourceBundleLocator, Collections.emptySet(), Locale.getDefault(), cacheMessages );
170172
}
171173

172174
/**
173175
* {@code MessageInterpolator} using the default resource bundle locators.
174176
*
175-
* @param localesToInitialize The set of locales to initialize at bootstrap.
177+
* @param localesToInitialize the set of locales to initialize at bootstrap
178+
* @param defaultLocale the default locale
176179
*
177-
* @since 6.1
180+
* @since 6.1.1
178181
*/
179-
public AbstractMessageInterpolator(Set<Locale> localesToInitialize) {
180-
this( null, localesToInitialize );
182+
@Incubating
183+
public AbstractMessageInterpolator(Set<Locale> localesToInitialize, Locale defaultLocale) {
184+
this( null, localesToInitialize, defaultLocale );
181185
}
182186

183187
/**
184188
* {@code MessageInterpolator} taking a resource bundle locator.
185189
*
186190
* @param userResourceBundleLocator {@code ResourceBundleLocator} used to load user provided resource bundle
187-
* @param localesToInitialize The set of locales to initialize at bootstrap.
191+
* @param localesToInitialize the set of locales to initialize at bootstrap
192+
* @param defaultLocale the default locale
188193
*
189-
* @since 6.1
194+
* @since 6.1.1
190195
*/
191-
public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, Set<Locale> localesToInitialize) {
192-
this( userResourceBundleLocator, null, localesToInitialize );
196+
@Incubating
197+
public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator, Set<Locale> localesToInitialize, Locale defaultLocale) {
198+
this( userResourceBundleLocator, null, localesToInitialize, defaultLocale );
193199
}
194200

195201
/**
196202
* {@code MessageInterpolator} taking two resource bundle locators.
197203
*
198204
* @param userResourceBundleLocator {@code ResourceBundleLocator} used to load user provided resource bundle
199205
* @param contributorResourceBundleLocator {@code ResourceBundleLocator} used to load resource bundle of constraint contributor
200-
* @param localesToInitialize The set of locales to initialize at bootstrap.
206+
* @param localesToInitialize the set of locales to initialize at bootstrap
207+
* @param defaultLocale the default locale
201208
*
202-
* @since 6.1
209+
* @since 6.1.1
203210
*/
211+
@Incubating
204212
public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator,
205-
ResourceBundleLocator contributorResourceBundleLocator, Set<Locale> localesToInitialize) {
206-
this( userResourceBundleLocator, contributorResourceBundleLocator, localesToInitialize, true );
213+
ResourceBundleLocator contributorResourceBundleLocator, Set<Locale> localesToInitialize,
214+
Locale defaultLocale) {
215+
this( userResourceBundleLocator, contributorResourceBundleLocator, localesToInitialize, defaultLocale, true );
207216
}
208217

209218
/**
210219
* {@code MessageInterpolator} taking two resource bundle locators.
211220
*
212221
* @param userResourceBundleLocator {@code ResourceBundleLocator} used to load user provided resource bundle
213222
* @param contributorResourceBundleLocator {@code ResourceBundleLocator} used to load resource bundle of constraint contributor
214-
* @param localesToInitialize The set of locales to initialize at bootstrap.
215-
* @param cacheMessages Whether resolved messages should be cached or not.
223+
* @param localesToInitialize the set of locales to initialize at bootstrap
224+
* @param defaultLocale the default locale
225+
* @param cacheMessages whether resolved messages should be cached or not
216226
*
217-
* @since 6.1
227+
* @since 6.1.1
218228
*/
229+
@Incubating
219230
public AbstractMessageInterpolator(ResourceBundleLocator userResourceBundleLocator,
220231
ResourceBundleLocator contributorResourceBundleLocator,
221232
Set<Locale> localesToInitialize,
233+
Locale defaultLocale,
222234
boolean cacheMessages) {
223-
defaultLocale = Locale.getDefault();
235+
Contracts.assertNotNull( localesToInitialize, MESSAGES.parameterMustNotBeNull( "localesToInitialize" ) );
236+
Contracts.assertNotNull( defaultLocale, MESSAGES.parameterMustNotBeNull( "defaultLocale" ) );
237+
238+
this.defaultLocale = defaultLocale;
224239

225240
if ( userResourceBundleLocator == null ) {
226241
this.userResourceBundleLocator = new PlatformResourceBundleLocator( USER_VALIDATION_MESSAGES, localesToInitialize );
@@ -297,7 +312,7 @@ public String interpolate(String message, Context context, Locale locale) {
297312
try {
298313
interpolatedMessage = interpolateMessage( message, context, locale );
299314
}
300-
catch (ValidationException e) {
315+
catch (MessageDescriptorFormatException e) {
301316
LOG.warn( e.getMessage() );
302317
}
303318
return interpolatedMessage;

engine/src/main/java/org/hibernate/validator/messageinterpolation/ParameterMessageInterpolator.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.Locale;
1212
import java.util.Set;
1313

14+
import org.hibernate.validator.Incubating;
1415
import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTerm;
1516
import org.hibernate.validator.internal.engine.messageinterpolation.ParameterTermResolver;
1617
import org.hibernate.validator.internal.util.logging.Log;
@@ -28,11 +29,15 @@ public class ParameterMessageInterpolator extends AbstractMessageInterpolator {
2829
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
2930

3031
public ParameterMessageInterpolator() {
31-
this( Collections.emptySet() );
32+
this( Collections.emptySet(), Locale.getDefault() );
3233
}
3334

34-
public ParameterMessageInterpolator(Set<Locale> localesToInitialize) {
35-
super( localesToInitialize );
35+
/**
36+
* @since 6.1.1
37+
*/
38+
@Incubating
39+
public ParameterMessageInterpolator(Set<Locale> localesToInitialize, Locale defaultLocale) {
40+
super( localesToInitialize, defaultLocale );
3641
}
3742

3843
@Override

0 commit comments

Comments
 (0)