Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Exceptional practices, Part 3

Use message catalogs for easy localization

  • Print
  • Feedback

Page 3 of 4

In order to prevent runtime errors caused by typos in key names, the key names used by an application should be given symbolic names; these names should be used instead of the actual key names. That way, if you mistype a message string's name, you'll find out at compile time. (The Javadoc pages for ResourceBundle make the error of embedding strings in the text as keys, but you should strive to do better in your programs.) Here is a simple example of how the above code fragments would be written using resource bundles:

Listing 3. Build error messages with resource bundles

public interface FooResourcesKeys {
  public static String MSG_FILE_NOT_FOUND = "MSG_FILE_NOT_FOUND";
  public static String MSG_CANT_OPEN_FILE = "MSG_CANT_OPEN_FILE";
}
public class FooResources extends ListResourceBundle
implements FooResourcesKeys {
  public Object[][] getContents() {
    return contents;
  }
  
  static final Object[][] contents = {
    // Localize from here
    {MSG_FILE_NOT_FOUND, "Cannot find file {1}"},
    {MSG_CANT_OPEN_FILE, "Cannot open file {1}"},
    // Localize to here
  };
}
public class MessageUtil {
  private static ResourceBundle myResources =
    ResourceBundle.getBundle("com.foo.FooResources");
  private static String getMessageString(String messageKey) {
    return myResources.getString(messageKey);
  }
  public static String formatMessage(String messageKey) {
    MessageFormat mf = new MessageFormat(getMessageString(messageKey));
    return mf.format(new Object[0]);
  }
  public static String formatMessage(String messageKey, 
                                     Object arg0) {
    MessageFormat mf = new MessageFormat(getMessageString(messageKey));
    Object[] args = new Object[1];
    args[0] = arg0;
    return mf.format(args);
  }
  public static String formatMessage(String messageKey, 
                                     Object arg0,
                                     Object arg1) {
    MessageFormat mf = new MessageFormat(getMessageString(messageKey));
    Object[] args = new Object[2];
    args[0] = arg0;
    args[1] = arg1;
    return mf.format(args);
  }
  // Include implementations of formatMessage() for as many arguments
  // as you need
}
public class SomeClass implements FooResourcesKeys {
  ...
  if (!file.exists()) {
    throw new ResourceException(
      MessageUtil.formatMessage(MSG_FILE_NOT_FOUND, 
                                file.getName()));
  }
}


In the above example, for each error message you will use in the application, you need to place one entry in FooResourcesKeys, and a corresponding entry in each resource bundle implementation (one for each locale) for that key. If you've named your resource bundles correctly, ResourceBundle will be able to chain lookups so that if an entry is not in the localized bundle, it will search the default bundle too. For each point in your code where you will throw an exception, you will need to create the message string through MessageUtil as shown in Listing 3's SomeClass.

In order to facilitate code reuse, you can extend this technique to support multiple resource bundles, with one bundle per component or even one per package. In this case, the MessageUtil class would be slightly more complicated, specifying an additional argument for identifying what bundle a given error message comes from.

  • Print
  • Feedback

Resources
  • Read more JavaWorld articles by Brian Goetz: