最爱Java

书山有路勤为径,学海无涯苦作舟

2009年10月23日

    Java程序的国际化主要通过三个类完成:

    java.util.ResourceBundle:用于加载一个资源包。

    java.util.Locale:对应一个特定的国家/区域、语言环境。

    java.text.MessageFormat:用于消息格式化。

    而资源文件的命名方式主要有三种:baseName_language_country.properties、baseName_language.properties和baseName.properties。

    以下的程序可以得到Java所支持的语言和国家:

public class LocalList {
    
public static void main(String[] args){
        Locale[] localeList 
= Locale.getAvailableLocales();
        
for (int i=0;i<localeList.length;i++){
            System.out.println(localeList[i].getdisplayCountry() 
+ "=" + localeList[i].getCountry() + " " + localeList[i].getDisplayLanguage() + "=" + localeList[i].getLanguage());
        }

    }

}

    使用国际化的代码如: 

public class Hello{
    
public static void main(String[] args){
        Locale myLocale 
= Locale.getDefault();
        ResourceBundle bundle 
= ResourceBundle.getBundle("mess",myLocale);
        System.out.println(bundler.getString(
"hello"));
    }

}

    如果在资源文件中,存在例如msg = Hello,{0}!Today is {1}.这样需要程序动态插入参数的文本,则需要使用MessageFormat类的format()方法。

    除了使用资源文件,我们也可以使用类文件来代替资源文件。使用Java文件代替资源文件的Java文件必须满足:1。类名必须是baseName_language_country,这与属性文件的命名相似。2。该类必须继承ListResourceBundle,并重写getContents方法,该方法返回Object数组。该数组的每一个项都是key-value对。

public class MyResource_zh_CN extends ListResourceBundle {
    
//定义资源
    private final Object myData[][] = {
        
{"msg","类文件消息:{0},您好!今天是{1}"}
    }
;
    @Override
    
public Object[][] getContents(){
        
return myData;
    }

}


    对于简体中文的Locale,ResourceBundler搜索资源的顺序是:

    baseName_zh_CN.class;baseName_zh_CH.properties;baseName_zh.class;baseName_zh.properties;baseName.class;baseName.properties

 

    Struts2访问国际化消息,主要有3种方式:1)JSP页面输出国际化消息,可以使用Struts2的<s:text.../>标签,该标签可以指定一个name属性,该属性就是资源文件中的key。2)在Action中,可以使用ActionSupport类的getText方法,该方法可以接受一个name参数,指定了国际化资源文件中的key。3)表单元素的Label,可以为表单标签指定一个key属性,这个key指定了国际化资源文件的key。

     对于带占位符的国际化消息,在Action中,则需要使用getText(String key, String[] args)来处理,其中args就是参数列表;而在页面中,则需要为<s:text.../>标签指定<s:param.../>子标签。如:

<s:text name="welcomeMsg">
    
<s:param><s:property value="username"></s:param>
</s:text>


   在Struts2中,还有一种更加简单的表达方式。我们可以在资源文件中写例如这样的表达式:failTip=${username},对不起,您不能登录!,通过使用表达式,可以从ValueStack中取出username属性值,自动填充到消息资源中。这在Action中很常用。

    对于一个大型应用而言,国际化资源文件的管理也是一个非常浩大的工程。为了能更好的分而治之,Struts2允许针对不同的模块、不同Action来组织国家化资源文件。

     为Strut2应用指定包范围资源文件的方法是:在包的跟路径下建立多个文件名为package_language_country.properties的文件,一旦建立了这个系列的国际化资源文件,应用中处于该包下的所有Action都可以访问该资源文件。

    例如一个Action为codes\packageScope\src\lee\action\LoginAction.java,那么我们可以提供package_zh_CN.properties和package_en_US.properties两个文件放在codes\packageScope\src\lee目录下,那么这两个文件就能被lee包及lee包下所有子包内的Action所能访问。

    同时,我们也可以为LoginAction单独指定一份国际化资源文件。即在codes\packageScope\src\lee\action目录下,分别建立LoginAction_zh_CN.properties和LoginAction_en_US.properties两个文件。

    有时候,处于某种特殊的原因,我们需要临时指定资源文件,那么就需要使用<s:i18n.../>来充当<s:text.../>标签的父标签了。如:

