关于Java:如何在ResourceBundle的资源属性中使用UTF-8

How to use UTF-8 in resource properties with ResourceBundle

我需要使用Java的ResourceBundle在资源属性中使用UTF-8。 当我直接在属性文件中输入文本时,它显示为mojibake。

我的应用程序在Google App Engine上运行。

谁能给我一个例子吗? 我无法完成这项工作。


指定.properties文件时,ResourceBundle#getBundle()PropertyResourceBundle的封面下使用。依次默认使用Properties#load(InputStream)来加载那些属性文件。根据javadoc,默认情况下将其读取为ISO-8859-1。

public void load(InputStream inStream) throws IOException

Reads a property list (key and element pairs) from the input byte stream. The input stream is in a simple line-oriented format as specified in load(Reader) and is assumed to use the ISO 8859-1 character encoding; that is each byte is one Latin1 character. Characters not in Latin1, and certain special characters, are represented in keys and elements using Unicode escapes as defined in section 3.3 of The Java? Language Specification.

因此,您需要将它们另存为ISO-8859-1。如果您有超出ISO-8859-1范围的任何字符,并且不能使用\uXXXX,因此不得不将文件另存为UTF-8,则需要使用native2ascii工具将UTF-8保存的属性文件转换为ISO-8859-1保存的属性文件,其中所有未发现的字符都转换为\uXXXX格式。下面的示例将UTF-8编码的属性文件text_utf8.properties转换为有效的ISO-8859-1编码的属性文件text.properties

1
native2ascii -encoding UTF-8 text_utf8.properties text.properties

当使用健全的IDE(例如Eclipse)时,在基于Java的项目中创建.properties文件并使用Eclipse自己的编辑器时,这已经自动完成。 Eclipse将透明地将超出ISO-8859-1范围的字符转换为\uXXXX格式。另请参见下面的屏幕截图(请注意底部的"属性"和"源"选项卡,单击以查看大图):

Properties Source

或者,您也可以创建一个自定义的ResourceBundle.Control实现,其中您可以使用InputStreamReader将属性文件显式读取为UTF-8,这样就可以将它们另存为UTF-8,而无需麻烦使用native2ascii。这是一个启动示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class UTF8Control extends Control {
    public ResourceBundle newBundle
        (String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
            throws IllegalAccessException, InstantiationException, IOException
    {
        // The below is a copy of the default implementation.
        String bundleName = toBundleName(baseName, locale);
        String resourceName = toResourceName(bundleName,"properties");
        ResourceBundle bundle = null;
        InputStream stream = null;
        if (reload) {
            URL url = loader.getResource(resourceName);
            if (url != null) {
                URLConnection connection = url.openConnection();
                if (connection != null) {
                    connection.setUseCaches(false);
                    stream = connection.getInputStream();
                }
            }
        } else {
            stream = loader.getResourceAsStream(resourceName);
        }
        if (stream != null) {
            try {
                // Only this line is changed to make it to read properties files as UTF-8.
                bundle = new PropertyResourceBundle(new InputStreamReader(stream,"UTF-8"));
            } finally {
                stream.close();
            }
        }
        return bundle;
    }
}

可以如下使用:

1
ResourceBundle bundle = ResourceBundle.getBundle("com.example.i18n.text", new UTF8Control());

也可以看看:

  • Unicode-如何正确获取字符?


假设您有一个ResourceBundle实例,则可以通过以下方式获取String:

1
String val = bundle.getString(key);

我通过以下方法解决了日语显示问题:

1
return new String(val.getBytes("ISO-8859-1"),"UTF-8");


看看这个:http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)

这些属性接受Reader对象作为参数,您可以从InputStream创建该对象。

在创建时,您可以指定阅读器的编码:

1
InputStreamReader isr = new InputStreamReader(stream,"UTF-8");

然后将此Reader应用于load方法:

1
prop.load(isr);

顺便说一句:从.properties文件获取流:

1
 InputStream stream = this.class.getClassLoader().getResourceAsStream("a.properties");

顺便说一句:从InputStreamReader获取资源包:

希望这可以帮到你 !


例如,如果属性文件使用cp1251字符集,则带有UTF-8的ResourceBundle.Control和新的String方法无效。

