代码之家  ›  专栏  ›  技术社区  ›  Maxim Manco

i18n,在JSF 2.0应用程序中使用UTF-8编码属性文件

  •  9
  • Maxim Manco  · 技术社区  · 14 年前

    我使用的是JSF RI2.0.3,需要希伯来语和俄语的支持。 问题是我在屏幕上看到的是胡言乱语,而不是正确的文本。

    首先,我为每种语言定义了bundles(*u 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 
    

    我的配置有什么问题?

    3 回复  |  直到 7 年前
        1
  •  22
  •   BalusC    13 年前

    对,您可以创建自定义 ResourceBundle 或者使用native2ascii转换器(如有必要,使用maven 2插件使转换更加透明)。由于另一个答案只与最后一个方法进行了详细的讨论,下面是另一个答案,您可以如何创建一个自定义的 资源束 在基于Java SE 1.6的环境中,在JSF 2 .x应用程序中加载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;
            }
        }
    }
    

    这需要utf-8编码的属性文件,比如 text.properties , text_en.properties com.example.i18n 包裹。不需要当地人2。

    顺便说一下,使用新的JSF 2.0样式 <resource-bundle> 申报 faces-config.xml文件 你不需要 <f:loadBundle> 在风景里。所有文本将直接通过 #{text} 在所有的观点。

        2
  •  3
  •   Maxim Manco    14 年前

    嗯,经过深入调查,我找到了解决办法。

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

    此问题有两种解决方案:

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

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

    以下是配置示例:

    <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
  •  0
  •   Ezhik    12 年前

    对于独立的SWT应用程序,我也有同样的问题。这是由WindowBuilder生成的已修改的资源加载程序。基本思想-消息类只包含字符串字段中的资源。所以我在原始的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
                }
            }
        }
    }
    

    }