ZetCode

Java ResourceBundle

最后修改于 2024 年 1 月 27 日

在本 Java ResourceBundle 教程中,我们将展示如何在 Java 中使用 ResourceBundle

硬编码特定于区域设置的数据不是一个好方法。诸如消息或标签之类的值应放置在单独的文件中。这样,我们就可以处理多个区域设置,而无需为每个区域设置编写不同的代码。这对于翻译人员来说也很方便,因为他们只处理可翻译的文本,而不查看编程代码。

Java ResourceBundle

资源包是一个 Java 属性文件,其中包含特定于区域设置的数据。它是一种通过使代码与区域设置无关来国际化 Java 应用程序的方式。

资源包被组织成具有公共基本名称的系列。例如,如果我们有一个 words 基本名称,words_sk 匹配斯洛伐克语的区域设置。如果不支持特定的区域设置,则使用默认资源包。

资源包还支持方言;例如,words_es_AR 用于阿根廷使用的西班牙语,而 words_es_BO 用于玻利维亚。

ResourceBundle 是一个抽象类,它有两个子类:PropertyResourceBundleListResourceBundlePropertyResourceBundle 从属性文件加载数据。属性文件是一个纯文本文件,其中包含可翻译的文本。属性文件不是 Java 源代码的一部分,它们只能包含 String 值。ListResourceBundle 使用方便的列表管理资源;它从类文件获取数据。我们可以将任何特定于区域设置的对象存储在 ListResourceBundle 中。

要获取适当的 ResourceBundle,我们调用 ResourceBundle.getBundle 方法。这是一个工厂方法,它查找 ListResourceBundle,如果找不到,则查找 PropertyResourceBundle。如果找不到资源包,则抛出 MissingResourceException

Java PropertyResourceBundle 示例

在第一个应用程序中,我们创建一个简单的 Java 应用程序,它使用三个资源包:默认的英语、德语和斯洛伐克语。

我们创建三个属性文件并将它们放在 resources 目录中。

resources/words.properties
w1 = Earth
w2 = ocean

这是默认的属性文件;它通常使用英语。我们在文件中包含两个单词。

resources/words_de.properties
w1 = Erde
w2 = ozean

words_de.properties 文件包含德语单词。

resources/words_sk.properties
w1 = Zem
w2 = oceán

words_sk.properties 文件包含斯洛伐克语单词。

com/zetcode/ResourceBundleEx.java
package com.zetcode;

import java.util.Locale;
import java.util.ResourceBundle;

public class ResourceBundleEx {

    static public void main(String[] args) {

        Locale[] locales = {
            Locale.GERMAN,
            new Locale("sk", "SK"),
            Locale.ENGLISH
        };

        System.out.println("w1:");

        for (Locale locale : locales) {

            getWord(locale, "w1");
        }

        System.out.println("w2:");

        for (Locale locale : locales) {

            getWord(locale, "w2");
        }
    }

    static void getWord(Locale curLoc, String key) {

        ResourceBundle words
                = ResourceBundle.getBundle("resources/words", curLoc);

        String value = words.getString(key);

        System.out.printf("Locale: %s, Value: %s %n", curLoc.toString(), value);

    }
}

在代码示例中,我们打印三个资源包中使用的所有单词。

Locale[] locales = {
    Locale.GERMAN,
    new Locale("sk", "SK"),
    Locale.ENGLISH
};

我们在示例中有三个区域设置:德语、斯洛伐克语和英语。

for (Locale locale : locales) {

    getWord(locale, "w1");
}

我们遍历这些区域设置并打印用 w1 键标记的单词。

ResourceBundle words
        = ResourceBundle.getBundle("resources/words", curLoc);

使用 ResourceBundle.getBundle 方法,我们获得当前使用区域设置的包。由于我们没有创建 ListResourceBundle,因此该方法使用 PropertyResourceBundle,它从属性文件加载数据。

String value = words.getString(key);

System.out.printf("Locale: %s, Value: %s %n", curLoc.toString(), value);