<s:i18n name="tmp">
    
<s:text name="loginPage"/>
</s:i18n>

<s:i18n name="tmp">
    
<s:form action="login">
        
<s:textfield name="username" key="user"/>
        
<s:textfield name="password" key="pass"/>
        
<s:submit key="login"/>
    
</s:form>
</s:i18n>


    加载资源文件的顺序:

  1.     优先加载系统中保存在ChildAction的类文件相同位置,且baseName为ChildAction的系列资源文件。
  2.     如果在1)中找不到key对应的消息,且ChildAction有父类ParentAction,则加载系统中保存在ParentAction的类文件相同位置,且baseName为ParentAction的系列资源文件。
  3.     如果2)中找不到key对应的消息,且ChildAction有实现接口IChildAction,则加载系统中保存在IChildAction的类文件相同位置,且baseName为IChildAction的系列资源文件。
  4.     如果3)中找不到key对应的消息,且ChildAction有实现接口ModelDriven(即使用模型驱动模式),则对于getModel()方法返回的model对象,重新执行1)步操作。
  5.     如果在4)中找不到key对应的消息,则查找当前包下baseName为package的系列资源文件。
  6.     如果在5)中找不到key对应的消息,则沿着当前包上溯,直到最顶层包来查找baseName为package的系列资源文件。
  7.     如果在6)中找不到key对应的消息,则查找struts.custom.i18n.resources常量指定baseName的系列资源文件。
  8.     如果经过上面步骤一直找不到key对应的消息,将直接输出该key的字符串值

      对于在JSP中访问国际化消息,则简单很多,可以分为两种形式:

      1)对于使用<s:i18n.../>标签作为父标签的<s:text.../>标签,将直接从<s:i18n.../>标签指定的国际化资源文件中加载指定key对应的消息,如果没有,则读取struts.custom.i18n.resources常量指定baseName的系列文件。如果还没有,直接输出key。

     2)对于没有使用<s:i18n.../>标签作为父标签的<s:text.../>标签,则直接读取struts.custom.i18n.resources常量指定baseName的系列文件。如果没有,直接输出key。

    Struts2中, 我们可以通过ActionContext.getContext().setLocale(Locale arg)设置用户的默认语言。同时在Struts2 的defaultStack拦截栈中,i18n拦截器也能设置默认语言。i18n在执行Action方法前,会自动查找一个名为request_locale的参数。如果这个参数存在,则拦截器会将其转换成Locale对象,并设为默认的Locale。同时,这个Locale对象会保存在Session的名为WW_TRANS_I18N_LOCALE的属性中。一旦用户的Session中有WW_TRANS_I18N_LOCALE属性,则属性指定的Locale将会作为浏览器的默认Locale。因此,用户也可利用此功能来开发自行选择语言的功能。

posted @ 2010-11-11 22:16 Brian 阅读(318) | 评论 (0)编辑 收藏
     摘要:     Struts2默认使用Jakarta的Common-FileUpload的文件上传解析器。见struts.properties配置文件中: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->#指定使用COS的文件上...  阅读全文
posted @ 2010-11-07 14:19 Brian 阅读(465) | 评论 (0)编辑 收藏

    对于login方法的校验,可以通过<ActionClassName>-<ActionAliasName>-validation.xml文件来校验,即RegistAction-login-validation.xml文件进行校验。同时,RegistAction-validation.xml的校验规则仍旧对login方法有效。即login方法的校验规则是Region-validation.xml和RegistAction-login-validation.xml的总和。
    如果RegistAction继承了BaseAction,那么对于BaseAction类的校验规则也会被RegistAction类所继承校验。具体来说,其校验规则的搜索文件规则如下:
        BaseAction-validation.xml
        BaseAction-别名-validation.xml
        RegistAction-validation.xml
        RegistAction-别名-validation.xml

    对于Struts2所支持的内建校验器,我们可以通过xwork.2.1.2.jar中的com/opensymphony/xwork2/validator/validators/default.xml文件查看。

    除了配置文件,Struts2也支持Annotation。下面就是使用Annotation配置的RegistAction:

@Validation()
public class RegistAction extends ActionSupport {
    private static final long serialVersionUID = -2113900523366315993L;
    
