Verwendung von UTF-8 in Ressourceneigenschaften mit ResourceBundle


259

Ich muss UTF-8 in meinen Ressourceneigenschaften mit Java verwenden ResourceBundle. Wenn ich den Text direkt in die Eigenschaftendatei eingebe, wird er als Mojibake angezeigt.

Meine App läuft auf Google App Engine.

Kann mir jemand ein Beispiel geben? Ich kann diese Arbeit nicht bekommen.


1
Java 1.6 Dies wurde behoben, da Sie einen Reader übergeben können. Siehe die @ Chinaxing Antwort ganz unten
Will

1
@ Will: Bei der Frage geht es hauptsächlich darum, sie über zu lesen java.util.ResourceBundle, nicht über java.util.Properties.
BalusC

1
Überprüfen Sie diese beantwortete Frage ,,, hoffe, es hilft Ihnen [ stackoverflow.com/questions/863838/… [1]: stackoverflow.com/questions/863838/…
Majdy der Programmierer Bboy

6
JDK9 sollte UTF-8 nativ unterstützen, siehe JEP 226
Paolo Fulgoni

Antworten:


375

Die ResourceBundle#getBundle()Verwendung unter dem Deckmantel, PropertyResourceBundlewenn eine .propertiesDatei angegeben wird. Dies wiederum verwendet standardmäßig Properties#load(InputStream)diese Eigenschaftendateien. Gemäß der javadoc , sind sie standardmäßig Lese- als ISO-8859-1.

public void load(InputStream inStream) throws IOException

Liest eine Eigenschaftsliste (Schlüssel- und Elementpaare) aus dem Eingabebyte-Stream. Der Eingabestream hat ein einfaches zeilenorientiertes Format, wie in load (Reader) angegeben, und es wird davon ausgegangen, dass die ISO 8859-1-Zeichencodierung verwendet wird . Das heißt, jedes Byte ist ein Latin1-Zeichen. Zeichen, die nicht in Latin1 enthalten sind, und bestimmte Sonderzeichen werden in Schlüsseln und Elementen mithilfe von Unicode-Escapezeichen dargestellt, wie in Abschnitt 3.3 der Java ™ -Sprachspezifikation definiert.

Sie müssen sie also als ISO-8859-1 speichern. Wenn Sie Zeichen außerhalb des ISO-8859-1-Bereichs haben und diese nicht ohne \uXXXXKopf verwenden können und daher gezwungen sind, die Datei als UTF-8 zu speichern, müssen Sie das native2ascii- Tool verwenden, um eine zu konvertieren UTF-8-gespeicherte Eigenschaftendatei in eine nach ISO-8859-1 gespeicherte Eigenschaftendatei, in der alle nicht abgedeckten Zeichen in das \uXXXXFormat konvertiert werden. Im folgenden Beispiel wird eine UTF-8-codierte Eigenschaftendatei text_utf8.propertiesin eine gültige ISO-8859-1-codierte Eigenschaftendatei konvertiert text.properties.

native2ascii -codierung UTF-8 text_utf8.properties text.properties

Wenn Sie eine vernünftige IDE wie Eclipse verwenden, erfolgt dies bereits automatisch, wenn Sie eine .propertiesDatei in einem Java-basierten Projekt erstellen und den Eclipse-eigenen Editor verwenden. Eclipse konvertiert die Zeichen über den ISO-8859-1-Bereich hinaus transparent in das \uXXXXFormat. Siehe auch die folgenden Screenshots (beachten Sie die Registerkarten "Eigenschaften" und "Quelle" unten, klicken Sie für eine große Auswahl):

Registerkarte "Eigenschaften" Registerkarte "Quelle"

Alternativ können Sie auch eine benutzerdefinierte ResourceBundle.ControlImplementierung erstellen , bei der Sie die Eigenschaftendateien explizit als UTF-8 mit lesen InputStreamReader, sodass Sie sie einfach als UTF-8 speichern können, ohne sich damit herumschlagen zu müssen native2ascii. Hier ist ein Kickoff-Beispiel:

public class UTF8Control extends Control {
    public ResourceBundle newBundle
        (String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
            throws IllegalAccessException, InstantiationException, IOException
    {
        // The below is a copy of the default implementation.
        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 {
                // Only this line is changed to make it to read properties files as UTF-8.
                bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"));
            } finally {
                stream.close();
            }
        }
        return bundle;
    }
}

Dies kann wie folgt verwendet werden:

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

Siehe auch:


Vielen Dank. Übrigens scheint es eine gute Idee zu sein, getFormats zu überschreiben, um FORMAT_PROPERTIES zurückzugeben.
Flávio Etrusco

Könnten Sie diesen Vorschlag näher erläutern, um getFormats () zu überschreiben?
Mark Roper

1
@ imgx64: Danke für die Benachrichtigung. Antwort wurde behoben.
BalusC

10
StandardCharsets.UTF_8Zögern Sie nicht zu verwenden, wenn Sie Java 7+
Niks

1
@Nyerguds: Wenn Sie Gründe sehen, es jemals programmatisch zu ändern (ich kann mir jedoch keinen vorstellen), können Sie dies gerne tun. Alle Code-Schnipsel, die ich poste, sind schließlich nur Kickoff-Beispiele.
BalusC

131

Vorausgesetzt, Sie haben eine Instanz von ResourceBundle und können String abrufen durch:

String val = bundle.getString(key); 

Ich habe mein japanisches Anzeigeproblem gelöst durch:

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

37
An alle naiven Aufsteiger / Kommentatoren hier: Dies ist keine Lösung, sondern eine Problemumgehung. Das wahre zugrunde liegende Problem bleibt bestehen und muss gelöst werden.
BalusC

2
Dies hat meine Situation behoben. Die Lösung wäre, dass Java UTF-8 nativ in Ressourcenpaketen und in Eigenschaftendateien verarbeitet. Bis dies passiert, werde ich eine Problemumgehung verwenden.
JohnRDOrazio

@BalusC; Was ist der Nachteil dieses Ansatzes? (außer einen zusätzlichen String zu erstellen?)
Paaske

8
@Paaske: Es ist eine Problemumgehung, keine Lösung. Sie müssen die Problemumgehung erneut auf alle Zeichenfolgenvariablen in der gesamten Codebasis anwenden. Das ist purer Unsinn. Korrigieren Sie es einfach an einer einzigen Stelle an der richtigen Stelle, damit die Zeichenfolgenvariablen sofort den richtigen Wert enthalten. Der Client sollte absolut nicht geändert werden müssen.
BalusC

3
Ja, wenn Sie die gesamte Anwendung ändern müssen, ist dies natürlich schlecht. Wenn Sie das ResourceBundle jedoch bereits als Singleton verwenden, müssen Sie es nur einmal beheben. Ich hatte den Eindruck, dass der Singleton-Ansatz die gebräuchlichste Methode zur Verwendung des ResourceBundle war.
Paaske

51

Schauen Sie sich das an: http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)

