天天看點

實戰程式化處理Struts1針對zh_HANS_CN國際化問題

最近為了解決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)。

準備工作:

  1. 建立資源檔案ApplicationResources_en_US.Properties和ApplicationResources_zh_CN.Properties。
  2. 建立一個LocaleFilter(過濾器)将LocaleFilter配置到web.Xml中。
  3. 在struts-config.Xml中配置
    <message-resources parameter="ApplicationResources" />
          
  4. 建立一個index.jsp來實作中英文的切換。

實作:

a.建立的資源檔案:

實戰程式化處理Struts1針對zh_HANS_CN國際化問題

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" οnclick="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" οnmοuseοver="clickLan();" οnmοuseοut="hideLan();">
		      	       <li><a href="javascript:languages(\'zh_CN\')" target="_blank" rel="external nofollow" >中文(簡體)</a></li>
		      	       <li class="rk_topborder_n"><a href="javascript:languages(\'en_US\')" target="_blank" rel="external nofollow" >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();