LocaleResolver

LocaleResolver 是一个解决国际化的策略接口。它包含两个方法:

  • resolveLocale
  • setLocale
1
2
3
4
5
6
7
public interface LocaleResolver {

Locale resolveLocale(HttpServletRequest request);

void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);

}

类图

AcceptHeaderLocaleResolver

SpringMVC 默认使用的是:AcceptHeaderLocaleResolver 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* LocaleResolver 接口的实现类
* 简单地使用 HTTP 请求头里的 Accept-Language 来指定 Locale对象(即客户端浏览器发送的语言环境,通常是客户端的操作系统)
*
* 注意:不支持 setLocale 方法,因为只能通过更改客户端的区域设置来更改 Accept-Language 请求头
*/
public class AcceptHeaderLocaleResolver implements LocaleResolver {

private final List<Locale> supportedLocales = new ArrayList<>(4);

@Nullable
private Locale defaultLocale;

/**
* 配置支持的区域设置列表
*/
public void setSupportedLocales(@Nullable List<Locale> locales) {
this.supportedLocales.clear();
if (locales != null) {
this.supportedLocales.addAll(locales);
}
}

/**
* 返回配置的支持的区域设置列表
*/
public List<Locale> getSupportedLocales() {
return this.supportedLocales;
}

/**
* 如果 HTTP 请求头没有 Accept-Language,则使用该默认的语言环境设置
*/
public void setDefaultLocale(@Nullable Locale defaultLocale) {
this.defaultLocale = defaultLocale;
}

/**
* 返回默认配置的语言环境(如果有)
*/
@Nullable
public Locale getDefaultLocale() {
return this.defaultLocale;
}

@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale defaultLocale = getDefaultLocale();
if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
return defaultLocale;
}
Locale requestLocale = request.getLocale();
if (isSupportedLocale(requestLocale)) {
return requestLocale;
}
Locale supportedLocale = findSupportedLocale(request);
if (supportedLocale != null) {
return supportedLocale;
}
return (defaultLocale != null ? defaultLocale : requestLocale);
}

private boolean isSupportedLocale(Locale locale) {
List<Locale> supportedLocales = getSupportedLocales();
return (supportedLocales.isEmpty() || supportedLocales.contains(locale));
}

@Nullable
private Locale findSupportedLocale(HttpServletRequest request) {
Enumeration<Locale> requestLocales = request.getLocales();
while (requestLocales.hasMoreElements()) {
Locale locale = requestLocales.nextElement();
if (getSupportedLocales().contains(locale)) {
return locale;
}
}
return null;
}

@Override
public void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) {
throw new UnsupportedOperationException(
"Cannot change HTTP accept header - use a different locale resolution strategy");
}

}
  • 处理流程

    • 如果 HTTP 请求头里不含有 Accept-Language ,并且默认的语言环境 defaultLocale 不为空,则使用默认的语言环境。否则,从请求里获得 Locale 。
    • 而一般的我们没有配置 supportedLocales 与defaultLocale 属性(需要配置注入),所以 AcceptHeaderLocaleResolver 使用Accept-Language 来构造 Locale 对象。

AbstractLocaleResolver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 实现 LocaleResolver 接口的抽象基类
* 提供对默认语言环境的支持
*/
public abstract class AbstractLocaleResolver implements LocaleResolver {

@Nullable
private Locale defaultLocale;
public void setDefaultLocale(@Nullable Locale defaultLocale) {
this.defaultLocale = defaultLocale;
}

@Nullable
protected Locale getDefaultLocale() {
return this.defaultLocale;
}

}

LocaleContextResolver

1
2
3
4
5
6
7
8
public interface LocaleContextResolver extends LocaleResolver {

LocaleContext resolveLocaleContext(HttpServletRequest request);

void setLocaleContext(HttpServletRequest request, @Nullable HttpServletResponse response,
@Nullable LocaleContext localeContext);

}

