ResourceBundle

通过设置Locale值 通常有值给你选的,可以通过获取配置文件中的配置项 来设置显示的国际化资源;

 

StringManager Tomcat学习

知识点: ResourceBundle  和  MessageFormat

在项目里用的得心应手的properites文件,大多要用到这两个类吧。

java.util.ResourceBundle
java.text.MessageFormat

1,ResourceBundle解析资源文件分两步:1加载资源文件,2获取资源文件中的信息

// 加载资源文件
ResourceBundle resource = ResourceBundle.getBundle("messages");
// 获取资源文件中的信息
String driverName = resource.getString("database.driver");

ResourceBundle支持多国语言:先把文件名取成类似这样myres_zh_CN.properties

然后:

Locale locale1 = new Locale("zh", "CN");
ResourceBundle resb1 = ResourceBundle.getBundle("myres", locale1);
resb1.getString("aaa");

2,MessageFormat用来格式化一个消息(字符串嘛)

直接网上类似代码:

String pig = "{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}{14}{15}{16}";  
Object[] array = new Object[]{"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q"};  
String value = MessageFormat.format(message, array);  
System.out.println(value);  //最终结果是:ABCDEFGHIJKLMNOPQ

3,结合在一起就可以实现,将properites文件解析出想要的消息体,然后格式化后给上一层方法用。

 

myResources.properties:

?
database.driver=com.mysql.jdbc.Drvier
database.url=jdbc:mysql: //localhost:3306:test
database.user={ 0 }
database.pass={ 0 }

例:

复制代码
public class ResourceBundleTest {
    public static void main(String[] args) {
        // 指明包路径和文件名即可
        ResourceBundle resource = ResourceBundle.getBundle("code.stu.ResourceBundle.myResources");
        String driverName = resource.getString("database.driver");
        String url = resource.getString("database.url");
        Object[] array1 = new Object[]{"root"};
        Object[] array2 = new Object[]{"test"};
        // 取得字符串,直接格式化
        String user = MessageFormat.format(resource.getString("database.user"), new Object[]{"root"});
        String pass = MessageFormat.format(resource.getString("database.pass"), new Object[]{"test"});

        System.out.println(driverName + url + user + pass);//结果:com.mysql.jdbc.Drvierjdbc:mysql://localhost:3306:testroottest
    }
}
复制代码


StringManager

在tomcat里,把错误日志信息的字符串写在properites文件里,如此一来,打印日志的事情就可以通过上面的两个类来解决了。

StringManager是管理打印日志的类,Tomcat的设计是,对每一个包提供自己的properites文件,也就是说,每一个包的日志信息只需要去各自包的properites文件里去找就可以了,然后Tomcat为每一个包提供一个StringManager实例,相当于一个包一个单例的效果(值得学习下)。各自的StringManager实例来管理各自包下的日志打印。

源码如下:

复制代码
package org.apache.catalina.util;

import java.text.MessageFormat;
import java.util.Hashtable;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.net.URLClassLoader;

public class StringManager {

    // ResourceBundle用于读取properties文件
    private ResourceBundle bundle;
    
    private static org.apache.juli.logging.Log log=
        org.apache.juli.logging.LogFactory.getLog( StringManager.class );
    
    // 私有的构造方法能够保证外界无法实例化自己,这也是单例实现的关键步骤
    private StringManager(String packageName) {
        // properties文件所在的package+“.LocalStrings”
        // 所有tomcat的日志使用的properties文件都依照这个形式来命名的
        String bundleName = packageName + ".LocalStrings";
        try {
            // 根据bundleName取得解析资源文件的实例
            bundle = ResourceBundle.getBundle(bundleName);
            return;
        } catch( MissingResourceException ex ) {// 好吧,异常就先不管了。
            // Try from the current loader ( that's the case for trusted apps )
            ClassLoader cl=Thread.currentThread().getContextClassLoader();
            if( cl != null ) {
                try {
                    bundle=ResourceBundle.getBundle(bundleName, Locale.getDefault(), cl);
                    return;
                } catch(MissingResourceException ex2) {
                }
            }
            if( cl==null )
                cl=this.getClass().getClassLoader();

            if (log.isDebugEnabled())
                log.debug("Can't find resource " + bundleName +
                    " " + cl);
            if( cl instanceof URLClassLoader ) {
                if (log.isDebugEnabled()) 
                    log.debug( ((URLClassLoader)cl).getURLs());
            }
        }
    }

    /**
     * Get a string from the underlying resource bundle.
     *
     * @param key The resource name
     */
    public String getString(String key) {
        return MessageFormat.format(getStringInternal(key), (Object [])null);
    }


