struts2國際化開發詳細過程

在看這篇文章之後,你必須要會struts2,否則 不建議繼續看下去

struts2-core.jar下面的struts2-default.xml中的攔截器可以看到有一個攔截器"i18n",這個就是對當前語言環境進行攔截的。

   <interceptors>
            <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
            <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
            <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
            <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
            <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/>
            <interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"/>
            <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" />
            <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
            <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
            <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
            <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
            <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
            <interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>
            <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
            <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>

而且默認的defaultStack 也包含"i18n"這個攔截器的

   <interceptor-stack name="defaultStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>

struts2只需要你在參數後面加上 ?request_locale=  相應的語言,在頁面中通過

<s:property value="%{getText('B_HOME_TEXT_MAINPAGE')}"/> 

即可。

下面就來講解一個實際開發的具體過程以及遇到的問題。


1.直接訪問.jsp  不通過struts攔截器,那麼你即使設置了語言,當前訪問的頁面還是原來的語言。因爲你未通過“i18n”這個攔截器。

有的網站,在web.xml中配置了有默認的訪問頁面。一般是.jsp頁面。

解決辦法:讓所有的訪問必須通過struts2攔截器

a.打開web.xml

 <welcome-file-list>
    <welcome-file>index.action</welcome-file>
  </welcome-file-list>

改成index.action.

b.在webapp(或者webroot)目錄下面建立一個空的名字叫做index.action的文件。至於爲什麼建立這個空的文件,因爲web會去尋找這個文件,沒有則報錯(未研究過)

c.打開struts.xml

	<action name="index">
			<result>/index.jsp</result>
<!-- 			<interceptor-ref name="defaultStack" /> -->
		</action>

添加index.action.

上面三個步驟即完成了首頁是通過action訪問的,而不是通過.jsp訪問的。

d.配置過濾器,讓所有的訪問必須是通過action形式,其它的訪問,使其拒絕訪問

package com.adcorp.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class ResourcesForbidFilter implements Filter {

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest res = (HttpServletRequest) request;
		
		String url = res.getRequestURL().toString();
		if(url.endsWith(".jsp") || url.endsWith(".html") ){
			return;
		} else{
			chain.doFilter(request, response);
		}
	}

	@Override
	public void destroy() {
		
	}

}
e.在web.xml中配置過濾器

<filter>    
   <filter-name>Resources</filter-name>    
   <filter-class>com.adcorp.filter.ResourcesForbidFilter</filter-class>      
  </filter>    
 <filter-mapping>       
    <filter-name>Resources</filter-name>    
    <url-pattern>/*</url-pattern>    
  </filter-mapping>  

上面5步就解決了這個問題。

2.在js中不能使用struts2標籤。

解決辦法:通過自定義el表達式,在js中使用el表達式。(前提是js不是單獨引入jsp中的,這個問題下面再講)

自定義el表達式過程

a:編寫java類,裏面的方法必須是靜態的。

package com.adcorp.tag;

import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

import com.opensymphony.xwork2.ActionContext;

/**
 * 自定義el標籤${myFn:getLanguage('F_LOGIN_JS_INPUT_YOUR_USERNAME', '1')} 的java實現類
 * js中使用
 * @author Administrator
 *
 */
