Swing国际化 - 如何在运行时更新语言

时间:2013-02-11 10:38:46

标签: java swing internationalization

我已按照Googles howto for its Window Builder Pro extension国际化我的应用。将在标签中显示的字符串现在存储在由“Externalize Strings”向导创建的属性文件中(我使用了经典的eclipse消息文件)。

在我的initialize方法中,我的所有标签都已初始化,其文字设置如下:

JLabel lblLanguage = new JLabel(Messages.getString("App.lblLanguage.text")); //$NON-NLS-1$

我在类App中创建了一个包含GUI的枚举类型:

private enum Lang { German(Locale.GERMAN), English(Locale.ENGLISH);

    private Locale loc;
    Lang (Locale l) {
        loc = l;
    }

    Locale getLocale() {
        return loc;
    }
}

该语言将使用组合框设置,该组合框使用枚举类型Lang来显示可用的语言:

    JComboBox cboLanguage = new JComboBox();
    cboLanguage.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            JComboBox cb = (JComboBox)e.getSource();
            Lang l = (Lang)cb.getSelectedItem();
            // TODO: update language
        }
    });
    cboLanguage.setModel(new DefaultComboBoxModel(Lang.values()));

我发现了许多其他的howtos和教程,涵盖了swing应用程序的国际化,但它们都没有涵盖如何更新所有标签(以及其中可能包含文本的其他控件)。这里有this answer,如果链接没有死,可能会有所帮助 因为我不熟悉java中的GUI编程,所以我现在不知道该怎么做,这是我的问题:

  • 如果设置了新语言,在运行时更改所有控件的语言的最佳方法是什么?
  • 是否可以(推荐)将所有控件声明为我的App类的私有成员,以使方法更新其文本属性( - > updateLanguage方法可以执行此操作)

4 个答案:

答案 0 :(得分:3)

当我这样做时,我找不到内置的方法。

所以我创建了自己的LocaleChangeListener界面。实现了所有UI屏幕/面板,并在我的中央UI控制器类上将自己注册为侦听器。然后,只要语言发生变化,就会向每个人发起localeChanged(lang)次呼叫。 我保持组件初始化是分开的 - 不需要再次完成所有这些。

他们只有这样的代码:

public void localeChanged(Lang lang) {
    nameLabel.setText(lang.getText("label.name"));
    submitButton.setText(lang.getText("button.submit"));
    etc...

    // I think on some screens I needed this to fix some quirky JTable/JTabbedPane layout issues:
    this.revalidate();
}

答案 1 :(得分:2)

我通过扩展JLabel并覆盖getText来返回对语言选择的评估来解决这个问题。

您还需要一些发布/订阅机制来“告诉”您的标签语言已更改。

我在这里使用Guava event bus

import com.google.common.eventbus.EventBus;

public class EventBusHolder {

    private static EventBus bus = new EventBus();

    public static EventBus get() {
        return bus;
    }
}

当用户更改语言时,您可以触发事件:

JComboBox cboLanguage = new JComboBox();
cboLanguage.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        JComboBox cb = (JComboBox)e.getSource();
        Lang l = (Lang)cb.getSelectedItem();
        // this is the event fire
        ChangeEvent event = getChangeEvent(l);
        EventBusHolder.get().post(event);
    }
});

要使用的标签组件:

public class MyLabel extends JLabel {

    private static final long serialVersionUID = 1L;

    public MyLabel (String key) {
        super(key);
        // register this in event bus
        EventBusHolder.get().register(this);
    }

    @Override
    public String getText() {
        return Messages.getString(super.getText());
    }

    @Subscribe 
    public void recordCustomerChange(ChangeEvent e) {
        revalidate();
    }

}

当您实例化标签时,您必须传递密钥才能进行翻译:

JLabel lbl = new JLabel("App.lblLanguage.text");

guava event bus

上的更多用法示例

答案 2 :(得分:1)

由于用户不会经常更改语言,因此重启几乎不可接受。这样做的好处是,旧语言的布局工件不会破坏文本。

这需要应用程序的可保存状态。

JLabel new JLabel(Messages.getString("App.lblLanguage.text"));会立即评估本地化文本,并可能布局到该文本的宽度。所以这不适合动态语言变化。

另一种方法是依靠名称属性或反射来填充所有文本值。或代码:

JLabel lblLanguage = new JLabel();
Nls.setText(lblLanguage, "App.lblLanguage");

使用一些Nls.setText在集合中对lblLanguage进行弱引用。

答案 3 :(得分:1)

核心Swing中没有任何东西可以缓解疼痛。如果您使用某种支持资源注入的应用程序框架,您可能会感到高兴,如f.i. (优于)SwingApplicationFramework。

假设所有文本都像往常一样在属性文件中声明,那么类似下面的代码段就可以解决问题:

public static class ToggleLocaleAction extends AbstractAction {

    private boolean isAlternative;
    private SingleFrameApplication app;

    public ToggleLocaleAction(SingleFrameApplication app) {
        super("Locale");
        this.app = app;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // just toggling as an example
        Locale.setDefault(isAlternative ? Locale.GERMAN : Locale.ENGLISH);
        isAlternative = !isAlternative;
        injectResources(app);
    }

    public void injectResources(SingleFrameApplication app) {
        app.getContext().getResourceMap().injectComponents(app.getMainFrame());
    }
}

刚发现一些故障:

  • 不更新框架标题(可能因为那不是bean属性)
  • 不会更新操作文本(可能需要强制执行某些上下文操作更新,不确定)