【问题标题】:i18n with UTF-8 encoded properties files in JSF 2.0 applicationJSF 2.0 应用程序中带有 UTF-8 编码的属性文件的 i18n
【发布时间】:2015-12-13 19:05:43
【问题描述】:

我正在使用需要希伯来语和俄语支持的 jsf-ri 2.0.3。 问题是我在屏幕上看到乱码而不是正确的文本。

首先,我为每种语言定义了包 (*_locale.properties)。这些文件采用 UTF-8 编码。 其次,我在 faces-config.xml 中定义了默认和支持的语言环境

<locale-config>
    <default-locale>iw</default-locale>
    <supported-locale>en</supported-locale>
    <supported-locale>ru</supported-locale>
</locale-config>

我添加了一个自定义过滤器,它将响应字符编码设置为 UTF-8。

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

最后,当我创建一个简单的 xhtml 来调试输出时,我看到了一个非常奇怪的结果

<f:loadBundle basename="i18n.frontend.homepage" var="msg"/>
<strong>i18n: </strong><h:outputText value="#{msg.language}"/>
<br/>
<strong>Locale: </strong>
<h:outputText value="#{facesContext.externalContext.response.locale}"/>
<br/>
<strong>Encoding: </strong>
<h:outputText value="#{facesContext.externalContext.response.characterEncoding}"/>

结果是:

i18n: ×¢×ר×ת
Locale: en_US
Encoding: UTF-8 

我的配置有什么问题?

