Cara menggunakan UTF-8 di properti sumber daya dengan ResourceBundle

259

Saya perlu menggunakan UTF-8 di properti sumber daya saya menggunakan Java ResourceBundle. Ketika saya memasukkan teks langsung ke file properti, ini akan ditampilkan sebagai mojibake.

Aplikasi saya berjalan di Google App Engine.

Adakah yang bisa memberi saya contoh? Saya tidak bisa mendapatkan pekerjaan ini.

nacho
sumber
1
Java 1.6 Memperbaiki ini karena Anda dapat meneruskan Pembaca. Lihat jawaban @Chinaxing di bawah
Will
1
@ Will: pertanyaan utamanya tentang membacanya java.util.ResourceBundle, bukan java.util.Properties.
BalusC
1
Lihat pertanyaan yang dijawab ini ,,, semoga membantu Anda [ stackoverflow.com/questions/863838/… [1]: stackoverflow.com/questions/863838/…
Majdy the programmer Bboy
6
JDK9 seharusnya mendukung UTF-8 secara asli, lihat JEP 226
Paolo Fulgoni

Jawaban:

375

The ResourceBundle#getBundle()penggunaan di bawah selimut PropertyResourceBundleketika .propertiesfile yang ditentukan. Ini pada gilirannya menggunakan secara default Properties#load(InputStream)untuk memuat file properti tersebut. Sesuai javadoc , mereka secara default dibaca sebagai ISO-8859-1.

public void load(InputStream inStream) throws IOException

Membaca daftar properti (pasangan kunci dan elemen) dari aliran byte input. Aliran input dalam format berorientasi garis sederhana seperti yang ditentukan dalam beban (Pembaca) dan diasumsikan menggunakan pengkodean karakter ISO 8859-1 ; yaitu setiap byte adalah satu karakter Latin1. Karakter tidak dalam Latin1, dan karakter khusus tertentu, diwakili dalam kunci dan elemen menggunakan Unicode escapes sebagaimana didefinisikan dalam bagian 3.3 dari Spesifikasi Bahasa Java ™.

Jadi, Anda harus menyimpannya sebagai ISO-8859-1. Jika Anda memiliki karakter di luar rentang ISO-8859-1 dan Anda tidak dapat menggunakan \uXXXXoff-head dan Anda terpaksa menyimpan file sebagai UTF-8, maka Anda harus menggunakan alat native2ascii untuk mengkonversi File properti tersimpan UTF-8 ke file properti tersimpan ISO-8859-1 di mana semua karakter yang tidak ditemukan dikonversi ke dalam \uXXXXformat. Contoh di bawah ini mengubah file properti yang disandikan UTF-8 text_utf8.propertiesmenjadi file properti yang disandikan ISO-8859-1 yang valid text.properties.

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

Saat menggunakan IDE waras seperti Eclipse, ini sudah secara otomatis dilakukan ketika Anda membuat .propertiesfile dalam proyek berbasis Java dan menggunakan editor Eclipse sendiri. Eclipse akan secara transparan mengkonversi karakter di luar rentang ISO-8859-1 ke \uXXXXformat. Lihat juga di bawah tangkapan layar (perhatikan tab "Properti" dan "Sumber" di bagian bawah, klik untuk besar):

Tab "Properti" Tab "Sumber"

Atau, Anda juga bisa membuat ResourceBundle.Controlimplementasi kustom di mana Anda secara eksplisit membaca file properti menggunakan UTF-8 InputStreamReader, sehingga Anda bisa menyimpannya sebagai UTF-8 tanpa perlu repot native2ascii. Berikut ini adalah contoh 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;
    }
}

Ini dapat digunakan sebagai berikut:

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

Lihat juga:

BalusC
sumber
Terima kasih. BTW tampaknya menjadi ide bagus untuk mengganti getFormats untuk mengembalikan FORMAT_PROPERTIES.
Flávio Etrusco
Bisakah Anda menguraikan saran ini untuk mengganti getFormats ()?
Mark Roper
1
@ imgx64: Terima kasih telah memberi tahu. Jawaban sudah diperbaiki.
BalusC
10
Jangan ragu untuk menggunakan StandardCharsets.UTF_8jika Anda menggunakan Java 7+
Niks
1
@Nyerguds: jika Anda melihat alasan untuk mengubahnya secara terprogram (meskipun saya tidak bisa membayangkannya seumur hidup), jangan ragu untuk melakukannya. Bagaimanapun, semua cuplikan kode yang saya posting hanyalah contoh kickoff.
BalusC
131

