1. 程式人生 > >Struts2實現國際化操作及中英文切換(九)

Struts2實現國際化操作及中英文切換(九)

“兩個蝴蝶飛”特別喜歡"java1234知識分享網"小峰的實用主義,所以本文及其系列文章均是採用實用主義,從專案和程式碼的角度去分析。由於本人經驗有限,嘴皮子不溜,所以學術性,概念性,底層性的知識點暫時不做介紹。文章中有錯誤之處,歡迎拍磚和指點。特別感謝"java1234知識分享網 "和"黑馬程式設計師官網",所有的資料大部分是兩者提供,為了方便書寫,故不一一指名出處,請諒解,非常抱歉。

上一章簡單介紹了Struts2攔截器的簡單應用,登入許可權攔截器及與過濾器的區別(八),如果沒有看過,請觀看上一章

Struts國際化,簡寫成i18n,全稱是internationalization,中間有18個字母. L10n,為本地化 localization。

在說Struts2國際化之前,先說一下Java SE的簡單國際化讀取。

一  Java SE的簡單國際化

JavaSE 提供了一個Locale的類。

一.一  取得所有地區的國家編碼和語言編碼

        //得到所有地區及它們的主流語言的環境字串.
		Locale [] localses=Locale.
				getAvailableLocales();
		for (Locale locale : localses) {
			//國家-->國家CN
			System.out.println(
					locale.getDisplayCountry()+
					"-->"+locale.getCountry());
			//語句--->國家的語言 zh
			System.out.println(
					locale.getDisplayLanguage()+
					"----->"+locale.getLanguage());
		}

常用的有中英方切換, 中文: zh-CN,  英文: en_US  。需要其他的編碼,可以執行一下程式,或者上網搜尋一下。

一.二   讀取國際化檔案的內容

讀取國際化檔案,需要有這個檔案。一般通常是在src下建立三個檔案,一個是基本的,另外兩個是中文的和英文的。檔案的常見格式命名為:baseName_language_country.properties或者baseName.properties

其中後者一般指的是預設的。 預設為中文。

建立三個屬性檔案,令baseName命名為i18n。

i18n.properties檔案和i18n_zh_CN.properties內容為:

###翻譯成中文是 兩個蝴蝶飛
i18n.name=\u4e24\u4e2a\u8774\u8776\u98de

i18n_en_US.properties內容為:

i18n.name=YJL

 需要將中文value值轉換成Unicode編碼。 可以使用線上工具進行轉換

用Java提供的ResourceBundle類。

執行預設的屬性檔案:

        //讀取檔案  引數為讀取的檔名
		ResourceBundle resourceBundle=ResourceBundle.getBundle("i18n");
		//讀取屬性,引數為key值
		String name=resourceBundle.getString("i18n.name");
		System.out.println("name是:"+name);

執行選擇的檔案,將語言編碼傳遞進去。

//讀取檔案  引數為讀取的檔名
		//中文
		//ResourceBundle resourceBundle=ResourceBundle.getBundle("i18n",new Locale("zh","CN"));
		//英文
		ResourceBundle resourceBundle=ResourceBundle.getBundle("i18n",new Locale("en","US"));
		//讀取屬性,引數為key值
		String name=resourceBundle.getString("i18n.name");
		System.out.println("name是:"+name);

一.三  引數傳遞佔位符國際化

中文是:

###帶引數傳遞,歡迎{0}登入,性別是{1},年齡{2}
i18n.welcome=\u6b22\u8fce\u007b\u0030\u007d\u767b\u5f55\u002c\u6027\u522b\u662f\u007b\u0031\u007d\u002c\u5e74\u9f84\u007b\u0032\u007d

英文是:

###帶引數
i18n.welcome=Welcome {0} login,sex is {1},age is{2}

是佔位符是從0開始的。 將其整體複製進來,連{}也要進行相應的轉換。

//讀取檔案  引數為讀取的檔名
		//中文
		ResourceBundle resourceBundle=ResourceBundle.getBundle("i18n",new Locale("zh","CN"));
		//讀取屬性,引數為key值
		String welcome=resourceBundle.getString("i18n.welcome");
		//實際化所需要的引數  引數不需要轉換編碼
		Object []params=new Object[]{"兩個蝴蝶飛","男",24};
		//利用MessageFormat類進行填充資料
		String wel=MessageFormat.format(welcome,params);
		System.out.println(wel);

 二   Strut2實現國際化

