【问题标题】:Reading unicode from messageSource gives problem with Java 5从 messageSource 读取 unicode 会导致 Java 5 出现问题
【发布时间】:2011-07-17 10:42:21
【问题描述】:

我用属性文件(例如:messages_en_US.properties、messages_de_DE.properties)制作了一个支持 i18n 的 Spring(2.5.6) Web 应用程序。

这个 .properties 文件带有 uni-codes。例如:

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, null);
    }
...

所以没有\

服务器上的文件也是UTF-8:

问题发生的环境:

  • Tomcat 5.5.28(从common/lib 运行jsp-api.jarservlet-api.jar
  • JDK 1.5.0_22
  • JSTL 1.1.2(从应用程序lib读取)

  • Tomcat 6.0.32(从lib 运行jsp-api.jarservlet-api.jar

  • JDK 1.5.0_22
  • JSTL 1.1.2(从应用程序lib读取)

解决问题的环境(完全相同的分布): - Tomcat 6.0.32(从 lib 运行 jsp-api.jarservlet-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:将资源属性文件放在类路径上并使用类加载器:

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

【问题讨论】:

  • 您能否发布一段来自applicationContext.xml 的摘录,它定义了ReloadableResourceBundleMessageSource messageSource
  • @Harry Lime:查看帖子更新
  • 看来ReloadableResourceBundleMessageSource中的规则this.propertiesPersister.load(props, new InputStreamReader(is, encoding));在编码为“UTF-8”时遇到问题。当我将编码改回ISO-8859-1 时,问题就解决了。现在我只需要签入例如中文代币作品。
  • 不幸的是,这种改进并非在所有情况下都有效,因此我制作了一个未定义编码的 CustomResourceBundleMessageSource 并使用props.load(inputstream)

标签: java unicode utf-8 internationalization character-encoding


【解决方案1】:

我看了DefaultPropertiesPersister的源代码(ReloadableResourceBundleMessageSource内部使用)。

如果指定了defaultEncoding,则从Reader 手动逐行加载属性,而不是使用传统的Properties.load() 方法。

在将键/值对添加到Properties 对象之前,在Strings 上调用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();
}

这是 \ 字符被删除的地方。

如果创建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>

它会起作用.. 可能需要进一步的 jiggery-pokery 才能获得与其他编码相关的确切内容,等等 :)

【讨论】:

  • 感谢您的完美回答。但现在我不需要使用DefaultPropertiesPersister。所以我只是用props.load(inputstream)替换它;这就像魅力一样。即使在 JDK5 和 JDK6 中使用中文或俄文字符。 (JDK5以下不需要)
【解决方案2】:
request.setCharacterEncoding("UTF-8");

当在任何getParameter() 调用之前调用时,这只指示servlet API 使用什么编码来解析POST(不是GET!)request 正文的参数.

您仍然必须更改 response 编码以使用 UTF-8,以便 servlet API 知道它应该使用什么编码将字符作为字节发送到 HTTP 连接的另一端。您还必须通过 HTTP 响应标头指示网络浏览器传输字节的编码是什么,以便网络浏览器可以正确地将它们解码为字符。

在 JSP 中,这两个任务都可以通过文件顶部的这一简单行来完成。您需要将其应用于所有 JSP,以及包含文件/片段。

<%@ page pageEncoding="UTF-8" %>

否则服务器平台默认编码将用于发送,客户端平台默认编码用于读取(尽管某些智能网络浏览器如 Firefox 可以在 HTTP 响应标头中未指定字符集时自动检测字符集)。

另见:


更新:您确定 unicode 转义本身不会在 Linux 机器的属性文件中重新转义吗?即,您看到\u00E 并且喜欢所有地方,因此不是\\u00E?这样就可以解释问题了。

【讨论】:

  • 谢谢你的回答,我希望这能解决问题。但是您提到的解决方案已经实现: 我使用 CharacterEncodingFilter。我使用 JSTL 来显示 i18n 消息...所以:.
  • 设置响应编码已经由 pageEncoding 隐式完成。在 JSP 中设置请求编码是没有意义的,因为它通常已经太晚了。只需响应编码(和标头)的 pageEncoding 和请求编码的过滤器就足够了。
  • 感谢您的 cmets。这就说得通了。不幸的是,这并不能解决问题......
  • 是的,我知道这一点。这只是一个评论。请注意底部的答案更新以解决具体问题。
  • "请注意底部的答案更新以解决具体问题" ??我不明白你的评论是什么意思。
【解决方案3】:

您需要确保以运行服务器的用户身份检查语言环境,或者更准确地说,您需要检查服务器启动环境的语言环境。出于调试目的,您可以编辑启动脚本以将“语言环境”的输出写入某个文件。

【讨论】:

  • 听起来不错,但是当您在 linux 服务器上以管理员身份登录时,是否有可能为其他用户检查这个(我只有一个 SSH 登录)
  • 是的,您需要以运行服务器的用户身份对其进行检查,因为该环境决定了 JVM 使用什么作为默认编码。仅当您以该用户身份运行服务器时,登录用户的环境编码才重要。
  • 所以我知道您无法检查其他用户的区域设置,例如“root”用户?也许您知道一个包含更多信息的页面?我是一个 linux 菜鸟。
  • 对。由于您显然不是管理运行您的应用程序的服务器的人,请联系负责人并让他们检查环境。
【解决方案4】:

引用 java.util.Properties 的 javadoc,

load(InputStream) / store(OutputStream, String) 方法与 load(Reader)/store(Writer, String) 对的工作方式相同,除了输入/输出流采用 ISO 8859-1 字符编码.不能用这种编码直接表示的字符可以使用 Unicode 转义来编写;转义序列中只允许使用单个 'u' 字符。 native2ascii 工具可用于将属性文件与其他字符编码进行转换。

也许您有一些构建阶段将您的 UTF-8 编码文件转换为 ascii。尝试将属性文件的编码更改为 8859-1。听起来您的属性文件已经适当地转义了 Unicode 字符。

还可以使用getClassLoader().getResourceAsStream(...) 自己获取到属性文件的流,然后加载到属性文件中。查看这些值是否是所需的字符串。这将是编码+包装问题与弹簧问题的 1/2。

根据评论线程更新:

Java 1.5 java.util.Properties没有load(Reader) API。很明显,这是 Java 1.6 时间框架中的一个改进领域。

【讨论】:

  • @Dilum Ranatunga:我尝试再次将文件转换为 8859-1(Eclipse 中的默认值),但这并不能解决问题。我稍后会尝试第二个建议。
  • @Dilum Ranatunga:您的第二个建议给出了一个有趣的结果。见我的更新2。我觉得SpringReloadableResourceBundleMessageSource有问题?还是我错了?
  • 我会破解该类的实现,看看它在做什么。
  • 我已经在阅读源代码并使用它;)source。似乎PropertiesPersister 做错了什么。就这一行:this.propertiesPersister.load(props, new InputStreamReader(is, encoding));后这个道具是错误的
  • 不幸的是,这种改进不起作用。我现在制作了一个未定义编码的 CustomResourceBundleMessageSource 并使用简单的props.load(inputstream)
猜你喜欢
  • 1970-01-01
  • 2019-05-26
  • 2023-04-04
  • 2016-05-20
  • 2014-12-26
  • 1970-01-01
  • 1970-01-01
  • 2015-04-03
  • 1970-01-01
相关资源
最近更新 更多