Mengingat Anda memiliki instance ResourceBundle dan Anda bisa mendapatkan String dengan:

String val = bundle.getString(key); 

Saya memecahkan masalah tampilan Jepang saya dengan:

return new String(val.getBytes("ISO-8859-1"), "UTF-8");
tongkat
sumber
37
Untuk semua pemberi komentar / komentar yang naif di sini: ini bukan solusi, tetapi solusi. Masalah mendasar yang sebenarnya masih ada dan perlu dipecahkan.
BalusC
2
Ini memperbaiki situasi saya. Solusinya adalah bagi Java untuk mulai menangani UTF-8 secara asli dalam kumpulan sumber daya dan dalam file properti. Sampai itu terjadi saya akan menggunakan solusi.
JohnRDOrazio
@BalusC; apa kerugian dari pendekatan ini? (selain membuat String ekstra?)
Paaske
8
@ Paaske: ini solusi, bukan solusi. Anda harus menerapkan kembali solusi di semua tempat pada semua variabel string di seluruh basis kode. Ini omong kosong. Hanya memperbaikinya di satu tempat, di tempat yang tepat sehingga variabel string segera berisi nilai yang tepat. Seharusnya sama sekali tidak perlu memodifikasi klien.
BalusC
3
Ya, jika Anda harus memodifikasi seluruh aplikasi, tentu saja ini buruk. Tetapi jika Anda sudah menggunakan ResourceBundle sebagai singleton Anda hanya perlu memperbaikinya sekali. Saya mendapat kesan bahwa pendekatan tunggal adalah cara paling umum menggunakan ResourceBundle.
Paaske
51

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

properti menerima objek Reader sebagai argumen, yang dapat Anda buat dari InputStream.

pada saat membuat, Anda dapat menentukan pengkodean Pembaca:

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

lalu terapkan Pembaca ini ke metode memuat:

prop.load(isr);

BTW: dapatkan aliran dari file .properties :

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

BTW: dapatkan bundel sumber daya dari InputStreamReader:

ResourceBundle rb = new PropertyResourceBundle(isr);

Semoga ini bisa membantu Anda!

Chinaxing
sumber
3
Pertanyaan sebenarnya di sini adalah tentang ResourceBundle.
Nyerguds
1
Benar, jawaban ini harus diterima jika Anda menggunakan Propertiesdan Anda ingin mengambil UTF-8String maka ini berfungsi seperti pesona. Namun untuk ResourceBundlesumber daya bahasa maka jawaban yang diterima elegan. Namun demikian, memilih suara.
Ilgıt Yıldırım
ResourceBundle rb = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"))
dedek
23

ResourceBundle.Control dengan UTF-8 dan metode String baru tidak berfungsi, jika file properti menggunakan cp1251 charset, misalnya.

Jadi saya merekomendasikan menggunakan metode umum: menulis dalam simbol unicode . Untuk ini:

IDEA - memiliki khusus " Transparan konversi asli-to-ASCII " option (Pengaturan> Berkas Encoding).

Eclipse - memiliki plugin " Properties Editor " . Ini dapat berfungsi sebagai aplikasi terpisah.

Kinjeiro
sumber
4
Di IntelliJ IDEA 14, ini terletak di Pengaturan -> Editor -> Penyandian File. Saya juga harus menghapus semua file properti yang ada, dan membuatnya kembali agar opsi ini berlaku.
Cypher
IDE tidak terlalu relevan dengan jawaban tetapi hanya alat yang benar-benar tidak mengatasi masalah mendasar dari tidak menyimpan konten dalam set karakter UTF-8 .... yang akan menyelesaikan masalah langsung tanpa konversi atau peretasan seperti menulis properti dalam simbol unicode di dalam file yang ditentukan dengan set karakter yang berbeda.
Darrell Teague
21

Masalah ini akhirnya telah diperbaiki di Java 9: https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9

Pengkodean default untuk file properti sekarang adalah UTF-8.