Struts2實現國際化可以在前端實現,也可以在後端進行相應的實現。 一般國際化的都是前端的顯示標籤和提示內容,故採用前端國際化的較多一點。

在前端國際化時,用一個登入表單的頁面來進行相應的說明。(前堤是先搭建一個基本的Struts的執行環境,包括struts.xml和web.xml的配置)

二.一 利用<s:text> 實際國際化

1.首先根據前端的頁面標籤和提示資訊,將國際化內容大致提取出來,放置在國際化的屬性檔案中。按照i18n.模組.標籤的方式進行命名。

zh_CN中文與預設的一致,內容為:

###下面是登入表單login.jsp的國際化配置
###登入頁面
i18n.login.title=\u767b\u5f55\u9875\u9762
###使用者名稱
i18n.login.userName=\u7528\u6237\u540d
###密碼
i18n.login.password=\u5bc6\u7801
###登陸
i18n.login.submit=\u767b\u9646
###重置
i18n.login.reset=\u91cd\u7f6e
###下面是登入成功表單success.jsp的國際化配置
###帶引數傳遞,歡迎{0}登入,性別是{1},年齡{2}
i18n.success.welcome=\u6b22\u8fce\u007b\u0030\u007d\u767b\u5f55\u002c\u6027\u522b\u662f\u007b\u0031\u007d\u002c\u5e74\u9f84\u007b\u0032\u007d

en_US英文為:

###下面是登入表單login.jsp的國際化配置
###登入頁面
i18n.login.title=login page
###使用者名稱
i18n.login.userName=userName
###密碼
i18n.login.password=password
###登陸
i18n.login.submit=login
###重置
i18n.login.reset=reset
###下面是登入成功表單success.jsp的國際化配置
###帶引數
i18n.success.welcome=Welcome {0} login,sex is {1},age is{2}

2. 在Struts.xml中配置資源所在的路徑,改變常量的值。(必須配置,否則找不到檔案在哪兒)

<!-- 新增國際化的資源所在的位置 -->
	<constant name="struts.custom.i18n.resources" value="i18n"></constant>

3. 寫前端介面,將內容和提示資訊都換成<s:text name="key">的形式。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><s:text name="i18n.login.title"/></title>
</head>
<body>
	<s:form action="User_login.action" namespace="/" method="post">
			<s:text name="i18n.login.userName"/>: <s:textfield  name="name"/>  <br/>
			<s:text name="i18n.login.password"/>: <s:password name="password"/><br/>
   			<%--
   			下面寫法都是錯誤的,無法正常顯示。
   			<s:submit value='%{<s:text name="i18n.login.submit"/>}'/>
   			<s:reset value="<s:text name='i18n.login.reset'/>"/>
   			 --%>
   			<input type="submit" value="<s:text name='i18n.login.submit'></s:text>"/>
   			<input type="reset" value="<s:text name='i18n.login.reset'></s:text>"/>
	</s:form>
</body>
</html>

注意,<s:submit>是無法使用的,改成標準的html標籤即可。

也可以使用<s:set> 先setter一樣,然後再使用。  用getText()方式來獲取。

             <s:set var="submit" value="getText('i18n.login.submit')"/>
   			 <s:submit value="%{submit}"></s:submit>

4. 在前端介面時,也可以傳遞相應的引數。用<s:param>

在成功介面,填充引數。

    <s:text name="i18n.success.welcome">
		<s:param name="0">蝴蝶飛</s:param>
		<s:param name="1">男</s:param>
		<s:param name="2">24</s:param>
	</s:text>

執行後結果為: 

現在改變成英文狀態, goole瀏覽器改變settings,高階選擇語言,新增美國英語,並將其選擇為瀏覽器語言,並move to up,移動到最上面

 執行一下login.jsp和success.jsp,結果顯示如下: 可以正常國際化

有三個佔位符,如果只填充兩個呢,將24去掉: 不出錯,多餘的不填充。

 如果多填充一個呢?  也不出錯,會將多餘的進行擷取。

5. 在Struts中也可以進行後端的讀取,在跳轉到"toLogin"---(顯示Login.jsp) 之前,也就是在toLogin()方法時,將login.jsp上所顯示的標籤全部讀取出來,進行設定存放,到達login.jsp頁面時再進行顯示。 特別麻煩,用前端<s:text>

6. 有的會將屬性檔案放在相對應的包下,即本專案中,在com.yjl.web包下新建一個properties包,然後將屬性檔案放置在裡面。感覺不太好用,故最好放在src目錄下。

三  中英文連結動態選擇語言