    protected String getStringInternal(String key) {
        // key 还是要保证不是null
        if (key == null) {
            String msg = "key is null";

            throw new NullPointerException(msg);
        }
        // 返回string
        String str = null;

        if( bundle==null )
            return key;
        try {
            // 资源文件里去查有没有对应的内容
            str = bundle.getString(key);
        } catch (MissingResourceException mre) {
            str = "Cannot find message associated with key '" + key + "'";
        }

        return str;
    }
    public String getString(String key, Object[] args) {
        String iString = null;
        String value = getStringInternal(key);

        // this check for the runtime exception is some pre 1.1.6
        // VM's don't do an automatic toString() on the passed in
        // objects and barf out

        try {
            // ensure the arguments are not null so pre 1.2 VM's don't barf
            Object nonNullArgs[] = args;
            for (int i=0; i<args.length; i++) {
                if (args[i] == null) {
                    if (nonNullArgs==args) nonNullArgs=(Object[])args.clone();
                    nonNullArgs[i] = "null";
                }
            }
            // 格式化,就是把一些变化的参数插入到value这个string中去,格式化成一个新的最终的string
            iString = MessageFormat.format(value, nonNullArgs);
        } catch (IllegalArgumentException iae) {
            StringBuffer buf = new StringBuffer();
            buf.append(value);
            for (int i = 0; i < args.length; i++) {
                buf.append(" arg[" + i + "]=" + args[i]);
            }
            iString = buf.toString();
        }
        return iString;
    }

    // 下面四个getString方法,最终都需要调用getString(String key, Object[] args)
    public String getString(String key, Object arg) {
        Object[] args = new Object[] {arg};
        return getString(key, args);
    }

    public String getString(String key, Object arg1, Object arg2) {
        Object[] args = new Object[] {arg1, arg2};
        return getString(key, args);
    }

    public String getString(String key, Object arg1, Object arg2,
                            Object arg3) {
        Object[] args = new Object[] {arg1, arg2, arg3};
        return getString(key, args);
    }

    public String getString(String key, Object arg1, Object arg2,
                            Object arg3, Object arg4) {
        Object[] args = new Object[] {arg1, arg2, arg3, arg4};
        return getString(key, args);
    }
    
    // Hashtable维护整个tomcat的StringManager
    private static Hashtable managers = new Hashtable();

    // 保证一个包一个StringManager,私有化构造函数+Hashtable维护实现(值得学习)
    // 从而避免大量的StringManager实例化和销毁的操作,毕竟写日志属于比较频繁的操作。
    public synchronized static StringManager getManager(String packageName) {
        // 用一个Hashtable来管理控制,保证每个包提供一个StringManager
        StringManager mgr = (StringManager)managers.get(packageName);
        // 属于这个包的Manager有了吗
        if (mgr == null) {
            mgr = new StringManager(packageName);
            // 实例化好后,把它放进Hashtable里去,下次就不用实例化了
            managers.put(packageName, mgr);
        }
        return mgr;
    }
}


坠落凡间的struts2(7)---struts2的国际化

   
   

国际化原理

国际化原理是通过Locale,和ResourceBundle来实现的,如果需要格式化输出也可以使用MessageFormat进行格式化输出,资源文件需要放在classes的根路径下
最理想的实现国际化的方法是将要显示的字符内容从程序中分离,然后统一存储到一个资源包中,当显示时,从资源包中取出和平Locale对象相一致的字符内容。在Java中, 这种资源包是由类来实现的,这个类必须要扩展java.util.ResourceBundle. 在编写国际化程序时, 要为不同的国家地区和语言编写不同的资源类,这些资源类同属一个资源系列,共享同一个基名(base name).不同语言所对应的资源类的名称为基名加上ISO-639标准的语言代码,而应用于某个特定国家或地区的资源类的名称,则是在基名和语言代码后加上ISO-3166标准的同家或地区代码。 例如:有一个资源包系列的基名是MyResource,那么说中文的所有国家或地区共享的资源属于MyResource_zh类,中国台湾的特定资源则属于MyResource_zh_TW类。一个资源包系列可以有一个默认的资源包, 它的名字就是基名,当请求的资源包不存在时,将使用默认的资源包。 要获取某个资源包,可以调用java.util.ResourceBundle类中的静态方法getBundle(),如下: public static final ResourceBundle getBundle(String baseName) 根据基名得到资源包,使用系统缺省的Locale对象。 public static final ResourceBundle getBundle(String baseName,Locale locale) 根据基名和Locale对象得到资源名。
[java]  view plain copy print ?
  1. package org.senssic.temp;  
  2.   
  3. import java.text.MessageFormat;  
  4. import java.util.Date;  
  5. import java.util.Locale;  
  6. import java.util.ResourceBundle;  
  7.   
  8. public class LocationC {  
  9.     public static void main(String[] args) {  
  10.         for (Locale locale : Locale.getAvailableLocales()) {  
  11.             System.out.println(locale.getCountry() + "--->"  
  12.                     + locale.getDisplayLanguage());  
  13.             System.out.println(locale.getDisplayCountry() + ":"  
  14.                     + locale.getDisplayName() + ":"  
  15.                     + locale.getDisplayVariant() + ":"  
  16.                     + locale.getISO3Country() + ":" + locale.getISO3Language()  
  17.                     + ":" + locale.getVariant());  
  18.         }  
  19.   
  20.         ResourceBundle resourceBundle = ResourceBundle.getBundle("db",  
  21.                 Locale.getDefault());  
  22.         Object[] obj = { "祁煜"new Date() };  
  23.         String str = MessageFormat.format(resourceBundle.getString("senssic"),  
  24.                 obj);  
  25.         System.out.println(str);  
  26.     }  
  27. }  
