最近为了解决Windows 8中对中文locale的变化,引入了zh_HANS_CN的支持,原来已经支持中文的站点无法正常工作。
当然你可以增加一种资源文件来支持着新的locale,但是涉及到不同浏览器的不同,其实这种支持只能针对IE10 or IE11生效。
为了解决这一问题我采用的是一种程序化的思路,保持原来的资源文件,在程序中对locale进行转换。由于站点由多种技术框架交叉在一起,解决之前需要了解多种框架的技术方案。
国际化是什么?
国际化(internationalization)是设计和制造容易适应不同区域要求的产品的一种方式。它要求从产品中抽离所有地域语言,国家/地区和文化相关的元素。换言之,应用程序的功能和代码设计考虑在不同地区运行的需要,其代码简化了不同本地版本的生产。开发这样程序的过程,就称为国际化。
通俗一点,就是你在一些网站上只需要改变浏览器的语言设置就能看到不同语言版本的界面,不需要修改一行代码的程序。
1, jstl国际化
在没有各种框架之前,j2EE国际化项目通常做法:
1.1 jsp环境
首先写一个messages.zh_CN.properties文件,放在class-path也就是/WEB-INF/classes里 welcome=欢迎 然后用native2ascii.exe把它转为 welcome=\欢\迎
在web.xml中定义messages文件
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>messages</param-value>
</context-param>
最后在jsp里使用
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
<fmt:message key="welcome"/>
如果有多个Resource Bundle文件, 就要在jsp里用<ftm:bundle>定义了.
1.2 pure Java环境
ResourceBundle rb = ResourceBundle.getBundle("messages");
String welcome = rb.getString("welcome");
2)Spring的增强做法
Spring增加了MessageSource的概念,一是ApplicationContext将充当一个单例的角色,不再需要每次使用i18时都初始化一次ResourceBundle,二是可以代表多个Resource Bundle.
在ApplicationContext的定义文件中,增加如下节点:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>
则在pure java环境中。 context.getMessage("welcome", null, Locale.CHINA)
而在jsp环境中,Controller调用JSTL viewResolver再调用Jsp时,<fmt:message>将继续发挥它的功效。
因此,appfuse等sample都是在appfuse-servlet.xml 中定义一个<messageSource>。
3)Struts的做法
3.1.Struts1和Struts2在配置文件中的配置。
Struts1 | Struts2 | |
struts-config.xml | <message-resources parameter="xxx.xx.xxx.ApplicationResources" /> | |
struts.xml | <constant name="struts.custom.i18n.resources" value="message"></constant> |
3.2.Struts1和Struts2在页面中的显示
Struts1 | Struts2 | |
index.jsp | <bean:message key="USERNAME"/> | |
index.jsp | <s:text name="USERNAME"></s:text> |
3.3.Struts1和Struts2用Java代码控制国际化
思路:首先获取到相关的语言信息,然后设置到环境中,最后通过ResourceBundle来获取相关的资源文件信息。
A:Struts1的相关代码
String currentLocale = request.getLocale().toString(); //浏览器默认的语言 request.getSession(). setAttribute(Globals.LOCALE_KEY, currentLocale);//将语言这种到环境中 //然后用ResourceBundle获取相关的资源文件 ResourceBundle resourceBundle = ResourceBundle. getBundle("ApplicationResources",currentLocale); |
B:Struts2的相关代码
Locale currentLocale = request.getLocale().toString();//获取本地语言 session.setAttribute("WW_TRANS_I18N_LOCALE", LocalizedTextUtil.localeFromString(currentLocale, null));//将语言这种到环境中 //然后用ResourceBundle获取相关的资源文件 ResourceBundle resourceBundle = ResourceBundle. getBundle("ApplicationResources",currentLocale); |
3.4.Struts1实例
实现思路:当我们在页面上选择中文或者是英文的时候就将相关的语言变量存在cookie中(便于在过滤器中得到是哪一种语言),写一个过滤器(LocaleFilter),过滤每一个页面的请求信息。在LocaleFilter中我们首先判断cookie是是否有约定好的cookie值,如果有就设置这种语言到环境中,如果没有就得到浏览器默认的语言再设置到环境中(用户在画面设置的locale优先级高于浏览器设置语言得到的locale)。
准备工作:
- 创建资源文件ApplicationResources_en_US.Properties和ApplicationResources_zh_CN.Properties。
- 创建一个LocaleFilter(过滤器)将LocaleFilter配置到web.Xml中。
- 在struts-config.Xml中配置
<message-resources parameter="ApplicationResources" />
- 创建一个index.jsp来实现中英文的切换。
实现:
a.创建的资源文件:
b.web.xml的配置:
<filter> <filter-name>localeFilter</filter-name> <filter-class>xx.xx.xx. LocaleFilter</filter-class> </filter> <filter-mapping> <filter-name>localeFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> |
c.struts-config.xml中的配置:
<message-resources parameter="ApplicationResources"/> |
d.创建index.jsp页面,然后实现国际化。
index.jsp页面html代码
<span class="rk_qh"> <a class="rk_qhuan" onclick="clickLan();"><span style="margin:0 2px;">${lan}</span> <img id="lan_img" style="position:relative;"src="/images/header/arrow_down.gif"/> </a> <ul id="ul_lans" class="chinese_english_huan" onmouseover="clickLan();" onmouseout="hideLan();"> <li><a href="javascript:languages(\‘zh_CN\‘)">中文(简体)</a></li> <li class="rk_topborder_n"><a href="javascript:languages(\‘en_US\‘)">English</a></li> </ul> </span> |
Index.jsp页面javascript方法
function languages(lan){ var cookiesPath = "/";//路径 var cookiesDomain = ".xxx.com";//设置有效的url document.cookie = "xxx_language="+lan+";path="+cookiesPath+";domain="+cookiesDomain; //将有关信息写到cookie中 location.reload();//刷新本页面进行国际化切换 } |
e.LocaleFilter(过滤器)代码
package com.ruanko.webapp.filter; import org.apache.struts.Globals; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Locale; import java.util.ResourceBundle; import javax.servlet.http.Cookie; public class LocaleFilter extends OncePerRequestFilter { public static final String CHINESELANGUAGE = "中文(简体)"; public static final String ENGLISHLANGUAGE = "English"; @SuppressWarnings("unchecked") public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { /*过滤器实现思路: (1)判断cookies中是否有值,有值就将cookies中的值得到一个Locale (2)cookies没值就根据浏览器默认的语言来显示*/ Locale currentLocale = null; //定义语言地区信息 String language = null;//客户端页面写的语言cookies Cookie[] cookies = request.getCookies(); if (cookies != null) { for(Cookie cookie : cookies){ if(cookie.getName().equals("xxx_language")){ language=cookie.getValue();//判断和设置cookies里面是否有值。 } } } try { /** * 1.如果cookies中是有值的(前提条件是从页面传过来的语言为空才进下面的逻辑) */ if(language != null){ if ("zh_CN".equals(language)) {//如果cookies中的语言为中文 currentLocale = new Locale("zh", "CN"); language = "zh_CN"; request.getSession().setAttribute("lan", CHINESELANGUAGE); } else if ("en_US".equals(language)) {//如果cookies中的语言为英文 currentLocale = new Locale("en", "US"); language = "en_US"; request.getSession().setAttribute("lan", ENGLISHLANGUAGE); } else { currentLocale = new Locale("zh", "CN"); language = "zh_CN"; request.getSession().setAttribute("lan", CHINESELANGUAGE); } } /** * 传过来的语言:如果既没有传过来的语言和cookies里面的值就根据浏览器默认的语言来显示 */ else { String defaultLanguage = request.getLocale().toString(); //浏览器默认的语言 if(defaultLanguage.equals("en_US")){ currentLocale = new Locale("en", "CN"); request.getSession().setAttribute("lan", ENGLISHLANGUAGE); }else{ currentLocale = new Locale("zh", "CN"); request.getSession().setAttribute("lan", CHINESELANGUAGE); } } } catch (Exception e) { e.printStackTrace(); }finally{ ///最后将设置好的环境类放到session中去 request.getSession().removeAttribute(Globals.LOCALE_KEY); request.getSession().setAttribute(Globals.LOCALE_KEY, currentLocale); } chain.doFilter(request, response); } } |
f.在Java代码中得到资源文件的值
Locale locale = (Locale) request.getSession().getAttribute(Globals.LOCALE_KEY); ResourceBundle resourceBundle = ResourceBundle.getBundle("ApplicationResources",locale); String course = resourceBundle.getString("COE_COURSE"); 2,实战总结 由于项目国际化主体还是通过jstl方案来获取相应资源文件的设置,但是同时用到了struts1的一些taglibs,同时对于一些插件中用到了server端的缺省locale.解决思路: 1〉针对jstl的设置,通过一个servletFilter 来实现locale的转换。 public class LocaleServletFilter implements Filter { @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { Locale sesionLocale=request.getLocale(); String locStr=sesionLocale.toString(); Locale proLoc = locFromString(locStr); LocaleSetId.setLocale(proLoc); request.setAttribute("javax.servlet.jsp.jstl.fmt.fallbackLocale", proLoc.toString()); request.setAttribute("javax.servlet.jsp.jstl.fmt.locale", proLoc.toString()); HttpServletRequest hprequest = (HttpServletRequest)request; HttpServletResponse hpresponse = (HttpServletResponse)response; hprequest.setCharacterEncoding("UTF-8"); HttpSession session = hprequest.getSession(); Config.set(session, Config.FMT_LOCALE, proLoc); session.removeAttribute(Globals.LOCALE_KEY); session.setAttribute(Globals.LOCALE_KEY, proLoc); chain.doFilter(hprequest, hpresponse); } public Locale locFromString(String locale) { String parts[] = locale.split("_"); if (parts.length == 1) return new Locale(parts[0]); else if (parts.length == 2) { if(parts[1].equalsIgnoreCase("HANS")){ return new Locale(parts[0], "CN"); } else{ return new Locale(parts[0], parts[1]); } } else if (parts.length == 3 && parts[1].equalsIgnoreCase("HANS")) { return new Locale(parts[0], parts[2]); } else return new Locale(parts[0], parts[1], parts[2]); } } 2〉针对struts1,taglib的locale 设置 /** i18n the title */ public String getTitle() { if (isLocalizedTitle) { Locale userLocale = RequestUtils.getUserLocale( (HttpServletRequest) pageContext.getRequest(), null); HttpServletRequest req = (HttpServletRequest) pageContext.getRequest(); Locale LocaleProcessed= locFromString(req.getLocale().toString()); req.getSession().setAttribute(Globals.LOCALE_KEY, LocaleProcessed); pageContext.setAttribute(Globals.LOCALE_KEY, LocaleProcessed); try { String title=TagUtils.getInstance().message(pageContext, null, Globals.LOCALE_KEY, super.getTitle()); return title; } catch (JspException e) { log.debug(e); // Do not localize then } } return super.getTitle(); } 3〉针对插件或服务端代码中用到缺省的locale的问题。 这个在前面servletfilter里面我设置了一个全局变量,在server端如果需要用locale调用如下方法得到。LocaleSetId.getLocale(); |
实战程序化处理Struts1针对zh_HANS_CN国际化问题