Internationalization of Error Message

Excerpted from .

A large application such as Tomcat needs to heandle error messages carefully. In Tomcat error messages are useful for both system administrators and servlet programmers. For example, Tomcat logs error messages in order for system administrators to easily pinpoint any abnormality that happened. For Servlet programmers, Tomcat sends a particular error message inside every javax.servlet.ServletException trown so that the programmer knows what gone wrong with his/her servlet.

The approach used in Tomcat is to store error messages in a properties file. so that editing them is easy. However, there are hundreds of classes in Tomcat. Storing all error messages used by all classes in one big properties file will easy create a maintenance nightware. To avoid this, Tomcat allocates a properties file for each package. For example, the properties file in the org.apache.catalina.connector package contains all error messages that can be thrown from any class in that package. Each properties file is handled by an instance of the org.apache.catalina.tribes.util.StringManager class. When Tomcat is run, there will be many instance of StringManager, each of which reads a properties file specific to a a package. Also, due to Tomcat's popularity, it makes sense to provide error messages in multi languages. Currently, four languages are supported. The properties file for English error messages is named LocalString.properties. The other three are for the French,  Spanish and Japanese languages. in the LocalString_fr.properties, LocalString_es.properties and LocalString_ja.properties files respectively.

When a class in a package needs to look up an error message in that package's properties file, it will first obtain an instance of StringManager. However, many classes in the package may need a StringManager and it is a waste of resources to create a StringManager instance for every object that needs error messages. The StringManager class therefore has been designed so that an instance of StringManager is shared by all objects inside a package. If you are familiar with design patterns, you'll guess correctly that StringManager is a singleton class. The only constructor it has is private so that you cannot use the new keyword to instantiate it from outside the class. You get an instance by calling its public static method getManager, passing a package name. Each instance is stored in a Hashtable with package names as its keys.

Pasted the StringManager class resided in Tomcat 7 of package org.apache.catalina.tribes.util as follows:

/*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package org.apache.catalina.tribes.util;
import java.text.MessageFormat;
import java.util.Hashtable;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* An internationalization / localization helper class which reduces
* the bother of handling ResourceBundles and takes care of the
* common cases of message formating which otherwise require the
* creation of Object arrays and such.
*
*

The StringManager operates on a package basis. One StringManager
* per package can be created and accessed via the getManager method
* call.
*
*

The StringManager will look for a ResourceBundle named by
* the package name given plus the suffix of "LocalStrings". In
* practice, this means that the localized information will be contained
* in a LocalStrings.properties file located in the package
* directory of the classpath.
*
*

Please see the documentation for java.util.ResourceBundle for
* more information.
*
* @version $Id: StringManager.java 939305 2010-04-29 13:43:39Z kkolinko $
*
* @author James Duncan Davidson [duncan@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]
* @author Mel Martinez [mmartinez@g1440.com]
* @see java.util.ResourceBundle
*/
public class StringManager {
    /**
     * The ResourceBundle for this StringManager.
     */
    private ResourceBundle bundle;
    private Locale locale;
    /**
     * Creates a new StringManager for a given package. This is a
     * private method and all access to it is arbitrated by the
     * static getManager method call so that only one StringManager
     * per package will be created.
     *
     * @param packageName Name of package to create StringManager for.
     */
    private StringManager(String packageName) {
        String bundleName = packageName + ".LocalStrings";
        try {
            bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault());
        } catch( MissingResourceException ex ) {
            // Try from the current loader (that's the case for trusted apps)
            // Should only be required if using a TC5 style classloader structure
            // where common != shared != server
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            if( cl != null ) {
                try {
                    bundle = ResourceBundle.getBundle(
                            bundleName, Locale.getDefault(), cl);
                } catch(MissingResourceException ex2) {
                    // Ignore
                }
            }
        }
        // Get the actual locale, which may be different from the requested one
        if (bundle != null) {
            locale = bundle.getLocale();
        }
    }
    /**
        Get a string from the underlying resource bundle or return
        null if the String is not found.
        @param key to desired resource String
        @return resource String matching key from underlying
                bundle or null if not found.
        @throws IllegalArgumentException if key is null.       
     */
    public String getString(String key) {
        if(key == null){
            String msg = "key may not have a null value";
            throw new IllegalArgumentException(msg);
        }
        String str = null;
        try {
            str = bundle.getString(key);
        } catch(MissingResourceException mre) {
            //bad: shouldn't mask an exception the following way:
            //   str = "[cannot find message associated with key '" + key + "' due to " + mre + "]";
            //     because it hides the fact that the String was missing
            //     from the calling code.
            //good: could just throw the exception (or wrap it in another)
            //      but that would probably cause much havoc on existing
            //      code.
            //better: consistent with container pattern to
            //      simply return null.  Calling code can then do
            //      a null check.
            str = null;
        }
        return str;
    }
    /**
     * Get a string from the underlying resource bundle and format
     * it with the given set of arguments.
     *
     * @param key
     * @param args
     */
    public String getString(final String key, final Object... args) {
        String value = getString(key);
        if (value == null) {
            value = key;
        }
        MessageFormat mf = new MessageFormat(value);
        mf.setLocale(locale);
        return mf.format(args, new StringBuffer(), null).toString();
    }
    // --------------------------------------------------------------
    // STATIC SUPPORT METHODS
    // --------------------------------------------------------------
    private static Hashtable managers =
        new Hashtable ();
    /**
     * Get the StringManager for a particular package. If a manager for
     * a package already exists, it will be reused, else a new
     * StringManager will be created and returned.
     *
     * @param packageName The package name
     */
    public synchronized static final StringManager getManager(String packageName) {
        StringManager mgr = managers.get(packageName);
        if (mgr == null) {
            mgr = new StringManager(packageName);
            managers.put(packageName, mgr);
        }
        return mgr;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值