Java 运行时刷新Swing元素的语言

Java 运行时刷新Swing元素的语言,java,swing,internationalization,properties-file,resourcebundle,Java,Swing,Internationalization,Properties File,Resourcebundle,我的Java 8/Swing应用程序使用一个ResourceBundle和两个.properties文件来更改语言,当用户从JComboBox中选择语言时: public static ResourceBundle resourceBundle; private JComboBox<Locale> comboBox; private JLabel myLabel; public Main() { //More GUI setup here resourceBundl

我的Java 8/Swing应用程序使用一个
ResourceBundle
和两个
.properties
文件来更改语言,当用户从
JComboBox
中选择语言时:

public static ResourceBundle resourceBundle;
private JComboBox<Locale> comboBox;
private JLabel myLabel;

public Main() {
    //More GUI setup here
    resourceBundle = ResourceBundle.getBundle("Bundle", Locale.ENGLISH); //Set first/default language
    comboBox = new JComboBox<Locale>();
    comboBox.addItem(Locale.ENGLISH);
    comboBox.addItem(Locale.GERMAN);
    comboBox.addItem(Locale.FRENCH);

    myLabel = new JLabel(resourceBundle.getString("myLabelText"));
    myLabel.setFont(new Font("Tahoma", Font.PLAIN, 14));
}
我的应用程序中有更多的标签和按钮通过
changeLanguage
设置语言(但并非所有标签和按钮都使用本地化,例如带有房屋图标的“主页”按钮),我打算添加更多。随着GUI项目数量的增加,忘记在函数中添加一项也变得越来越容易,这就是为什么我的问题是:


有没有办法“注册”一个
JLabel
,。。。它对某个类的键(直接在创建之后)以及随后更改语言(通过加载另一个
区域设置
)也会自动更改
JLabel
,。。。?是否有一种常用的方法与我的做法不同?

我最近遇到了这个问题,因此我将分享我的尝试和它对我的作用。请注意,我还需要在运行时实现更改字体操作

在Swing应用程序中,我们通常为核心容器扩展容器类。假设我们想为桌面创建Facebook。有人可以创建3个核心类(扩展JPanel或JScrollPane)。比如说:
LeftPanel扩展了JPanel
MiddlePanel扩展了JPanel
righpanel扩展了JPanel
。左面板代表左菜单,中面板代表主滚动视图,最后右面板代表广告区域。当然,这些面板中的每一个都继承了
JPanel
s(其中一些可能也会有一个新类,例如
PostPanel
CommentSectionPanel
等)

现在,假设您已经阅读了,您的应用程序只使用一个
JFrame
,它承载其中的每个组件。甚至modal
JDialog
s也基于它(将它作为其父对象)。所以,你可以把它放在某个地方,就像一个
单身汉一样

为了更改组件文本,我们必须为每个组件调用
setText
。调用
JFrame
getComponents
将为我们提供其所有组件。如果其中一个是container,我们将不得不为它调用
getComponents
,因为它可能也包含组件。解决方案是一个递归,以找到所有这些问题:

private static <T extends Component> List<T> getChildren(Class<T> clazz, final Container container) {
    Component[] components;
    if (container instanceof JMenu)
        components = ((JMenu) container).getMenuComponents();
    else
        components = container.getComponents();
    List<T> compList = new ArrayList<T>();
    for (Component comp : components) {
        if (clazz.isAssignableFrom(comp.getClass())) {
            compList.add(clazz.cast(comp));
        }
        if (comp instanceof Container)
            compList.addAll(getChildren(clazz, (Container) comp));
    }
    return compList;
}
现在,我们不给他们每个人这个能力,而是给大容器(facebook的例子):
LeftPanel扩展JPanel实现LocaleChangeable
righpanel扩展JPanel实现LocaleChangeable
,因为它们具有具有文本属性的组件

这些类现在负责更改其组件的文本。pseduo的一个例子是:

public class LeftPanel extends JPanel implements LocaleChangeable {
    private JLabel exitLabel;

    @Override
    public void localeChanged(Locale newLocale) {
        if (newLocale == Locale.ENGLISH) {
            exitLabel.setText("Exit");
        } else if (newLocale == Locale.GREEK) {
            exitLabel.setText("Έξοδος"); //Greek exit
        }
    }
}
public class LocaleTest extends JFrame {
    private static final long serialVersionUID = 1L;

    public LocaleTest() {
        super("test");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        getContentPane().setLayout(new BorderLayout());
        add(new MainPanel());

        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    private class MainPanel extends JPanel implements LocaleChangeable {
        private JLabel label;
        private JButton changeLocaleButton;

        public MainPanel() {
            super(new FlowLayout());
            label = new JLabel(Locale.ENGLISH.toString());
            add(label);

            changeLocaleButton = new JButton("Change Locale");
            changeLocaleButton.addActionListener(e -> {
                broadcastLocaleChange(Locale.CANADA);
            });
            add(changeLocaleButton);
        }

        @Override
        public void localeChanged(Locale newLocale) {
            label.setText(newLocale.toString());
            System.out.println("Language changed.");
        }

        private void broadcastLocaleChange(Locale locale) {
            List<Component> components = getChildren(Component.class, LocaleTest.this);
            components.stream().filter(LocaleChangeable.class::isInstance).map(LocaleChangeable.class::cast)
                    .forEach(lc -> lc.localeChanged(locale));
        }
    }

    private static <T extends Component> List<T> getChildren(Class<T> clazz, final Container container) {
        Component[] components;
        if (container instanceof JMenu)
            components = ((JMenu) container).getMenuComponents();
        else
            components = container.getComponents();
        List<T> compList = new ArrayList<T>();
        for (Component comp : components) {
            if (clazz.isAssignableFrom(comp.getClass())) {
                compList.add(clazz.cast(comp));
            }
            if (comp instanceof Container)
                compList.addAll(getChildren(clazz, (Container) comp));
        }
        return compList;
    }

    public static interface LocaleChangeable {
        void localeChanged(Locale newLocale);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new LocaleTest().setVisible(true));
    }
}
(当然,将发生
ResourceBundle
逻辑,而不是一堆
if-else
条件)

所以。。。让我们为所有类容器调用此方法:

private void broadcastLocaleChange(Locale locale) {
    List<Component> components = getChildren(Component.class, myFrame);
    components.stream().filter(LocaleChangeable.class::isInstance).map(LocaleChangeable.class::cast)
            .forEach(lc -> lc.localeChanged(locale));
}
private void broadcastLocaleChange(区域设置){
List components=getChildren(Component.class,myFrame);
components.stream().filter(LocaleChangeable.class::isInstance).map(LocaleChangeable.class::cast)
.forEach(lc->lc.localeChanged(locale));
}
就这样!一个完整的例子是:

public class LeftPanel extends JPanel implements LocaleChangeable {
    private JLabel exitLabel;

    @Override
    public void localeChanged(Locale newLocale) {
        if (newLocale == Locale.ENGLISH) {
            exitLabel.setText("Exit");
        } else if (newLocale == Locale.GREEK) {
            exitLabel.setText("Έξοδος"); //Greek exit
        }
    }
}
public class LocaleTest extends JFrame {
    private static final long serialVersionUID = 1L;

    public LocaleTest() {
        super("test");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        getContentPane().setLayout(new BorderLayout());
        add(new MainPanel());

        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    private class MainPanel extends JPanel implements LocaleChangeable {
        private JLabel label;
        private JButton changeLocaleButton;

        public MainPanel() {
            super(new FlowLayout());
            label = new JLabel(Locale.ENGLISH.toString());
            add(label);

            changeLocaleButton = new JButton("Change Locale");
            changeLocaleButton.addActionListener(e -> {
                broadcastLocaleChange(Locale.CANADA);
            });
            add(changeLocaleButton);
        }

        @Override
        public void localeChanged(Locale newLocale) {
            label.setText(newLocale.toString());
            System.out.println("Language changed.");
        }

        private void broadcastLocaleChange(Locale locale) {
            List<Component> components = getChildren(Component.class, LocaleTest.this);
            components.stream().filter(LocaleChangeable.class::isInstance).map(LocaleChangeable.class::cast)
                    .forEach(lc -> lc.localeChanged(locale));
        }
    }

    private static <T extends Component> List<T> getChildren(Class<T> clazz, final Container container) {
        Component[] components;
        if (container instanceof JMenu)
            components = ((JMenu) container).getMenuComponents();
        else
            components = container.getComponents();
        List<T> compList = new ArrayList<T>();
        for (Component comp : components) {
            if (clazz.isAssignableFrom(comp.getClass())) {
                compList.add(clazz.cast(comp));
            }
            if (comp instanceof Container)
                compList.addAll(getChildren(clazz, (Container) comp));
        }
        return compList;
    }

    public static interface LocaleChangeable {
        void localeChanged(Locale newLocale);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new LocaleTest().setVisible(true));
    }
}
public类LocaleTest扩展了JFrame{
私有静态最终长serialVersionUID=1L;
公共LocaleTest(){
超级(“测试”);
setDefaultCloseOperation(关闭时退出);
getContentPane().setLayout(新的BorderLayout());
添加(新的主面板());
包装();
setLocationRelativeTo(空);
setVisible(真);
}
私有类主面板扩展JPanel实现LocaleChangeable{
私人标签;
私有JButton changeLocaleButton;
公共主面板(){
超级(新FlowLayout());
label=newjlabel(Locale.ENGLISH.toString());
添加(标签);
changeLocaleButton=新JButton(“更改区域设置”);
changeLocaleButton.addActionListener(e->{
广播localechange(Locale.CANADA);
});
添加(changeLocaleButton);
}
@凌驾
public void localeChanged(Locale newLocale){
label.setText(newLocale.toString());
System.out.println(“语言更改”);
}
专用void broadcastLocaleChange(区域设置){
List components=getChildren(Component.class,LocaleTest.this);
components.stream().filter(LocaleChangeable.class::isInstance).map(LocaleChangeable.class::cast)
.forEach(lc->lc.localeChanged(locale));
}
}
私有静态列表getChildren(类clazz,最终容器){
组件[]组件;
if(JMenu的容器实例)
组件=((JMenu)容器).getMenuComponents();
其他的
components=container.getComponents();
List compList=new ArrayList();
用于(组件组件:组件){
if(clazz.isAssignableFrom(comp.getClass())){
成分列表添加(分类铸造(成分));
}
if(容器的组件实例)
compList.addAll(getChildren(clazz,(Container)comp));
}
返回compList;
}
公共静态接口LocaleChange{
void localeChanged(Locale newLocale);
}
公共静态void main(字符串[]args){
调用器(()->new LocaleTest().setVisible(true));
}
}
我的结局是:

class ComponentInfo {
    JComponent component;
    String key;

    public ComponentInfo(JComponent c,String k) {
        component = c;
        key = k;
    }

    public JComponent getComponent() {return component;}
    public String getKey() {return key;}
}
要使用此选项,请创建一个
ArrayList
,并在创建后直接向其中添加应更改其语言的每个组件:

List<ComponentInfo> allComponentInfos = new ArrayList<ComponentInfo>();
JLabel label_pw = new JLabel(resourceBundle.getString("pw_key"));
label_pw.setFont(new Font("Tahoma", Font.PLAIN, 14));
allComponentInfos.add(new ComponentInfo(label_pw, "pw_key"));
我知道这会为每个组件创建一个额外的对象。:/不幸的是,这是我发现的唯一让我改变语言的方法
private void changeLanguage() {
    Locale locale = comboBox_language.getItemAt(comboBox_language.getSelectedIndex());
    resourceBundle = ResourceBundle.getBundle("Bundle", locale);

    for(ComponentInfo c:allComponentInfos) {
        if(c.getComponent() instanceof JLabel) {
            ((JLabel) c.getComponent()).setText(resourceBundle.getString(c.getKey()));
        } else if(c.getComponent() instanceof JButton) {
            ((JButton) c.getComponent()).setText(resourceBundle.getString(c.getKey()));
        }
    }
}