所谓的i18n问题指的是(软件的)国际化问题,简单来讲就是使我们的软件可以让世界使用任何语言的人们都能使用,软件自身会根据语言环境的不同进行自动配置,如果你是中文环境那界面以中文显示,如果是英文环境就以英文显示。在i18n问题的解决中,国际化资源文件是不可或缺的,程序正是通过对资源文件的读取来决定究竟以何种界面显示。说到这里如果你认为国际化资源文件仅仅能够解决不同语言版本的显示问题那你就错了,通过对国际化资源文件进行配置我们同样可以实现消息的动态创建和显示。下面我们就来通过实例详细讲解struts如何解决i18n问题。
首先来简单了解国际化资源文件。国际化资源文件是由basename + local+.properties构成的,local指的是语言环境,缺省的local是由操作系统决定的,local由语言和国家代码组成,basename为MessagesBundle,以中文为例,其资源文件名称为:MessagesBundle_zh_CN.properties,缺省的国际化资源文件名称为MessagesBundle.properties。在该文件中,存储方式为key-value形式,所有的key均对应一个value值,程序通过读取该文件中的key值确定应显示的文本。下面通过实例来讲解用struts如何应用资源文件解决i18n问题。
1. struts的国际化配置。在struts-config.xml文件中加入<message-resourcesparameter="MessageResources"/>。注意parameter的值为国际化资源文件所在的路径,如果国际化资源文件位于src下一个名为resources的文件夹下,则此处parameter的值应为“resources.MessageResources”。
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd"> <struts-config> <form-beans> <form-bean name="loginForm" type="com.bjpowernode.struts.LoginActionForm"/> </form-beans> <action-mappings> <action path="/login" type="com.bjpowernode.struts.LoginAction" name="loginForm" scope="request" > <forward name="success" path="/login_success.jsp"/> <forward name="error" path="/login.jsp"/> </action> <action path="/changLang" type="com.bjpowernode.struts.ChangeLanguageAction" > <forward name="index" path="/index.jsp"></forward> </action> </action-mappings> <message-resources parameter="resources.MessageResources"/> </struts-config>
2. 提供国际化资源文件。在本例中我们提供三个国际化资源文件,分别为:中文的国际化资源文件MessageResources_zh_CN.properties,英文的国际化资源文件MessageResources_en_US.properties以及缺省的国际化资源文件:MessageResources.properties。注意在中文的国际化文件中,需要把所有的中文转换成unicode编码格式。
3.利用struts默认将locale放到session中的特性,我们可以完成语言的自动切换。
package com.bjpowernode.struts; import java.util.Locale; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.Globals; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; public class ChangeLanguageAction extends Action { @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { //取得客户端传过来的参数 String lang = request.getParameter("lang"); //取得session中默认的locale Locale locale = Locale.getDefault(); //如果传过来的参数为“zh”将locale设为中文,如果参数为“en”则将locale设为英文 if("zh".equals(lang)){ locale = new Locale("zh","CN"); }else if("en".equals(lang)){ locale = new Locale("en", "US"); } //将locale设置到session中 //request.getSession().setAttribute(Globals.LOCALE_KEY, locale); this.setLocale(request, locale); return mapping.findForward("index"); } }
4.上文中我们已经提过国际化资源文件的作用并不仅仅在于语言环境的转变,他还支持页面的个性化设计以及动态消息的创建。下面我们提供一个英文的资源文件,并以此文件为例介绍动态消息的创建。
4.1 英文国际化资源文件:
errors.header=<UL> errors.prefix=<font color="blue"><LI> errors.suffix=</LI></font> errors.footer=</UL> login.form.field.username=User Name login.form.field.password=Password login.form.button.login=Login login.success={0},Login Success login.user.not.found=User Not Found,UserName=[{0}] login.password.error=Password Error
4.2 创建动态消息。动态消息的创建分为三个步骤,分别是 创建国际化消息文本,传递国际化消息文本,显示国际化消息文本。在上面的资源文件按中我们可以看到占位符的存在,占位符的存在就给我们动态的创建信息提供了可能,下面看我们是如何通过填充占位符创建动态信息的。
package com.bjpowernode.struts; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; public class LoginAction extends Action { @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { LoginActionForm laf = (LoginActionForm)form; String username = laf.getUsername(); String password = laf.getPassword(); UserManager userManager = new UserManager(); ActionMessages messages = new ActionMessages(); try{ userManager.login(username, password); //创建国际化消息文本 //ActionMessage message = new ActionMessage("login.success",new Object[]{username}); ActionMessage message = new ActionMessage("login.success",username); messages.add("login_success_1",message); ActionMessage message1 = new ActionMessage("login.success",username); messages.add("login_success_2",message1); //传递国际化消息 this.saveMessages(request, messages); return mapping.findForward("success"); }catch(UserNotFoundException e){ e.printStackTrace(); //创建国际化消息文本 ActionMessage error = new ActionMessage("login.user.not.found",username); messages.add("error_1",error); //传递国际化消息 this.saveErrors(request, messages); }catch(PasswordErrorException e){ e.printStackTrace(); //创建国际化消息文本 ActionMessage error = new ActionMessage("login.password.error"); messages.add("error_2",error); //传递国际化消息 this.saveErrors(request, messages); } return mapping.findForward("error"); } }
请注意上面代码中的这条语句:ActionMessagemessage = new ActionMessage("login.success",username);,该语句的作用就在于填充国际化资源文件中的占位符,通过这种方式我们可以动态的创建消息。
5. 在jsp中采用<bean:message>读取国际化消息文本(本例中以登录成功后的页面为例)。
<%@ page language="java" contentType="text/html; charset=GB18030" pageEncoding="GB18030"%> <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GB18030"> <title>Insert title here</title> </head> <body> <html:messages id="msg" message="true" > <bean:write name="msg"/> </html:messages> </body> </html>
在该jsp中我们采用的是html:messages标签来读取信息的,在这里需要注意的一点是,这个标签既可以读取message key又可以读取error key,而html:error标签只可以读取error key。根据这一点我们可以决定在错误处理页面和成功页面具体使用哪种标签进行读取。另外使用jstl标签同样可以读取国际化资源文件中的消息,再引入jstl格式化标签库后,使用<html:errors>标签可以读取错误信息,使用<fmt:message>标签可以读取消息。