【问题讨论】:

    标签: java jsf utf-8 internationalization jsf-2


    【解决方案1】:

    对了,你可以创建一个自定义的ResourceBundle 或者使用 native2ascii 转换器(如果需要,可以使用 Maven 2 插件使转换更加透明)。由于其他答案仅详细说明最后一种方法,因此这里有另一个答案,您可以在基于 Java SE 1.6 的环境中的 JSF 2.x 应用程序中创建自定义 ResourceBundle 以将属性文件加载为 UTF-8。

    faces-config.xml

    <application>
        <resource-bundle>
            <base-name>com.example.i18n.Text</base-name>
            <var>text</var>
        </resource-bundle>
    </application>
    

    com.example.i18n.Text

    package com.example.i18n;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.URL;
    import java.net.URLConnection;
    import java.util.Enumeration;
    import java.util.Locale;
    import java.util.PropertyResourceBundle;
    import java.util.ResourceBundle;
    
    import javax.faces.context.FacesContext;
    
    public class Text extends ResourceBundle {
    
        protected static final String BUNDLE_NAME = "com.example.i18n.text";
        protected static final String BUNDLE_EXTENSION = "properties";
        protected static final String CHARSET = "UTF-8";
        protected static final Control UTF8_CONTROL = new UTF8Control();
    
        public Text() {
            setParent(ResourceBundle.getBundle(BUNDLE_NAME, 
                FacesContext.getCurrentInstance().getViewRoot().getLocale(), UTF8_CONTROL));
        }
    
        @Override
        protected Object handleGetObject(String key) {
            return parent.getObject(key);
        }
    
        @Override
        public Enumeration<String> getKeys() {
            return parent.getKeys();
        }
    
        protected static class UTF8Control extends Control {
            public ResourceBundle newBundle
                (String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
                    throws IllegalAccessException, InstantiationException, IOException
            {
                // The below code is copied from default Control#newBundle() implementation.
                // Only the PropertyResourceBundle line is changed to read the file as UTF-8.
                String bundleName = toBundleName(baseName, locale);
                String resourceName = toResourceName(bundleName, BUNDLE_EXTENSION);
                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 {
                        bundle = new PropertyResourceBundle(new InputStreamReader(stream, CHARSET));
                    } finally {
                        stream.close();
                    }
                }
                return bundle;
            }
        }
    }
    

    这需要com.example.i18n 包中的text.propertiestext_en.properties 等UTF-8 编码属性文件。不需要 native2ascii。

    顺便说一句,使用 faces-config.xml 中的新 JSF 2.0 样式 &lt;resource-bundle&gt; 声明,您不再需要视图中的 &lt;f:loadBundle&gt;。在所有视图中,#{text} 将直接提供所有文本。

    【讨论】:

    • 这是一个很棒的工具。有一天它会被添加到 OmniFaces 中吗?
    • @Med:我考虑过这一点,但我认为这与“Hacky”有关。而是使用正确的编辑器和构建工具,例如 Eclipse 和/或 Maven/Ant。例如,当使用 Eclipse 的内置属性文件编辑器时,UTF-8 将透明地保存为 ISO-8859-1,必要时带有 unicode 代码点。
    • @BalusC 非常有用。非常感谢。我有一个问题,在 xhtml 页面中,我们可以使用可变文本获取任何键的值,但是如果我们想在 java 代码中获取它,我们该怎么做呢?
    【解决方案2】:

    好吧,经过深入调查,我找到了解决方案。

    Java 1.6 之前的 PropertyResourceBundle 只有一个构造函数,它具有以下文档 The property file read with this constructor must be encoded in ISO-8859-1. 这意味着可以在资源包中仅使用英文文本。

    这个问题有两种解决方案:

    第一个是编写自定义 loadBundle 组件,它将使用正确的ResourceBundle 实例化方法。

    第二个(我的选择)是使用Native-to-ASCII 转换器,可以使用Native2Ascii maven plugin 与maven 一起使用。

    下面是配置示例:

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>native2ascii-maven-plugin</artifactId>
        <executions>
            <execution>
                <goals>
                    <goal>native2ascii</goal>
                </goals>
                <configuration>
                    <src>${basedir}/src/main/resources</src>                
                    <dest>${project.build.directory}/native2ascii</dest>
                    <encoding>UTF8</encoding>
                    <includes>**/*.properties</includes>
                </configuration>
            </execution>
        </executions>
    </plugin>
    

    【讨论】:

      【解决方案3】:

      我对独立的 SWT 应用程序也有同样的问题。这是由 WindowBuilder 生成的修改后的资源加载器。基本思想 - Messages 类仅包含字符串字段中的资源。所以我在加载原始 ISO-8859-1 后将它们转换为 UTF8(如果可能的话)。

      import java.lang.reflect.Field;
      
      import org.eclipse.osgi.util.NLS;
      
      public class Messages extends NLS {
      private static final String BUNDLE_NAME = "org.digimead.tabuddy.desktop.res.messages"; //$NON-NLS-1$
      public static String MainWindow_newShell_text;
      public static String MainWindow_actionOpenFile_text;
      public static String MainWindow_actionCloseFile_text;
      
      // //////////////////////////////////////////////////////////////////////////
      //
      // Constructor
      //
      // //////////////////////////////////////////////////////////////////////////
      private Messages() {
          // do not instantiate
      }
      
      // //////////////////////////////////////////////////////////////////////////
      //
      // Class initialization
      //
      // //////////////////////////////////////////////////////////////////////////
      static {
          // load message values from bundle file
          NLS.initializeMessages(BUNDLE_NAME, Messages.class);
          final Field[] fieldArray = Messages.class.getDeclaredFields();
          final int len = fieldArray.length;
          for (int i = 0; i < len; i++) {
              final Field field = (Field) fieldArray[i];
              if (field.getType() == java.lang.String.class) {
                  if (!field.isAccessible())
                      field.setAccessible(true);
                  try {
                      final String rawValue = (String) field.get(null);
                      field.set(null, new String(rawValue.getBytes("ISO-8859-1"),
                              "UTF-8"));
                  } catch (Exception e) {
                      // skip field modification
                  }
              }
          }
      }
      

      }

      【讨论】:

        猜你喜欢
        • 2021-03-28
        • 2014-09-11
        • 2011-04-27
        • 2013-09-14
        • 2010-10-26
        • 2012-03-04
        相关资源
        最近更新 更多