i18n с файлами свойств в кодировке UTF-8 в приложении JSF 2.0

Я использую jsf-ri 2.0.3, где необходима поддержка иврита и русского языка. Проблема в том, что вместо правильного текста я вижу на экране тарабарщину.

Прежде всего я определил пакеты (*_locale.properties) для каждого языка. Файлы в кодировке UTF-8. Во-вторых, я определил язык по умолчанию и поддерживаемые локали в файле face-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 ответа

Решение

Правильно, вы можете создать кастом ResourceBundle или используйте конвертер native2ascii (при необходимости с плагином Maven 2, чтобы сделать преобразование более прозрачным). Так как другой ответ подробно описывает только последний подход, вот еще один ответ, как вы могли бы создать ResourceBundle загружать файлы свойств как UTF-8 в приложении JSF 2.x в среде на основе Java SE 1.6.

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 пакет. Нет необходимости в native2ascii.

Кстати, с новым стилем JSF 2.0 <resource-bundle> декларация в faces-config.xml тебе не нужно <f:loadBundle> во взглядах больше. Весь текст будет доступен напрямую #{text} во всех взглядах.

Ну, после глубокого расследования я нашел решение.

Раньше к яве 1.6 PropertyResourceBundle был только один конструктор, который имеет следующую документацию The property file read with this constructor must be encoded in ISO-8859-1.Это означает, что в комплектах ресурсов можно использовать только текст на английском языке.

Есть два решения этой проблемы:

Первый пишет пользовательский компонент loadBundle, который будет использовать правильный ResourceBundle метод создания экземпляров.

Второй (Мой выбор) использует конвертер Native-ASCII, который можно использовать с maven с помощью плагина Native2Ascii 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>

У меня та же проблема с отдельным приложением SWT. Это модифицированный загрузчик ресурсов, сгенерированный WindowBuilder. Основная идея - класс Messages содержит только ресурсы в строковых полях. Поэтому я конвертирую их в UTF8 (если это возможно) после загрузки исходного ISO-8859-1.

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

}

Другие вопросы по тегам