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思想的使用。
謝謝!!!