struts的message标签换成EL表达式(国际化相关)
对于习惯于使用EL表达式的人来说,用struts的message标签简直是种倒退。message用到地方非常多,每次都敲<bean:message key="...."/>,在这个标签污染严重的时代可不是什么好事啊。
幸好,将其转换为EL表达式不难。
要点:
EL表达式对java.util.Map对象的点运算符的递归调用。举例登录页面,${message.login.username}。
解释一下:
只要request里面有个属性名叫"message"的Map,这个Map里有个字符串"login"的key,这个Key指向一个Map,而这个Map里有个字符串"username"的key,这个key指向一个字符串。 |
而这个字符串就是我们要的Message。我们要做的就是把这些MessageResource文件中的key转换成这样的格式 ----- message.login.username。
唯一的缺点就是需要有一个"根"key在JSP的某个Context上下里(最好是Session,下面会讲到)。"message"就是"根"key,但我们可以让它更简便些${_msg.login.username}或者干脆${_.login.username}.这样,资源文件中的键值对就被转换到树形的Map群里了,这里我们给它个名字“资源Map”。
好了,还有一个需要解决的问题,国际化。如果对struts有研究的话,应该会知道用户的语言种类是记录在HttpSession "org.apache.struts.action.LOCALE"(struts的org.apache.struts.Globals.LOCALE_KEY)属性中的java.util.Locale对象。通过HttpSessionAttributeListener可以侦测到其值变化(用户改变语言的时候)。这样,只需要一个全局的Map记住所有资源文件转换而成的“资源Map”,key就是Locale.toString()。只要用户的Locale发生了变化,通过侦听器切换用户Session的“根”key指向“资源Map”即可。
废话不多说,开始行动:
第一个,先做一个读取资源文件并转换“资源Map”的类MessagePreLoader,假设你已经用上Spring了。
Java代码
1 package com.your.struts.base.bean; 2 3 import java.io.InputStream; 4 import java.util.HashMap; 5 import java.util.Iterator; 6 import java.util.Map; 7 import java.util.Properties; 8 9 import org.springframework.beans.factory.InitializingBean; 10 11 public class MessagePreLoader implements InitializingBean { 12 /** 13 * 默认资源Map对应的Key 14 */ 15 private static final String DEFAULT_LOCAL_KEY = "default"; 16 /** 17 * 全局的Map,所有资源Map的挂载点。 18 */ 19 private Map resourceHolder = new HashMap(); 20 /** 21 * struts的资源文件的位置。假设struts的配置是: 22 * <message-resources parameter="com.your.struts.message.ApplicationResources" > 23 * 那么此处则是com/your/struts/message/ApplicationResources 24 * 所有“.”换成“/” 25 */ 26 private String resourceName ; 27 /** 28 * 所支持的语言对象集合. 29 */ 30 private String[] locales = {}; 31 /** 32 * 设定资源文件是xml格式还是普通的properties文件。此处默认是xml格式(不用借助插件就可以直接用XML编辑器编辑) 33 */ 34 private boolean xmlResouce=true; 35 36 public boolean isXmlResouce() { 37 return xmlResouce; 38 } 39 40 public void setXmlResouce(boolean xmlResouce) { 41 this.xmlResouce = xmlResouce; 42 } 43 44 public String[] getLocales() { 45 return locales; 46 } 47 48 public void setLocales(String[] locales) { 49 this.locales = locales; 50 } 51 52 public String getResourceName() { 53 return resourceName; 54 } 55 56 public void setResourceName(String resourceName) { 57 this.resourceName = resourceName; 58 } 59 60 public Map getResourceHolder() { 61 return resourceHolder; 62 } 63 64 private void addMap(String localeKey) throws Exception{ 65 String name=null; 66 if(DEFAULT_LOCAL_KEY.equals(localeKey)){ 67 name=this.resourceName+(xmlResouce?".xml":".properties"); 68 }else{ 69 name=this.resourceName+"_"+localeKey+(xmlResouce?".xml":".properties"); 70 } 71 Map map = new HashMap(); 72 this.resourceHolder.put(localeKey, map); 73 ClassLoader classLoader = 74 Thread.currentThread().getContextClassLoader(); 75 76 if (classLoader == null) { 77 classLoader = this.getClass().getClassLoader(); 78 } 79 80 InputStream is = classLoader.getResourceAsStream(name); 81 Properties props = new Properties(); 82 if(xmlResouce){ 83 props.loadFromXML(is);//此处是因为本人项目所用的资源文件是xml,方便编辑而不必用特殊的插件。 84 }else{ 85 props.load(is);//不用InputStreamReader的方式是因为struts资源文件本身就只使用InputStream方式。 86 } 87 for(Iterator iter = props.keySet().iterator();iter.hasNext();){ 88 String key = (String)iter.next(); 89 String[] strs=key.split("\\."); 90 String value = props.getProperty(key); 91 Map last = map; 92 for (int j = 0; j < strs.length; j++) { 93 Object o=last.get(strs[j]); 94 if(o==null){ 95 if(j!=strs.length-1){//不是最后一个key 96 o=new HashMap(); 97 last.put(strs[j], o); 98 last=(Map)o; 99 }else{//最后一个Key 100 last.put(strs[j], value); 101 } 102 }else{ 103 if(j!=strs.length-1){//不是最后一个key 104 if(o instanceof String)throw new Exception(" is a java.lang.String ,expact java.util.Map," + 105 "cause by key conflict,such as: \"x.y.z\" and \"x.y\" all exists. current key:"+key); 106 last=(Map)o; 107 }else{//最后一个Key 108 throw new Exception(" dulpicat key exists , current key:"+key);//此类情况应该永远不会发生 109 } 110 } 111 } 112 } 113 } 114 115 public void afterPropertiesSet() throws Exception { 116 this.addMap(DEFAULT_LOCAL_KEY); 117 for (int i = 0; i < locales.length; i++) { 118 this.addMap(locales[i]); 119 } 120 } 121 122 public Map getLocaleMessageSet(String localeKey){ 123 Object obj=this.resourceHolder.get(localeKey); 124 return (Map)(obj==null?this.resourceHolder.get(DEFAULT_LOCAL_KEY):obj); 125 } 126
Spring 配置:
1 <bean id="messagePreLoader" class="com.your.struts.base.bean.MessagePreLoader"> 2 <property name="xmlResouce" value="false"></property> 3 <property name="resourceName" value="com/your/struts/message/ApplicationResources"></property> 4 <property name="locales"> 5 <list> 6 <value>zh_CN</value> 7 <value>en</value> 8 </list> 9 </property> 10 </bean>
制作SessionListener的委派类:
SessionListenerDelegate用于侦听locale信息的改变。
1 <listener> 2 <listener-class> 3 com.your.servlet.listener.SessionListenerDelegate 4 </listener-class> 5 </listener>
这样。我们就可以在JSP中使用${_msg....}这样的EL表达式来访问国际化的资源信息了。
1 <table> 2 <tr><td>${_msg.commons.login.username}:</td><td><input type='text' name='j_username' value='<c:if test="${not empty param.login_error}"><c:out value="${lastUserName}"/></c:if>'/></td></tr> 3 <tr><td>${_msg.commons.login.password}:</td><td><input type='password' name='j_password'></td></tr> 4 </table>
没有了message标签,输入也方便了。何乐不为?
最后,需要注意的地方,你的message文件中不能出现这样的打架现象:
1 commons.login=User Login 2 commons.login.username=User Name
这样小小的请求,我想你是可以忍受的。
另外,如果觉得这个方式对你项目没有帮助,请勿使用。。。
谢谢原作者,学习了,转自:http://llade.iteye.com/blog/264372