實現的效果是,在登入頁面或者主頁面選擇一種語言,然後全域性都使用這種語言。 如Login.jsp時顯示一種預設的語言,有一個選擇語言的框,點選中文後,跳轉到success.jsp顯示中文,success.jsp跳轉到list.jsp也會顯示中文。在login.jsp頁面點選英文後,跳轉到success.jsp顯示英文,success.jsp跳轉到list.jsp也會顯示英文。 模擬這種全域性都顯示一種語言的專案。要想全域性都使用這種語言,就需要對全域性性進行一次判斷,故最好用攔截器實現。 非常幸運的是,Struts2中有一個i18n的攔截器,我們只需要稍微新增一些操作即可。

1. 正常寫LoginAction程式碼。

package com.yjl.web.action;

import com.opensymphony.xwork2.ActionSupport;

/**
* @author 兩個蝴蝶飛
* @version 建立時間:Aug 27, 2018 10:56:50 AM
* 登入的國際化操作
*/
public class LoginAction extends ActionSupport{
	private static final long serialVersionUID = 1L;
	//跳轉到首頁,用的是預設的語言環境
	public String toLogin(){
		return "toLogin";
	}
	//跳轉到登入頁面,用的是選擇的語言環境
	public String login(){
		return SUCCESS;
	}
	//根據所選擇的語言環境,繼續相應的跳轉,表示全域性性選擇語言。
	public String list(){
		return "list";
	}
}

2. 正常配置Struts.xml的簡單配置

<struts>
	<!--修改國際化編碼 -->
	<constant name="struts.i18n.encoding" value="UTF-8"></constant>
	<constant name="struts.devMode" value="true"></constant>
	<!-- 新增國際化的資源所在的位置 -->
	<constant name="struts.custom.i18n.resources" value="i18n"></constant>
	<!--修改struts中ui的主題,為simple-->
	<constant name="struts.ui.theme" value="simple"></constant>
	<package name="user" extends="struts-default" namespace="/">
		<action name="Login_*" class="com.yjl.web.action.LoginAction" method="{1}">
				<result name="toLogin">/login.jsp</result>
				<result name="success">/success.jsp</result>
				<result name="list">/list.jsp</result>
		</action>
	</package>
</struts>

3. 開發各個頁面

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><s:text name="i18n.login.title"/></title>
</head>
<body>
	<s:form action="Login_login.action" namespace="/" method="post">
			<s:text name="i18n.login.userName"/>: <s:textfield  name="name"/>  <br/>
			<s:text name="i18n.login.password"/>: <s:password name="password"/><br/>
 
   			<input type="submit" value="<s:text name='i18n.login.submit'></s:text>"/>
   			<input type="reset" value="<s:text name='i18n.login.reset'></s:text>"/>
   			<s:a action="Login_login.action?request_locale=zh_CN" namespace="/">中文</s:a>
   			<s:a action="Login_login.action?request_locale=en_US" namespace="/">英文</s:a>
	</s:form>
</body>
</html>

success.jsp頁面

<body>
	<s:text name="i18n.success.welcome">
		<s:param name="0">兩個蝴蝶飛</s:param>
		<s:param name="1">男</s:param>
		<s:param name="2">24</s:param>
	</s:text>
	<s:a action="Login_list" namespace="/">跳轉到list頁面</s:a>
</body>
</html>

list.jsp頁面

<body>
	<s:text name="i18n.success.welcome">
		<s:param name="0">YJL</s:param>
		<s:param name="1">男</s:param>
		<s:param name="2">24</s:param>
	</s:text>
</body>
</html>

4. 重啟伺服器,檢測邏輯是否正確

重啟伺服器,頁面按照設定進行相應的跳轉,只是語言只是一種語言,沒有改變。 因為並沒有寫攔截器呢。

5. 建立國際化攔截器類I18nInterceptor

在com.yjl.web.interceptor包下建立一個類 I18nInterceptor

package com.yjl.web.interator;
import java.util.Locale;
import java.util.Map;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

/**
* @author 兩個蝴蝶飛
* @version 建立時間:Aug 27, 2018 5:44:21 PM
* 類說明  國際化的攔截器
*/
public class I18NInterceptor extends MethodFilterInterceptor{
	private static final long serialVersionUID = 1L;
	@Override
	protected String doIntercept(ActionInvocation ai) throws Exception {
		//1. 得到ActionContext物件,從而獲取session
		ActionContext actionContext=ai.getInvocationContext();
		Map<String,Object> session=(Map<String, Object>) actionContext.getSession();
		//2.得到裡面設定的值Locale  注意key值
		Locale locale=(Locale) session.get("WW_TRANS_I18N_LOCALE");
		//3. 判斷這個值是否為null,如果為null則設定一個預設值
		if(locale==null){
			Locale defaultLocale=new Locale("zh","CN");
			//將其設定到session中
			session.put("WW_TRANS_I18N_LOCALE", defaultLocale);
		}
		//返回
		return ai.invoke();
	}
}