Sebagian besar file properti yang ada tidak boleh terpengaruh: UTF-8 dan ISO-8859-1 memiliki pengkodean yang sama untuk karakter ASCII, dan pengkodean non-ASCII ISO-8859-1 yang dapat dibaca manusia tidak valid UTF-8. Jika urutan UTF-8 byte yang tidak valid terdeteksi, Java runtime secara otomatis membaca ulang file di ISO-8859-1.

stenix
sumber
19

Kami membuat file resources.utf8 yang berisi sumber daya di UTF-8 dan memiliki aturan untuk menjalankan berikut ini:

native2ascii -encoding utf8 resources.utf8 resources.properties
andykellr
sumber
Dari mana kita dapatkan native2ascii? Saya baru saja melakukannya find / -name native2ascii*dan tidak mendapatkan hasil, jadi saya berasumsi itu bukan hanya bagian dari JDK ...
ArtOfWarfare
Hm Ini bukan bagian dari JDK IBM, tetapi tampaknya termasuk dalam Oracle JDK, di jdk1.*.0_*/bin.
ArtOfWarfare
Tampaknya menjadi bagian dari JDK IBM, setidaknya di 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 <[email protected]> 
 */  
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);  
            }  
        }  
    }  
}  
marcolop
sumber
1
Saya suka solusi ini dan saya mempostingnya seperti Gist gist.github.com/enginer/3168dd4a374994718f0e
Sllouyssgort
Ini bekerja dengan sangat baik. Baru saja menambahkan file properti Terjemahan Cina di UTF8 dan dimuat tanpa masalah.
tresf
9

Perhatian: file properti java harus dikodekan dalam ISO 8859-1!

Pengkodean karakter ISO 8859-1. Karakter yang tidak dapat langsung direpresentasikan dalam pengkodean ini dapat ditulis menggunakan Unicode escapes; hanya satu karakter 'u' yang diizinkan dalam urutan pelarian.

@lihat Properti Java Doc

Jika Anda masih benar-benar ingin melakukan ini: lihatlah: Java properties UTF-8 encoding di Eclipse - ada beberapa contoh kode

Muntah
sumber
1
Java! = Eclipse ... yang terakhir adalah IDE. Data lebih lanjut! = Java. Java mendukung pemrosesan aliran menggunakan serangkaian rangkaian karakter yang luas, yang untuk internasionalisasi (bagaimanapun juga adalah pertanyaan tentang ResourceBundles) ... memutuskan untuk menggunakan UTF-8 sebagai jawaban yang paling mudah. Menulis file properti dalam set karakter yang tidak didukung oleh bahasa target tidak perlu mempersulit masalah.
Darrell Teague
@ Darell Teague: "petunjuk" bahwa file propertie yang dimuat untuk ResouceBundle harus ISO 8859-1 adalah pernyataan java: docs.oracle.com/javase/8/docs/api/java/util/… .. Bagian kedua dari jawaban saya hanyalah "petunjuk" bagaimana menangani masalah topi.
Ralph
3

Berikut adalah solusi Java 7 yang menggunakan pustaka dukungan Guava yang sangat baik dan konstruk try-with-resources. Itu membaca dan menulis file properti menggunakan UTF-8 untuk pengalaman keseluruhan yang paling sederhana.

Untuk membaca file properti sebagai 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
  }
}

Untuk menulis file properti sebagai 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
}
Gary Rowe
sumber
Jawaban ini bermanfaat. Masalah inti di sini dengan berbagai jawaban tampaknya adalah kesalahpahaman tentang kumpulan data dan karakter. Java dapat membaca data apa pun (dengan benar) hanya dengan menentukan set karakter tempat penyimpanannya seperti yang ditunjukkan di atas. UTF-8 umumnya digunakan untuk mendukung sebagian besar jika tidak setiap bahasa di planet ini dan karena itu sangat berlaku untuk properti berbasis ResourceBundle.
Darrell Teague
@DarrellTeague: Ya, "UTF-8 biasanya digunakan untuk mendukung ..." - seharusnya ada " Unicode biasanya digunakan untuk mendukung ..." :) karena UTF-8 hanyalah pengkodean karakter dari Unicode ( en .wikipedia.org / wiki / UTF-8 ).
Honza Zidek
Sebenarnya UTF-8 dimaksudkan untuk secara khusus dipanggil sebagai "set karakter" (dibandingkan hanya merujuk 'set karakter UniCode') sebagai UTF-8 dalam konteks ini (data) telah mendominasi penggunaan di Internet dengan beberapa ukuran setinggi 67%. Ref: stackoverflow.com/questions/8509339/…
Darrell Teague
3