AbstractLocaleContextResolver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* LocaleContextResolver 实现的抽象基类:提供对默认语言环境和默认时区的支持
* 还提供了 resolveLocale 和 setLocale 的预实现版本,委托给 resolveLocaleContext 和 setLocaleContext
*/
public abstract class AbstractLocaleContextResolver extends AbstractLocaleResolver implements LocaleContextResolver {

@Nullable
private TimeZone defaultTimeZone;
public void setDefaultTimeZone(@Nullable TimeZone defaultTimeZone) {
this.defaultTimeZone = defaultTimeZone;
}

@Nullable
public TimeZone getDefaultTimeZone() {
return this.defaultTimeZone;
}

@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = resolveLocaleContext(request).getLocale();
return (locale != null ? locale : request.getLocale());
}

@Override
public void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) {
setLocaleContext(request, response, (locale != null ? new SimpleLocaleContext(locale) : null));
}

}

SessionLocaleResolver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
public class SessionLocaleResolver extends AbstractLocaleContextResolver {

public static final String LOCALE_SESSION_ATTRIBUTE_NAME = SessionLocaleResolver.class.getName() + ".LOCALE";
public static final String TIME_ZONE_SESSION_ATTRIBUTE_NAME = SessionLocaleResolver.class.getName() + ".TIME_ZONE";
private String localeAttributeName = LOCALE_SESSION_ATTRIBUTE_NAME;
private String timeZoneAttributeName = TIME_ZONE_SESSION_ATTRIBUTE_NAME;

/**
* 在 HttpSession 中指定相应属性的名称,保存当前的 Locale 值
* 默认值为 LOCALE_SESSION_ATTRIBUTE_NAME
*/
public void setLocaleAttributeName(String localeAttributeName) {
this.localeAttributeName = localeAttributeName;
}

/**
* 在 HttpSession 中指定相应属性的名称,保存当前的 TimeZone 值
* 默认值为 TIME_ZONE_SESSION_ATTRIBUTE_NAME
*/
public void setTimeZoneAttributeName(String timeZoneAttributeName) {
this.timeZoneAttributeName = timeZoneAttributeName;
}

@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = (Locale) WebUtils.getSessionAttribute(request, this.localeAttributeName);
if (locale == null) {
locale = determineDefaultLocale(request);
}
return locale;
}

@Override
public LocaleContext resolveLocaleContext(final HttpServletRequest request) {
return new TimeZoneAwareLocaleContext() {

@Override
public Locale getLocale() {
Locale locale = (Locale) WebUtils.getSessionAttribute(request, localeAttributeName);
if (locale == null) {
locale = determineDefaultLocale(request);
}
return locale;
}

@Override
@Nullable
public TimeZone getTimeZone() {
TimeZone timeZone = (TimeZone) WebUtils.getSessionAttribute(request, timeZoneAttributeName);
if (timeZone == null) {
timeZone = determineDefaultTimeZone(request);
}
return timeZone;
}

};
}

@Override
public void setLocaleContext(HttpServletRequest request, @Nullable HttpServletResponse response,
@Nullable LocaleContext localeContext) {

Locale locale = null;
TimeZone timeZone = null;
if (localeContext != null) {
locale = localeContext.getLocale();
if (localeContext instanceof TimeZoneAwareLocaleContext) {
timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone();
}
}
// 将 Locale 与 TimeZone 保存到 HttpSession
WebUtils.setSessionAttribute(request, this.localeAttributeName, locale);
WebUtils.setSessionAttribute(request, this.timeZoneAttributeName, timeZone);
}


/**
* 确定给定请求的默认语言环境,如果没有找到 Locale 会话属性,则调用
* 默认实现返回指定的默认语言环境(如果有的话)返回到请求的Accept-Header 语言环境
*/
protected Locale determineDefaultLocale(HttpServletRequest request) {
Locale defaultLocale = getDefaultLocale();
if (defaultLocale == null) {
defaultLocale = request.getLocale();
}
return defaultLocale;
}

/**
* 确定给定请求的默认时区,如果未找到TimeZone会话属性,则调用
* 默认实现返回指定的默认时区(如果有),否则返回null
*/
@Nullable
protected TimeZone determineDefaultTimeZone(HttpServletRequest request) {
return getDefaultTimeZone();
}

}