package cz.cvut.fel.restauracefel.library.service; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.text.MessageFormat; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractButton; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.KeyStroke; /** * <p> * <code>LocalizationManager</code> is representing main localization class. It has * methods for localization of some components frow Swing framework and for showing * dialogs with localized messages. <strong>It uses default locale!</strong> * </p> * * @author Štěpán Osmík, steve21@seznam.cz */ public class LocalizationManager { /** * Enum with types of messages which are used for showing messages. */ public enum MessageType { /** * Error message */ ERROR("ErrorMessageTitle", JOptionPane.ERROR_MESSAGE), /** * Information message */ INFORMATION("InformationMessageTitle", JOptionPane.INFORMATION_MESSAGE), /** * Plain message (no icon) */ PLAIN("PlainMessageTitle", JOptionPane.PLAIN_MESSAGE), /** * Warning message */ WARNING("WarningMessageTitle", JOptionPane.WARNING_MESSAGE); private String messageTitleKey; private int optionPaneMessageType; /** * Constructs a new instance. * * @param messageTitleKey message title key in default resource bundle * @param optionPaneMessageType option pane constant for icon */ private MessageType(String messageTitleKey, int optionPaneMessageType) { this.messageTitleKey = messageTitleKey; this.optionPaneMessageType = optionPaneMessageType; } /** * Method returns message title key in default resource bundle. * * @return message title key */ public String getMessageTitleKey() { return messageTitleKey; } /** * Method returns option pane message type constant. * * @return option pane message type */ public int getOptionPaneMessageType() { return optionPaneMessageType; } } /** * Accelerator constant for bundle */ public static final String ACCELERATOR = "Accelerator"; /** * Constant with default locale key */ public static final String DEFAULT_LOCALE = "default-locale"; /** * In locale delimiter (splits the locale into language, country and variant) */ public static final String IN_LOCALE_DELIMITER = "_"; /** * Locale delimiter */ public static final String LOCALE_DELIMITER = ":"; /** * Mnemonic constant for bundle */ public static final String MNEMONIC = "Mnemonic"; /** * Constant with properties file name */ public static final String PROPERTIES_FILE_NAME = "config" + File.separator + "localization.ini"; /** * Constant with supported locales key */ public static final String SUPPORTED_LOCALES = "supported-locales"; /** * Text constant for bundle */ public static final String TEXT = "Text"; /** * Title constant for bundle */ public static final String TITLE = "Title"; /** * Tool tip text for constant */ public static final String TOOL_TIP_TEXT = "ToolTipText"; /* * Singleton */ private static LocalizationManager instance; /* * Properties for localization (default locale and supported locales) */ private Properties properties; /* * Next default locale */ private Locale defaultLocale; /* * Array of supported locales (locales must be defined in property file and * also in JVM) */ private Locale[] supportedLocales; /* * Map with resource bundles */ private Map<String, ResourceBundle> resourceBundles; /** * Creates an instance. */ private LocalizationManager() { try { resourceBundles = new HashMap<String, ResourceBundle>(); properties = new Properties(); properties.load(new FileInputStream(PROPERTIES_FILE_NAME)); Set<Locale> availableLocales = new HashSet<Locale>(Arrays.asList(Locale.getAvailableLocales())); Set<Locale> definedLocales = new HashSet<Locale>(); for (String locale : properties.getProperty(SUPPORTED_LOCALES).split(LOCALE_DELIMITER)) { definedLocales.add(parseLocale(locale)); } availableLocales.retainAll(definedLocales); supportedLocales = new Locale[availableLocales.size()]; availableLocales.toArray(supportedLocales); defaultLocale = parseLocale(properties.getProperty(DEFAULT_LOCALE)); Locale.setDefault(defaultLocale); } catch (Exception e) { Logger.getAnonymousLogger().log(Level.SEVERE, "Localization initialization error.", e); System.exit(0); } } /** * Returns next default locale. * * @return next default locale */ public Locale getDefaultLocale() { return defaultLocale; } /** * Returns instance of this class - singleton. * * @return singleton instance */ public static LocalizationManager getInstance() { return instance == null ? instance = new LocalizationManager() : instance; } /** * Returns bundle with specified base name and actual locale. * * @param bundleBaseName bundle base name * @return specified bundle */ public ResourceBundle getResourceBundle(String bundleBaseName) { ResourceBundle resourceBundle = resourceBundles.get(bundleBaseName); if (resourceBundle == null || !Locale.getDefault().equals(resourceBundle.getLocale())) { resourceBundle = ResourceBundle.getBundle(bundleBaseName, Locale.getDefault()); } return resourceBundle; } /** * Method returns supported locales (locales specified in properties file intersected with * JVM available locales). * * @return supported locales */ public Locale[] getSupportedLocales() { return supportedLocales; } /** * Method localize abstract button. * * @param bundleBaseName bundle base name * @param componentName component name * @param component localized component */ public void localizeAbstractButton(String bundleBaseName, String componentName, AbstractButton component) { ResourceBundle resourceBundle = getResourceBundle(bundleBaseName); localizeComponent(bundleBaseName, componentName, component); if (resourceBundle.containsKey(componentName + TEXT)) { component.setText(resourceBundle.getString(componentName + TEXT)); } if (resourceBundle.containsKey(componentName + MNEMONIC)) { component.setMnemonic(parseMnemonic(resourceBundle.getString(componentName + MNEMONIC))); } } /** * Method localize button. * * @param bundleBaseName bundle base name * @param componentName component name * @param component localized component */ public void localizeButton(String bundleBaseName, String componentName, JButton component) { localizeAbstractButton(bundleBaseName, componentName, component); } /** * Method localize component. * * @param bundleBaseName bundle base name * @param componentName component name * @param component localized component */ public void localizeComponent(String bundleBaseName, String componentName, JComponent component) { ResourceBundle resourceBundle = getResourceBundle(bundleBaseName); if (resourceBundle.containsKey(componentName + TOOL_TIP_TEXT)) { component.setToolTipText(resourceBundle.getString(componentName + TOOL_TIP_TEXT)); } } /** * Method localize dialog. * * @param bundleBaseName bundle base name * @param componentName component name * @param component localized component */ public void localizeDialog(String bundleBaseName, String componentName, JDialog component) { ResourceBundle resourceBundle = getResourceBundle(bundleBaseName); component.setTitle(resourceBundle.getString(componentName + TITLE)); } /** * Method localize frame. * * @param bundleBaseName bundle base name * @param componentName component name * @param component localized component */ public void localizeFrame(String bundleBaseName, String componentName, JFrame component) { ResourceBundle resourceBundle = getResourceBundle(bundleBaseName); component.setTitle(resourceBundle.getString(componentName + TITLE)); } /** * Method localize label. * * @param bundleBaseName bundle base name * @param componentName component name * @param component localized component */ public void localizeLabel(String bundleBaseName, String componentName, JLabel component) { ResourceBundle resourceBundle = getResourceBundle(bundleBaseName); localizeComponent(bundleBaseName, componentName, component); component.setText(resourceBundle.getString(componentName + TEXT)); } /** * Method localize menu. * * @param bundleBaseName bundle base name * @param componentName component name * @param component localized component */ public void localizeMenu(String bundleBaseName, String componentName, JMenu component) { localizeMenuItem(bundleBaseName, componentName, component); } /** * Method localize menu item. * * @param bundleBaseName bundle base name * @param componentName component name * @param component localized component */ public void localizeMenuItem(String bundleBaseName, String componentName, JMenuItem component) { ResourceBundle resourceBundle = getResourceBundle(bundleBaseName); localizeAbstractButton(bundleBaseName, componentName, component); if (resourceBundle.containsKey(componentName + ACCELERATOR)) { component.setAccelerator(parseAccelerator(resourceBundle.getString(componentName + ACCELERATOR))); } } /** * Returns accelerator represented in the string. * * @param accelerator parsed string * @return accelerator */ public KeyStroke parseAccelerator(String accelerator) { return KeyStroke.getKeyStroke(accelerator); } /** * Returns locale represented in the string. * * @param locale parsed locale * @return locale */ private Locale parseLocale(String locale) { String[] strings = locale.split(IN_LOCALE_DELIMITER); switch (strings.length) { case 1: return new Locale(strings[0]); case 2: return new Locale(strings[0], strings[1]); case 3: return new Locale(strings[0], strings[1], strings[2]); default: return null; } } /** * Returns mnemonic represented in the string. * * @param mnemonic parsed string * @return mnemonic */ public char parseMnemonic(String mnemonic) { return KeyStroke.getKeyStroke(mnemonic).getKeyChar(); } /** * Method sets next default locale. All changes will be visible after * restart of application. * * @param defaultLocale next default locale */ public void setDefaultLocale(Locale defaultLocale) throws IOException { this.defaultLocale = defaultLocale; properties.setProperty(DEFAULT_LOCALE, defaultLocale.toString()); properties.store(new FileOutputStream(PROPERTIES_FILE_NAME), new Date().toString()); } /** * Method show dialog with message. * * @param messageType type of message (error, information, plain or warning) * @param bundleBaseName bundle base name (bundle with message) * @param message message * @param arguments arguments which should be inserted in the localized message */ public void showMessage(MessageType messageType, String bundleBaseName, String message, Object... arguments) { String title = getResourceBundle(bundleBaseName).getString(messageType.getMessageTitleKey()); JOptionPane.showMessageDialog(null, MessageFormat.format(getResourceBundle(bundleBaseName).getString(message), arguments), title, messageType.getOptionPaneMessageType()); } }