One day in February, I was studying resource bundles. This is what I learned.
The properties files can't be placed willy-nilly, but must occupy a subdirectory named "resources" in the classpath. In Eclipse, this means that the resources directory gets created as a source directory. I accomplished this by:
Build Path->Use as Source Folder
. Visually (in Eclipse's
Package Explorer pane), this results in an entity,
src/resources, with an icon identical to the one already gracing
the src subdirectory.
key = This is the key to my office.
To ResourceBundle.getBundle() a resource name is passed in which to find a properties file to open, read and to which a ResourceBundle is returned. This bundle can be exploited by calls to the getString() method.
Typically, a bundle isn't constructed (in the object-oriented sense), but created via the call to (static) getBundle().
The ability to specify locale also exists, yielding a translation capability as also illustrated here. In fact, it's rather important to set a locale when writing applications in Java because not to set one means you'll consume the default locale of the host on which your program is running. There are a lot of Java classes that live and die by what the current application locale is, InputReader for instance, and their behavior could become unpredictable as a result.
Notice that great care is taken against referencing null: there is nothing magically solid about this interface.
Last, we show how to enumerate all the keys in a bundle. This would also work for Properties. Notice how Java 1.5 generics gets us around having to coerce the return from nextElement() to String.
See http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html
Inside this codeIn terms of beginning Java samples you have:
|
Eclipse tip: Where's the library (JAR)?
In Dynamic Web Projects, you can't see libraries maintained on the path
project-name/WebContent/WEB-INF/lib in the Package Explorer
pane. To see them, open the Navigator:
If you have a separate project with "library" code and the output is a JAR copied to your main project by an ant script, you'll need to commit the JAR in place of the older version, so it's crucial to find it unless you do a commit from higher up and uncheck all the files and directories you don't want to commit. |
package com.etretatlogiciels.samples.resbundle; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Enumeration; import java.util.Locale; /** * Demonstration of the ResourceBundle package that reads property files and * coughs up strings therefrom. * * @author Russell Bateman, February 2009 */ public class ResBundleDemo { private static final String RESOURCE = "properties/demo"; public static void main( String[] args ) { Locale defloc = Locale.getDefault(); Locale frn = new Locale( "fr" ); String locale = defloc.toString(); ResourceBundle bundle = null; System.out.print( "Our default system locale is " ); if( locale != null ) System.out.println( locale + "." ); else System.out.println( "(broken--very unlikely)." ); /* We're running in en_US, here's how we get the string associated with "key" * out of "resources/demo_en.properties". */ try { bundle = ResourceBundle.getBundle( RESOURCE ); } catch( MissingResourceException e ) { e.printStackTrace(); System.out.println( "No bundle \"" + RESOURCE + "\" was found."); } if( bundle != null ) { System.out.println( "Bundle resources name is: \"" + bundle.toString() + "\"" ); String string = bundle.getString( "key" ); if( string == null ) string = "Key \"key\" did not exist in " + RESOURCE; System.out.println( "The bundle key \"key\" has the value \"" + string + "\"" ); } /* This is very instructive: it gets us the contents of "demo.properties" * instead of "demo_en.properties". */ System.out.println( "\nLet's try out a null locale " + "(uses demo.properties)..." ); Locale nullLocale = new Locale( "" ); Locale.setDefault( nullLocale ); try { bundle = ResourceBundle.getBundle( RESOURCE, nullLocale ); } catch( MissingResourceException e ) { e.printStackTrace(); System.out.println( "No bundle \"" + RESOURCE + "\" was found."); } if( bundle != null ) { String string = bundle.getString( "key" ); if( string == null ) string = "Key \"key\" did not exist in " + RESOURCE; System.out.println( "The bundle key \"key\" has the value \"" + string + "\"" ); } /* Now, let's try this in French just to prove our point! */ System.out.println( "\nAnd now, in French! (uses demo_fr.properties)"); Locale.setDefault( frn ); try { bundle = ResourceBundle.getBundle( RESOURCE ); } catch( MissingResourceException e ) { e.printStackTrace(); System.out.println( "Aucun faisceau \"" + RESOURCE + "\" n'existe."); } if( bundle != null ) { String string = bundle.getString( "key" ); if( string == null ) string = "Clef \"key\" n'existe pas dans " + RESOURCE; System.out.println( "La clef de faisceau \"key\" se compose de \"" + string + "\"" ); } /* Now, let's try this in English again to demonstrate enumeration. */ Locale eng = new Locale( "en" ); System.out.println( "\nAnd now, let's enumerate keys " + "(uses demo_en.properties)"); Locale.setDefault( eng ); try { bundle = ResourceBundle.getBundle( RESOURCE ); } catch( MissingResourceException e ) { e.printStackTrace(); System.out.println( "No bundle \"" + RESOURCE + "\" was found."); } if( bundle != null ) { Enumeration< String > keys = bundle.getKeys(); if( keys == null ) { System.out.println( "Error attempting to enumerate keys in this bundle" ); } else { while( keys.hasMoreElements() ) { //String k = ( String ) keys.nextElement(); String k = keys.nextElement(); String v = bundle.getString( k ); System.out.println( " " + k + " = " + v ); } } } } } // vim: set tabstop=2 shiftwidth=2 noexpandtab:
These are kept under a package directory named properties in Eclipse as illustrated on the right.
These files have the following content:
# This is a properties file opened and consumed by ResBundleDemo. Except that it # is not! This 'key' should not be the answer because, in the default en_US # locale, resource file "demo_en.properties" will be chosen instead. Only if that # file did not exist, would "demo.properties" be used instead. key = (waves hand) This is not the key you're looking for
# This is a properties file opened and consumed by ResBundleDemo. key = This is the key to my office. skeleton = This is a freaky key to be sure. Smee = Captain Hook's right-hand man
# Voici le fichier des aiguillages consomme par ResBundleDemo. key = Voici la clef de mon bureau.
Our default system locale is en_US. Bundle resources name is: "java.util.PropertyResourceBundle@1a46e30" The bundle key "key" has the value "This is the key to my office." Let's try out a null locale (uses demo.properties)... The bundle key "key" has the value "(waves hand) This is not the key you're looking for." And now, in French! (uses demo_fr.properties) La clef de faisceau "key" se compose de "Voici la clef de mon bureau." And now, let's enumerate keys (uses demo_en.properties) Smee = Captain Hook's right-hand man! skeleton = This is a freaky key to be sure. key = This is the key to my office.
The slightly more elaborate arrangements made above might go beyond what you need. Here's the minimal approach:
package com.etretatlogiciels.bundles; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import org.apache.log4j.Logger; /** * Static interfaces to handle all messages, prompts and any other resources we * can think of including permitting us to localize them to other languages. */ public class AppResources { private static Logger log = Logger.getLogger( AppResources.class ); private static final String MESSAGES = "messages"; private static String status = null; private static Locale defaultLocale = null; private static ResourceBundle bundle = null; static { defaultLocale = Locale.getDefault(); bundle = ResourceBundle.getBundle( MESSAGES ); if( bundle == null ) status = "Bundle not found"; } public static String getStatus() { return status; } /** * Change locales, to another language. * * @param newLocale - the new locale, as created using new Locale( "fr" ) * and passed in; if null return to start-up locale * @return true/false that the locale change worked out */ public static boolean changeLocale( Locale newLocale ) { if( newLocale == null ) newLocale = defaultLocale; Locale.setDefault( newLocale ); try { bundle = ResourceBundle.getBundle( MESSAGES ); } catch( MissingResourceException e ) { log.trace( "changeLocale(): Unable to find message bundle for "); return false; } return true; } /** * Fetch the message or prompt we need. * * @param key - to the message or prompt looked for. * @return the message or prompt wanted. */ public static final String getMessage( String key ) { return bundle.getString( key ); } /** * Quick and dirty test to work out the kinks the first time without * raising this to the level of a formal unit test. * * @param args (unused) */ public static void main( String[] args ) { System.out.println( "A message in the start-up locale: " ); System.out.println( AppResources.getMessage( "startup.error" ) ); System.out.println( "If any messages remain in English, it's because " + "their localization has been botched."); System.out.println( "\nThe same message in French-France:" ); Locale frn = new Locale( "fr" ); AppResources.changeLocale( frn ); System.out.println( AppResources.getMessage( "startup.error" ) ); System.out.println( "\nThe same message in Spanish-Spain:" ); Locale esp = new Locale( "es" ); AppResources.changeLocale( esp ); System.out.println( AppResources.getMessage( "startup.error" ) ); System.out.println( "\nThe same message in Finnish (which we won't do " + "so it demonstrates what happens" ); System.out.println( " when a locale/language isn't supported):" ); Locale suo = new Locale( "fi" ); AppResources.changeLocale( suo ); System.out.println( AppResources.getMessage( "startup.error" ) ); } }
Here's the output from the test above:
A message in the start-up locale: There must be test fodder to run this test If any messages remain in English, it's because their localization has been botched. The same message in French-France: Des données doivent exister pour effectuer cet essai The same message in Spanish-Spain: Datos deben existir para efectuar este ensayo The same message in Finnish (which we won't do so it demonstrates what happens when a locale/language isn't supported): There must be test fodder to run this test