我们获取该值并打印区域设置名称、键和值。

w1:
Locale: de, Value: Erde
Locale: sk_SK, Value: Zem
Locale: en, Value: Earth
w2:
Locale: de, Value: ozean
Locale: sk_SK, Value: oceán
Locale: en, Value: ocean

Java ListResourceBundle 示例

在以下应用程序中,我们使用 ListResourceBundle

我们为斯洛伐克语和捷克语创建区域设置资源。

com/zetcode/myres/MyResources_sk.java
package com.zetcode.myres;

import java.util.ListResourceBundle;

public class MyResources_sk extends ListResourceBundle {

    @Override
    protected Object[][] getContents() {

        return resources;
    }

    private final Object[][] resources = {

            { "Capital", "Bratislava" },
            { "Area", 49035 },
            { "Currency", "EUR" },
    };
}

这里我们有一个 ListResourceBundle 的斯洛伐克语实现。我们必须覆盖 getContents 方法。该方法返回键/值对的数组。

com/zetcode/myres/MyResources_cs_CZ.java
package com.zetcode.myres;

import java.util.ListResourceBundle;

public class MyResources_cs_CZ extends ListResourceBundle {

    @Override
    protected Object[][] getContents() {

        return resources;
    }

    private final Object[][] resources = {

            { "Capital", "Praha" },
            { "Area", 78866 },
            { "Currency", "CZK" },
    };
}

这是捷克语的实现。

com/zetcode/ResourceBundleEx2.java
package com.zetcode;

import java.util.Locale;
import java.util.ResourceBundle;

public class ResourceBundleEx2 {

    public static void main(String[] args) {

        Locale sk_loc = new Locale("sk", "SK");
        ResourceBundle bundle =
            ResourceBundle.getBundle("com.zetcode.myres.MyResources", sk_loc);

        System.out.println("Capital: " + bundle.getObject("Capital"));
        System.out.println("Area: " + bundle.getObject("Area"));
        System.out.println("Currency: " + bundle.getObject("Currency"));

        System.out.println();

        Locale cz_loc = new Locale("cs", "CZ");
        ResourceBundle bundle2 =
            ResourceBundle.getBundle("com.zetcode.myres.MyResources", cz_loc);

        System.out.println("Capital: " + bundle2.getObject("Capital"));
        System.out.println("Area: " + bundle2.getObject("Area"));
        System.out.println("Currency: " + bundle2.getObject("Currency"));
    }
}

该示例打印斯洛伐克和捷克共和国的一些地理数据。

Locale sk_loc = new Locale("sk", "SK");
ResourceBundle bundle =
    ResourceBundle.getBundle("com.zetcode.myres.MyResources", sk_loc);

使用 ResourceBundle.getBundle 方法,我们从 com.zetcode.myres.MyResources_sk.class 创建一个资源包。

Capital: Bratislava
Area: 49035
Currency: EUR
Capital: Praha
Area: 78866
Currency: CZK

Swing 应用程序

在第三个示例中,我们使用 Java Swing 创建一个简单的 GUI 应用程序。该示例可以动态更改 UI 的语言。 该示例使用 ListResourceBundle 类。

源代码和图像可在作者的 Github 存储库中找到。

com/zetcode/myres/MyResources_sk.java
package com.zetcode.myres;

import java.util.ListResourceBundle;
import javax.swing.ImageIcon;

public class MyResources_sk extends ListResourceBundle {

    @Override
    protected Object[][] getContents() {

        return resources;
    }

    private final Object[][] resources = {

        {"name", "Slovensko"},
        {"lang_menu", "Jazyk"},
        {"lang_sk", "Slovenčina"},
        {"lang_hu", "Maďarčina"},
        {"flag", new ImageIcon("src/resources/slovakia.png")},
        {"description", "Slovensko je vnútrozemský štát v strednej Európe."}
    };
}

这些是斯洛伐克语的资源。我们有五个字符串和一个 ImageIcon

com/zetcode/myres/MyResources_hu.java
package com.zetcode.myres;