Die Eigenschaften akzeptieren ein Reader- Objekt als Argumente, die Sie aus einem InputStream erstellen können.

Zum Zeitpunkt der Erstellung können Sie die Codierung des Readers angeben:

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

Wenden Sie dann diesen Reader auf die Lademethode an:

prop.load(isr);

Übrigens : Holen Sie sich den Stream aus der .properties- Datei:

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

Übrigens: Holen Sie sich das Ressourcenpaket von InputStreamReader:

ResourceBundle rb = new PropertyResourceBundle(isr);

hoffe das kann dir helfen!


3
Die eigentliche Frage hier ist ResourceBundlejedoch.
Nyerguds

1
Richtig, dies sollte als Antwort akzeptiert werden, wenn Sie String verwenden Propertiesund UTF-8String abrufen möchten, dann funktioniert dies wie ein Zauber. Für ResourceBundlesolche Sprachressourcen ist die akzeptierte Antwort jedoch elegant. Trotzdem stimmte die Antwort ab.
Ilgıt Yıldırım

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

23

ResourceBundle.Control mit UTF-8 und neuen String-Methoden funktionieren nicht, wenn die Eigenschaftendatei beispielsweise den Zeichensatz cp1251 verwendet.

Daher empfahl ich die Verwendung einer gängigen Methode: Schreiben Sie in Unicode- Symbole. Dafür:

