Skip to content

Commit 864dcf6

Browse files
vpavicsbrannen
authored andcommitted
Simplify default locale/timezone resolution in cookie/session locale resolvers
At present, the customization of the default locale and timezone resolution in CookieLocaleResolver and SessionLocaleResolver requires subclassing them and overriding determineDefaultLocale and/or determineDefaultTimeZone methods. This commit simplifies resolution of the default locale and timezone resolution by introducing dedicated functions for these purposes, thus allowing customization without needing to resort to subclassing the locale resolvers. Closes gh-27609
1 parent 94cc2da commit 864dcf6

File tree

4 files changed

+146
-21
lines changed

4 files changed

+146
-21
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/i18n/CookieLocaleResolver.java

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.Locale;
2020
import java.util.TimeZone;
21+
import java.util.function.Function;
2122

2223
import jakarta.servlet.http.Cookie;
2324
import jakarta.servlet.http.HttpServletRequest;
@@ -50,6 +51,7 @@
5051
*
5152
* @author Juergen Hoeller
5253
* @author Jean-Pierre Pawlak
54+
* @author Vedran Pavic
5355
* @since 27.02.2003
5456
* @see #setDefaultLocale
5557
* @see #setDefaultTimeZone
@@ -94,6 +96,15 @@ public class CookieLocaleResolver extends CookieGenerator implements LocaleConte
9496
@Nullable
9597
private TimeZone defaultTimeZone;
9698

99+
private Function<HttpServletRequest, Locale> defaultLocaleFunction = request -> {
100+
Locale defaultLocale = getDefaultLocale();
101+
if (defaultLocale == null) {
102+
defaultLocale = request.getLocale();
103+
}
104+
return defaultLocale;
105+
};
106+
107+
private Function<HttpServletRequest, TimeZone> defaultTimeZoneFunction = request -> getDefaultTimeZone();
97108

98109
/**
99110
* Create a new instance of {@link CookieLocaleResolver} using the
@@ -137,8 +148,8 @@ public boolean isLanguageTagCompliant() {
137148
* @since 5.1.7
138149
* @see #setDefaultLocale
139150
* @see #setDefaultTimeZone
140-
* @see #determineDefaultLocale
141-
* @see #determineDefaultTimeZone
151+
* @see #setDefaultLocaleFunction(Function)
152+
* @see #setDefaultTimeZoneFunction(Function)
142153
*/
143154
public void setRejectInvalidCookies(boolean rejectInvalidCookies) {
144155
this.rejectInvalidCookies = rejectInvalidCookies;
@@ -186,6 +197,35 @@ protected TimeZone getDefaultTimeZone() {
186197
return this.defaultTimeZone;
187198
}
188199

200+
/**
201+
* Set the function used to determine the default locale for the given request,
202+
* called if no {@link Locale} session attribute has been found.
203+
* <p>The default implementation returns the specified default locale,
204+
* if any, else falls back to the request's accept-header locale.
205+
* @param defaultLocaleFunction the function used to determine the default locale
206+
* @since 6.0
207+
* @see #setDefaultLocale
208+
* @see jakarta.servlet.http.HttpServletRequest#getLocale()
209+
*/
210+
public void setDefaultLocaleFunction(Function<HttpServletRequest, Locale> defaultLocaleFunction) {
211+
Assert.notNull(defaultLocaleFunction, "defaultLocaleFunction must not be null");
212+
this.defaultLocaleFunction = defaultLocaleFunction;
213+
}
214+
215+
/**
216+
* Set the function used to determine the default time zone for the given request,
217+
* called if no {@link TimeZone} session attribute has been found.
218+
* <p>The default implementation returns the specified default time zone,
219+
* if any, or {@code null} otherwise.
220+
* @param defaultTimeZoneFunction the function used to determine the default time zone
221+
* @since 6.0
222+
* @see #setDefaultTimeZone
223+
*/
224+
public void setDefaultTimeZoneFunction(Function<HttpServletRequest, TimeZone> defaultTimeZoneFunction) {
225+
Assert.notNull(defaultTimeZoneFunction, "defaultTimeZoneFunction must not be null");
226+
this.defaultTimeZoneFunction = defaultTimeZoneFunction;
227+
}
228+
189229

190230
@Override
191231
public Locale resolveLocale(HttpServletRequest request) {
@@ -260,9 +300,9 @@ private void parseLocaleCookieIfNecessary(HttpServletRequest request) {
260300
}
261301

262302
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME,
263-
(locale != null ? locale : determineDefaultLocale(request)));
303+
(locale != null ? locale : this.defaultLocaleFunction.apply(request)));
264304
request.setAttribute(TIME_ZONE_REQUEST_ATTRIBUTE_NAME,
265-
(timeZone != null ? timeZone : determineDefaultTimeZone(request)));
305+
(timeZone != null ? timeZone : this.defaultTimeZoneFunction.apply(request)));
266306
}
267307
}
268308

@@ -291,9 +331,9 @@ public void setLocaleContext(HttpServletRequest request, @Nullable HttpServletRe
291331
removeCookie(response);
292332
}
293333
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME,
294-
(locale != null ? locale : determineDefaultLocale(request)));
334+
(locale != null ? locale : this.defaultLocaleFunction.apply(request)));
295335
request.setAttribute(TIME_ZONE_REQUEST_ATTRIBUTE_NAME,
296-
(timeZone != null ? timeZone : determineDefaultTimeZone(request)));
336+
(timeZone != null ? timeZone : this.defaultTimeZoneFunction.apply(request)));
297337
}
298338

299339

@@ -335,13 +375,11 @@ protected String toLocaleValue(Locale locale) {
335375
* @return the default locale (never {@code null})
336376
* @see #setDefaultLocale
337377
* @see jakarta.servlet.http.HttpServletRequest#getLocale()
378+
* @deprecated as of 6.0, in favor of {@link #setDefaultLocaleFunction(Function)}
338379
*/
380+
@Deprecated
339381
protected Locale determineDefaultLocale(HttpServletRequest request) {
340-
Locale defaultLocale = getDefaultLocale();
341-
if (defaultLocale == null) {
342-
defaultLocale = request.getLocale();
343-
}
344-
return defaultLocale;
382+
return this.defaultLocaleFunction.apply(request);
345383
}
346384

347385
/**
@@ -352,10 +390,12 @@ protected Locale determineDefaultLocale(HttpServletRequest request) {
352390
* @param request the request to resolve the time zone for
353391
* @return the default time zone (or {@code null} if none defined)
354392
* @see #setDefaultTimeZone
393+
* @deprecated as of 6.0, in favor of {@link #setDefaultTimeZoneFunction(Function)}
355394
*/
395+
@Deprecated
356396
@Nullable
357397
protected TimeZone determineDefaultTimeZone(HttpServletRequest request) {
358-
return getDefaultTimeZone();
398+
return this.defaultTimeZoneFunction.apply(request);
359399
}
360400

361401
}

spring-webmvc/src/main/java/org/springframework/web/servlet/i18n/SessionLocaleResolver.java

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818

1919
import java.util.Locale;
2020
import java.util.TimeZone;
21+
import java.util.function.Function;
2122

2223
import jakarta.servlet.http.HttpServletRequest;
2324
import jakarta.servlet.http.HttpServletResponse;
2425

2526
import org.springframework.context.i18n.LocaleContext;
2627
import org.springframework.context.i18n.TimeZoneAwareLocaleContext;
2728
import org.springframework.lang.Nullable;
29+
import org.springframework.util.Assert;
2830
import org.springframework.web.util.WebUtils;
2931

3032
/**
@@ -54,6 +56,7 @@
5456
* against the current {@code HttpServletRequest}.
5557
*
5658
* @author Juergen Hoeller
59+
* @author Vedran Pavic
5760
* @since 27.02.2003
5861
* @see #setDefaultLocale
5962
* @see #setDefaultTimeZone
@@ -85,6 +88,15 @@ public class SessionLocaleResolver extends AbstractLocaleContextResolver {
8588

8689
private String timeZoneAttributeName = TIME_ZONE_SESSION_ATTRIBUTE_NAME;
8790

91+
private Function<HttpServletRequest, Locale> defaultLocaleFunction = request -> {
92+
Locale defaultLocale = getDefaultLocale();
93+
if (defaultLocale == null) {
94+
defaultLocale = request.getLocale();
95+
}
96+
return defaultLocale;
97+
};
98+
99+
private Function<HttpServletRequest, TimeZone> defaultTimeZoneFunction = request -> getDefaultTimeZone();
88100

89101
/**
90102
* Specify the name of the corresponding attribute in the {@code HttpSession},
@@ -106,12 +118,40 @@ public void setTimeZoneAttributeName(String timeZoneAttributeName) {
106118
this.timeZoneAttributeName = timeZoneAttributeName;
107119
}
108120

121+
/**
122+
* Set the function used to determine the default locale for the given request,
123+
* called if no {@link Locale} session attribute has been found.
124+
* <p>The default implementation returns the specified default locale,
125+
* if any, else falls back to the request's accept-header locale.
126+
* @param defaultLocaleFunction the function used to determine the default locale
127+
* @since 6.0
128+
* @see #setDefaultLocale
129+
* @see jakarta.servlet.http.HttpServletRequest#getLocale()
130+
*/
131+
public void setDefaultLocaleFunction(Function<HttpServletRequest, Locale> defaultLocaleFunction) {
132+
Assert.notNull(defaultLocaleFunction, "defaultLocaleFunction must not be null");
133+
this.defaultLocaleFunction = defaultLocaleFunction;
134+
}
135+
136+
/**
137+
* Set the function used to determine the default time zone for the given request,
138+
* called if no {@link TimeZone} session attribute has been found.
139+
* <p>The default implementation returns the specified default time zone,
140+
* if any, or {@code null} otherwise.
141+
* @param defaultTimeZoneFunction the function used to determine the default time zone
142+
* @since 6.0
143+
* @see #setDefaultTimeZone
144+
*/
145+
public void setDefaultTimeZoneFunction(Function<HttpServletRequest, TimeZone> defaultTimeZoneFunction) {
146+
Assert.notNull(defaultTimeZoneFunction, "defaultTimeZoneFunction must not be null");
147+
this.defaultTimeZoneFunction = defaultTimeZoneFunction;
148+
}
109149

110150
@Override
111151
public Locale resolveLocale(HttpServletRequest request) {
112152
Locale locale = (Locale) WebUtils.getSessionAttribute(request, this.localeAttributeName);
113153
if (locale == null) {
114-
locale = determineDefaultLocale(request);
154+
locale = this.defaultLocaleFunction.apply(request);
115155
}
116156
return locale;
117157
}
@@ -123,7 +163,7 @@ public LocaleContext resolveLocaleContext(final HttpServletRequest request) {
123163
public Locale getLocale() {
124164
Locale locale = (Locale) WebUtils.getSessionAttribute(request, localeAttributeName);
125165
if (locale == null) {
126-
locale = determineDefaultLocale(request);
166+
locale = SessionLocaleResolver.this.defaultLocaleFunction.apply(request);
127167
}
128168
return locale;
129169
}
@@ -132,7 +172,7 @@ public Locale getLocale() {
132172
public TimeZone getTimeZone() {
133173
TimeZone timeZone = (TimeZone) WebUtils.getSessionAttribute(request, timeZoneAttributeName);
134174
if (timeZone == null) {
135-
timeZone = determineDefaultTimeZone(request);
175+
timeZone = SessionLocaleResolver.this.defaultTimeZoneFunction.apply(request);
136176
}
137177
return timeZone;
138178
}
@@ -167,13 +207,11 @@ public void setLocaleContext(HttpServletRequest request, @Nullable HttpServletRe
167207
* @return the default locale (never {@code null})
168208
* @see #setDefaultLocale
169209
* @see jakarta.servlet.http.HttpServletRequest#getLocale()
210+
* @deprecated as of 6.0, in favor of {@link #setDefaultLocaleFunction(Function)}
170211
*/
212+
@Deprecated
171213
protected Locale determineDefaultLocale(HttpServletRequest request) {
172-
Locale defaultLocale = getDefaultLocale();
173-
if (defaultLocale == null) {
174-
defaultLocale = request.getLocale();
175-
}
176-
return defaultLocale;
214+
return this.defaultLocaleFunction.apply(request);
177215
}
178216

179217
/**
@@ -184,10 +222,12 @@ protected Locale determineDefaultLocale(HttpServletRequest request) {
184222
* @param request the request to resolve the time zone for
185223
* @return the default time zone (or {@code null} if none defined)
186224
* @see #setDefaultTimeZone
225+
* @deprecated as of 6.0, in favor of {@link #setDefaultTimeZoneFunction(Function)}
187226
*/
227+
@Deprecated
188228
@Nullable
189229
protected TimeZone determineDefaultTimeZone(HttpServletRequest request) {
190-
return getDefaultTimeZone();
230+
return this.defaultTimeZoneFunction.apply(request);
191231
}
192232

193233
}

spring-webmvc/src/test/java/org/springframework/web/servlet/i18n/CookieLocaleResolverTests.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
* @author Juergen Hoeller
4242
* @author Rick Evans
4343
* @author Sam Brannen
44+
* @author Vedran Pavic
4445
*/
4546
class CookieLocaleResolverTests {
4647

@@ -410,4 +411,24 @@ void setLocaleContextToNullWithDefault() {
410411
assertThat(localeCookie.getValue()).isEqualTo("");
411412
}
412413

414+
@Test
415+
void testCustomDefaultLocaleFunction() {
416+
request.addPreferredLocale(Locale.TAIWAN);
417+
418+
resolver.setDefaultLocaleFunction(request -> Locale.GERMAN);
419+
420+
assertThat(resolver.resolveLocale(request)).isEqualTo(Locale.GERMAN);
421+
}
422+
423+
@Test
424+
void testCustomDefaultTimeZoneFunction() {
425+
request.addPreferredLocale(Locale.TAIWAN);
426+
427+
resolver.setDefaultTimeZoneFunction(request -> TimeZone.getTimeZone("GMT+1"));
428+
429+
TimeZoneAwareLocaleContext context = (TimeZoneAwareLocaleContext) resolver.resolveLocaleContext(request);
430+
assertThat(context.getLocale()).isEqualTo(Locale.TAIWAN);
431+
assertThat(context.getTimeZone()).isEqualTo(TimeZone.getTimeZone("GMT+1"));
432+
}
433+
413434
}

spring-webmvc/src/test/java/org/springframework/web/servlet/i18n/SessionLocaleResolverTests.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
package org.springframework.web.servlet.i18n;
1818

1919
import java.util.Locale;
20+
import java.util.TimeZone;
2021

2122
import jakarta.servlet.http.HttpSession;
2223
import org.junit.jupiter.api.Test;
2324

25+
import org.springframework.context.i18n.TimeZoneAwareLocaleContext;
2426
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
2527
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
2628

@@ -31,6 +33,7 @@
3133
*
3234
* @author Juergen Hoeller
3335
* @author Sam Brannen
36+
* @author Vedran Pavic
3437
*/
3538
class SessionLocaleResolverTests {
3639

@@ -94,4 +97,25 @@ void setLocaleToNullLocale() throws Exception {
9497
assertThat(resolver.resolveLocale(request)).isEqualTo(Locale.TAIWAN);
9598
}
9699

100+
@Test
101+
void testCustomDefaultLocaleFunction() {
102+
request.addPreferredLocale(Locale.TAIWAN);
103+
104+
SessionLocaleResolver resolver = new SessionLocaleResolver();
105+
resolver.setDefaultLocaleFunction(request -> Locale.GERMAN);
106+
107+
assertThat(resolver.resolveLocale(request)).isEqualTo(Locale.GERMAN);
108+
}
109+
110+
@Test
111+
void testCustomDefaultTimeZoneFunction() {
112+
request.addPreferredLocale(Locale.TAIWAN);
113+
114+
resolver.setDefaultTimeZoneFunction(request -> TimeZone.getTimeZone("GMT+1"));
115+
116+
TimeZoneAwareLocaleContext context = (TimeZoneAwareLocaleContext) resolver.resolveLocaleContext(request);
117+
assertThat(context.getLocale()).isEqualTo(Locale.TAIWAN);
118+
assertThat(context.getTimeZone()).isEqualTo(TimeZone.getTimeZone("GMT+1"));
119+
}
120+
97121
}

0 commit comments

Comments
 (0)