public class HanldingLanguage {
	/**
	 * 根據語言環境,由key獲取到對應的value
	 * @param key
	 * @param type  為1,代表從FrontMsg中尋找,為2,代表通過ajax,從System中尋找,3 代表從 BackMsg中尋找
	 * @return
	 */
	public static String getLanguage(String key, String type) {
		Map<String, Object> session = ActionContext.getContext().getSession();<span style="font-family: Arial, Helvetica, sans-serif;">//<strong>注意第一次訪問網站時,是沒有session的,因此這裏需要做判斷</strong></span>
		Locale currentLocale = (Locale) session.get("WW_TRANS_I18N_LOCALE");
		Locale defaultLocale = Locale.getDefault();
		ResourceBundle resource = null;
		String value = null;
		
		if ("1".equals(type)) {
		
			if (currentLocale == null) {
				
				resource = ResourceBundle.getBundle("FrontMsg_" + defaultLocale);
			} else {
				
				resource = ResourceBundle.getBundle("FrontMsg_" + currentLocale);
			}
		} else if ("2".equals(type)) {
			
			if (currentLocale == null) {
				
				resource = ResourceBundle.getBundle("System_" + defaultLocale);
			} else {
				
				resource = ResourceBundle.getBundle("System_" + currentLocale);
			}
		} else if ("3".equals(type)) {
			
			if (currentLocale == null) {
				
				resource = ResourceBundle.getBundle("BackMsg_" + defaultLocale);
			} else {
				
				resource = ResourceBundle.getBundle("BackMsg_" + currentLocale);
			}
		}
		
		value = resource.getString(key);
		
		
		return value;
	}
	
}

b:編寫tld文件

在WEB-INF下面建立tld/myTag.tld 文件

<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
  version="2.0"> 
  <description>JSTL 1.1 functions library</description>
  <display-name>JSTL functions</display-name>
  <tlib-version>1.2</tlib-version>
  <short-name>myFn</short-name>
  <uri>/myFn</uri>
  <function>
    <description>lj</description>
    <name>getLanguage</name>
    <function-class>
		com.adcorp.tag.HanldingLanguage
    </function-class>
    <function-signature>
       java.lang.String getLanguage(java.lang.String, java.lang.String)
    </function-signature>
  </function>
   <function>
    <description>lj</description>
    <name>getURL</name>
    <function-class>
		com.adcorp.tag.HanldingLanguage
    </function-class>
    <function-signature>
       java.lang.String getURL(java.lang.String)
    </function-signature>
  </function>
</taglib>

c:在web.xml中添加.tld文件的位置

<jsp-config>  
        <taglib>  
            <!-- 配置標籤的引用地址 JSP頁面中引用時使用-->  
            <taglib-uri>/eltag</taglib-uri>  
            <!-- 配置標籤的TLD文件地址 -->  
            <taglib-location>tld/myTag.tld</taglib-location>  
        </taglib>  
    </jsp-config> 

d:在頁面中添加標籤

<%@taglib prefix="myFn" uri="/eltag"%>

e:在js中使用這個自定義el表達式。

if (result.success) {// 表示登錄成功
					alert("${myFn:getLanguage('TIPS_LOGIN_SUCCESS', '2')}");
					top.location = 'dashBoard_index.action';

				}

3.解決在jsp中引入外部js文件,el表達式不起效問題。至於爲什麼是因爲jsp文件最終會被解析爲java代碼,當它解析到有自定義的el表達式時,它會立即去執行這個方法。假如是js文件,它是不會被解析的。。。

解決辦法:

將外部js文件改成.jsp後綴,並且在引入js的jsp頁面中 將這個改成jsp後綴的js文件 包含進來。

詳細步驟:

a:將要引入的js文件後綴名改成jsp

如下面的index.js 文件改成index.jsp文件。添加相應的標籤

