Come utilizzare UTF-8 nelle proprietà delle risorse con ResourceBundle


259

Devo usare UTF-8 nelle proprietà delle mie risorse usando Java ResourceBundle. Quando inserisco il testo direttamente nel file delle proprietà, viene visualizzato come mojibake.

La mia app funziona su Google App Engine.

Qualcuno può darmi un esempio? Non riesco a ottenere questo lavoro.


1
Java 1.6 Risolto questo problema in quanto è possibile passare un Reader. Vedi la risposta @Chinaxing in basso
sarà il

1
@Will: la domanda riguarda principalmente la loro lettura tramite java.util.ResourceBundle, non java.util.Properties.
BalusC,

1
Controllare questa domanda ha risposto ,,, spero che ti aiuta [ stackoverflow.com/questions/863838/... [1]: stackoverflow.com/questions/863838/...
Majdy il programmatore Bboy

6
JDK9 dovrebbe supportare UTF-8 in modo nativo, vedere JEP 226
Paolo Fulgoni,

Risposte:


375

Gli ResourceBundle#getBundle()usi sotto le copertine PropertyResourceBundlequando .propertiesviene specificato un file. Questo a sua volta utilizza per impostazione predefinita Properties#load(InputStream)per caricare i file delle proprietà. Come da javadoc , vengono letti per impostazione predefinita come ISO-8859-1.

public void load(InputStream inStream) throws IOException

Legge un elenco di proprietà (coppie chiave ed elemento) dal flusso di byte di input. Il flusso di input è in un semplice formato orientato alla linea come specificato nel caricamento (Reader) e si presume che utilizzi la codifica dei caratteri ISO 8859-1 ; cioè ogni byte ha un carattere Latin1. I caratteri non in latino1 e alcuni caratteri speciali sono rappresentati in chiavi ed elementi usando escape Unicode come definito nella sezione 3.3 di The Java ™ Language Specification.

Quindi, dovresti salvarli come ISO-8859-1. Se hai caratteri oltre l'intervallo ISO-8859-1 e non puoi usarli \uXXXXal di sopra della testa e sei quindi costretto a salvare il file come UTF-8, allora dovresti usare lo strumento native2ascii per convertire un File di proprietà salvato UTF-8 in un file di proprietà salvato ISO-8859-1 in cui tutti i caratteri scoperti vengono convertiti in \uXXXXformato. L'esempio seguente converte un file di proprietà codificato UTF-8 in un file di proprietà codificato text_utf8.propertiesISO-8859-1 validotext.properties .

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

Quando si utilizza un IDE sano come Eclipse, questo viene già fatto automaticamente quando si crea un .propertiesfile in un progetto basato su Java e si utilizza il proprio editor di Eclipse. Eclipse converte in modo trasparente i caratteri oltre l'intervallo ISO-8859-1 nel \uXXXXformato. Vedi anche gli screenshot seguenti (nota le schede "Proprietà" e "Sorgente" in basso, fai clic per ingrandirle):

Scheda "Proprietà" Scheda "Sorgente"

In alternativa, puoi anche creare ResourceBundle.Controlun'implementazione personalizzata in cui leggi esplicitamente i file delle proprietà come UTF-8 InputStreamReader, in modo da poterli semplicemente salvare come UTF-8 senza doverti preoccupare native2ascii. Ecco un esempio di kickoff:

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

Questo può essere usato come segue:

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

Guarda anche:


Grazie. A proposito, sembra essere una buona idea sostituire getFormats per restituire FORMAT_PROPERTIES.
Flávio Etrusco,

Potresti approfondire questo suggerimento per sovrascrivere getFormats ()?
Mark Roper,

1
@ imgx64: grazie per aver avvisato. La risposta è stata corretta
BalusC

10
Non esitare a utilizzare StandardCharsets.UTF_8se stai utilizzando Java 7+
Niks il

1
@Nyerguds: se vedi dei motivi per cambiarlo programmaticamente (non riesco a immaginarmene uno per tutta la vita), sentiti libero di farlo. Tutti i frammenti di codice che invio sono solo esempi di kickoff dopo tutto.
BalusC

131

Dato che hai un'istanza di ResourceBundle e puoi ottenere String:

String val = bundle.getString(key); 

Ho risolto il mio problema di visualizzazione giapponese tramite:

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

37
A tutti gli utenti / commentatori ingenui qui: questa non è una soluzione, ma una soluzione alternativa. Il vero problema di fondo è ancora valido e deve essere risolto.
BalusC,

2
Ciò ha risolto la mia situazione. La soluzione sarebbe che Java iniziasse a gestire nativamente UTF-8 in fasci di risorse e in file delle proprietà. Fino a quando ciò non accadrà, userò una soluzione alternativa.
Giovanni Orazio,

@BalusC; qual è lo svantaggio di questo approccio? (diverso dalla creazione di una stringa aggiuntiva?)
Paaske,

8
@ Paaske: è una soluzione alternativa, non una soluzione. Dovresti riapplicare la soluzione alternativa su tutte le posizioni su tutte le variabili di stringa in tutta la base di codice. Questa è pura assurdità. Basta risolverlo in un unico posto, nel posto giusto in modo che le variabili di stringa contengano immediatamente il valore giusto. Non dovrebbe essere assolutamente necessario modificare il client.
BalusC,

3
Sì, se devi modificare l'intera applicazione, ovviamente non va bene. Ma se stai già utilizzando ResourceBundle come singleton devi solo ripararlo una volta. Avevo l'impressione che l'approccio singleton fosse il modo più comune di usare ResourceBundle.
Paaske,

51

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

le proprietà accettano un lettore oggetto come argomenti, che è possibile creare da un InputStream.

al momento della creazione, è possibile specificare la codifica del Reader:

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

quindi applicare questo Reader al metodo di caricamento:

prop.load(isr);

A proposito: ottenere lo stream dal file .properties :

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

A proposito: ottenere il pacchetto di risorse da InputStreamReader:

ResourceBundle rb = new PropertyResourceBundle(isr);

Spero che questo possa aiutarti !


3
La vera domanda qui riguarda ResourceBundle, però.
Nyerguds,

1
È vero, questa dovrebbe essere una risposta accettata se si sta utilizzando Propertiese si desidera recuperare UTF-8String, quindi funziona come un incantesimo. Tuttavia, per una ResourceBundlerisorsa linguistica, la risposta accettata è elegante. Tuttavia su votato la risposta.
Ilgıt Yıldırım,

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

23

ResourceBundle.Control con UTF-8 e i nuovi metodi String non funzionano, ad esempio se il file delle proprietà utilizza il set di caratteri cp1251.

Quindi ho raccomandato di usare un metodo comune: scrivere in simboli unicode . Per questo:

IDEA - ha una speciale " conversione da nativo ad ASCII trasparente " un'opzione (Impostazioni> Codifica file).

Eclipse - ha un plug-in " Editor proprietà " . Può funzionare come applicazione separata.


4
In IntelliJ IDEA 14, questo si trova in Impostazioni -> Editor -> Codifiche file. Ho anche dovuto eliminare tutti i file delle proprietà esistenti e ricrearli per rendere effettiva questa opzione.
Cypher,

Gli IDE non sono particolarmente rilevanti per la risposta, ma solo strumenti che in realtà non risolvono il problema di fondo di non archiviare contenuti nel set di caratteri UTF-8 .... che risolverebbe immediatamente il problema senza conversione o hacking come le proprietà di scrittura in simboli unicode all'interno di un file definito con un set di caratteri diverso.
Darrell Teague,

21

Questo problema è stato finalmente risolto in Java 9: https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9

La codifica predefinita per i file delle proprietà è ora UTF-8.

La maggior parte dei file delle proprietà esistenti non dovrebbe essere interessata: UTF-8 e ISO-8859-1 hanno la stessa codifica per i caratteri ASCII e la codifica ISO-8859-1 non ASCII leggibile dall'uomo non è valida UTF-8. Se viene rilevata una sequenza di byte UTF-8 non valida, il runtime Java rilegge automaticamente il file in ISO-8859-1.


19

Creiamo un file resources.utf8 che contiene le risorse in UTF-8 e abbiamo una regola per eseguire quanto segue:

native2ascii -encoding utf8 resources.utf8 resources.properties

Da dove veniamo native2ascii? Ho appena fatto find / -name native2ascii*e non ho ottenuto risultati, quindi presumo che non sia solo una parte del JDK ...
ArtOfWarfare il

Hm. Non fa parte di IBM JDK, ma sembra essere incluso in Oracle JDK, in jdk1.*.0_*/bin.
ArtOfWarfare il

Sembra essere parte dell'IBM JDK, almeno 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
Mi piace questa soluzione e la inserisco come Gist gist.github.com/enginer/3168dd4a374994718f0e
Sllouyssgort

Funziona molto bene Ho appena aggiunto un file delle proprietà della traduzione cinese in UTF8 e si carica senza problemi.
tresf

9

Attenzione: i file delle proprietà java devono essere codificati in ISO 8859-1!

Codifica ISO 8859-1 caratteri. I caratteri che non possono essere rappresentati direttamente in questa codifica possono essere scritti usando le escape Unicode; è consentito solo un singolo carattere 'u' in una sequenza di escape.

@vedi Proprietà Java Doc

Se vuoi davvero farlo: dai un'occhiata a: Proprietà Java Codifica UTF-8 in Eclipse - ci sono alcuni esempi di codice


1
Java! = Eclipse ... quest'ultimo è un IDE. Ulteriori dati! = Java. Java supporta l'elaborazione in streaming utilizzando una vasta gamma di set di caratteri, che per l'internazionalizzazione (dopo tutto la domanda riguarda ResourceBundles) ... risolve l'utilizzo di UTF-8 come risposta più diretta. Scrivere file di proprietà in un set di caratteri non supportato dalla lingua di destinazione complica inutilmente il problema.
Darrell Teague,

@Darell Teague: il "suggerimento" che deve essere stato un file di proprietà caricato per un ResouceBundle è ISO 8859-1 è una dichiarazione java: docs.oracle.com/javase/8/docs/api/java/util/… .. La seconda parte della mia risposta è solo un "suggerimento" su come affrontare il problema del cappello.
Ralph,


3

Ecco una soluzione Java 7 che utilizza l'eccellente libreria di supporto di Guava e il costrutto try-with-resources. Legge e scrive i file delle proprietà usando UTF-8 per la più semplice esperienza complessiva.

Per leggere un file delle proprietà come 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
  }
}

Per scrivere un file delle proprietà come 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
}

Questa risposta è utile Il problema principale qui con varie risposte sembra essere un malinteso su dati e set di caratteri. Java può leggere tutti i dati (correttamente) semplicemente specificando il set di caratteri in cui è stato memorizzato come mostrato sopra. UTF-8 è comunemente usato per supportare la maggior parte, se non tutte le lingue del pianeta, ed è quindi molto applicabile alle proprietà basate su ResourceBundle.
Darrell Teague,

@DarrellTeague: Beh, "UTF-8 è comunemente usato per supportare ..." - dovrebbe piuttosto esserci " Unicode è comunemente usato per supportare ..." :) dato che UTF-8 è solo una codifica dei caratteri di Unicode ( en .wikipedia.org / wiki / UTF-8 ).
Honza Zidek,

In realtà UTF-8 doveva essere specificamente definito come "il set di caratteri" (rispetto al semplice riferimento a "qualsiasi set di caratteri UniCode") in quanto UTF-8 in questo contesto (dati) ha un utilizzo predominante su Internet da alcune misure fino a 67%. Rif: stackoverflow.com/questions/8509339/…
Darrell Teague

3

Come ho suggerito, ho seguito l'implementazione del pacchetto di risorse ... ma ciò non ha aiutato .. poiché il pacchetto veniva sempre chiamato in locale en_US ... ho cercato di impostare le impostazioni internazionali predefinite su una lingua diversa e ancora l'implementazione del pacchetto di risorse il controllo veniva chiamato con en_US ... ho provato a inserire i messaggi di registro e ad eseguire il debug e vedere se è stata effettuata una chiamata locale diversa dopo aver modificato le impostazioni locali in fase di esecuzione tramite chiamate xhtml e JSF ... che non è successo ... poi ho provato a fare un sistema impostato di default su utf8 per leggere i file dal mio server (server tomcat) .. ma questo ha causato pronlem poiché tutte le librerie delle mie classi non sono state compilate in utf8 e tomcat ha iniziato a leggere in formato utf8 e il server non funzionava correttamente ... poi ho finito con l'implementazione di un metodo nel mio controller java da chiamare dai file xhtml ..in quel metodo ho fatto quanto segue:

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

Ero particolarmente nervoso in quanto ciò potrebbe rallentare le prestazioni della mia applicazione ... tuttavia, dopo averlo implementato, sembra che la mia applicazione sia più veloce ora .. Penso che sia perché, ora accedo direttamente alle proprietà invece di lasciare JSF analizza la sua strada per accedere alle proprietà ... In questo invito passo specificatamente argomenti booleani perché so che alcune proprietà non verrebbero tradotte e non dovranno essere in formato utf8 ...

Ora ho salvato il mio file delle proprietà in formato UTF8 e funziona benissimo poiché ogni utente nella mia applicazione ha una preferenza di locale referente.


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

Per quello che vale il mio problema era che i file stessi avevano una codifica sbagliata. Usare iconv ha funzionato per me

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

+1 per la menzione iconv. Non ne ho mai sentito parlare prima, ma l'ho digitato nella console ed ecco ed è una cosa che esiste (in CentOS 6, comunque.)
ArtOfWarfare,

Ora che in realtà ho provato ad usarlo, non ha funzionato: ha vomitato sul primo personaggio che non poteva essere convertito in ISO-8559-1.
ArtOfWarfare il

1

Ho cercato di utilizzare l'approccio fornito da Rod, ma tenendo conto della preoccupazione di BalusC di non ripetere la stessa soluzione in tutte le applicazioni e sono arrivato con questa classe:

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

Il modo di utilizzare questo sarebbe molto simile al normale utilizzo di ResourceBundle:

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

Oppure puoi usare il costruttore alternativo che usa UTF-8 per impostazione predefinita:

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

0

Apri la finestra di dialogo Impostazioni / Preferenze ( Ctrl+ Alt+ S), quindi fai clic su Editor e Codifiche file.

Schermata della finestra mostrata

Quindi, in fondo, troverai le codifiche predefinite per i file delle proprietà. Scegli il tipo di codifica.

In alternativa puoi usare i simboli unicode invece del testo nel tuo pacchetto di risorse (ad esempio "ів"uguale a \u0456\u0432)


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.