资源包查找顺序:
假设Locale为使用中文的中国大陆地区,资源包基名为MyResource , ResourceBundle类的静态方法getBundle(String baseName)按照下列的顺序查找资源包: MyResource_zh_CN.class MyResource_zh_CN.properties MyResource_zh.class MyResource_zh.properties MyResource.class MyResource.properties

struts2的国际化

Struts2资源包的组织和加载方式

Struts2提供了灵活的资源包组织和加载方式,你可以为某个类提供一个资源包,也可以为某一个接口提供一个资源包,还可以为包中所有的类提供一个资源包,不同级别的资源包适用的范围的加载的顺序是不一样的。 在查找字符串时,Struts2将按照下面的顺序搜索资源包: 1)查找与调用的Action类同名的资源文件(与Action在同一个包中)。例如,Action类名为RegAction,当访问RegAction时,将首先查找RegAction.properties. 2)查找所有与Action类的基类同名的资源文件,直到Object.properties.例如,RegAction的基类为ActionSupport,那么将查找ActionSupport.properties,如果没有找到消息字符串,则继续查找ActionSupport基类,直到object.properties. 3)查找所有与Action类实现的接口同名的资源文件,例如,RegAction实现了Action, Validateable接口,则依次搜索Action.properties和Validateable.properties. 4)如果Action类实现了ModelDriven接口,则struts2会调用getModel()方法获得模型对象,然后以模型对象所属的类进行了类层次的查找,这将从步骤1)开始重复。 5)查找类所在的包和父包中的package.properties,直到最顶层包。例如:RegAction在com.ibm.action中,则依次查找action目录、ibm目录、com目录中的package.properties资源文件。 6)查找I18N消息key本身的层次关系。例如:某个消息key是user.label.username,如果user是Action类的某个属性,则以该属性所属的类,在它的类层次中去查找key为label.username的消息字符串,这与前面的步骤是一样的。 7)查找默认的资源包。

查找资源文件的优先级

顺序:
高  |       action范围资源文件
      |          当前action类对应的资源
      |          当前action类实现的接口对应的资源   
      |          当前action类父类对应的资源
      |
      |       包范围资源文件
      |           当前包对应资源
      |           当前包父包对应资源
      |        
      |        全局范围资源文件
      |
      |
      |        struts2默认资源文件
底\ |/
你可以在struts.properties或struts.xml文件中,通过struts.custom.i18n.resources属性设置默认的资源包。例如: struts.custom.i18n.resources=com.ibm.action.ApplicationResources 这表明ApplicationResources是默认的资源包,它可以在com.ibm.action包中找到。 除了上述加载资源包的方式外,我们还可以使用struts2和i18n标签来加载特定的资源包。如果消息字符串没有在i18n标签指定的资源包中找到,则将按照上述的7个步骤进行查找。

在消息文本中使用参数

