从messageSource读取unicode会给Java 5带来问题

问题描述

| 我制作了一个i18n支持的Spring(2.5.6)Web应用程序,该应用程序带有属性文件(例如:messages_en_US.properties,messages_de_DE.properties)。 该.properties文件带有Unicode。例如:
busy = Besch\\u00E4ftigt
messageSource
读取
busy
关键字时,得出以下结果:
...
private static ReloadableResourceBundleMessageSource messageSource;

    /**
     * Gets a message from the resources (.properties) defined in the applicationContext.xml
     *
     * @param input string to hook up
     * @return the the message hooked up from the resources
     */
    public static String getMessage(String input){
        System.out.println(input); //busy
        System.out.println(messageSource.getDefaultEncoding()); //UTF-8
        System.out.println(messageSource.getMessage(input,null,null)); //Beschu00E4ftigt
        return messageSource.getMessage(input,null);
    }
...
所以没有the4 服务器上的文件也是UTF-8: 发生问题的环境: Tomcat 5.5.28(从
common/lib
运行
jsp-api.jar
servlet-api.jar
) JDK 1.5.0_22 JSTL 1.1.2(从应用程序
lib
读取) Tomcat 6.0.32(从
lib
运行
jsp-api.jar
servlet-api.jar
) JDK 1.5.0_22 JSTL 1.1.2(从应用程序
lib
读取) 解决问题的环境(分布完全相同):  -Tomcat 6.0.32(从
lib
运行
jsp-api.jar
servlet-api.jar
)  -JDK 1.6.0_13  -JSTL 1.1.2(从应用程序
lib
中读取) 如果您需要更多信息,请告诉我。而且不要说我不需要更新JDK,因为这是不可能的。 更新applicationContext.xml中的绑定messageSource
<b:bean id=\"messageSource\" class=\"org.springframework.context.support.ReloadableResourceBundleMessageSource\">
    <b:property name=\"defaultEncoding\" value=\"UTF-8\"/>
    <b:property name=\"fallbackToSystemLocale\" value=\"false\" />
    <b:property name=\"basenames\">
        <b:list>
            <b:value>classpath:messages</b:value>
            <b:value>/public/custom/i18n/portalmessages</b:value>
        </b:list>
    </b:property>    
    <b:property name=\"cacheSeconds\" value=\"1\"/>
</b:bean>
更新2:将资源属性文件放置在classpath上并使用classloader:
urlclassloader cl = (urlclassloader) IoUtils.class.getClassLoader();
InputStream resourceAsstream = cl.getResourceAsstream(\"messages_de_DE.properties\");
Properties prop = new Properties();
prop.load(resourceAsstream);
System.out.println(\"From classpath --> \" + prop.get(\"busy\")); //Beschäftigt
System.out.println(\"From i18n folder --> \" + I18nFunctions.getMessage(\"busy\")); //Beschu00E4ftigt
    

解决方法

我看了一下
DefaultPropertiesPersister
的源代码(
ReloadableResourceBundleMessageSource
在内部使用了它)。 如果指定了
defaultEncoding
,则从
Reader
逐行手动加载属性,而不是使用常规的
Properties.load()
方法。 在将键/值对添加到
Properties
对象之前,在
String
上调用
unescape()
方法
protected String unescape(String str) {
    StringBuffer outBuffer = new StringBuffer(str.length());
    for (int index = 0; index < str.length();) {
        char c = str.charAt(index++);
        if (c == \'\\\\\') {
            c = str.charAt(index++);
            if (c == \'t\') {
                c = \'\\t\';
            }
            else if (c == \'r\') {
                c = \'\\r\';
            }
            else if (c == \'n\') {
                c = \'\\n\';
            }
            else if (c == \'f\') {
                c = \'\\f\';
            }
        }
        outBuffer.append(c);
    }
    return outBuffer.toString();
}
这是ѭ4字符被删除的地方。 如果您按如下方式创建
DefaultPropertiesPersister
的子类
package com.something;

import org.apache.commons.lang.StringEscapeUtils;
import org.springframework.util.DefaultPropertiesPersister;

public class MyPropertiesPersister extends DefaultPropertiesPersister {
    protected String unescape(String str)
    {
        return StringEscapeUtils.unescapeJava(str);
    }    
}
像这样在您的spring配置中设置它:
<b:bean id=\"messageSource\" class=\"org.springframework.context.support.ReloadableResourceBundleMessageSource\">
    <b:property name=\"defaultEncoding\" value=\"UTF-8\"/>
    <b:property name=\"fallbackToSystemLocale\" value=\"false\" />
    <b:property name=\"basenames\">
        <b:list>
            <b:value>classpath:messages</b:value>
            <b:value>/public/custom/i18n/portalmessages</b:value>
        </b:list>
    </b:property>    
    <b:property name=\"cacheSeconds\" value=\"1\"/>
    <b:property name=\"propertiesPersister\">
        <b:bean class=\"com.something.MyPropertiesPersister\"/>
    </b:property>
</b:bean>
它将工作..可能需要进一步的拼图才能获得与其他编码等有关的确切信息:)     ,
request.setCharacterEncoding(\"UTF-8\");
在任何“ 33”调用之前被调用时,它仅指示Servlet API使用哪种编码来解析POST(不是GET!)请求正文的参数。 您仍然必须更改响应编码以使用UTF-8,以便Servlet API知道应该使用哪种编码将字符作为字节发送到HTTP连接的另一端。您还必须通过HTTP响应标头指示Web浏览器所传输的字节的编码格式,以便Web浏览器可以正确地将其解码为字符。 在JSP中,这两个任务都可以通过文件顶部的这一简单行来完成。您需要将其应用于所有JSP,以及包含文件/片段。
<%@ page pageEncoding=\"UTF-8\" %>
否则,服务器平台默认编码将用于发送,客户端平台默认编码用于读取(尽管某些智能Web浏览器(如Firefox)可以在HTTP响应标头中未指定字符集时自动检测其字符集)。 也可以看看: Unicode-如何正确获取字符? 更新:您确定unicode转义本身不会在Linux机器的属性文件中重新转义吗?也就是说,您看到的是
\\u00E
,而且在所有地方都很喜欢,因此看不到
\\\\u00E
?那可以解释这个问题。     ,您需要确保以运行服务器的用户身份检查语言环境,或者更确切地说,需要检查服务器启动环境的语言环境。出于调试目的,您可以将启动脚本编辑为将“语言环境”的输出写入某个文件。     ,从javadoc引用java.util.Properties,   load(InputStream)/ store(OutputStream,String)方法的工作方式与load(Reader)/ store(Writer,String)对相同,不同之处在于输入/输出流以ISO 8859-1字符编码进行编码。不能使用此编码直接表示的字符可以使用Unicode转义符编写;转义序列中只允许使用一个'u \'字符。 native2ascii工具可用于在其他字符编码之间来回转换属性文件。 也许您有一些构建阶段正在将您的UTF-8编码文件转换为ascii。尝试将属性文件的编码更改为8859-1。听起来您的属性文件已经适当地转义了Unicode字符。 也可以使用
getClassLoader().getResourceAsStream(...)
自己获取流到属性文件的流,并加载到属性文件中。查看值是否为所需的字符串。这将使问题变成编码+打包问题,而不是弹簧问题。 根据评论主题更新: Java 1.5 java.util.Properties没有
load(Reader)
API。显然,这是Java 1.6时间框架中需要改进的地方。