“两个蝴蝶飞”特别喜欢"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思想的使用。
谢谢!!!