|
Improve Access to Externalized Text
Apply rigorous controls not present in String-based APIs to access externalized text for more efficient code and easier maintenance
by Charles L. Owen
October 18, 2005
Software applications are usually full of text that is meant to inform and guide users. Error messages, widget labels, and help dialogs are a few common examples. The decision to separate text from source code is often simple; doing so enables localization and eases the effort of keeping text crisp, consistent, and correct. However, without proper controls on how text is accessed it may be difficult to guarantee that source code is correct, particularly in large applications where loose APIs readily lead to chaos.
Consider a specific example. Suppose we are building a Web application. Our design problem is to aggregate validation errors during request processing, and then send a complete collection of helpful messages to the user in his or her preferred language. This seems simple enough. We will specify an Errors object with an addError() method. Every anomaly found in the request data results in a call to addError() with information characterizing the problem. The Errors instance will be sent off for display if any issues are found. We have to decide how to handle localization, but so far so good. Let's examine a few of the many possible input parameters of addError().
Our first thought might be to provide the message itself as input to addError(). Sensing that this might be a bit too loose, a rule or pattern might be agreed upon that governs the use of the API. In this case, we use a String key to get the localized message before calling addError(). I call this idiom Control By Agreement, and it should be avoided in all but the smallest applications:
// method in Errors class
public void addError(
String message) {…}
// client code
if (name==null ||
name.length()==0) {
bdl = ResourceBundle.
getBundle(…);
msg = bdl.getString(
NAME_REQD);
errors.addError(msg);
}
Because this wide-open version of addError() accepts any String as input, there is no control over what messages can be sent. In particular, the compiler cannot prevent delivery of hard-coded messages. While marginally helpful, the Control By Agreement idiom can be enforced only by code inspection. Moreover, there is no guarantee that the specified key represents a valid message. If the source base is large it may be unnecessarily complicated to make such guarantees.
Getting There
Forcing the client to supply the key rather than the message itself can solve some of these problems. Thankfully, Control By Agreement is no longer necessary, but ambiguities remain:
// method in Errors class
public void addError(
String key) {…}
// client code
if (name==null ||
name.length()==0) {
errors.addError(NAME_REQD);
}
This approach is an improvement: the client code does not rely on a weak idiom, and only messages associated with keys can be displayed. The details of finding the specific localized message are left to the Errors implementation. Still there is no automatic check that the specified key refers to a message. The compiler, for example, cannot distinguish misspellings.
Back to top
|