package translator; import java.io.FileNotFoundException; import java.text.MessageFormat; import java.util.Enumeration; import java.util.Hashtable; import java.util.Locale; import java.util.Vector; import animal.misc.MessageDisplay; /** * Provides internationalization by translating messages. Requires an * appropriate resource file containing the message translations. * * @version 1.1 2000-01-11 * @author Guido Rößling ( * roessling@acm.org) */ public class Translator { // ====================================================================== // Attributes // ====================================================================== /** * The current locale, responsible for element translation */ public final static Locale DEFAULT_LOCALE = new Locale("en", "US"); /** * The current locale, responsible for element translation */ private Locale currentLocale = new Locale("en", "US"); /** * The object used for formatting messages */ private MessageFormat messageFormat = new MessageFormat(""); /** * The base name of the resource to load */ private String resourceBaseName = "AnimalResources"; /** * Store all language resources we know about */ private Hashtable resources = new Hashtable(17); private ExtendedResourceManagement currentBundle = null; private TranslatableGUIElement generator = null; /** * Store all unknown resource keywords */ private Hashtable unknownResources = new Hashtable( 41); // ====================================================================== // Attribute Access / Setting // ====================================================================== /** * Retrieve the current locale. May be used to check whether elements have to * be translated, e.g. this is not needed if getCurrentLocale().equals(new * Locale) * * @return the current locale object */ public Locale getCurrentLocale() { return currentLocale; } /** * returns the current TranslatableGUIElement instance for this Translator * * @return the current TranslatableGUIElement handler */ public TranslatableGUIElement getGenerator() { if (generator == null) generator = new TranslatableGUIElement(this); return generator; } /** * Return the current ExtendedResourceBundle for translation purposes * * @return the current resource bundle */ public ExtendedResourceManagement getResourceBundle() { // TODO Check if this ok if (currentBundle == null) currentBundle = resources.get(currentLocale.toString()); return currentBundle; // return resources.get(currentLocale.toString()); } /** * Lists the unknown resources for all known locales to the standard output */ public void listUnknownResources() { StringBuilder buffer = new StringBuilder(5000); Enumeration keys = unknownResources.keys(); if (keys.hasMoreElements()) buffer.append("Unknown resources in format 'resource name -- locale':").append(MessageDisplay.LINE_FEED); while (keys.hasMoreElements()) { Object key = keys.nextElement(); buffer.append(key).append(": ").append(unknownResources.get(key)); buffer.append("\n"); } if (buffer.length() > 0) System.out.print(buffer.toString()); } /** * Set the current locale to the one passed in This will also translate all * GUI components if the state of the Translator is 'initialized' * * @param targetLocale * the new locale for the application. If null, uses Locale.US * instead */ public void setTranslatorLocale(Locale targetLocale) { setTranslatorLocale(targetLocale, new Vector(1)); /* if (targetLocale != null) { messageFormat.setLocale(targetLocale); String localeCode = targetLocale.toString(); if (!resources.containsKey(localeCode)) { ExtendedResourceManagement resourceBundle = new ExtendedResourceManagement( resourceBaseName + "." + localeCode); // ExtendedResourceBundle resourceBundle = new ExtendedResourceBundle( // resourceBaseName + "." + localeCode); resourceBundle.setTranslator(this); resources.put(localeCode, resourceBundle); } currentLocale = targetLocale; getGenerator().translateGUIElements(); } */ } /** * Set the current locale to the one passed in This will also translate all * GUI components if the state of the Translator is 'initialized' * * @param targetLocale * the new locale for the application. If null, uses Locale.US * instead * @param additionalResources the file names (without ".xx_YY") for additional * language resources to be read in */ public void setTranslatorLocale(Locale targetLocale, Vector additionalResources) { // System.err.println("set locale to " + targetLocale.toString() +", #add: " +additionalResources.size()); if (targetLocale != null) { ExtendedResourceManagement resourceBundle = null; messageFormat.setLocale(targetLocale); String localeCode = targetLocale.toString(); // System.err.println("already 'in' for " + localeCode +"? " + resources.containsKey(localeCode)); // if (!resources.containsKey(localeCode)) { resourceBundle = new ExtendedResourceManagement( resourceBaseName + "." +localeCode); resourceBundle.setTranslator(this); for (String key: additionalResources) { // System.err.println("Trying to add resource: " + key +'.' +localeCode); try { resourceBundle.addPropertyResource(key+'.'+localeCode); } catch (FileNotFoundException fnfe) { System.err.println("Adding resource ' "+key+'.'+localeCode +"' failed..."); } // } resources.put(localeCode, resourceBundle); } currentLocale = targetLocale; currentBundle = resourceBundle; // System.err.println(translateMessage("GenericEditor.nameBL")); getGenerator().translateGUIElements(); } } public void setResourceBaseName(String resourceName) { resourceBaseName = resourceName; } // ====================================================================== // Message and Element Translation // ====================================================================== public Translator() { // this("resources", new Locale("en", "US")); try { throw new IllegalArgumentException( "Please do not use the empty constructor for Translator instances!"); } catch (IllegalArgumentException e) { System.err.println(e.getMessage()); e.printStackTrace(); } } public Translator(String resourceName, Locale targetLocale) { setResourceBaseName(resourceName); setTranslatorLocale(targetLocale); } /** * Convenience wrapper the translated message of key messageKey. Internally * invokes generateMessage(messageKey, null); * * @see #translateMessage(String, Object[], boolean) * @param messageKey * the key of the message to be displayed */ public String translateMessageWithoutParameterExpansion(String messageKey) { String messageSource = getResourceBundle().getMessage(messageKey, true); if (messageSource != null) { return messageSource; } if (unknownResources.containsKey(messageKey)) { String entry = unknownResources.get(messageKey); if (entry.indexOf(currentLocale.getCountry()) == -1) unknownResources.put(messageKey, unknownResources.get(messageKey) + ", " + currentLocale.getCountry()); } else unknownResources.put(messageKey, currentLocale.getCountry()); return "Invalid Message Key '" + messageKey + "'"; } /** * Convenience wrapper the translated message of key messageKey. Internally * invokes generateMessage(messageKey, null); * * @see #translateMessage(String, Object[], boolean) * @param messageKey * the key of the message to be displayed */ public String translateMessage(String messageKey) { return translateMessage(messageKey, null, true); } /** * Return the translated message of key messageKey using the message params * Internally invokes generateMessage(messageKey, null); * * @see #translateMessage(String, Object[], boolean) * * @param messageKey * the key of the message to be displayed * @param messageParams * the values to be substituted into the message. */ public String translateMessage(String messageKey, Object[] messageParams) { return translateMessage(messageKey, messageParams, true); } /** * Return the translated message of key messageKey using the message params * Internally invokes generateMessage(messageKey, null); * * @see #translateMessage(String, Object[], boolean) * * @param messageKey * the key of the message to be displayed * @param params * the values to be substituted into the message. */ public String translateMessage(String messageKey, String... params) { return translateMessage(messageKey, params, true); } /** * Return the translated message of key messageKey using the message params * * @param messageKey * the key of the message to be displayed * @param messageParams * the values to be substituted into the message. * @param warnOnError * if true, log warnings due to invalid accesses or unavailable keys. */ public String translateMessage(String messageKey, Object[] messageParams, boolean warnOnError) { String messageSource = getResourceBundle().getMessage(messageKey, warnOnError); if (messageSource != null) { try { messageFormat.applyPattern(messageSource); } catch(IllegalArgumentException illegal) { System.err.println(illegal.toString() +" for key '" +messageKey +"'"); } return messageFormat.format(messageParams); } if (unknownResources.containsKey(messageKey)) { String entry = unknownResources.get(messageKey); if (entry.indexOf(currentLocale.getCountry()) == -1) unknownResources.put(messageKey, unknownResources.get(messageKey) + ", " + currentLocale.getCountry()); } else unknownResources.put(messageKey, currentLocale.getCountry()); return "Invalid Message Key '" + messageKey + "'"; } }