其中Locale defaultLocale=new Locale("zh","CN"); 是採用硬編碼編碼進去的。

六  在struts.xml中配置攔截器 (攔截器的具體使用請參照上一章)\

<package name="user" extends="struts-default" namespace="/">
		<!-- 配置攔截器,對每一個action都進行攔截 -->
		<interceptors>
			<interceptor name="i18nInterceptor" class="com.yjl.web.interator.I18NInterceptor"></interceptor>
			<interceptor-stack name="defaultStack">
				<interceptor-ref name="i18nInterceptor"></interceptor-ref>
				<interceptor-ref name="defaultStack"></interceptor-ref>
			</interceptor-stack>
		</interceptors>
		<action name="Login_*" class="com.yjl.web.action.LoginAction" method="{1}">
				<result name="toLogin">/login.jsp</result>
				<result name="success">/success.jsp</result>
				<result name="list">/list.jsp</result>
		</action>
	</package>

七  重啟伺服器,驗證語言是否可以進行選擇

經過驗證,發現可以正常的進行跳轉,是多種語言的跳轉。

選擇中文:

 選擇英文:

八  國際化完善操作

專案中有兩個小的不完美的地方,第一,預設語言是硬編碼。 第二,選擇中英文時,不能是兩個單獨的連結,而應該是一個select框進行選擇。

八.一 解決預設語言硬編碼

在I18nInterceptor攔截器中新增兩個引數,country和language,並指明預設引數為"zh","cn". 表明使用者可以不選擇這兩個引數。

private String country="zh";
	private String language="CN";
	public String getCountry() {
		return country;
	}
	public void setCountry(String country) {
		this.country = country;
	}
	public String getLanguage() {
		return language;
	}
	public void setLanguage(String language) {
		this.language = language;
	}

在struts.xml中配置時:

<interceptors>
			<interceptor name="i18nInterceptor" class="com.yjl.web.interator.I18NInterceptor"></interceptor>
			<interceptor-stack name="defaultStack">
				<interceptor-ref name="i18nInterceptor">
					<param name="country">en</param>
					<param name="language">US</param>
				</interceptor-ref>
				<interceptor-ref name="defaultStack"></interceptor-ref>
			</interceptor-stack>
		</interceptors>

八.二 解決連結顯示的問題(最後終於成功)

login.jsp頁面

	<div class="content">
		<s:form action="Login_login.action" namespace="/" method="post">
				<s:text name="i18n.login.userName"/>: <s:textfield  name="name"/>  <br/>
				<s:text name="i18n.login.password"/>: <s:password name="password"/><br/>
	   			<input type="submit" value="<s:text name='i18n.login.submit'/>"/>
	   			<input type="reset" value="<s:text name='i18n.login.reset'/>"/>
		</s:form>
	</div>
	<s:select name="i18nCharset" id="i18nSelect" list="#{'zh_CN':'中文','en_US':'英文'}">
   				<%--這裡中文和英文也應該用國際化<s:text>顯示的,為了簡便,為中文寫出來 
   					不能用html註釋--%>
   				<!-- <option value="zh_CN">中文</option>
   				<option value="en_US">英文</option> -->
   	</s:select>

引入相應的js並且實現程式碼

<!-- 線上引入jquery 需要聯網,使用者可以自己下載後本地引用即可-->
<script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script>
<script>
	$(document).ready(function(){
		$("select#i18nSelect").change(function(){
			//select改變時觸發整個
			//取得option中的值
			var charset=$("select#i18nSelect option:selected").val();
			$.post("/Struts_i18n/Login_charsetChange.action",{"request_locale":charset},
					 function(data,status){
		       			 //成功之後,重新整理一下介面
		       				window.location.reload();
		       				//設定回顯所選擇的語言型別 沒有作用的
		       					$("select#i18nSelect").val(charset);
		       				
					}
		    );
		   //window.location.href="/Struts_i18n/Login_charsetChange.action?//request_locale="+charset;
		})
		
	});
	
</script>

那麼Action中:

