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\$
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\$
  • \$\begingroup\$ I think I'll implement the PropertyChangedListener structure. This way seems more flexible to me \$\endgroup\$ – nidomiro Jul 3 '12 at 18:39
  • \$\begingroup\$ Agree; it's included in every Component. Do you have any i18n plans? \$\endgroup\$ – trashgod Jul 3 '12 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 '12 at 19:40
  • \$\begingroup\$ Yes; skimmed right past it! :-) \$\endgroup\$ – trashgod Jul 3 '12 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\$
  • \$\begingroup\$ UIManager.addResourceBundle() does not exist in my Java version (1.7) |||| but UIManager.getDefaults().addResourceBundle(bundleName) does \$\endgroup\$ – nidomiro Jul 5 '12 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 '12 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 '12 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 '12 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 '12 at 12:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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