Struts2提供了两种在消息文本中设置参数的方式。一种是沿袭了Java中设置文本参数的方式,即使用从{0}到{9}的占位符。当使用MessageFormat类的format方法格式化消息字符串时,参数被传进来,用来替换消息文本中的占位符。另一种方式是在消息文本中使用OGNL表达式,不同于在标签的属性中使用OGNL表达式,在消息文本中使用的OGNL表达式以“${”开始,并以“}”结束,其语法格式为:${expr}. 例如:当用户登录后, 我们要向用户显示如下的欢迎信息: 张三,你好,欢迎你的访问! 那么我们可以在资源文件中编写如下的消息文本: Greeting=${username},你好,欢迎你的访问! 在登录成功页面中,使用text标签输出资源文件中的消息文本,如下所示: <s:text name=“greeting” /> 在获取键为greeting的消息文本时,“${“和“}”中的表达式username将根据值栈自动进行计算,最终action类的username属性值将被用于替换消息文本中的”${username}”. 在消息文本中使用数字占位符可以看成是被动地接受值,而使用OGNL表达式则可以看成主动的去获取值。

访问国际化消息

Struts2提供了多种方式来访问资源文件中的本地化消息,以适应不同的应用场景,主要分为

在action中访问本地化消息

Struts2在com.opensymphony.xwork2.TextProvider接口中定义了访问本地化消息的方法,ActionSupport类实现了这个接口,如果我们编写的action类继承自ActionSupport类,那么在action中就可以直接使用这些方法。这也是为什么我们在大多数的开发中,我们应该首选让action类继承ActionSupport,而不是实现Action接口。 public String getText(String aTextName)  获取以参数aTextName为键的消息字符串,如果没有找到,则返回null. public String getText(String aTextName, String defaultValue)  获取以参数aTextName为键的消息字符串,如果没有找到,则返回defaultValue. public String getText(String aTextName, List args)  获取以参数aTextName为键的消息字符串,参数args用于替换消息字符串中的占位符,列表中的第一个元素替换占位符{0},第二个元素替换占位符{1}….,依次类推。 public String getText(String aTextName, String[] args)  获取以参数aTextName为键的消息字符串,参数args用于替换消息字符串中的占位符,数组中的第一个元素替换占位符{0},第二个元素替换占位符{1}….,依次类推。 
在action中访问本地消息示例 greeting={0},欢迎学习struts2,今天是 {1}. 在action中可以使用getText()方法按照如下调用方式获取键为greeting的消息字符串: String msg=getText(“greeting”,new String[ ]{“张三”, new java.util.Date().toString()});

在JSP页面中访问本地化消息

Struts2提供了text标签,用于在JSP页面中访问本地化消息。例如:对于下面的消息文本: title=用户注册 在JSP页面中可以使用text标签访问键为title的消息字符串,如下所示: <s:text name=“title”  /> 如果消息文本中有参数,那么可以使用嵌套的param标签来设置参数。例如,对于下面的消息文本: greeting={0},欢迎学习struts2,今天是 {1}. 在JSP页面中可以按照如下方式使用text标签: <s:text name=“greeting”> <s:param value=“ ’张三’ ”  /> <s:param value=“new java.util.Date()”  /> </s:text> Param标签的顺序对应了消息文本中的数字占位符,第一个param标签传递的参数替换占位符{0},第二个元素替换占位符{1}….,依次类推,最多可以使用10个param标签。

在表单标签的属性中访问本地消息

由于struts2标签的属性可以使用OGNL表达式,而OGNL表达式又支持对类中的方法进行调用,所以在需要访问本地化消息时,如果值栈中存在着从ActionSupport类继承的action类实例,那么也可以使用getText()方法获取资源文件中的消息字符串。 在使用表单标签时,label属性的值通常都是从资源文件中获取的。在label属性中,我们可以使用getText()方法来获取消息字符串,如下所示: <s:textfield name=“user.username”  label=“%{getText(‘username’)}”  /> 除了使用getText()方法外,我们还可以使用表单标签的key属性来指定消息字符串的key,该属性可以使用消息字符串来自动生成label属性的值,如下所示: <s:textfield name=“user.username” key=“username” />

在资源文件中访问本地消息。

在消息文本中可以使用OGNL表达式,当然也可以利用OGNL表达式在一个消息文本中去访问另一个本地化消息。 在资源文件中使用OGNL表达式${getText(“key”)}来访问本地化消息。有时候,一个消息文本可能需要由另一个消息文本来组成,使用OGNL表达式将给我们提供极大的便利。例如,有如下的消息文本: email=邮件地址 error.email.invalid=${getText(“email”)} 格式错误 在程序运行过程中,当访问键为error.email.invalid的消息时,表达式getText(“email”)将被计算,消息文本中的“${getText(“email”)} ”将被“邮件地址”所替换。当我们需要修改键为email的消息文本时(例如,改为“邮箱地址”),键为error.email.invalid的消息文本不需要做任何改动。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值