4
\$\begingroup\$

I thought about the best way to implement localisation with runtime in Swing.

I currently solve the problem like that:

JMenu menuData = new JMenu("Data");
    menuData.setName("mainframe.menu.data"); // property key
    localeChangedListener.add(menuData);

The LocaleChangedListener:

public class SwingLocaleChangedListener implements LocaleChangedListener {

    private ArrayList<AbstractButton>   abstractButtons;

    @Override
    public void localeChanged(ResourceBundle rb) {
        logger.info("Locale changed to '" + rb.getLocale() + "'");
        for (AbstractButton b : abstractButtons) {
            b.setText(rb.getString(b.getName()));
            b.setComponentOrientation(ComponentOrientation.getOrientation(rb.getLocale())); //EDIT: Line added
        }

    }

    public boolean add(AbstractButton b) {
        initAbstractButtons();
        return abstractButtons.add(b);
    }

    private void initAbstractButtons() {
        if (abstractButtons == null) {
            this.abstractButtons = new ArrayList<AbstractButton>();
        }
    }
}

And the registration of the Listener:

public class GuiBundleManager {

    private String                  filePrefix  = "language.lang";
    private ResourceBundle          rb         = null;
    private LocaleChangedListener   listener    = null;

    private static GuiBundleManager instance    = null;

    private GuiBundleManager() { 
        setLocale(Locale.getDefault());
    }

    public String getString(String key) {
        return rb.getString(key);
    }

    public String[] getStringArray(String key) {
        return rb.getStringArray(key);
    }

    public Locale getLocale() {
        return rb.getLocale();
    }

    public void setLocale(Locale l) {
        rb = ResourceBundle.getBundle(filePrefix, l);
        if (listener != null) {
            listener.localeChanged(rb);
        }
    }

    public LocaleChangedListener getLocaleChangedListener() {
        return listener;
    }

    public void setLocaleChangedListener(LocaleChangedListener listener) {
        this.listener = listener;
        if (listener != null) {
            listener.localeChanged(rb);
        }
    }

    public static GuiBundleManager get() {
        if (instance == null) {
            instance = new GuiBundleManager();
        }
        return instance;
    }
}

Another way I'm thinking of is using Component.setLocale() combined with an PropertyChangedListener:

public abstract class GUIComponentFactory {

    public JLabel createLocalisedJLabel(final String key) {
        final JLabel label = new JLabel(GuiBundleManager.get().getString(key));
        label.addPropertyChangeListener("locale", new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent e) {
                label.setText(GuiBundleManager.get().getString(key));
                label.setComponentOrientation(ComponentOrientation.getOrientation(e.getNewValue())); //Edit: Line added
                for(Component c : getComponents()){
                    c.setLocale(e.getNewValue());
                }
            }
        });
                return label;
    }
    .
    .
    .
}
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Consider applyComponentOrientation(), which recursively "Sets the ComponentOrientation property of this component and all components contained within it." Examples may be found here.

\$\endgroup\$
4
  • \$\begingroup\$ I think I'll implement the PropertyChangedListener structure. This way seems more flexible to me \$\endgroup\$
    – nidomiro
    Jul 3, 2012 at 18:39
  • \$\begingroup\$ Agree; it's included in every Component. Do you have any i18n plans? \$\endgroup\$
    – trashgod
    Jul 3, 2012 at 19:29
  • \$\begingroup\$ I have a method in GuiBundleManager that list all locates with existing properties File (and you can change at runtime) - is this what you mean? \$\endgroup\$
    – nidomiro
    Jul 3, 2012 at 19:40
  • \$\begingroup\$ Yes; skimmed right past it! :-) \$\endgroup\$
    – trashgod
    Jul 3, 2012 at 19:42
2
\$\begingroup\$

Whatever approach you finally decide upon: you don't have to handle the resourceBundles yourself. Instead, add them to the UIManager and query that for the localized values:

// edited: it's the defaults which take the bundle, my bad I didn't check core api
// SwingX has a UIManagerExt which takes it directly :-)
UIManager.getDefaults().addResourceBundle("com.xypackage.resources.MyBundle");
...
label.setText(UIManager.get(key, label.getLocale());

There used to be (didn't check if it's fixed) a bug which prevented this to work reliably in webstartables.

\$\endgroup\$
7
  • \$\begingroup\$ UIManager.addResourceBundle() does not exist in my Java version (1.7) |||| but UIManager.getDefaults().addResourceBundle(bundleName) does \$\endgroup\$
    – nidomiro
    Jul 5, 2012 at 10:59
  • \$\begingroup\$ And this is also a problem: public Object get(Object key, Locale l) { Object value = getFromHashtable( key ); return (value != null) ? value : getFromResourceBundle(key, l); } \$\endgroup\$
    – nidomiro
    Jul 5, 2012 at 11:13
  • \$\begingroup\$ this means that it only returns the message of the first .properties and ignores the given Locale \$\endgroup\$
    – nidomiro
    Jul 5, 2012 at 11:21
  • \$\begingroup\$ darn, you are right with your first comment (will edit). Don't understand your conclusion: the lookup will return the most fitting value for the given locale, falling down from the most specific to the base. \$\endgroup\$
    – kleopatra
    Jul 5, 2012 at 11:48
  • \$\begingroup\$ UIManager.get(key, Locale.GERMAN) and UIManager.get(key, Locale.ENGLISH) return the same value (the German one) \$\endgroup\$
    – nidomiro
    Jul 5, 2012 at 12:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.