如何在资源包的资源属性中使用UTF-8

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

我的应用程序运行在谷歌应用程序引擎。

谁能给我举个例子?我找不到这份工作。

268944 次浏览

注意:在Java <= 8 java属性文件应该编码在ISO 8859-1!

ISO 8859-1字符编码。 不能直接输入的字符 用这种编码表示可以 使用Unicode转义符编写;只有 允许输入一个“u”字符 转义序列

Java文档

如果你真的想这样做:看看: Eclipse中的Java属性UTF-8编码——有一些代码样本


从Java 9开始:属性文件是用UTF-8编码的,所以应该没有问题/疑问

在Java SE 9中,属性文件以UTF-8编码方式加载。在以前的版本中,ISO-8859-1编码用于加载属性资源包。

(https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9.htm#JSINT-GUID-9DCDB41C-A989-4220-8140-DBFB844A0FCA)

Java 9及更新版本

从Java 9开始属性文件默认编码为UTF-8,使用ISO-8859-1之外的字符应该可以开箱即用。

Java 8及以上版本

当指定.properties文件时,ResourceBundle#getBundle()在封面下使用PropertyResourceBundle。这反过来使用默认的Properties#load(InputStream)来加载这些属性文件。根据javadoc,它们默认读取为ISO-8859-1。

public void load(InputStream inStream) throws IOException

从输入字节流中读取属性列表(键和元素对)。输入流是一种简单的面向行的格式,如load(Reader) 并且假定使用ISO 8859-1字符编码;即每个字节是一个拉丁字符。非拉丁字符1和某些特殊字符使用Java™语言规范3.3节中定义的Unicode转义符在键和元素中表示。

因此,您需要将它们保存为ISO-8859-1。如果你有任何超出ISO-8859-1范围的字符,并且你不能直接使用\uXXXX,因此你被迫将文件保存为UTF-8,那么你需要使用< em > native2ascii < / em >工具将UTF-8保存的属性文件转换为ISO-8859-1保存的属性文件,其中所有未覆盖的字符都转换为\uXXXX格式。下面的例子将UTF-8编码的属性文件text_utf8.properties转换为有效的ISO-8859-1编码的属性文件text.properties

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

When using a sane IDE such as Eclipse, this is already automatically done when you create a .properties file in a Java based project and use Eclipse's own editor. Eclipse will transparently convert the characters beyond ISO-8859-1 range to \uXXXX format. See also below screenshots (note the "Properties" and "Source" tabs on bottom, click for large):

"Properties" tab "Source" tab

Alternatively, you could also create a custom ResourceBundle.Control implementation wherein you explicitly read the properties files as UTF-8 using InputStreamReader, so that you can just save them as UTF-8 without the need to hassle with native2ascii. Here's a kickoff example:

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;
}
}

可以这样使用:

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

参见:

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

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

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

我们创建了一个资源。utf8文件,包含UTF-8格式的资源,并有一个规则运行如下:

native2ascii -encoding utf8 resources.utf8 resources.properties
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 <tomas.varaneckas@gmail.com>
*/
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);
}
}
}
}

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

String val = bundle.getString(key);

我解决了我的日语显示问题:

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

例如,如果属性文件使用cp1251字符集,那么带有UTF-8和new String方法的ResourceBundle.Control将不起作用。

所以我建议使用一种常用的方法:使用unicode编写符号。:

的想法——有一个特殊的"本机到ascii的透明转换"选项(设置>文件编码)。

Eclipse——有一个插件属性编辑器"。它可以作为单独的应用程序工作。

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

属性接受读者对象作为参数,你可以从InputStream中创建它。

在创建时,你可以指定Reader的编码:

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

然后将这个Reader应用到load方法:

prop.load(isr);

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

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

顺便说一句:从InputStreamReader得到资源包:

ResourceBundle rb = new PropertyResourceBundle(isr);

希望这能帮助到你!

下面是一个Java 7解决方案,它使用了Guava出色的支持库和try-with-resources结构。它使用UTF-8读取和写入属性文件,以获得最简单的整体体验。

读取一个UTF-8属性文件:

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格式写一个属性文件:

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
}

不管怎样,我的问题是文件本身的编码是错误的。使用iconv对我很有效

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

正如有人建议的那样,我经历了资源包的实现。但这并没有帮助。因为bundle总是在en_US locale下调用…我试图将我的默认区域设置为不同的语言,但我的资源包控制的实现仍在被en_US调用…我试着把日志消息,并通过调试做一个步骤,看看如果一个不同的本地调用后,我改变地区在运行时通过xhtml和JSF调用…这并没有发生……然后我尝试做一个系统设置默认为utf8读取文件由我的服务器(tomcat服务器)..但这导致了pronlem,因为我所有的类库都没有在utf8下编译,tomcat开始以utf8格式读取,服务器运行不正常…然后我最终实现了一个方法在我的Java控制器被XHTML文件调用..用这种方法,我做了以下工作:

        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格式保存了我的属性文件,它工作正常,因为我的应用程序中的每个用户都有一个引用的区域设置偏好。

我尝试使用Rod提供的方法,但考虑到BalusC关注的是不要在所有应用程序中重复相同的工作,并附带了这个类:

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使用非常相似:

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

或者你可以使用默认使用UTF-8的替代构造函数:

private MyResourceBundle labels = new MyResourceBundle("es");
Properties prop = new Properties();
String fileName = "./src/test/resources/predefined.properties";
FileInputStream inputStream = new FileInputStream(fileName);
InputStreamReader reader = new InputStreamReader(inputStream,"UTF-8");
这个问题终于在Java 9中被修复了: https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9 < / p >

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

大多数现有属性文件不应受到影响:UTF-8和 ISO-8859-1对ASCII字符有相同的编码 人类可读的非ascii ISO-8859-1编码不是有效的UTF-8。如果一个 检测到无效的UTF-8字节序列,Java运行时 自动重读ISO-8859-1中的文件

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

窗口显示的截图

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

或者,你可以在资源包中使用unicode符号而不是文本(例如"ів" = \u0456\u0432)

从Java 9开始,加载属性文件的默认值已更改为UTF-8。https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9.htm

对于当前(2021-2)Java版本,仍然存在旧的ISO-8859-1函数跑龙套。属性#负载

请允许我引用官方文件。

PropertyResourceBundle

PropertyResourceBundle可以从表示属性文件的InputStream或Reader构造。从InputStream构造PropertyResourceBundle实例要求输入流用UTF-8编码。默认情况下,如果在读取输入流时发生malformdinputexception或UnmappableCharacterException,那么PropertyResourceBundle实例将重置到异常之前的状态,重新读取ISO-8859-1中的输入流,并继续读取。如果系统属性java.util.PropertyResourceBundle.encoding设置为"ISO-8859-1"或"UTF-8",则输入流仅以该编码读取,并在遇到无效序列时抛出异常。如果“iso - 8859 - 1 -“;如果指定了,则不能用ISO-8859-1编码表示的字符必须用Java™语言规范3.3节中定义的Unicode转义来表示,而其他带有Reader的构造函数则没有此限制。对于此系统属性,其他编码值将被忽略。在初始化该类时读取并计算system属性。更改或删除属性在初始化后没有效果。

https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/PropertyResourceBundle.html

属性#负载

从输入字节流中读取属性列表(键和元素对)。输入流采用load(Reader)中指定的简单的面向行的格式,并假定使用ISO 8859-1字符编码;即每个字节是一个拉丁字符。非拉丁字符1和某些特殊字符使用Java™语言规范3.3节中定义的Unicode转义符在键和元素中表示。

https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/Properties.html#load(java.io.InputStream)