IDEA - verfügt über eine spezielle Option " Transparente Konvertierung von Native in ASCII " (Einstellungen> Dateicodierung).

Eclipse - hat ein Plugin " Properties Editor " . Es kann als separate Anwendung arbeiten.


4
In IntelliJ IDEA 14 befindet sich dies unter Einstellungen -> Editor -> Dateikodierungen. Ich musste auch alle vorhandenen Eigenschaftendateien löschen und neu erstellen, damit diese Option wirksam wurde.
Cypher

IDEs sind für die Antwort nicht besonders relevant, sondern nur Tools, die das zugrunde liegende Problem der Nichtspeicherung von Inhalten im UTF-8-Zeichensatz wirklich nicht lösen. Dies würde das Problem sofort ohne Konvertierung oder Hackery wie das Schreiben von Eigenschaften lösen in Unicode-Symbolen in einer Datei, die mit einem anderen Zeichensatz definiert ist.
Darrell Teague

21

Dieses Problem wurde endlich in Java 9 behoben: https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9

Die Standardcodierung für Eigenschaftendateien ist jetzt UTF-8.

Die meisten vorhandenen Eigenschaftendateien sollten nicht betroffen sein: UTF-8 und ISO-8859-1 haben dieselbe Codierung für ASCII-Zeichen, und die von Menschen lesbare Nicht-ASCII-ISO-8859-1-Codierung ist nicht gültig. Wenn eine ungültige UTF-8-Byte-Sequenz erkannt wird, liest die Java-Laufzeit die Datei in ISO-8859-1 automatisch erneut.


19

Wir erstellen eine Datei resources.utf8, die die Ressourcen in UTF-8 enthält, und haben eine Regel, um Folgendes auszuführen:

native2ascii -encoding utf8 resources.utf8 resources.properties

Woher kommen wir native2ascii? Ich habe es gerade getan find / -name native2ascii*und keine Ergebnisse erzielt, daher
gehe

Hm. Es ist nicht Teil des IBM JDK, aber es scheint im Oracle JDK enthalten zu sein jdk1.*.0_*/bin.
ArtOfWarfare

Es scheint Teil des IBM JDK zu sein, zumindest in JDK 6.
Eric Finn

19
package com.varaneckas.utils;  

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

/** 
 * UTF-8 friendly ResourceBundle support 
 *  
 * Utility that allows having multi-byte characters inside java .property files. 
 * It removes the need for Sun's native2ascii application, you can simply have 
 * UTF-8 encoded editable .property files. 
 *  
 * Use:  
 * ResourceBundle bundle = Utf8ResourceBundle.getBundle("bundle_name"); 
 *  
 * @author Tomas Varaneckas <tomas.varaneckas@gmail.com> 
 */  
public abstract class Utf8ResourceBundle {  

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

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

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

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

