2010-09-05 6 views
9

Estoy usando jsf-ri 2.0.3 donde se necesita soporte hebreo y ruso. El problema es que veo un galimatías en la pantalla en lugar del texto correcto.i18n con archivos de propiedades codificadas UTF-8 en la aplicación JSF 2.0

Antes que nada, he definido los paquetes (* _locale.properties) para cada idioma. Los archivos están en codificación UTF-8. En segundo lugar, he definido el valor predeterminado y apoyado locales en faces-config.xml

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

de lo que he añadido un filtro personalizado que establecer la codificación charcter respuesta a 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> 

Y finalmente cuando creo un XHTML sencilla para depurar la salida Veo muy extraños resultados

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

El resultado es:

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

Lo que está mal con mi configuración?

Respuesta

22

derecho, puede crear una costumbre ResourceBundle o utilizar el convertidor native2ascii (si es necesario con el Maven 2 plugin para hacer la conversión más transparente). Dado que la otra respuesta solo va con el último enfoque en detalle, aquí hay otra respuesta sobre cómo se puede crear un ResourceBundle personalizado para cargar archivos de propiedades como UTF-8 en una aplicación JSF 2.x en un entorno basado en 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; 
     } 
    } 
} 

Esta espera codificación UTF-8 como archivos de propiedades text.properties, text_en.properties, etc en com.example.i18n paquete. No es necesario para native2ascii.

Por cierto, con la nueva declaración de estilo JSF 2.0 <resource-bundle> en faces-config.xml, ya no necesita <f:loadBundle> en las vistas. Todo el texto estará disponible directamente en #{text} en todas las vistas.

+0

Esta es una gran herramienta. ¿Se agregará a OmniFaces algún día? – Med

+1

@Med: He considerado esto, pero decidí que esto está al límite con "Hacky". Más bien use los editores correctos y herramientas de compilación como Eclipse y/o Maven/Ant. Por ejemplo, cuando se utiliza el editor de archivos de propiedades integradas de Eclipse, UTF-8 se guardará de forma transparente como ISO-8859-1 con puntos de código Unicode cuando sea necesario. – BalusC

+0

Ok, ya entendí! Gracias. – Med

3

Bueno, después de una profunda investigación, he encontrado la solución.

A principios de Java 1.6 PropertyResourceBundle tenían sólo un constructor que tiene la siguiente documentación The property file read with this constructor must be encoded in ISO-8859-1. Esto significa que es posible utilizar sólo texto Inglés en los paquetes de recursos.

Hay dos soluciones para este problema:

El primero de ellos está escribiendo un componente wich loadBundle personalizada se utilice el método de instancias ResourceBundle correcta.

El segundo (Mi elección) es usar el convertidor Native-to-ASCII que se puede usar con maven usando el Native2Ascii maven plugin.

Aquí está el ejemplo de configuración:

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

Tengo el mismo problema con la aplicación autónoma SWT. Este es un cargador de recursos modificado generado por WindowBuilder. Idea básica: la clase de mensajes contiene solo recursos en campos de cadenas. Así que los convierto en UTF8 (si es posible) después de cargar Raw 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 
      } 
     } 
    } 
} 

}

Cuestiones relacionadas