一、简单国际化
1、操作步骤
(1)编写国际化资源文件;
(2)让 SpringMVC 的 ResourceBundleMessageSource 管理国际化资源文件;
(3)在页面中通过标签取值;
2、代码示例
(1)国际化资源文件
中文:login_zh_CN.properties
welcomeInfo=欢迎来到登录页面
username=用户名
password=密码
loginBtn=登录
英文:login_en_US.properties
welcomeInfo=welcom to login
username=USERNAME
password=PASSWORD
loginBtn=LOGIN
(2)配置文件
<!-- 让SpringMVC管理国际化 id 必须是 messageSource-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="login"/>
</bean>
(3)控制器跳转
@RequestMapping(value = "/toLoginPage")
public String toLoginPage() {
return "login";
}
(4)页面取值
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<h1>
<fmt:message key="welcomeInfo" />
</h1>
<form action="" method="post">
<fmt:message key="username" />:<input /><br>
<fmt:message key="password" />:<input /><br>
<input type="submit" value='<fmt:message key="loginBtn" />'/>
</form>
</body>
</html>
二、区域信息解析器
国际化的区域信息是决定国际化显示的因素。
SpringMVC 中区域信息是又是区域信息解析器得到的:
DispatcherServlet 中:
/** LocaleResolver used by this servlet */
private LocaleResolver localeResolver;
现象:区域是按照浏览器带来语言信息决定
Locale locale = request.getLocale(); //获取到浏览器的区域信息
来看 LocaleResolver 的装配:
/**
* Initialize the LocaleResolver used by this class.
* <p>If no bean is defined with the given name in the BeanFactory for this namespace,
* we default to AcceptHeaderLocaleResolver.
*/
private void initLocaleResolver(ApplicationContext context) {
try {
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);//加载默认的策略
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver + "]");
}
}
}
如果配置文件中没有配置区域解析器,会加载 DispatcherServlet.properties 中的区域解析器:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
所有用到区域信息的地方,默认都会用AcceptHeaderLocaleResolver获取的:
public class AcceptHeaderLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
return request.getLocale();
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
throw new UnsupportedOperationException(
"Cannot change HTTP accept header - use a different locale resolution strategy");
}
}
等到其他地方再用到区域解析器的时候,直接获取:
Locale locale = this.localeResolver.resolveLocale(request);
三、程序中获取国际化信息
SpringMVC 使用 MessageSource 帮我们管理了国际化配置,可以通过注入 messageSource 来获取国际化信息
@Controller
public class I18nTestController {
@Autowired
private ResourceBundleMessageSource messageSource;
@RequestMapping(value = "/toLoginPage")
public String toLoginPage(Locale locale) {
System.out.println("locale = " + locale);
String key = "welcomeInfo";
//第一个参数:获取的key;第二个参数:国际化文件中动态参数;第三个参数:Locale 区域信息
String message = messageSource.getMessage(key, null, locale);
System.out.println("message = " + message);
return "login";
}
}
四、动态切换国际化——自定义区域解析器
我们可以通过自定义区域解析器来替换默认的 AcceptHeaderLocaleResolver 区域解析器:
(1)页面发送请求及请求参数
<a href="${ctp}/toLoginPage?locale=zh_CN">中文</a>|<a href="${ctp}/toLoginPage?locale=en_US">English</a>
(2)自定义区域解析器
public class MyLocaleResolver implements LocaleResolver {
/**
* 解析返回 Locale,如果带了请求参数 locale,就获取参数的信息,否则就获取请求的区域信息
* @param request
* @return
*/
@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = null;
String localeStr = request.getParameter("locale");
System.out.println("localeStr = " + localeStr);
if (localeStr != null && !"".equals(localeStr)) {
locale = new Locale(localeStr.split("_")[0], localeStr.split("_")[1]);
} else {
locale = request.getLocale();
}
return locale;
}
/**
* 修改 Locale
* @param request
* @param response
* @param locale
*/
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
throw new UnsupportedOperationException(
"Cannot change HTTP accept header - use a different locale resolution strategy");
}
}
(3)配置区域解析器
<!-- 自定义区域信息解析器 -->
<bean id="localeResolver" class="com.njf.component.MyLocalResolver"></bean>
(4)控制器
@Controller
public class I18nTestController {
@Autowired
private ResourceBundleMessageSource messageSource;
@RequestMapping(value = "/toLoginPage")
public String toLoginPage(Locale locale) {
System.out.println("locale = " + locale);
String key = "welcomeInfo";
String message = messageSource.getMessage(key, null, locale);
System.out.println("message = " + message);
return "login";
}
}
(5)页面取值
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<h1>
<fmt:message key="welcomeInfo" />
</h1>
<form action="" method="post">
<fmt:message key="username" />:<input /><br>
<fmt:message key="password" />:<input /><br>
<input type="submit" value='<fmt:message key="loginBtn" />'/>
</form>
</body>
</html>
五、各种区域解析器
区域解析器继承树:
(1)AcceptHeaderLocaleResolver:使用请求头的区域信息
public class AcceptHeaderLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
return request.getLocale();
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
throw new UnsupportedOperationException(
"Cannot change HTTP accept header - use a different locale resolution strategy");
}
}
(2)MyLocaleResolver:自定义区域解析器
(3)SessionLocalResolver:区域信息是从 session 中获取,可以根据请求参数创建一个 locale 对象,放在 session 中;
public class SessionLocaleResolver extends AbstractLocaleContextResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = (Locale) WebUtils.getSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME);
if (locale == null) {
locale = determineDefaultLocale(request);
}
return locale;
}
@Override
public void setLocaleContext(HttpServletRequest request, HttpServletResponse response, LocaleContext localeContext) {
Locale locale = null;
TimeZone timeZone = null;
if (localeContext != null) {
locale = localeContext.getLocale();
if (localeContext instanceof TimeZoneAwareLocaleContext) {
timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone();
}
}
WebUtils.setSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME, locale);
WebUtils.setSessionAttribute(request, TIME_ZONE_SESSION_ATTRIBUTE_NAME, timeZone);
}
}
(4)FixedLocalResolver:获取本地固定的区域信息,使用系统默认的区域信息
public class FixedLocaleResolver extends AbstractLocaleContextResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = getDefaultLocale();
if (locale == null) {
locale = Locale.getDefault();
}
return locale;
}
@Override
public LocaleContext resolveLocaleContext(HttpServletRequest request) {
return new TimeZoneAwareLocaleContext() {
@Override
public Locale getLocale() {
return getDefaultLocale();
}
@Override
public TimeZone getTimeZone() {
return getDefaultTimeZone();
}
};
}
@Override
public void setLocaleContext(HttpServletRequest request, HttpServletResponse response, LocaleContext localeContext) {
throw new UnsupportedOperationException("Cannot change fixed locale - use a different locale resolution strategy");
}
}
(5)CookieLocaleResolver:区域信息是从 cookie中获取
public class CookieLocaleResolver extends CookieGenerator implements LocaleContextResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
parseLocaleCookieIfNecessary(request);
return (Locale) request.getAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME);
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
setLocaleContext(request, response, (locale != null ? new SimpleLocaleContext(locale) : null));
}
}
六、动态切换国际化——使用 SessionLocaleResolver
1、配置 SessionLocaleResolver
<!-- 区域信息从 session 中获取 -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>
2、控制器方法
从请求参数中获取 locale 参数,然后把区域信息手动放到 session 域中:
@RequestMapping(value = "/toLoginPage")
public String toLoginPage(@RequestParam(value = "locale", defaultValue = "zh_CN") String localeStr, Locale locale, HttpSession session) {
System.out.println("locale = " + locale);
String key = "welcomeInfo";
String message = messageSource.getMessage(key, null, locale);
System.out.println("message = " + message);
Locale localeInfo = null;
System.out.println("localeStr = " + localeStr);
if (localeStr != null && !"".equals(localeStr)) {
localeInfo = new Locale(localeStr.split("_")[0], localeStr.split("_")[1]);
} else {
localeInfo = locale;
}
//把区域信息存在session中
session.setAttribute(SessionLocaleResolver.class.getName() + ".LOCALE", localeInfo);
return "login";
}
七、动态切换国际化——使用SessionLocaleResolver 与LocaleChangeInterceptor 拦截器
1、配置拦截器(需要配置 SessionLocaleResolver 和 LocaleChangeInterceptor )
<!-- 区域信息从 session 中获取 -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>
<!-- 配置国际化拦截器 -->
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
</mvc:interceptors>
2、控制器方法
@RequestMapping(value = "/toLoginPage")
public String toLoginPage(@RequestParam(value = "locale", defaultValue = "zh_CN") String localeStr, Locale locale, HttpSession session) {
System.out.println("locale = " + locale);
String key = "welcomeInfo";
String message = messageSource.getMessage(key, null, locale);
System.out.println("message = " + message);
return "login";
}
3、LocaleChangeInterceptor 源码
public class LocaleChangeInterceptor extends HandlerInterceptorAdapter {
/**
* Default name of the locale specification parameter: "locale".
*/
public static final String DEFAULT_PARAM_NAME = "locale";
private String paramName = DEFAULT_PARAM_NAME;
/**
* Set the name of the parameter that contains a locale specification
* in a locale change request. Default is "locale".
*/
public void setParamName(String paramName) {
this.paramName = paramName;
}
/**
* Return the name of the parameter that contains a locale specification
* in a locale change request.
*/
public String getParamName() {
return this.paramName;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws ServletException {
String newLocale = request.getParameter(this.paramName);
if (newLocale != null) {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
}
localeResolver.setLocale(request, response, StringUtils.parseLocaleString(newLocale));
}
// Proceed in any case.
return true;
}
}
拦截器会从请求中获取 locale 参数,然后把区域化信息放在 session 域中。
注意:使用拦截器切换语言的参数名称必须:locale
八、总结
1、默认情况下,SpringMVC 根据 Accept-Language 参数判断客户端的本地化类型。
2、当接受到请求时,SpringMVC 会在上下文中查找一个本地化解析器(LocalResolver),找到后使用它获取请求所对应的本地化类型信息。
3、SpringMVC 还允许装配一个动态更改本地化类型的拦截器,这样通过指定一个请求参数就可以控制单个请求的本地化类型。
4、SessionLocaleResolver & LocaleChangeInterceptor 工作原理
5、本地化解析器和本地拦截器
AcceptHeaderLocaleResolver:根据 HTTP 请求头的 Accept-Language 参数确定本地化类型,如果没有显式定义本地化解析器, SpringMVC 使用该解析器。
CookieLocaleResolver:根据指定的 Cookie 值确定本地化类型
SessionLocaleResolver:根据 Session 中特定的属性确定本地化类型
LocaleChangeInterceptor:从请求参数中获取本次请求对应的本地化类型。