Spring 国际化
之前在做spring国际化的时候,资源文件都是写在properties里面,管理起来不是很方便,今天我将这些资源文件都保存在数据库中,启动的时候进行初始化操作,从而进行多语言的管理。这里记录下过程:
其他的操作还是跟之前一样,这里就不多解释了,直接贴上对应的配置或者代码信息。使用spring框架,我这里用了两个配置文件,一个是springmvc.xm,另一个是spring-framework.xml文件,主要是想分开进行配置。
国际化配置在spring-webmvc这个jar的org.springframework.web.servlet.i18n包下面,这里我采用SessionLocaleResolver来对Locale进行处理,当前你还可以将Locale保存到Cookie中,spring为我们提供了几种实现。
配置如下:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
注意,这里一定要指定一个id,不然spring会去找他默认的,而不是你这个bean。
我们拦截用户的请求,如果用户变更了locale,那么我们也需要对应修改session中的locale数据,这里需要配置一个bean来拦截用户的请求,org.springframework.web.servlet.i18n.LocaleChangeInterceptor
<mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <property name="paramName" value="Lang"/> </bean> </mvc:interceptors>
这样当用户在请求的时候,如果添加了参数Lang=xxx ,那么这个bean就会自动帮我们切换locale信息,从而改变当前的语言信息。我这里为了控制我指定的几种语言,所以我重新写了一个拦截器,内容跟它差不多,只是多了一个判断信息:
package com.jacksoft.iconsole.bootstrap;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.beans.propertyeditors.LocaleEditor;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.support.RequestContextUtils;
import com.jacksoft.iconsole.utils.LanguageType;
public class MyLocaleChangeInterceptor extends HandlerInterceptorAdapter{
/**
* define logger for this class
*/
private static Logger logger = Logger.getLogger(MyLocaleChangeInterceptor.class);
/**
* 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 Exception {
String newLocale = request.getParameter(this.paramName);
if (newLocale != null) {
if(!LanguageType.checkLanguageType(newLocale)){
if(logger.isDebugEnabled()){
logger.debug(" current parameter " + this.paramName + " is not correct,use last local to display language");
}
return true;
}
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
}
LocaleEditor localeEditor = new LocaleEditor();
localeEditor.setAsText(newLocale);
localeResolver.setLocale(request, response, (Locale) localeEditor.getValue());
}
// Proceed in any case.
return true;
}
}
LanguageType:
package com.jacksoft.iconsole.utils;
/**
* 多语言
*
*
* @Filename LanguageType.java
*
* @author Jack.Zhou
*
* @Date 2013-11-8
*
*/
public enum LanguageType {
en_US,zh_CN;
public static boolean checkLanguageType(String languageCode){
for(LanguageType lang : LanguageType.values()){
if(lang.name().equals(languageCode)){
return true;
}
}
return false;
}
}
这样就可以指定语言的种类了,如果输入其他的语言,比如zh_TW,那么系统还是默认使用当前的语言,并不会进行修改操作。
接下来就是配置ResourceBundleMessageSource了,因为我们需要解析语言,为了能从数据库获取语言信息,我单独写了个类ResourceBundleMessageSourceFromDB来集成ResourceBundleMessageSource,这里先贴上代码:
package com.jacksoft.iconsole.bootstrap;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ResourceBundleMessageSource;
import com.jacksoft.spring.generator.dao.LanguageMapper;
import com.jacksoft.spring.generator.model.Language;
import com.jacksoft.spring.generator.model.LanguageExample;
/**
* 从数据库加载多语言来进行显示
*
*
* @Filename ResourceBundleMessageSourceFromDB.java
*
* @author Jack.Zhou
*
* @Date 2013-11-8
*
*/
public class ResourceBundleMessageSourceFromDB extends
ResourceBundleMessageSource {
@Autowired
private LanguageMapper languageMapper;
private static Logger log = Logger.getLogger(ResourceBundleMessageSourceFromDB.class);
private static LanguageMapper staticLanguageMapper;
/**
* store language
* zh_cnCode,Test
*/
private static Map<String,String> langMap = new HashMap<String,String>();
public static void init(){
LanguageExample example = new LanguageExample();
List<Language> list = staticLanguageMapper.selectByExample(example);
if(list == null || list.isEmpty()){
log.warn("There is no language message ,please check....");
}else{
for(Language lang : list){
if(log.isDebugEnabled()){
log.debug("load language code:" + lang.getLangCode()
+ " translate " + lang.getLang() + " is "
+ lang.getLangName());
}
langMap.put(lang.getLang().toLowerCase() + lang.getLangCode(), lang.getLangName());
}
}
}
@PostConstruct
public void initMethod(){
log.info("copy langaugeMapper to local static ver");
staticLanguageMapper = languageMapper;
init();
}
@Override
protected String getMessageInternal(String code, Object[] args,
Locale locale) {
if(log.isDebugEnabled()){
log.debug("The code is:" + code + " and locale is :" + locale.toLanguageTag());
}
String localName = locale.toString().toLowerCase();
String message = langMap.get(localName + code);
if(message != null){
for(int i = 0;args != null && i<args.length;i++){
message = message.replace("{"+i+"}", args[i].toString());
}
}else if(isUseCodeAsDefaultMessage()){
message = code;
if(log.isDebugEnabled()){
log.debug("No message found under code [" + code + "] for local [" + localName + "] and use code to display" );
}
}else{
message = null;
if(log.isDebugEnabled()){
log.debug("No message found under code [" + code + "] for local [" + localName + "] " );
}
}
return message;
}
/**
* 根据code和locale来获取多语言信息
* @param code
* @param locale
* @return
*/
public static String getMessage(String code,Locale locale){
String localName = locale.toString().toLowerCase();
String message = langMap.get(localName + code);
if(message == null){
message = code;
if(log.isDebugEnabled()){
log.debug("No message found under code [" + code + "] for local [" + localName + "]" );
}
}
return message;
}
}
因为我使用的是mybatis,而为了让spring给静态属性注入数据,我这里加了个initMethod方法,通过注解@PostConstruct来完成调用,注意,该注解是要在实例化之后才调用的,让这个方法来为我的静态属性进行赋值操作,然后在init方法中将多语言查询出来,存放到map中保存起来。
通过重写getMessageInternal方法,就可以根据Local和code去刚才的map中获取对应的数据信息,该方法有三个参数,分别是
code:对应多语言的code,
args:在显示多语言时,添加的参数,这里我定义了格式为: {0}这样的替换方式,
locale:本地语言
这样将获取到的message返回,就可以了。
然后在将这个bean配置到spring来管理:
<bean id="messageSource" class="com.jacksoft.iconsole.bootstrap.ResourceBundleMessageSourceFromDB"> <property name="useCodeAsDefaultMessage" value="true"></property> </bean>
这里我配置了一个参数:useCodeAsDefaultMessage
在代码中也有体现,就是当找不到code的时候,是否直接输出code
最后在jsp中写上测试信息
<spring:message code="TEST"></spring:message>
当然还需要引入spring的标签库
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
然后启动测试吧~
可以通过添加参数Lang=zh_CN 或者en_US来进行切换试试~
推荐阅读
-
详解Spring框架下向异步线程传递HttpServletRequest参数的坑
-
Spring Boot 整合mybatis 与 swagger2
-
Spring Boot 2.X优雅的解决跨域问题
-
Spring Boot使用RestTemplate消费REST服务的几个问题记录
-
Spring Boot使用过滤器和拦截器分别实现REST接口简易安全认证示例代码详解
-
Spring中@Async注解执行异步任务的方法
-
详解spring boot应用启动原理分析
-
详解在Spring中如何使用AspectJ来实现AOP
-
深入Spring Boot之ClassLoader的继承关系和影响
-
Spring Boot中自动化配置的利弊以及解决方法