293 votes

Comment utiliser l'UTF-8 dans les propriétés de ressources avec ResourceBundle

Je dois utiliser UTF-8 dans mes propriétés de ressource en utilisant ResourceBundle de Java. Lorsque j'entre le texte directement dans le fichier de propriétés, il s'affiche comme du mojibake.

Mon application s'exécute sur Google App Engine.

Quelqu'un peut-il me donner un exemple? Je n'arrive pas à faire fonctionner cela.

1 votes

Java 1.6 a corrigé cela car vous pouvez passer un Reader. Voir la réponse de @Chinaxing plus bas.

1 votes

@Will : la question concerne principalement leur lecture via java.util.ResourceBundle, et non java.util.Properties.

1 votes

Vérifiez cette question répondue,,, j'espère que cela vous aidera [stackoverflow.com/questions/863838/… [1]: stackoverflow.com/questions/863838/…

414voto

BalusC Points 498232

Java 9 et plus récent

À partir de Java 9, les fichiers de propriétés sont encodés en UTF-8 par défaut, et l'utilisation de caractères en dehors de l'ISO-8859-1 devrait fonctionner sans problème.

Si vous utilisez un IDE pour les éditer, alors vous devrez peut-être renseigner l'IDE pour les lire en utilisant UTF-8. Voici comment faire dans les paramètres de IntelliJ :

entrer la description de l'image ici

Et dans les préférences d'Eclipse :

entrer la description de l'image ici

Java 8 et plus ancien

Le ResourceBundle#getBundle() utilise en interne PropertyResourceBundle lorsqu'un fichier .properties est spécifié. Celui-ci utilise par défaut Properties#load(InputStream) pour charger ces fichiers de propriétés. Selon la javadoc, ils sont lus par défaut en tant qu'ISO-8859-1.

public void load(InputStream inStream) throws IOException

Lit une liste de propriétés (paires de clé et d'élément) à partir du flux d'octets d'entrée. Le flux d'entrée est dans un format simple orienté ligne tel que spécifié dans load(Reader) et est supposé utiliser l'encodage de caractères ISO 8859-1 ; c'est-à-dire que chaque octet correspond à un caractère Latin1. Les caractères qui ne sont pas en Latin1, ainsi que certains caractères spéciaux, sont représentés dans les clés et les éléments en utilisant des échappements Unicode tels que définis dans la section 3.3 de la spécification du langage Java™.

Ainsi, vous devriez les sauvegarder en tant qu'ISO-8859-1. Si vous avez des caractères au-delà de la plage ISO-8859-1 et que vous ne pouvez pas utiliser \uXXXX de mémoire et êtes donc obligé de sauvegarder le fichier en UTF-8, alors vous devrez utiliser l'outil native2ascii pour convertir un fichier de propriétés enregistré en UTF-8 en un fichier de propriétés enregistré en ISO-8859-1 où tous les caractères non couverts sont convertis en format \uXXXX. L'exemple ci-dessous convertit un fichier de propriétés encodé en UTF-8 text_utf8.properties en un fichier de propriétés valide encodé en ISO-8859-1 text.properties.

native2ascii -encoding UTF-8 text\_utf8.properties text.properties

Lorsque vous utilisez un IDE tel qu'Eclipse ou IntelliJ, cela est déjà automatiquement fait lorsque vous créez un fichier .properties dans un projet basé sur Java et utilisez l'éditeur de fichiers de propriétés intégré à l'IDE. Il convertira de manière transparente les caractères au-delà de la plage ISO-8859-1 en format \uXXXX. Voir aussi les captures d'écran ci-dessous d'Eclipse (remarquez les onglets "Propriétés" et "Source" en bas, cliquez pour agrandir) :

Alternativement, vous pouvez également créer une implémentation personnalisée de ResourceBundle.Control dans laquelle vous lisez explicitement les fichiers de propriétés en UTF-8 à l'aide de InputStreamReader, afin de pouvoir les sauvegarder simplement en UTF-8 sans avoir à vous embêter avec native2ascii. Voici un exemple de démarrage :

public class UTF8Control extends Control {
    public ResourceBundle newBundle
        (String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
            throws IllegalAccessException, InstantiationException, IOException
    {
        // La ligne ci-dessous est modifiée pour lire les fichiers de propriétés en UTF-8.
        String bundleName = toBundleName(baseName, locale);
        String resourceName = toResourceName(bundleName, "properties");
        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, "UTF-8"));
            } finally {
                stream.close();
            }
        }
        return bundle;
    }
}

Cela peut être utilisé comme suit :

ResourceBundle bundle = ResourceBundle.getBundle("com.example.i18n.text", new UTF8Control());

Voir aussi :

0 votes

Merci. Au fait, il semble être une bonne idée de remplacer getFormats pour renvoyer FORMAT_PROPERTIES.

0 votes

Pouvez-vous développer cette suggestion pour remplacer getFormats()?

0 votes

Je prépare une nouvelle mise en oeuvre pour Java 8. Voyons ici : gist.github.com/Hasacz89/d93955ec91afc73a06e3

143voto

Rod Points 679

Étant donné que vous avez une instance de ResourceBundle et que vous pouvez obtenir une chaîne en utilisant :

String val = bundle.getString(key); 

J'ai résolu mon problème d'affichage japonais en utilisant :

