{ by david linsin }

April 20, 2008

How to Internationalize Exceptions

Yesterday I read a german article about the best practises of handling Exceptions in Java. Somewhat it is one of those articles which try to tell you don't do this, don't do that and never come up with any useful alternatives after all. Anyways, as far as I can remember it is the first article I've read, trying to come up with a solution to tackle the error code/message problem.

In an ideal world your custom designed Exception tells a user what went wrong, maybe with a simple error message. Additionally you might add error codes, so that the guys at support can easily identify what went wrong, in case a customer calls. This is the ideal world - the real world looks different: most Exceptions don't even have error messages, they are simply wrappers around various kind of other Exceptions.

Take a look at what the article calls Exception Facade:

public class PluginInitialisationException extends Exception {
public enum PluginInitialisationError {
PLUGIN_LIFECYCLE ("Plugin lifecycle has ended"),
ILLEGAL_ACCESS ("Illegal access while creating the plugin instance"),
INSTANTIATION ("Error while creating the plugin instance"),
CLASS_CAST ("Plugin class cannot be cast"),
CLASS_NOT_FOUND ("Plugin class not found");
private String message;
private PluginInitialisationError(String message) {
this.message = message;
}
}
private PluginInitialisationError error;
public PluginInitialisationException(PluginInitialisationError error) {
this.error = error;
}
public PluginInitialisationException(PluginInitialisationError error, Exception cause) {
super(error.message);
this.error = error;
this.initCause(cause);
}
public String getErrorMessage() {
return error.message;
}
}

I like this approach, because it gives you type safe error messages and a clean design at the same time. If you need error codes you can simply extend PluginInitialisationError enum or add a new enum handling the codes.

I used this kind of Exception in one of my projects, but only with error codes. Now I'm asking you, how to handle internationalization of error messages with this approach? Let's assume we don't use any fancy framework, just plain old Java. The standard way of internationalizing a String goes as follows:

Locale currentLocale = new Locale(language, country);
ResourceBundle messages = ResourceBundle.getBundle("MessagesBundle", currentLocale);
System.out.println(messages.getString("greetings"));

Where does this code belong? Should it be in your Exception? How to determine language and country? I'm sill looking for resources on the web to find a clean designed solution. Since my next project is going to be a Eclipse RCP desktop app, I guess there must be someone out there which already had the same problem.

10 comments:

Alex said...

Hi David,

Matthias Ostermaier describes his approach in Javamagazin 4.08 on pages 45-50. The key element is an overwritten getMessage method of the Exception class, which provides automated I18N for wrapped exceptions. A call triggers an automated replacement (MessageFormat) of the supplied messages.

Greetings,

Alex

P.S.: See you on Friday :-)

public class ServiceException {

private Object[] messageArguments;
private ResourceKey resourceKey;

public ServiceException(ResourceKey key, Object... messageArguments) {
resourceKey = key;
messageArguments = messages;
}

public String getMessage(Locale locale) {
return
ResourceUtil.getMessage(resourceKey, locale, messageArguments);
}

}

public interface ResourceKey {
String getBundleKey();
String getBundleName();
}

david said...

hey Alex,

thx for the feedback, I was able to obtain the source code from the article you mentioned. It'll definitely help me coming up with a clean solution.

Alex said...

The full article is available in german at http://www.oneinmedia.com/de/publications.html

martin said...

Hi David,
funny ;) I've been struggling with the same questions for some time now. We're currently using error codes as static final ints in some class called ErrorMessages. This is legacy code and I hate it ;) I'm in the process of changing this approach to the one you described above, namely enum constants for error messages in the exception class where they belong.
As to internationalization: I was about to recommend the same article that alex already referred to. I think the ideas are very good. But I'd use the method getLocalizedMessage() from java.lang.Throwable for this kind of stuff. The JavaDoc says: Creates a localized description of this throwable. Subclasses may override this method in order to produce a locale-specific message. For subclasses that do not override this method, the default implementation returns the same result as getMessage().

Greetings from Munich
Martin

martin said...

Oh and by the way... you cannot extend an Enum class. They are final. So you either have to reference another Enum type in your Enum or just add some constants...

Martin

david said...

Hey guys,

thx for the feedback, I read the article. The solution seems to be well thought out.

Nevertheless, I think there might be a little too much OO going on in Matthias design. I'd go for a more straight forward approach. I guess I'll post a follow-up with the design I'm going to use.

Anders said...

It is up to the client to choose how to display/resolve the message.

I would have an Exception with a Enum and a descriptive text in english. The descriptive text is not meant for the end user, it is used for debugging/technical staff. Then it is the responsibility to tie the specific Exception + Enum value combination to a text.

This means that the errornumber is replaced by a composite key of Exception name and Enum.

anders said...

Oh, and it should say "it's the clients responsibility above".

david said...

Hi Anders,

Trevor over at dzone raised the same issue in his comment. My reply was the following:

I totally agree that in terms of layering and responsibility you shouldn't handle view layer concerns in any other layer than the presentation tier. On the other hand you might wanna produce a language specific log file or send an internationalized Email through some back-end service. Don't you think a generic Exception class handling this concern would be a clean designed solution?

Writing this almost seems like this problem is a cross-cutting-concern...

Jörg Hohwiller said...

For a solution you should have a look at this:

http://m-m-m.sourceforge.net/apidocs/net/sf/mmm/util/nls/api/package-summary.html#documentation


com_channels

  • mail(dlinsin@gmail.com)
  • jabber(dlinsin@gmail.com)
  • skype(dlinsin)

recent_postings

loading...