Skip to content

Commit d6bee29

Browse files
committed
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 the customization without needing to resort to subclassing the locale resolvers.
1 parent 60f7560 commit d6bee29

File tree

4 files changed

+147
-22
lines changed

4 files changed

+147
-22
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 the {@link CookieLocaleResolver} class
@@ -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

@@ -334,13 +374,11 @@ protected String toLocaleValue(Locale locale) {
334374
* @return the default locale (never {@code null})
335375
* @see #setDefaultLocale
336376
* @see jakarta.servlet.http.HttpServletRequest#getLocale()
377+
* @deprecated as of 6.0, in favor of {@link #setDefaultLocaleFunction(Function)}
337378
*/
379+
@Deprecated
338380
protected Locale determineDefaultLocale(HttpServletRequest request) {
339-
Locale defaultLocale = getDefaultLocale();
340-
if (defaultLocale == null) {
341-
defaultLocale = request.getLocale();
342-
}
343-
return defaultLocale;
381+
return this.defaultLocaleFunction.apply(request);
344382
}
345383

346384
/**
@@ -351,10 +389,12 @@ protected Locale determineDefaultLocale(HttpServletRequest request) {
351389
* @param request the request to resolve the time zone for
352390
* @return the default time zone (or {@code null} if none defined)
353391
* @see #setDefaultTimeZone
392+
* @deprecated as of 6.0, in favor of {@link #setDefaultTimeZoneFunction(Function)}
354393
*/
394+
@Deprecated
355395
@Nullable
356396
protected TimeZone determineDefaultTimeZone(HttpServletRequest request) {
357-
return getDefaultTimeZone();
397+
return this.defaultTimeZoneFunction.apply(request);
358398
}
359399

360400
}

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

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -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
}
@@ -165,13 +205,11 @@ public void setLocaleContext(HttpServletRequest request, @Nullable HttpServletRe
165205
* @return the default locale (never {@code null})
166206
* @see #setDefaultLocale
167207
* @see jakarta.servlet.http.HttpServletRequest#getLocale()
208+
* @deprecated as of 6.0, in favor of {@link #setDefaultLocaleFunction(Function)}
168209
*/
210+
@Deprecated
169211
protected Locale determineDefaultLocale(HttpServletRequest request) {
170-
Locale defaultLocale = getDefaultLocale();
171-
if (defaultLocale == null) {
172-
defaultLocale = request.getLocale();
173-
}
174-
return defaultLocale;
212+
return this.defaultLocaleFunction.apply(request);
175213
}
176214

177215
/**
@@ -182,10 +220,12 @@ protected Locale determineDefaultLocale(HttpServletRequest request) {
182220
* @param request the request to resolve the time zone for
183221
* @return the default time zone (or {@code null} if none defined)
184222
* @see #setDefaultTimeZone
223+
* @deprecated as of 6.0, in favor of {@link #setDefaultTimeZoneFunction(Function)}
185224
*/
225+
@Deprecated
186226
@Nullable
187227
protected TimeZone determineDefaultTimeZone(HttpServletRequest request) {
188-
return getDefaultTimeZone();
228+
return this.defaultTimeZoneFunction.apply(request);
189229
}
190230

191231
}

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)