    //该请求包含的4个请求参数
    private String name;
    private String pass;
    private int age;
    private Date birth;
    
    
    public String getName() {
        return name;
    }
        @RequiredStringValidator(type=ValidatorType.FIELD,key="name.required",message="")
        @RegexFieldValidator(type=ValidatorType.FIELD,expression="\\w{4,25}",key="name.regex",message="")
    public void setName(String name) {
        this.name = name;
    }
    public String getPass() {
        return pass;
    }
        @RequiredStringValidator(type=ValidatorType.FIELD,key="pass.required",message="")
        @RegexFieldValidator(type=ValidatorType.FIELD,expression="\\w{4,25}",key="pass.regex",message="")
    public void setPass(String pass) {
        this.pass = pass;
    }
    public int getAge() {
        return age;
    }
        @IntRangeFieldValidator(message="",key="age.range",min="1",max="150")
    public void setAge(int age) {
        this.age = age;
    }
    public Date getBirth() {
        return birth;
    }
        @DateRangeFieldValidator(message="",key="birth.range",min="1900/01/01",max="2050/01/21")
    public void setBirth(Date birth) {
        this.birth = birth;
    }
}

     

    对于不能通过内置校验器进行校验的逻辑,则可通过重写validate()方法来实现。如果一个Action中存在多个逻辑处理,则需要通过validateXxx()方法来分别进行校验,其中Xxx代表方法名。如login()方法对应的校验方法为validateLogin()。

    Struts2的输入校验流程:

    1。类型转换器负责对字符串的请求参数执行类型转换,并将这些值设置成Action的属性值。

    2。在执行类型转换过程中,如果发生异常,则将异常保存到ActionContext中,并由conversionError拦截器负责将其封装到fieldError里。然后执行第3步;如无异常,直接执行第3步。

    3。调用Struts2内置的输入校验规则进行输入校验

    4。通过放射调用validateXxx()方法。

    5。调用validate()方法。

    6。如果上述都未发生fieldError,将调用Action里处理用户请求的处理方法;如果出现了fieldError,则转入input逻辑视图所指定的视图资源。

