Easy I18N/L10N Using TE-CommonAndrew S. Townley 21-Nov-2004This article will provide a brief overview of the I18N facilities provided by TE-Common and illustrate how they may be used to localize your programs.
The Java platform provides all of the basic building blocks
for allowing you to write I18N-aware programs. However, the
default facilities for managing application resources do not
easily support sharing of common strings across your
applications without dealing with a lot of code. Using the
Imagine that you have an application which consists of 3 packages, related as in Figure 1, below:
Logically, the core localized versions of the application
resources should be defined within
Fortunately, this is exactly the problem that the
Accessing the Resources
First, we create the package-private class definition. The
one below is from the Unfortunately, there's a lot of boilerplate code here that I would prefer to get rid of, however, I really haven't come up with a better way. You could create a new class which gets wrapped by the package-private class, but I still think you'd end up with something very similar to what's below. 1 package com.townleyenterprises.tool; 2 3 import java.util.MissingResourceException; 4 5 import com.townleyenterprises.common.Version; 6 import com.townleyenterprises.common.ResourceManager; 7 import com.townleyenterprises.common.ResourceLoader; 8 9 final class Strings 10 { 11 static void addResources(Object obj, String name) 12 { 13 try 14 { 15 _resources.manage( 16 new ResourceLoader( 17 obj.getClass(), name)); 18 } 19 catch(MissingResourceException e) 20 { 21 // ignored 22 } 23 } 24 25 static ResourceManager getManager() 26 { 27 return _resources; 28 } 29 30 static String get(String key) 31 { 32 String rc = _resources.getString(key); 33 if(rc == null) 34 rc = key; 35 36 return rc; 37 } 38 39 static String format(String key, Object[] args) 40 { 41 String rc = _resources.format(key, args); 42 if(rc == null) 43 rc = key; 44 45 return rc; 46 } 47 48 private static final ResourceManager _resources = new 49 ResourceManager(); 50 51 static 52 { 53 _resources.manage(new ResourceLoader(Version.class)); 54 _resources.manage(new ResourceLoader(Strings.class)); 55 } 56 } In this version, I have removed all of the comments and reformatted it so that it fits better in the browser. To see the full version, please download the source code.
The main methods of interest are the
The How Does It Work?
The implementation of the
The
In my own code, I've adopted a simple resource naming
convention which is a bit of a hold-over from my days working
with Informix in Lenexa. The convention says that string
resources which are normal messages should start with
Normally, I hate this sort of naming convention--especially for variables, but in this situation, I find that it easily lets me keep track of those strings which have format specifiers and those that don't. Once you start having a lot of messages, these things can matter around 4:00AM when you're trying to figure out why something doesn't work. Putting It All Together
Once the 81 public final class mkcpbat 82 { 83 public static void main(String[] args) 84 { 85 new mkcpbat(args); 86 } 87 88 /** 89 * The constructor is the 'main' of the application. 90 * 91 * @param args the command-line arguments 92 */ 93 94 private mkcpbat(String[] args) 95 { 96 // add the resources 97 Strings.addResources(this, "mkcpbat"); 98 _parser = new CommandParser("mkcpbat", 99 Strings.get("sFileArgs")); 100 _parser.setExitOnMissingArg(true, -1); 101 _parser.addCommandListener(new OptionHandler()); 102 _parser.setExtraHelpText( 103 Strings.get("smkcpbatExtraHelpTextPreamble"), 104 Strings.get("smkcpbatExtraHelpTextPostamble")); An interesting point to note is line 97. This line adds an additional resource bundle which is specific to the application. When you have many command-line utilities in the same package, but with slightly different resource requirements, this technique is a good way to ensure that for the execution of the JVM, the resources needed by the tool are always on the "top" of the stack.
In a similar fashion to the above listing, this error handling
routine uses the 125 catch(IOException e) 126 { 127 System.err.println(Strings.format("fGeneralError", 128 new Object[] { e.getMessage() })); 129 130 if(_verbose.getMatched()) 131 { 132 e.printStackTrace(); 133 } 134 System.exit(-1); 135 }
Should your application need access to images or other types
of resources, it is a simple matter to add in a new method to
the
Hopefully, you now have an overview of how you can introduce
i18n handling into your applications in a straightforward
fashion. The technique using the |