return new String(val.getBytes("ISO-8859-1"), "UTF-8");

43 votes

À tous les upvoters/commentateurs naïfs : ceci n'est pas une solution, mais une solution de contournement. Le véritable problème sous-jacent persiste et doit être résolu.

2 votes

Cela a résolu ma situation. La solution serait que Java commence à gérer nativement l'UTF-8 dans les bundles de ressources et les fichiers de propriétés. Jusqu'à ce que cela se produise, j'utiliserai une solution de contournement.

2 votes

@BalusC; quel est l'inconvénient de cette approche? (autre que la création d'une chaîne supplémentaire?)

57voto

Chinaxing Points 394

Regardez ceci : http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)

les propriétés acceptent un objet Reader comme argument, que vous pouvez créer à partir d'un InputStream.

au moment de la création, vous pouvez spécifier l'encodage du Reader :

InputStreamReader isr = new InputStreamReader(stream, "UTF-8");

ensuite appliquez ce Reader à la méthode load :

prop.load(isr);

BTW: obtenez le flux à partir d'un fichier .properties :

 InputStream stream = this.class.getClassLoader().getResourceAsStream("a.properties");

BTW: obtenez le bundle de ressources à partir de InputStreamReader :

ResourceBundle rb = new PropertyResourceBundle(isr);

j'espère que cela peut vous aider !

5 votes

La question actuelle ici concerne ResourceBundle.

2 votes

Vrai, cette réponse devrait être acceptée si vous utilisez Properties et que vous souhaitez récupérer une chaîne UTF-8, alors cela fonctionne comme un charme. Cependant, pour un ResourceBundle tel que des ressources linguistiques, la réponse acceptée est élégante. Néanmoins, j'ai voté pour la réponse.

0 votes

ResourceBundle rb = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"))

20voto

marcolopes Points 1861
package com.varaneckas.utils;  

import java.io.UnsupportedEncodingException;  
import java.util.Enumeration;  
import java.util.PropertyResourceBundle;  
import java.util.ResourceBundle;  

/** 
 * Support ResourceBundle Utf-8 
 *  
 * Utility that allows for characters multioctets within java .property files. 
 * Eliminates the need for the native2ascii Sun application, you can simply 
 * have .property files encoded in UTF-8. 
 *  
 * Use:  
 * ResourceBundle bundle = Utf8ResourceBundle.getBundle("bundle_name"); 
 *  
 * @author Tomas Varaneckas  
 */  
public abstract class Utf8ResourceBundle {  

    /** 
     * Obtains the resource bundle friendly unicodes 
     *  
     * @param baseName 
     * @see ResourceBundle#getBundle(String) 
     * @return Resource bundle friendly unicodes 
     */  
    public static final ResourceBundle getBundle(final String baseName) {  
        return createUtf8PropertyResourceBundle(  
                ResourceBundle.getBundle(baseName));  
    }  

    /** 
     * Creates a friendly unicode {@link PropertyResourceBundle} if possible. 
     *  
     * @param bundle  
     * @return Resource bundle friendly unicodes 
     */  
    private static ResourceBundle createUtf8PropertyResourceBundle(  
            final ResourceBundle bundle) {  
        if (!(bundle instanceof PropertyResourceBundle)) {  
            return bundle;  
        }  
        return new Utf8PropertyResourceBundle((PropertyResourceBundle) bundle);  
    }  

    /** 
     * Resource Bundle that performs the hard work 
     */  
    private static class Utf8PropertyResourceBundle extends ResourceBundle {  

        /** 
         * Bundle with unicode information 
         */  
        private final PropertyResourceBundle bundle;  

        /** 
         * Constructor initialization 
         *  
         * @param bundle 
         */  
        private Utf8PropertyResourceBundle(final PropertyResourceBundle bundle) {  
            this.bundle = bundle;  
        }  

        @Override  
        @SuppressWarnings("unchecked")  
        public Enumeration getKeys() {  
            return bundle.getKeys();  
        }  

        @Override  
        protected Object handleGetObject(final String key) {  
            final String value = bundle.getString(key);  
            if (value == null)  
                return null;  
            try {  
                return new String(value.getBytes("ISO-8859-1"), "UTF-8");  
            } catch (final UnsupportedEncodingException e) {  
                throw new RuntimeException("Encoding not supported", e);  
            }  
        }  
    }  
}

1 votes

J'aime cette solution et je la poste comme Gist gist.github.com/enginer/3168dd4a374994718f0e

0 votes

Cela fonctionne très bien. Il suffit d'ajouter un fichier de propriétés de traduction chinois en UTF8 et il se charge sans aucun problème.

19voto

pulazzo Points 572

Nous créons un fichier resources.utf8 qui contient les ressources en UTF-8 et avons une règle pour exécuter ce qui suit :

native2ascii -encoding utf8 resources.utf8 resources.properties

0 votes

Où obtenons-nous native2ascii ? Je viens de faire find / -name native2ascii* et je n'ai obtenu aucun résultat, je suppose donc que ce n'est pas simplement partie du JDK...

0 votes

Hm. Ce n'est pas partie du JDK IBM, mais il semble être inclus dans le JDK Oracle, dans jdk1.*.0_*/bin.

0 votes

Il semble effectivement faire partie du JDK IBM, du moins dans le JDK 6.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X