//語言編碼改變時呼叫這一個
	public String charsetChange(){
		return "charsetChange";
	}

在struts.xml中

<package name="user" extends="struts-default,json-default" namespace="/">
		<!-- 配置攔截器,對每一個action都進行攔截 -->
		<interceptors>
			<interceptor name="i18nInterceptor" class="com.yjl.web.interator.I18NInterceptor"></interceptor>
			<interceptor-stack name="defaultStack">
				<interceptor-ref name="i18nInterceptor">
					<param name="country">en</param>
					<param name="language">US</param>
				</interceptor-ref>
				<interceptor-ref name="defaultStack"></interceptor-ref>
			</interceptor-stack>
		</interceptors>
		<action name="Login_*" class="com.yjl.web.action.LoginAction" method="{1}">
				<result name="toLogin">/login.jsp</result>
				<result name="charsetChange">/login.jsp</result>
				<result name="success">/success.jsp</result>
				<result name="list">/list.jsp</result>
		</action>
	</package>
</struts>

需要引入關於json的包json-default, 其中必須要引入json與struts的jar包。

 重啟伺服器,執行。 並不能達到預期的效果。原因是,每次在重新整理之後,都會將整個頁面重新整理,後面的js程式碼根本不執行。除錯了很長時間,不行。

最後決定用兩個ajax來實現,也就是說將設定select的值單獨放在一個ajax裡面。這樣就可以使用了。(用到的知識點是Struts2利用ajax傳遞json資料)

Action中:

新增一個charset字串資料,實現setter和getter方法,傳遞選擇的資料。然後新增一個getCharacterSelect()方法。

package com.yjl.web.action;

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

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

/**
* @author 兩個蝴蝶飛
* @version 建立時間:Aug 27, 2018 10:56:50 AM
* 登入的國際化操作
*/
public class LoginAction extends ActionSupport{
	private static final long serialVersionUID = 1L;
	private String charset;
	public void setCharset(String charset) {
		this.charset = charset;
	}
	public String getCharset() {
		return charset;
	}

	//跳轉到首頁,用的是預設的語言環境
	public String toLogin(){
		return "toLogin";
	}
	//跳轉到登入頁面,用的是選擇的語言環境
	public String login(){
		return SUCCESS;
	}
	//根據所選擇的語言環境,繼續相應的跳轉,表示全域性性選擇語言。
	public String list(){
		return "list";
	}
	//語言編碼改變時呼叫這一個
	public String charsetChange(){
		return "charsetChange";
	}
	//獲取相應的編碼
	public String getCharsetSelect(){
		Map<String,Object> session=(Map<String, Object>) ActionContext.getContext().getSession();
		Locale locale=(Locale) session.get("WW_TRANS_I18N_LOCALE");
		//取得值
		charset=locale.getLanguage()+"_"+locale.getCountry();
		System.out.println("獲取的值:"+charset);
		return "getCharsetSelect";
	}
}

在struts.xml中:

    <action name="Login_*" class="com.yjl.web.action.LoginAction" method="{1}">
				<result name="toLogin">/login.jsp</result>
				<result name="charsetChange">/login.jsp</result>
                <!--注意json形式的寫法-->
				<result name="getCharsetSelect" type="json">
					<param name="root">charset</param>
				</result>
				<result name="success">/success.jsp</result>
				<result name="list">/list.jsp</result>
				
		</action>

在js中:

<!-- 線上引入jquery 需要聯網,使用者可以自己下載後本地引用即可-->
<script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script>
<script>
	$(document).ready(function(){
		$("select#i18nSelect").change(function(){
			//select改變時觸發整個
			//取得option中的值
			var charset=$("select#i18nSelect option:selected").val();
				$.post("/Struts_i18n/Login_charsetChange.action",{"request_locale":charset},
						 function(data,status){
			       			 //成功之後,重新整理一下介面
			       			window.location.reload();
						}
			    );
		   // window.location.href="/Struts_i18n/Login_charsetChange.action?request_locale="+charset;
		});
	});
	$(document).ready(function(){
		$.post("/Struts_i18n/Login_getCharsetSelect.action",
				 function(data,status){
					//alert("執行成功");
					//alert(data);
					var charset=data;
					//alert(charset);
					//alert("zh_CN");
					//alert(charset=="zh_CN");
					//alert(charset);
	       			 $("select#i18nSelect").val(charset);
				}
		);
	});
</script>

 這樣就可以實現自動回顯的操作了。 注意兩個js思想的使用。

 

謝謝!!!