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



我已经按照谷歌的操作指南进行了Window Builder Pro扩展,以使我的应用程序国际化。将显示在标签中的字符串现在存储在由"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()));

我发现了许多其他关于swing应用程序国际化的说明和教程,但没有一个涵盖如何更新所有标签(以及可能的其他带有文本的控件)。SO上有这样的答案,如果链接没有死的话,可能会有所帮助
因为我对java中的GUI编程有点陌生,所以我现在真的不知道该怎么办,下面是我的问题:

  • 如果设置了新语言,在运行时更改所有控件的语言的最佳方法是什么
  • 是否可以(建议)将所有控件声明为我的应用程序类的私有成员,以使方法更新其文本属性(->updateLanguage方法可以这样做)

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

所以我创建了自己的LocaleChangeListener接口。所有的UI屏幕/面板都实现了这一点,并在我的中央UI控制器类上注册为监听器。然后,每当语言发生变化时,就会向它们中的每一个发出localeChanged(lang)调用。不过,我把组件初始化分开了——没有必要再做一遍。

他们只有这样的代码:

公共无效本地更改(郎朗){nameLabel.setText(lang.getText("label.name"));submitButton.setText(lang.getText("button.submit"));等//我想在一些屏幕上,我需要这个来解决一些奇怪的JTable/JTabedPane布局问题:this.revalidate();}

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

你还需要一些pub/sub机制来"告诉"你的标签语言已经改变。

这里我使用的是Guava事件总线

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事件总线的更多使用示例

由于用户不会经常更改语言,因此重新启动是不可接受的。这样做的优点是,布局旧语言的工件不会损坏文本。

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

JLabel new JLabel(Messages.getString("App.lblLanguage.text"));立即计算为本地化文本,并可能按该文本的宽度进行布局。因此,这不适合动态语言变化。

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

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

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

核心Swing中没有任何东西可以减轻痛苦。如果您正在使用某种支持资源注入的应用程序框架,如f.i.(Better)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属性)
  • 没有更新动作文本(可能需要强制进行一些上下文动作更新,不确定)

最新更新