Try alternatives to ResourceBundle
Most implementations of multilingual applications use
ResourceBundle
and
Locale
. However, there
are some problems with this style, so one might consider alternatives to ResourceBundle.
Here's a simple illustration of the basic ResourceBundle
mechanism:
BankMachine
is a user of a ResourceBundle
,
whose Locale
is set by the end user. This illustration uses
ListResourceBundle
,
but
PropertyResourceBundle
is likely used more often.
import java.util.ResourceBundle; import java.util.Locale; public final class BankMachine { /** * Run first a French version, then an English version. */ public static void main(String[] arguments) { BankMachine bankMachine = new BankMachine(); bankMachine.selectLanguage(FRENCH); bankMachine.showDisplay(); bankMachine.selectLanguage(ENGLISH); bankMachine.showDisplay(); } /** * The user's first action is to select a language. */ public void selectLanguage(String language){ if (ENGLISH.equals(language)) { text = ResourceBundle.getBundle(BUNDLE_NAME, Locale.ENGLISH); } else if (FRENCH.equals(language)) { text = ResourceBundle.getBundle(BUNDLE_NAME, Locale.FRENCH); } else { throw new IllegalStateException("Unknown language"); } } public void showDisplay() { //use the bundle to get the proper version of a string //note that the variable names - Text.Hello, etc - reflect the content, //so these method calls clearly indicate what is being displayed //to the user log(text.getString(Text.Hello)); log(text.getString(Text.PleaseSelectAction)); } // PRIVATE private static String ENGLISH = Text.English; private static String FRENCH = Text.French; private static String BUNDLE_NAME = "Text"; /** * Default is English. * Note that this default is perfectly suitable for any initial user * selection of language, since the Strings representing the languages * themselves have only one representation, which are defined in the * default bundle. */ private ResourceBundle text = ResourceBundle.getBundle(BUNDLE_NAME, Locale.ENGLISH); private static void log(String msg) { System.out.println(msg); } }
BankMachine
gives:
Bonjour
Veuillez choisir une action
Hello
Please select an action
The default ResourceBundle
contains both constants and the
default representations of all text:
import java.util.*; public final class Text extends ListResourceBundle { public final Object[][] getContents() { return contents; } /** * These constants are used by the caller to identify what text is * needed; which version of the text is actually returned is determined * by the ResourceBundle mechanism. * * Using variable names which reflect the content clarifies the * intent of the code in the user of this class. */ public static final String Hello = "Hello"; public static final String PleaseSelectAction = "PleaseSelectAction"; /** * Language names presented as a list of choices to the user * are special, since they are always presented in the native language. * Thus, this default bundle defines the * unique representations of all language names, and no other * bundle provides a translation for them. */ public static final String English = "English"; public static final String French = "Francais"; // PRIVATE private static final Object[][] contents = { {Text.Hello, "Hello"}, {Text.PleaseSelectAction, "Please select an action"} , {Text.English, "English"}, //never defined elsewhere {Text.French, "Francais"}, //never defined elsewhere }; }
The other ResourceBundle
classes do not define constants, just
the translations :
import java.util.*; public final class Text_fr extends ListResourceBundle { public final Object[][] getContents() { return contents; } //No constants are defined here // PRIVATE private static final Object[][] contents = { {Text.Hello, "Bonjour"}, {Text.PleaseSelectAction, "Veuillez choisir une action"} }; }