Forum Stats

  • 3,757,132 Users
  • 2,251,200 Discussions
  • 7,869,737 Comments

Discussions

Localized Accelorator Keys

843806
843806 Member Posts: 49,998
edited Feb 20, 2008 9:41AM in Swing
Hi folks,
I have a problem with localization and accelerator keys. I am developing a localized Swing GUI where a user can set his preffered Locale and then all texts are translated.... That's all no prob. The problem is, that in JMenus the accelerator keys for items are displayed not according to the set default locale (e.g., en-US) but are displayed according to the OS user.language (in my case de [German]). Hence, the accel. keys read, e.g., '*Strg-N*' instead of '*Ctrl-N*' as expected for default locale en.

This is just a display problem, the functionality / the keys work fine, of course.

I guess my prob is (somehow) related to the awt.properties. When invoking, e.g.,
KeyEvent.getKeyModifiersText(keyStroke.getModifiers() 
(which I assume is called for JMenuItems to display the acc keys) I get the modifier key as string in German. These properties are loaded on startup? according to the OS user language. If i change my user language (e.g., with the jvm switch
-Duser.language=en
) everything is fine. When changing this system property inside Java Code (e.g. in my main-method) the awt.properties are already loaded (somehow) with the OS user.language. Hence, this option doesn't work.

Has anybody an idea / workaround how I get these modifier keys displayed localized. Is there perhaps a way to change the awt-property on runtime or, at least, to set the values of these properties, e.g. AWT.control?

Thanks!

Comments

  • Jörg
    Jörg Member Posts: 1,301
    Hello Luc,

    I just had a look in the sources and found that KeyEvent reads the accelerator text with
    Toolkit.getProperty("AWT.control", "Ctrl")
    Toolkit, however, has no setProperty method. So that's bad news. At least I have no other idea than to vote for an enhancement at SUNs.

    Greetings

    J�rg
  • 843806
    843806 Member Posts: 49,998
    Can't you just use one of the KeyStroke#getKeyStroke() methods to return a KeyStroke, and set the JMenuItem's accelerator to that?
  • camickr
    camickr Member Posts: 24,931
    I just had a look in the sources and found that KeyEvent reads the accelerator text with
    Toolkit.getProperty("AWT.control", "Ctrl")
    I just looked at the getProperty(...) method in the Toolkit class and it uses a ResourceBundle to get the string. Here is a comment from the code:
        /**
         * Support for I18N: any visible strings should be stored in
         * sun.awt.resources.awt.properties.  The ResourceBundle is stored
         * here, so that only one copy is maintained.
         */
        private static ResourceBundle resources;
    I have never used a resource bundle so I don't know how to go about creating one, but there is a tutorial on "Internationalization" that may help:

    http://java.sun.com/docs/books/tutorial/
  • Jörg
    Jörg Member Posts: 1,301
    Well, a ResourceBundle is very similar to a properties file. In I18N you generally create one for each language.

    But suppose you want to modify or replace sun.awt.resources.awt.properties, do you know how to do that (find that location)?
    If, however, Luc is right with his supposition that the properties are loaded on startup, the file had to be replaced/reloaded at runtime to change the language - another problem.
  • 800379
    800379 Member Posts: 301
    The solution would be to set the user language or the locale before the Toolkit class gets loaded. This would generally be VERY early in the startup sequence, say the first lines of your main method. Because as soon as you start using any GUI related classes such as JFrame, the Toolkit class will also get loaded.

    In my first attempt to solve your problem it didn't work even though I set the locale on the FIRST line of my main method. The problem was that my test class itself extended from JFrame, so the JFrame class got loaded before the main method executed. And loading JFrame means also loading Component means loading Toolkit means the resources got loaded with the default VM user language.

    See the Toolkit class, where you can find the following code (I put a break point on this line):
     resources = ResourceBundle.getBundle("sun.awt.resources.awt", CoreResourceBundleControl.getRBControlInstance());
    Find below a running example which solves your problem:
    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.util.Locale;
    
    import javax.swing.AbstractAction;
    import javax.swing.Action;
    import javax.swing.JFrame;
    import javax.swing.JMenu;
    import javax.swing.JMenuBar;
    import javax.swing.JMenuItem;
    import javax.swing.JOptionPane;
    import javax.swing.KeyStroke;
    import javax.swing.SwingUtilities;
    
    public class Accelerators {
    
        static void createAndShowGui() {
            JMenuBar bar = new JMenuBar();
            JMenu menu = new JMenu("File");
            bar.add(menu);
            menu.add(new JMenuItem(new AbstractAction() {
                {
                    putValue(Action.NAME, "Save");
                    putValue(Action.ACCELERATOR_KEY, KeyStroke
                            .getKeyStroke("ctrl S"));
                }
    
                public void actionPerformed(ActionEvent e) {
                    JOptionPane
                            .showConfirmDialog(
                                    null,
                                    "Notice language of dialog caption & buttons.\nThese also depend on the user.language");
                }
            }));
    
            JFrame f = new JFrame("Accelerators");
            f.getContentPane().add(bar, BorderLayout.NORTH);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setLocationRelativeTo(null);
            f.setSize(300, 300);
            f.setVisible(true);
        }
    
        public static void main(String[] args) {
    
            // Locale.setDefault(Locale.US);
            Locale.setDefault(Locale.GERMAN);
    
            // or:
            // System.setProperty("user.language", "en");
            // System.setProperty("user.language", "de");
    
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    createAndShowGui();
                }
            });
        }
    }
  • Jörg
    Jörg Member Posts: 1,301
    Hello Numain,

    thank you for your solution. Setting the user language had no effect on the accelerator display at my site (1.6.0_03), but setting the locale worked fine.
    So we can set the accelerator text for the predefined locales (10 altogether), but it is not possible to set the text for other (own) locales or change the text at runtime, isn't it?
  • 800379
    800379 Member Posts: 301
    Your are right, the property setting does not work for me, either. I guess I haven't tested it... As to your questions, I have asked myself the same things, of course.

    So far I have not seen any java program that was able to change these specific strings during runtime (which doesn't mean it's not possible - i'm thinking something like setting the Toolkit.resources field via reflection and then forcing the menu component tree to update its UI - but I wouldn't really go there).

    About providing additional localizations:
    For testing I created an additional rudimentary resource bundle class "awt_xx" (here the package ist important, or it will not be found by the resource loading mechanism which will look for "sun.awt.resources.awt_xx" class. Just create such a package in your test project.
    package sun.awt.resources;
    
    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.util.ListResourceBundle;
    import java.util.Locale;
    import java.util.ResourceBundle;
    
    import javax.swing.AbstractAction;
    import javax.swing.Action;
    import javax.swing.JFrame;
    import javax.swing.JMenu;
    import javax.swing.JMenuBar;
    import javax.swing.JMenuItem;
    import javax.swing.KeyStroke;
    
    import sun.util.CoreResourceBundleControl;
    
    public class awt_xx extends ListResourceBundle {
        protected Object[][] getContents() {
            return new Object[][] { { "AWT.control", "XXX" } };
        }
    
        public static void main(String[] args) {
            // what keys are there anyway in this infamous awt bundle?
            Locale.setDefault(Locale.US);
            ResourceBundle awtBundle = ResourceBundle.getBundle(
                    "sun.awt.resources.awt", CoreResourceBundleControl
                            .getRBControlInstance());
            for (String key : awtBundle.keySet()) {
                System.out.println(key + "=" + awtBundle.getObject(key));
            }
            
            // now switch to "dirty dirty location" ;-)
            Locale.setDefault(new Locale("xx"));
    
            JMenuBar bar = new JMenuBar();
            JMenu menu = new JMenu("File");
            bar.add(menu);
            Action action = new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                }
            };
            action.putValue(Action.NAME, "Save");
            action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl S"));
            menu.add(new JMenuItem(action));
    
            JFrame f = new JFrame("Accelerators");
            f.getContentPane().add(bar, BorderLayout.NORTH);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setLocationRelativeTo(null);
            f.setSize(300, 300);
            f.setVisible(true);
        }
    }
  • Jörg
    Jörg Member Posts: 1,301
    Great! So one problem is solved.
  • 843806
    843806 Member Posts: 49,998
    Great, thank you all for your replies!!!
This discussion has been closed.