import java.util.ListResourceBundle;
import javax.swing.ImageIcon;

public class MyResources_hu extends ListResourceBundle {

    @Override
    protected Object[][] getContents() {

        return resources;
    }

    private final Object[][] resources = {

        {"name", "Magyarország"},
        {"lang_menu", "Nyelv"},
        {"lang_sk", "Szlovák"},
        {"lang_hu", "Magyar"},
        {"flag", new ImageIcon("src/resources/hungary.png")},
        {"description", "Magyarország közép-európai ország "
            + "a Kárpát-medencében."}
    };
}

这些是匈牙利语的资源。

com/zetcode/ResourceBundleEx3.java
package com.zetcode;

import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.swing.ButtonGroup;
import javax.swing.GroupLayout;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.LayoutStyle;

/*
 * Java ResourceBundle tutorial
 *
 * This program uses a ResourceBundle in a
 * Java Swing application.
 *
 * Author: Jan Bodnar
 * Website: zetcode.com
 * Last modified: October 2022
 */
public class ResourceBundleEx3 extends JFrame {

    private ResourceBundle bundle;
    private JLabel flag;
    private JLabel lbl;
    private JMenu langMenu;
    private JRadioButtonMenuItem skMenuItem;
    private JRadioButtonMenuItem huMenuItem;

    public ResourceBundleEx3() {

        initUI();
    }

    private void initUI() {

        createMenuBar();

        flag = new JLabel();
        lbl = new JLabel();

        updateLanguage(new Locale("sk", "SK"));

        createLayout(lbl, flag);
        pack();

        setTitle(bundle.getString("name"));
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    private void updateLanguage(Locale locale) {

        bundle = ResourceBundle.getBundle("com.zetcode.myres.MyResources", locale);
        langMenu.setText(bundle.getString("lang_menu"));
        skMenuItem.setText(bundle.getString("lang_sk"));
        huMenuItem.setText(bundle.getString("lang_hu"));
        flag.setIcon((Icon) bundle.getObject("flag"));
        lbl.setText(bundle.getString("description"));
        setTitle(bundle.getString("name"));
        pack();
    }

    private void createMenuBar() {

        JMenuBar menubar = new JMenuBar();

        langMenu = new JMenu();
        langMenu.setMnemonic(KeyEvent.VK_F);

        ButtonGroup btnGroup = new ButtonGroup();

        skMenuItem = new JRadioButtonMenuItem("Slovak", true);
        btnGroup.add(skMenuItem);

        skMenuItem.addActionListener((ActionEvent e) -> {
            updateLanguage(new Locale("sk", "SK"));
        });

        langMenu.add(skMenuItem);

        huMenuItem = new JRadioButtonMenuItem("Hungarian");
        btnGroup.add(huMenuItem);

        huMenuItem.addActionListener((ActionEvent e) -> {
            updateLanguage(new Locale("hu", "HU"));
        });

        langMenu.add(huMenuItem);

        menubar.add(langMenu);

        setJMenuBar(menubar);
    }

    private void createLayout(JComponent... arg) {

        Container pane = getContentPane();
        GroupLayout gl = new GroupLayout(pane);
        pane.setLayout(gl);

        gl.setAutoCreateContainerGaps(true);

        gl.setHorizontalGroup(gl.createParallelGroup()
                .addComponent(arg[0])
                .addComponent(arg[1])
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(arg[0])
                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(arg[1])
        );
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {
            ResourceBundleEx3 ex = new ResourceBundleEx3();
            ex.setVisible(true);
        });
    }
}

我们有一个菜单栏,其中包含一个菜单,该菜单包含两个单选按钮菜单项。 选择一个单选按钮菜单项会更改应用程序用户界面的语言。

private void updateLanguage(Locale locale) {

    bundle = ResourceBundle.getBundle("com.zetcode.myres.MyResources", locale);
    langMenu.setText(bundle.getString("lang_menu"));
    skMenuItem.setText(bundle.getString("lang_sk"));
    huMenuItem.setText(bundle.getString("lang_hu"));
    flag.setIcon((Icon) bundle.getObject("flag"));
    lbl.setText(bundle.getString("description"));
    setTitle(bundle.getString("name"));
    pack();
}