posted @ 2010-10-24 15:35 Brian 阅读(469) | 评论 (1)编辑 收藏
     摘要:     Struts2提供了基于验证框架的输入校验,在这种校验方式下,所有的输入校验只需要通过指定的配置文件即可。Struts2中每个Action都有一个校验文件,其规则为:<Action名字>-validation.xml。如: Code highlighting produced by Actipro CodeHighlighter (freewar...  阅读全文
posted @ 2010-10-24 15:01 Brian 阅读(345) | 评论 (0)编辑 收藏
     摘要: Ext.data.Connection     Ext.data.Connection是对Ext.lib.Ajax的封装,它提供了配置使用Ajax的通用方式,它在内部通过Ext.lib.Ajax实现与后台的异步调用。与底层的Ext.lib.Ajax相比,Ext.data.Connection提供了更简洁的配置方式,使用起来更方便。   ...  阅读全文
posted @ 2009-10-27 11:00 Brian 阅读(1388) | 评论 (1)编辑 收藏
     摘要: 简单菜单 //创建工具条 var tb = new Ext.Toolbar(); tb.render('toolbar'); //为工具条添加按钮 tb.add({     text:'新建',     //对应的事件处理函数   ...  阅读全文
posted @ 2009-10-26 14:43 Brian 阅读(2038) | 评论 (0)编辑 收藏
     摘要: 布局概述              在EXT中,所有的布局都是从Ext.Container开始的,Ext.Container的父类是Ext.BoxComponent。Ext.BoxComponent是一个盒子组件,可以定义宽度,高度和位置等属性。作为子类,Ext.Co...  阅读全文
posted @ 2009-10-26 12:23 Brian 阅读(2820) | 评论 (0)编辑 收藏
Ext.MessageBox

 1//Ext.MessageBox.alert()
 2Ext.MessageBox.alert('标题','内容',function(btn){
 3    alert('你刚刚点击了' + btn);
 4}
);
 5
 6//Ext.MessageBox.confirm()
 7Ext.MessageBox.confirm('选择框','你到底是选择Yes还是No?', function(btn) {
 8    alert('你刚刚点击了' + btn);
 9}
);
10
11//Ext.MessageBox.prompt()
12Ext.MessageBox.prompt('输入框','随便输入一些东西', function(btn,text) {
13    alert('你刚刚点击了' + btn + ", 刚刚输入了" + text);
14}
);
    
对话框的更多配置

 1//可以输入多行的输入框
 2Ext.MessageBox.show({
 3    title:'多行输入框',
 4    msg:'你可以多输入好几行',
 5    width:300,
 6    buttons:Ext.MessageBox.OKCANCEL,
 7    multiline:true,
 8    fn:function(btn,text){
 9        alert('你刚刚点击了' + btn + ", 刚刚输入了" + text);
10    }

11}
);
12
13//自定义对话框的按钮
14Ext.MessageBox.show({
15    title:'随便按个按钮',
16    msg:'从三个按钮里随便选择一个',
17    buttons:Ext.MessageBox.YESNOCANCEL,
18    multiline:true,
19    fn:function(btn){
20        alert('你刚刚点击了' + btn);
21    }

22}
);

    Ext.MessageBox中预设的4个按钮分别是OK,Yes,No,Cancel。如果不使用YESNOCANCEL这种预设变量,也可以直接使用{ok:true, yes:true, no:true,cancel:true}的形式,这样4个按钮都会显示在对话框中。

进度条

 1Ext.MessageBox.show({
 2    title:'请等待',
 3    msg:'读取数据中',
 4    width:240,
 5    progress:true,
 6    closable:false  //隐藏对话框右上角的关闭按钮,从而禁止用户关闭进度条
 7}
);
 8
 9//也可以直接使用Ext.MessageBox.progress()
10Ext.MessageBox.progress('请等待',msg:'读取数据中');
11

    上述的进度状态时不会发生变化的,我们需要调用Ext.MessageBox.updateProgress()函数,如以下为每秒变化,10秒后隐藏:

 1var f = function(v){
 2    return function(){
 3        if (v == 11{
 4            Ext.MessageBox.hide();
 5        }
 esle {
 6            Ext.MessageBox.updateProgress(v/10,'正在读取第' + v + '个,一共10个');
 7        }

 8    }
;
 9}
;
10for (var i = 1 ; i < 12 ; i++{
11    setTimeout(f(i) , i*1000);
12}

    还可以使用一种自动变化的进度条提示框,如Ext.MessageBox.wait('请等待', msg: ' 读取数据中');

动画效果
    
可以为对话框这是弹出和关闭的动画效果,使用animEl参数指定一个HTML元素,对话框就会依据指定的HTML元素播放弹出和关闭的动画。

窗口分组

 1        <script type="text/javascript">
 2var i = 0 , mygroup;
 3
 4function newWin(){
 5    var win = new Ext.Window({
 6        title:'窗口'+ i++,
 7        width:400,
 8        height:300,
 9        maximizable:true,
10        manager:mygroup
11    }
);
12    win.show();
13}

14
15function toBack(){
16    mygroup.sendToBack(mygroup.getActive());
17}

18
19function hideAll(){
20    mygroup.hideAll();
21}

22
23Ext.onReady(function(){
24    mygroup = new Ext.WindowGroup();
25
26    Ext.get("btn").on("click",newWin);
27    Ext.get("btnToBack").on("click",toBack);
28    Ext.get("btnHide").on("click",hideAll);
29}
);
30        </script>
31
32 <BODY>
33    <input id="btn" type="button" name="add" value="新窗口"/>
34    <input id="btnToBack" type="button" name="btnToBack" value="放到后台"/>
35    <input id="btnHide" type="button" name="btnHide" value="隐藏所有"/>
36 </BODY>

    示例中,所有通过newWind()方法产生的窗口都属于mygroup窗口组
posted @ 2009-10-25 11:24 Brian 阅读(5574) | 评论 (2)编辑 收藏
     摘要: 先看最简单的拖放: 1new Ext.dd.DDProxy('block'); 2//对应的HTML部分代码 3<div id="block" style="background:red;">&nbsp;</div>  拖放组件的体系     简单来说,左面4个组件都是...  阅读全文
posted @ 2009-10-23 23:42 Brian 阅读(3105) | 评论 (0)编辑 收藏

公告


导航

<2009年10月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

统计

常用链接

留言簿(4)

随笔分类

随笔档案

收藏夹

搜索

最新评论

阅读排行榜

评论排行榜