因此,我推荐使用一种通用方法:以unicode符号编写。为了这:

IDEA-具有特殊的"透明的本机到ASCII转换"选项("设置">"文件编码")。

Eclipse-有一个插件" Properties Editor"。它可以作为单独的应用程序工作。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.varaneckas.utils;  

import java.io.UnsupportedEncodingException;  
import java.util.Enumeration;  
import java.util.PropertyResourceBundle;  
import java.util.ResourceBundle;  

/**
 * UTF-8 friendly ResourceBundle support
 *  
 * Utility that allows having multi-byte characters inside java .property files.
 * It removes the need for Sun's native2ascii application, you can simply have
 * UTF-8 encoded editable .property files.
 *  
 * Use:  
 * ResourceBundle bundle = Utf8ResourceBundle.getBundle("bundle_name");
 *  
 * @author Tomas Varaneckas <[email protected]>
 */
 
public abstract class Utf8ResourceBundle {  

    /**
     * Gets the unicode friendly resource bundle
     *  
     * @param baseName
     * @see ResourceBundle#getBundle(String)
     * @return Unicode friendly resource bundle
     */
 
    public static final ResourceBundle getBundle(final String baseName) {  
        return createUtf8PropertyResourceBundle(  
                ResourceBundle.getBundle(baseName));  
    }  

    /**
     * Creates unicode friendly {@link PropertyResourceBundle} if possible.
     *  
     * @param bundle  
     * @return Unicode friendly property resource bundle
     */
 
    private static ResourceBundle createUtf8PropertyResourceBundle(  
            final ResourceBundle bundle) {  
        if (!(bundle instanceof PropertyResourceBundle)) {  
            return bundle;  
        }  
        return new Utf8PropertyResourceBundle((PropertyResourceBundle) bundle);  
    }  

    /**
     * Resource Bundle that does the hard work
     */
 
    private static class Utf8PropertyResourceBundle extends ResourceBundle {  

        /**
         * Bundle with unicode data
         */
 
        private final PropertyResourceBundle bundle;  

        /**
         * Initializing constructor
         *  
         * @param bundle
         */
 
        private Utf8PropertyResourceBundle(final PropertyResourceBundle bundle) {  
            this.bundle = bundle;  
        }  

        @Override  
        @SuppressWarnings("unchecked")  
        public Enumeration getKeys() {  
            return bundle.getKeys();  
        }  

        @Override  
        protected Object handleGetObject(final String key) {  
            final String value = bundle.getString(key);  
            if (value == null)  
                return null;  
            try {  
                return new String(value.getBytes("ISO-8859-1"),"UTF-8");  
            } catch (final UnsupportedEncodingException e) {  
                throw new RuntimeException("Encoding not supported", e);  
            }  
        }  
    }  
}


这个问题终于在Java 9中得以解决:
https://docs.oracle.com/javase/9??/intl/internationalization-enhancements-jdk-9

现在,属性文件的默认编码为UTF-8。

Most existing properties files should not be affected: UTF-8 and
ISO-8859-1 have the same encoding for ASCII characters, and
human-readable non-ASCII ISO-8859-1 encoding is not valid UTF-8. If an
invalid UTF-8 byte sequence is detected, the Java runtime
automatically rereads the file in ISO-8859-1.


我们创建一个resources.utf8文件,其中包含UTF-8中的资源,并具有运行以下规则:

1
native2ascii -encoding utf8 resources.utf8 resources.properties


注意:java属性文件应使用ISO 8859-1编码!

ISO 8859-1 character encoding.
Characters that cannot be directly
represented in this encoding can be
written using Unicode escapes ; only
a single 'u' character is allowed in
an escape sequence.

@see属性Java文档

如果您仍然真的想要这样做:看一下:
Eclipse中的Java属性UTF-8编码-有一些代码示例


http://sourceforge.net/projects/eclipse-rbe/

如前所述,属性文件应使用ISO 8859-1进行编码

您可以使用上面的eclipse IDE插件为您进行Unicode转换。


这是一个Java 7解决方案,它使用了Guava出色的支持库和try-with-resources构造。它使用UTF-8读写属性文件,以提供最简单的整体体验。

要将属性文件读取为UTF-8:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
File file =  new File("/path/to/example.properties");

// Create an empty set of properties
Properties properties = new Properties();