当我们选择一个单选按钮菜单项时,将调用 updateLanguage 方法。 它基于给定的区域设置创建一个新的 ResourceBundle,并更新菜单、单选菜单项、图像图标、描述和框架标题。

skMenuItem.addActionListener((ActionEvent e) -> {
    updateLanguage(new Locale("sk", "SK"));
});

选择一个斯洛伐克单选按钮菜单项,我们调用 updateLanguage 方法并传递一个斯洛伐克区域设置为参数。

Swing application
图:Swing 应用程序

Spring Boot 应用程序

在下一个示例中,我们在 Spring Boot 应用程序中使用资源包。Spring 是一个流行的 Java 应用程序框架。Spring Boot 是一种新的解决方案,可以用最少的精力创建独立的、生产级的基于 Spring 的应用程序。

同样,我们创建三个属性文件并将它们放在 src/main/resources/messages 目录中。

resources/messages/words.properties
w1 = Earth
w2 = ocean

这是默认的属性文件。

resources/messages/words_de.properties
w1 = Erde
w2 = ozean

words_de.properties 文件包含德语单词。

resources/messages/words_sk.properties
w1 = Zem
w2 = oceán

words_sk.properties 文件包含斯洛伐克语单词。

build.gradle
plugins {
    id 'org.springframework.boot' version '2.6.7'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.zetcode'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
}

这是 Gradle 构建文件。

com/zetcode/Application.java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ResourceBundleMessageSource;

@SpringBootApplication
public class Application {

    @Bean
    public ResourceBundleMessageSource messageSource() {

        ResourceBundleMessageSource source = new ResourceBundleMessageSource();
        source.setBasenames("messages/words");
        source.setUseCodeAsDefaultMessage(true);

        return source;
    }

    public static void main(String[] args) {

        SpringApplication.run(Application.class, args);
    }
}

Application 是主应用程序类。我们设置 Spring Boot 程序。

@Bean
public ResourceBundleMessageSource messageSource() {

    ResourceBundleMessageSource source = new ResourceBundleMessageSource();
    source.setBasenames("messages/words");
    source.setUseCodeAsDefaultMessage(true);

    return source;
}

使用 @Bean 注解,我们生成一个 ResourceBundleMessageSource bean,它由 Spring 容器管理。ResourceBundleMessageSource 是一个 MessageSource 实现,它使用指定的基本名称访问资源包。此类依赖于底层 JDK 的 ResourceBundle 实现。

com/zetcode/MyRunner.java
package com.zetcode;

import org.springframework.boot.CommandLineRunner;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;

import java.util.Locale;

@Component
public class MyRunner implements CommandLineRunner {

    private MessageSource messageSource;

    public MyRunner(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    @Override
    public void run(String... args) throws Exception {

        System.out.println(messageSource.getMessage("w1",
                null, Locale.GERMAN));
        System.out.println(messageSource.getMessage("w1",
                null, Locale.ENGLISH));
        System.out.println(messageSource.getMessage("w2",
                null, new Locale("sk", "SK")));
    }
}

MyRunner 是 Spring Boot 应用程序的命令行运行器。

private MessageSource messageSource;

public MyRunner(MessageSource messageSource) {
    this.messageSource = messageSource;
}

我们将 MessageSource 注入到该字段中。

System.out.println(messageSource.getMessage("w1",
        null, Locale.GERMAN));

我们使用 getMessage 方法获取德语区域设置中的单词 w1。

...
Erde
Earth
oceán
...

来源

Java ResourceBundle - 语言参考

在本文中,我们介绍了 Java ResourceBundle。我们创建了两个 Java 控制台应用程序、一个 Swing 应用程序和一个 Spring Boot 应用程序。

作者

我的名字是 Jan Bodnar,我是一位充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。到目前为止,我已经撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有超过十年的经验。

列出所有Java教程