<%@taglib prefix="myFn" uri="/eltag"%>
<script>
function changeSign() {
	$("#regUser").attr("status", "true");
}
function login() {
	var username = $.trim($("#j_username").val());

	var password = $.trim($("#j_password").val());
<span style="white-space:pre">	</span>if (username == null || username == "") {
	<span style="white-space:pre">	</span>layer.tips("${myFn:getLanguage('F_LOGIN_JS_INPUT_YOUR_USERNAME', '1')}", document.getElementById("mzp_msg"), {


b:在頁面中將jsp頁面包含進來。
<%@taglib prefix="s" uri="/struts-tags" %>
<%@taglib prefix="myFn" uri="/eltag"%>
<%@include file="js/index.jsp" %>

包含方式有兩種,這裏我選取其中一種,這裏特別注意引入.jsp文件在jsp中引入的位置順序。(有可能會遇到,這裏提出來)

這樣就解決了上面的問題。


下面來講解整個項目的具體過程。

1:建立相應的國際化資源語言文件。

如:FrontMsg_en_US.properties,FrontMsg_zh_CN.properties,BackMsg_en_US.properties,BackMsg_zh_CN.properties

System_en_US.properties,System_zh_CN.properties.

下面是FrontMsg_en_US.properties

#front login model text   en_US
F_LOGIN_TEXT_LOGIN=Log in
F_LOGIN_TEXT_EMAIL=Email
F_LOGIN_TEXT_PASSWORD=Password
F_LOGIN_TEXT_NOT_ACCOUNT=Don't have an account

F_LOGIN_JS_INPUT_YOUR_USERNAME=Please enter your user name
F_LOGIN_JS_INPUT_YOUR_PASSWORD=Please enter your password

下面是FrontMsg_zh_CN.properties

#front login model text  zh_CN
F_LOGIN_TEXT_LOGIN=\u767B\u5F55
F_LOGIN_TEXT_EMAIL=\u90AE\u7BB1
F_LOGIN_TEXT_PASSWORD=\u5BC6\u7801
F_LOGIN_TEXT_NOT_ACCOUNT=\u6CA1\u6709\u8D26\u53F7?

F_LOGIN_JS_INPUT_YOUR_USERNAME=\u8BF7\u8F93\u5165\u4F60\u7684\u7528\u6237\u540D
F_LOGIN_JS_INPUT_YOUR_PASSWORD=\u8BF7\u8F93\u5165\u4F60\u7684\u5BC6\u7801


這裏我是分成前臺FrontMsg,以及後臺BackMsg,以及System 注意後面的en_US.properties,zh_CN.properties必須是固定的。


2:在struts2.xml中配置默認的語言環境,以及國際化資源名字前綴

	<constant name="struts.ognl.allowStaticMethodAccess" value="true" />
	<constant name="struts.locale" value="zh_CN"/>
	<constant name="struts.custom.i18n.resources" value="System,FrontMsg,BackMsg"><!-- 國際化資源文件也即System_zh_CN.properties -->
	</constant>

3:建立用戶可以切換的語言種類配置文件 languageResources.properties 建立這個文件的目的是爲了後期可以方便的添加其它的語言。

如果後期需要添加其它語言,只需要在文件下面加上即可,然後再編寫對應的國際資源文件即可。

1,zh_CN=\u4E2D\u6587
2,zh_TW=\u7E41\u4F53
3,en_US=English

至於爲什麼以這種形式寫,是方便控制在前臺語言排列顯示的順序


4:在.jsp頁面中添加可以選擇的語言種類

<s:bean id="locales" name="com.adcorp.common.LocaleUtils">
				<!-- 給lee.Locales的參數current注入值SESSION_LOCALE -->
</s:bean>
				
下面是LocaleUtils.java的代碼
package com.adcorp.common;

import java.util.*;

import com.opensymphony.xwork2.ActionContext;

/**
 * 
 * @author Administrator
 *
 */
public class LocaleUtils {

	public List<Language> getLocales() {
		List<Language> locales = new ArrayList<Language>();
        ResourceBundle bundle = ResourceBundle.getBundle("languageResources");
        //查詢出所有的Keys
        Enumeration<String> en = bundle.getKeys();
        
        List<String> keys = new ArrayList<String>();
        
        //查詢出所有的Keys
        while(en.hasMoreElements()) {
        	String key = en.nextElement();
        	
        	keys.add(key);
        	
        }
        
        //對keys進行排序
        Collections.sort(keys);
        
        for (String key : keys) {
        	String value = bundle.getString(key);
        	Language language = new Language();
        	language.setLanguage(key.split(",")[1]);
        	language.setCountry(value);
        	locales.add(language);
        }
        
		return locales;
	}
	
	
	/**
	 * 得到當前系統語言<s:property>,在日曆中,WdatePicker.js中,根據當前語言,將Lang屬性設置成相應的環境,返回en或者zh-cn或者zh-tw在lang目錄下面
	 * 有對應的語言
	 * @return
	 */
	public static String getCurrentLanguageInWdatePicker() {
		Map<String, Object> session = ActionContext.getContext().getSession();
		Locale currentLocale = (Locale) session.get("WW_TRANS_I18N_LOCALE");
		Locale defaultLocale = Locale.getDefault();
		String language = null;
		String country = null;
		if (currentLocale == null) {
			
			language = defaultLocale.getLanguage();
			country = defaultLocale.getCountry();
		} else {
			language = currentLocale.getLanguage();
			country = currentLocale.getCountry();
		}	
		
		return language + "_" + country;
		
		
	}
}

下面是Language.java這個實體類

package com.adcorp.common;

public class Language {
	private String language;
	
	private String country;

	public String getLanguage() {
		return language;
	}

	public void setLanguage(String language) {
		this.language = language;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}
	
	
}
下面完整的貼出在頁面中顯示所有可以選擇語言的代碼

<s:bean id="locales" name="com.adcorp.common.LocaleUtils">
				<!-- 給lee.Locales的參數current注入值SESSION_LOCALE -->
				</s:bean>
				<s:iterator value="#locales.locales" id="loca" status="st">
				<span class="sp_sig">
					
					<a href="<s:property value="#request['struts.request_uri']"/><s:property value="@com.adcorp.tag.HanldingLanguage@getURL(#request['javax.servlet.forward.query_string'])"/>request_locale=<s:property value='#loca.language'/>"><s:property value="#loca.country"/></a>
				</span>
				</s:iterator>	

其中最爲複雜的是a標籤裏面的。下面一一講解

a:首先是

<s:property value="#request['struts.request_uri']"/>是爲了得到當前訪問頁面根路徑後面的action地址,比如頁面地址是http://localhost:8080/test/index.action,那麼這個地址就是index.action,
這樣在用戶切換語言時,還是在當前頁面下面。


b:如果頁面訪問地址存在參數  ?id=1007,這樣的形式,那麼還通過上面這種形式,那麼參數將會丟掉。

解決辦法:通過struts2訪問靜態方法的形式。至於說request裏面的參數是哪裏來的,可以將<s:debug>打開,看裏面的參數即可

<s:property value="@com.adcorp.tag.HanldingLanguage@getURL(#request['javax.servlet.forward.query_string'])"/> 其中#request['javax.servlet.forward.query_string就可以得到url後面的參數。
而函數getURL就是解決有url參數的情況以及url中是否存在request_local這個參數的情況(如果有request_local參數,那麼就需要捨棄掉這個參數,因爲我們後面已經添加了request_locale這個參數,因爲這個參數纔是我們用戶現在可能需要的語言)


下面我貼出getURL的代碼

	public static String getURL(String url) {
		
		//no parameter
		if ("".equals(url) || url == null) {
			
			return "?";
		} 
		
		//?..&request_locale
		if (url.toLowerCase().contains("&request_locale")) {
			
			url = url.substring(0, url.toLowerCase().indexOf("&request_locale"));
			
			return "?" + url + "&";
		}
		
		//?request_locale
		if (url.toLowerCase().contains("request_locale")) {
			
			url = url.substring(0, url.toLowerCase().indexOf("request_locale"));
			
			return "?" + url;
		}
		
		//hava parameter
		return "?" + url + "&";
		
		
	}

c:
request_locale=<s:property value='#loca.language'/>"><s:property value="#loca.country"/>

前面的即是需要選擇的語言環境(zh_CN,en_US等等),後面即是value值

d:在頁面中顯示如下


注意:

1:如果是ajax提交到struts的話,如果你在action中使用getText(),那麼可能得到的結果不是當前語言環境下的value,解決辦法,調用上述的getLanguage()方法。

2:有可能會遇到使用控件時裏面有中文需要替換成其它語言,那麼你必須找到插件裏面控制語言的位置,如日曆控件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章