if (file.exists()) {

  // Use a UTF-8 reader from Guava
  try (Reader reader = Files.newReader(file, Charsets.UTF_8)) {
    properties.load(reader);
  } catch (IOException e) {
    // Do something
  }
}

要将属性文件编写为UTF-8:

1
2
3
4
5
6
7
8
9
File file =  new File("/path/to/example.properties");

// Use a UTF-8 writer from Guava
try (Writer writer = Files.newWriter(file, Charsets.UTF_8)) {
  properties.store(writer,"Your title here");
  writer.flush();
} catch (IOException e) {
  // Do something
}


正如一个建议一样,我经历了资源包的实现..但这没有帮助..因为总是在en_US语言环境下调用资源包...我试图将默认语言环境设置为另一种语言,但仍然是我对资源包的实现使用en_US调用控件...我试图放置日志消息并进行调试,以查看在运行时通过xhtml和JSF调用更改语言环境后是否正在进行其他本地调用...这没有发生...然后我尝试将系统默认设置为utf8,以便通过我的服务器(tomcat服务器)读取文件。但这引起了麻烦,因为我的所有类库都没有在utf8下编译,并且tomcat开始以utf8格式读取并且服务器运行不正常...然后我最终在java控制器中实现了一个从xhtml文件调用的方法。在该方法中,我执行了以下操作:

1
2
3
4
5
6
7
8
9
10
        public String message(String key, boolean toUTF8) throws Throwable{
            String result ="";
            try{
                FacesContext context = FacesContext.getCurrentInstance();
                String message = context.getApplication().getResourceBundle(context,"messages").getString(key);

                result = message==null ?"" : toUTF8 ? new String(message.getBytes("iso8859-1"),"utf-8") : message;
            }catch(Throwable t){}
            return result;
        }

我特别紧张,因为这可能会降低我的应用程序的性能……但是,实现此功能之后,我的应用程序看起来好像现在更快了。我认为这是因为,我现在直接访问属性而不是让它JSF解析其访问属性的方式...我在此调用中专门传递了布尔参数,因为我知道某些属性将不会被转换并且不需要采用utf8格式...

现在,我已将属性文件保存为UTF8格式,并且可以正常工作,因为应用程序中的每个用户都具有引用的语言环境首选项。


1
2
3
4
Properties prop = new Properties();
String fileName ="./src/test/resources/predefined.properties";
FileInputStream inputStream = new FileInputStream(fileName);
InputStreamReader reader = new InputStreamReader(inputStream,"UTF-8");

对于我的问题,值得考虑的是文件本身的编码错误。使用iconv对我有用

1
iconv -f ISO-8859-15 -t UTF-8  messages_nl.properties > messages_nl.properties.new


我尝试使用Rod提供的方法,但考虑到BalusC担心在所有应用程序中不重复相同的解决方法,因此附带了此类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.io.UnsupportedEncodingException;
import java.util.Locale;
import java.util.ResourceBundle;

public class MyResourceBundle {

    // feature variables
    private ResourceBundle bundle;
    private String fileEncoding;

    public MyResourceBundle(Locale locale, String fileEncoding){
        this.bundle = ResourceBundle.getBundle("com.app.Bundle", locale);
        this.fileEncoding = fileEncoding;
    }

    public MyResourceBundle(Locale locale){
        this(locale,"UTF-8");
    }

    public String getString(String key){
        String value = bundle.getString(key);
        try {
            return new String(value.getBytes("ISO-8859-1"), fileEncoding);
        } catch (UnsupportedEncodingException e) {
            return value;
        }
    }
}

使用此方法的方式将与常规ResourceBundle的用法非常相似:

1
2
private MyResourceBundle labels = new MyResourceBundle("es","UTF-8");
String label = labels.getString(key)

或者,您可以使用默认情况下使用UTF-8的备用构造函数:

1
private MyResourceBundle labels = new MyResourceBundle("es");

打开"设置/首选项"对话框(Ctrl + Alt + S),然后单击"编辑器"和"文件编码"。

Screenshot of window shown

然后,在底部,您将为属性文件指定默认编码。选择您的编码类型。

或者,您可以使用unicode符号代替资源束中的文本(例如"?в"等于\u0456\u0432)