        /** 
         * Initializing constructor 
         *  
         * @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
Ich mag diese Lösung und ich poste
Sllouyssgort

Das funktioniert sehr gut. Ich habe gerade eine Eigenschaftendatei für die chinesische Übersetzung in UTF8 hinzugefügt und sie wird ohne Probleme geladen.
Tresf

9

Achtung: Java-Eigenschaftendateien sollten in ISO 8859-1 codiert sein!

ISO 8859-1 Zeichenkodierung. Zeichen, die in dieser Codierung nicht direkt dargestellt werden können, können mit Unicode-Escapezeichen geschrieben werden. In einer Escape-Sequenz ist nur ein einziges 'u'-Zeichen zulässig.

@see Eigenschaften Java Doc

Wenn Sie dies dennoch wirklich tun möchten: Schauen Sie sich Folgendes an: Java-Eigenschaften UTF-8-Codierung in Eclipse - es gibt einige Codebeispiele


1
Java! = Eclipse ... letzteres ist eine IDE. Weitere Daten! = Java. Java unterstützt die Stream-Verarbeitung mit einer Vielzahl von Zeichensätzen, die für die Internationalisierung (schließlich geht es um ResourceBundles) ... die Verwendung von UTF-8 als einfachste Antwort beschließen. Das Schreiben von Eigenschaftendateien in einem Zeichensatz, der von der Zielsprache nicht unterstützt wird, erschwert das Problem unnötig.
Darrell Teague

@Darell Teague: Der "Hinweis", dass eine für ein ResouceBundle geladene Eigenschaftendatei ISO 8859-1 sein muss, ist eine Java-Anweisung: docs.oracle.com/javase/8/docs/api/java/util/… .. Der zweite Teil meiner Antwort ist nur ein "Hinweis", wie man mit Hutproblemen umgeht.
Ralph


3

Hier ist eine Java 7-Lösung, die die hervorragende Support-Bibliothek von Guava und das Konstrukt "Try-with-Resources" verwendet. Es liest und schreibt Eigenschaftendateien mit UTF-8 für die einfachste Gesamterfahrung.

So lesen Sie eine Eigenschaftendatei als UTF-8:

File file =  new File("/path/to/example.properties");

// Create an empty set of properties
Properties properties = new Properties();

if (file.exists()) {

  // Use a UTF-8 reader from Guava
  try (Reader reader = Files.newReader(file, Charsets.UTF_8)) {
    properties.load(reader);
  } catch (IOException e) {
    // Do something
  }
}

So schreiben Sie eine Eigenschaftendatei als UTF-8:

File file =  new File("/path/to/example.properties");

// Use a UTF-8 writer from Guava
try (Writer writer = Files.newWriter(file, Charsets.UTF_8)) {
  properties.store(writer, "Your title here");
  writer.flush();
} catch (IOException e) {
  // Do something
}

Diese Antwort ist nützlich. Das Kernproblem bei verschiedenen Antworten scheint ein Missverständnis über Daten und Zeichensätze zu sein. Java kann alle Daten (korrekt) lesen, indem es einfach den Zeichensatz angibt, in dem es gespeichert wurde, wie oben gezeigt. UTF-8 wird häufig verwendet, um die meisten, wenn nicht jede Sprache auf dem Planeten zu unterstützen, und ist daher sehr gut auf ResourceBundle-basierte Eigenschaften anwendbar.
Darrell Teague

@DarrellTeague: Nun, "UTF-8 wird üblicherweise zur Unterstützung verwendet ..." - es sollte eher " Unicode wird üblicherweise zur Unterstützung verwendet ..." :) geben, da UTF-8 nur eine Zeichencodierung des Unicodes ( en) ist .wikipedia.org / wiki / UTF-8 ).
Honza Zidek

Eigentlich sollte UTF-8 speziell als "Zeichensatz" bezeichnet werden (im Gegensatz zur bloßen Bezugnahme auf "jeden UniCode-Zeichensatz"), da UTF-8 in diesem Zusammenhang (Daten) im Internet durch einige Maßnahmen, die so hoch sind wie, vorherrscht 67%. Ref: stackoverflow.com/questions/8509339/…
Darrell Teague

3

Wie bereits erwähnt, habe ich die Implementierung des Ressourcenpakets durchlaufen. Dies hat jedoch nicht geholfen. Da das Paket immer unter dem Gebietsschema en_US aufgerufen wurde, habe ich versucht, mein Standardgebietsschema auf eine andere Sprache festzulegen und trotzdem das Ressourcenpaket zu implementieren Die Steuerung wurde mit en_US aufgerufen ... Ich habe versucht, Protokollnachrichten zu platzieren und einen Schritt durch das Debugging zu machen, um zu sehen, ob ein anderer lokaler Anruf getätigt wurde, nachdem ich das Gebietsschema zur Laufzeit durch xhtml- und JSF-Aufrufe geändert habe ... das ist nicht passiert ... dann habe ich versucht, ein System festzulegen, das standardmäßig auf utf8 gesetzt ist, um Dateien von meinem Server (Tomcat-Server) zu lesen. Dies verursachte jedoch ein Problem, da alle meine Klassenbibliotheken nicht unter utf8 kompiliert wurden und Tomcat dann im utf8-Format zu lesen begann und der Server lief nicht richtig ... dann implementierte ich eine Methode in meinem Java-Controller, die aus XML-Dateien aufgerufen werden sollte.Bei dieser Methode habe ich Folgendes getan:

        public String message(String key, boolean toUTF8) throws Throwable{
            String result = "";
            try{
                FacesContext context = FacesContext.getCurrentInstance();
                String message = context.getApplication().getResourceBundle(context, "messages").getString(key);

                result = message==null ? "" : toUTF8 ? new String(message.getBytes("iso8859-1"), "utf-8") : message;
            }catch(Throwable t){}
            return result;
        }

Ich war besonders nervös, da dies die Leistung meiner Anwendung verlangsamen könnte. Nach der Implementierung sieht es jedoch so aus, als ob meine Anwendung jetzt schneller ist. Ich denke, das liegt daran, dass ich jetzt direkt auf die Eigenschaften zugreife, anstatt sie zuzulassen JSF analysiert seinen Weg in den Zugriff auf Eigenschaften ... Ich übergebe in diesem Aufruf speziell das Boolesche Argument, da ich weiß, dass einige der Eigenschaften nicht übersetzt werden und nicht im utf8-Format vorliegen müssen ...

Jetzt habe ich meine Eigenschaftendatei im UTF8-Format gespeichert und sie funktioniert einwandfrei, da jeder Benutzer in meiner Anwendung eine bevorzugte Ländereinstellung hat.


2
Properties prop = new Properties();
String fileName = "./src/test/resources/predefined.properties";
FileInputStream inputStream = new FileInputStream(fileName);
InputStreamReader reader = new InputStreamReader(inputStream,"UTF-8");

1

Mein Problem war, dass die Dateien selbst in der falschen Codierung waren. Die Verwendung von iconv hat bei mir funktioniert

iconv -f ISO-8859-15 -t UTF-8  messages_nl.properties > messages_nl.properties.new

+1 für die Erwähnung iconv. Ich habe noch nie davon gehört, aber ich habe es in die Konsole eingegeben und siehe da, es ist eine Sache, die es gibt (jedenfalls in CentOS 6).
ArtOfWarfare

Jetzt, wo ich es tatsächlich versucht habe, hat es nicht funktioniert: Es hat sich auf das erste Zeichen geworfen, das nicht in ISO-8559-1 konvertiert werden konnte.
ArtOfWarfare

1

Ich habe versucht, den von Rod bereitgestellten Ansatz zu verwenden, aber unter Berücksichtigung der Bedenken von BalusC, nicht in allen Anwendungen dieselbe Umgehung zu wiederholen, und bin mit dieser Klasse gekommen:

import java.io.UnsupportedEncodingException;
import java.util.Locale;
import java.util.ResourceBundle;

public class MyResourceBundle {

    // feature variables
    private ResourceBundle bundle;
    private String fileEncoding;

    public MyResourceBundle(Locale locale, String fileEncoding){
        this.bundle = ResourceBundle.getBundle("com.app.Bundle", locale);
        this.fileEncoding = fileEncoding;
    }

    public MyResourceBundle(Locale locale){
        this(locale, "UTF-8");
    }

    public String getString(String key){
        String value = bundle.getString(key); 
        try {
            return new String(value.getBytes("ISO-8859-1"), fileEncoding);
        } catch (UnsupportedEncodingException e) {
            return value;
        }
    }
}

Die Verwendung wäre sehr ähnlich wie bei der regulären Verwendung von ResourceBundle:

private MyResourceBundle labels = new MyResourceBundle("es", "UTF-8");
String label = labels.getString(key)

Oder Sie können den alternativen Konstruktor verwenden, der standardmäßig UTF-8 verwendet:

private MyResourceBundle labels = new MyResourceBundle("es");

0

Öffnen Sie das Dialogfeld Einstellungen / Einstellungen ( Ctrl+ Alt+ S) und klicken Sie dann auf Editor und Dateikodierungen.

Screenshot des gezeigten Fensters

Unten sehen Sie dann die Standardcodierungen für Eigenschaftendateien. Wählen Sie Ihren Codierungstyp.

Alternativ können Sie Unicode-Symbole anstelle von Text in Ihrem Ressourcenpaket verwenden (z. B. "ів"gleich \u0456\u0432).


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.