Seperti yang disarankan, saya pergi melalui implementasi bundel sumber daya .. tapi itu tidak membantu .. karena bundel selalu disebut di bawah en_US lokal ... saya mencoba untuk menetapkan lokal default saya ke bahasa yang berbeda dan masih penerapan bundel sumber daya saya kontrol dipanggil dengan en_US ... saya mencoba untuk menempatkan pesan log dan melakukan langkah melalui debug dan melihat apakah panggilan lokal yang berbeda dilakukan setelah saya mengubah lokal pada saat dijalankan melalui xhtml dan panggilan JSF ... yang tidak terjadi ... maka saya mencoba untuk melakukan sistem yang ditetapkan default untuk utf8 untuk membaca file oleh server saya (tomcat server) .. tapi itu menyebabkan masalah karena semua perpustakaan kelas saya tidak dikompilasi di bawah utf8 dan tomcat mulai membaca kemudian dalam format utf8 dan server tidak berjalan dengan benar ... maka saya akhirnya menerapkan metode di pengontrol java saya untuk dipanggil dari file xhtml ..dalam metode itu saya melakukan hal berikut:

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

Saya sangat gugup karena ini dapat memperlambat kinerja aplikasi saya ... namun, setelah menerapkan ini, sepertinya aplikasi saya lebih cepat sekarang .. saya pikir itu karena, saya sekarang langsung mengakses properti daripada membiarkan JSF menguraikan cara mengakses properti ... saya secara khusus melewati argumen Boolean dalam panggilan ini karena saya tahu beberapa properti tidak akan diterjemahkan dan tidak perlu dalam format utf8 ...

Sekarang saya telah menyimpan file properti saya dalam format UTF8 dan berfungsi dengan baik karena setiap pengguna dalam aplikasi saya memiliki preferensi lokal referensi.

Masoud
sumber
2
Properties prop = new Properties();
String fileName = "./src/test/resources/predefined.properties";
FileInputStream inputStream = new FileInputStream(fileName);
InputStreamReader reader = new InputStreamReader(inputStream,"UTF-8");
Вассесуарий Пупочкин
sumber
1

Untuk apa layaknya masalah saya adalah bahwa file-file itu sendiri berada dalam pengkodean yang salah. Menggunakan iconv bekerja untuk saya

iconv -f ISO-8859-15 -t UTF-8  messages_nl.properties > messages_nl.properties.new
Zack Bartel
sumber
+1 untuk disebutkan iconv. Saya belum pernah mendengarnya sebelumnya tetapi saya mengetiknya di konsol dan lihatlah, itu adalah sesuatu yang ada (di CentOS 6, lagian.)
ArtOfWarfare
Sekarang saya sudah benar-benar mencoba menggunakannya, itu tidak berhasil: itu muntah pada karakter pertama yang tidak dapat dikonversi ke ISO-8559-1.
ArtOfWarfare
1

Saya mencoba menggunakan pendekatan yang diberikan oleh Rod, tetapi dengan mempertimbangkan keprihatinan BalusC tentang tidak mengulangi pekerjaan yang sama di semua aplikasi dan datang dengan kelas ini:

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

Cara menggunakan ini akan sangat mirip dengan penggunaan ResourceBundle biasa:

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

Atau Anda dapat menggunakan konstruktor alternatif yang menggunakan UTF-8 secara default:

private MyResourceBundle labels = new MyResourceBundle("es");
carlossierra
sumber
0

Buka dialog Pengaturan / Preferensi ( Ctrl+ Alt+ S), lalu klik Editor dan Penyandian File.

Cuplikan layar jendela ditampilkan

Kemudian, di bagian bawah, Anda akan meraba penyandian default untuk file properti. Pilih jenis penyandian Anda.

Atau Anda dapat menggunakan simbol unicode alih-alih teks dalam bundel sumber daya Anda (misalnya "ів"sama dengan \u0456\u0432)

Юра Чорнота
sumber