structs2实现语言本地化,默认是通过获取浏览器的语言(服务器获取:request.getLocale())来作为默认值。
jsp页面使用s标签(引入:<%@taglib prefix=“s” uri=“/structs-tags”%>)支持本地化,如<s:text name=“key”/>
备注:request为javax.servlet.http.HttpServletRequest类型
方式1、structs2自带I18nInterceptor过滤器进行语言本地化,通过访问action(携带request_locale参数自行设定语言)跳转到jsp页面,即可实现指定语言本地化。
备注:需要通过action跳转jsp;一次会话中,指定本地化语言只需进行一次即可(会保存在会话中,一般都是登陆时携带参数指定)
方式2、直接访问jsp无法实现指定语言本地化,因为没有经过过滤器,从而拿到的语言是浏览器作为默认语言。处理方式为:在structs.properties中设定structs.locale=指定语言,如zh_CN、en_US、vi_VN。
原理是org.apache.structs2.dispatcher.Dispatcher类的defaultLocale不为空时,采用该值作为本地化语言
补充:
a、如需动态修改,只需更新defaultLocale(Dispatcher.getInstance().setDefaultLocale(“zh_CN”))即可。
b、直接访问jsp(经过org.apache.structs2.dispatcher.ng.filter.StructsPrepareAndExecuteFilter的doFilter方法)时,拿到ActionContext上下文都是重新创建的。
备注:第二种方式本地化语言的生命周期不是会话(session)周期,而是application,如某一浏览器设置某种语言后,其他浏览器而是该语言。
方式3、修改每次请求传输的请求头accept-language(request.getLocale()拿取的就是该字段的值,如“zh-CN,zh;q=0.9,vi;q=0.8,en;q=0.7”)的值,通过过滤器Filter来修改每次请求的请求头。
javax.servlet.http.HttpServletRequest只能获取请求头,而无法修改请求头,因此需要通过反射来修改请求头数据,此处需要引入tomcat-coyote.jar(在tomcat安装目录lib下可以找到):
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @Author xiye
* @File I18nFilter
* @Date 2021/5/11 10:12
* @Desc 动态设置本地化语言,一次会话作为生命周期
*/
public class I18nFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpSession session = httpServletRequest.getSession();
// httpServletRequest.getLocale() --> Header(请求头)的accept-language字段
if (session != null) {
// request_locale作为参数目的: structs2自带的本地化拦截器为该参数名
// 该值形如: zh_CN、en_US、vi_VN等
String request_locale = request.getParameter("request_locale");
if (request_locale != null) {
session.setAttribute("GM_I18N_LOCALE", request_locale);
}
// 将本地化语言设置到
Object ww_trans_i18N_locale = session.getAttribute("GM_I18N_LOCALE");
if (ww_trans_i18N_locale != null) {
reflectSetHeader(httpServletRequest, "accept-language", ((String) ww_trans_i18N_locale).replace("_", "-"));
}
}
chain.doFilter(httpServletRequest, response);
return;
} catch (IOException | ServletException ignored) {}
chain.doFilter(request, response);
}
@Override
public void destroy() { }
/**
* @author xiye
* @date 2021/5/11 19:17
* @param request HttpServletRequest对象
* @param name 请求头字段
* @param value 请求头字段值
* @desc 通过反射修改HttpServletRequest的请求头字段
* 备注: 需要引入tomcat-coyote.jar包
* @return void
*/
private void reflectSetHeader(HttpServletRequest request, String name, String value){
Class<? extends HttpServletRequest> requestClass = request.getClass();
// System.out.println("request实现类="+requestClass.getName()); // org.apache.catalina.connector.RequestFacade,在catalina.jar,但无须引入该包,除非有显示引用该类
try {
Field field = requestClass.getDeclaredField("request");
field.setAccessible(true);
Object o = field.get(request);
Field coyoteRequest = o.getClass().getDeclaredField("coyoteRequest");
coyoteRequest.setAccessible(true);
Object o1 = coyoteRequest.get(o);
Field headers = o1.getClass().getDeclaredField("headers");
headers.setAccessible(true);
// o3类型: org.apache.tomcat.util.http.MimeHeaders
Object o3 = headers.get(o1);
/*// 转换出错
MimeHeaders o2 = (MimeHeaders) o3;
o2.addValue(key).setString(value);
*/
// 先移除,后添加
o3.getClass().getMethod("removeHeader", String.class).invoke(o3, name);
// o4类型: org.apache.tomcat.util.buf.MessageBytes
Object o4 = o3.getClass().getMethod("addValue", String.class).invoke(o3, name);
Method setString = o4.getClass().getMethod("setString", String.class);
setString.invoke(o4, value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
备注:
1、request.getLocale()应该获取请求头accept-language的第一组值(每一组以逗号分隔。原因不明确,猜测是权重q【0-1之间】越大,优先获取,第一个组值权重应该为1,最大)。
2、该过滤器需要放置在web.xml比较前面(相对其他过滤器配置),放置其他过滤器后面可能导致上面的反射修改失败(实现类不是org.apache.catalina.connector.RequestFacade,不符合以上逻辑而抛异常)<!-- 2021-05-11 为所有请求头设置本地化语言(从会话获取替换浏览器默认) --> <filter> <filter-name>i18nFilterChain</filter-name> <filter-class>xxx.xxx.I18nFilter</filter-class> </filter> <filter-mapping> <filter-name>i18nFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3、过滤器也可以继承org.springframework.web.filter.OncePerRequestFilter