ResourceBundle ile kaynak özelliklerinde UTF-8 nasıl kullanılır


259

Java kullanarak kaynak özellikleri UTF-8 kullanmanız gerekir ResourceBundle. Metni doğrudan özellikler dosyasına girdiğimde, mojibake olarak görüntülenir.

Uygulamam Google App Engine'de çalışıyor.

Biri bana bir örnek verebilir mi? Bu işi alamıyorum.


1
Java 1.6 Bir Reader'a geçebildiğiniz için bunu düzelttim. Aşağıda @Chinaxing cevap yolu aşağı bakın
Will

1
@Will: soru öncelikle onları okumakla ilgilidir java.util.ResourceBundle, değil java.util.Properties.
BalusC

1
Cevaplanan bu soruyu kontrol edin ,,, size yardımcı olacağını umuyoruz [ stackoverflow.com/questions/863838/… [1]: stackoverflow.com/questions/863838/…
Programcı Majed Bboy

6
JDK9 yerel olarak UTF-8'i desteklemelidir, bkz. JEP 226
Paolo Fulgoni

Yanıtlar:


375

ResourceBundle#getBundle()Yorganın altında kullanımları PropertyResourceBundlebir zaman .propertiesdosya belirtildi. Bu da varsayılan Properties#load(InputStream)olarak bu özellikler dosyalarını yüklemek için kullanılır. Gereğince javadoc , bunlar ISO-8859-1 olarak varsayılan okuma göredir.

public void load(InputStream inStream) throws IOException

Giriş bayt akışından bir özellik listesi (anahtar ve öğe çiftleri) okur. Giriş akışı, yükte (Reader) belirtildiği gibi basit bir satır yönelimli formattadır ve ISO 8859-1 karakter kodlamasını kullanacağı varsayılır ; yani her bayt bir Latin1 karakteridir. Latin1 dilinde olmayan karakterler ve bazı özel karakterler, Java ™ Dil Belirtimi'nin 3.3 bölümünde tanımlanan Unicode çıkışlarını kullanan anahtarlarda ve öğelerde temsil edilir.

Bu yüzden onları ISO-8859-1 olarak kaydetmeniz gerekir. ISO-8859-1 aralığının dışında herhangi bir karakteriniz varsa ve \uXXXXbaşınızın üstünü kullanamıyorsanız ve bu nedenle dosyayı UTF-8 olarak kaydetmek zorunda kalırsanız , dönüştürmek için native2ascii aracını kullanmanız gerekir. UTF-8 kaydedilmiş özellikler dosyasını, ortaya çıkarılan tüm karakterlerin \uXXXXformata dönüştürüldüğü bir ISO-8859-1 kaydedilmiş özellikler dosyasına kaydedin . Aşağıdaki örnek, UTF-8 kodlu özellikler dosyasını text_utf8.propertiesgeçerli bir ISO-8859-1 kodlu özellikler dosyasına dönüştürür text.properties.

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

Eclipse gibi aklı başında bir IDE kullanırken, bu, .propertiesJava tabanlı bir projede bir dosya oluşturup Eclipse'nin kendi düzenleyicisini kullandığınızda otomatik olarak yapılır . Eclipse şeffaf bir şekilde ISO-8859-1 aralığının ötesindeki karakterleri \uXXXXformata dönüştürecektir. Ayrıca aşağıdaki ekran görüntülerine bakın (alttaki "Özellikler" ve "Kaynak" sekmelerine dikkat edin, büyük olanlar için tıklayın):

"Özellikler" sekmesi "Kaynak" sekmesi

Alternatif olarak, ResourceBundle.Controlözellik dosyalarını UTF-8 kullanarak açıkça okuduğunuz özel bir uygulama da oluşturabilirsiniz InputStreamReader, böylece bunları sorunsuz bir şekilde UTF-8 olarak kaydedebilirsiniz native2ascii. İşte bir başlangıç ​​örneği:

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

Bu aşağıdaki gibi kullanılabilir:

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

Ayrıca bakınız:


Teşekkürler. BTW FORMAT_PROPERTIES döndürmek için getFormats'ı geçersiz kılmak iyi bir fikir gibi görünüyor.
Flávio Etrusco

GetFormats () yöntemini geçersiz kılmak için bu öneri hakkında ayrıntılı bilgi verebilir misiniz?
Mark Roper

1
@ imgx64: Bildirdiğiniz için teşekkür ederiz. Yanıt düzeltildi.
BalusC

10
StandardCharsets.UTF_8Java 7+ kullanıyorsanız kullanmaktan çekinmeyin
Niks

1
@Nyerguds: Programlı olarak değiştirmenin nedenlerini görüyorsanız (yaşam için bir tane hayal bile edemiyorum), çekinmeyin. Sonuçta gönderdiğim tüm kod parçacıkları sadece başlangıç ​​örnekleri.
BalusC

131

ResourceBundle örneğiniz olduğu ve String'i şu şekilde elde edebileceğiniz göz önüne alındığında:

String val = bundle.getString(key); 

Japon ekran sorunumu şu şekilde çözdüm:

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

37
Buradaki tüm naif yükselenler / yorumcular için: bu bir çözüm değil, bir çözümdür. Gerçek altta yatan sorun hala ayakta ve çözülmesi gerekiyor.
BalusC

2
Bu durumumu düzeltti. Çözüm, Java'nın UTF-8'i kaynak paketlerinde ve özellik dosyalarında yerel olarak işlemeye başlaması olacaktır. Bu gerçekleşene kadar bir geçici çözüm kullanacağım.
JohnRDOrazio

@BalusC; bu yaklaşımın dezavantajı nedir? (fazladan bir String oluşturmaktan başka?)
Paaske

8
@Paaske: Bu bir çözüm değil, bir çözüm. Geçici çözüm, kod tabanı boyunca tüm dize değişkenlerinde tüm yere yeniden uygulamanız gerekir. Bu tamamen saçmalık. Dize değişkenlerinin hemen doğru değeri içermesi için tek bir yerde, doğru yerde sabitlemeniz yeterlidir. İstemciyi değiştirmeye kesinlikle gerek yoktur.
BalusC

3
Evet, tüm uygulamayı değiştirmeniz gerekiyorsa, bu kötüdür. Ancak zaten ResourceBundle'ı tek birton olarak kullanıyorsanız, bunu yalnızca bir kez düzeltmeniz gerekir. Singleton yaklaşım ResourceBundle kullanmanın en yaygın yolu olduğu izlenimi altındaydı.
Paaske

51

şuna bakın: http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Okuyucu)

özellikler bir Reader nesnesini bir InputStream öğesinden oluşturabileceğiniz bağımsız değişkenler olarak kabul eder .

oluşturma zamanında Reader'ın kodlamasını belirtebilirsiniz:

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

ardından bu Reader'ı yükleme yöntemine uygulayın:

prop.load(isr);

BTW: akışı .properties dosyasından alın:

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

BTW: kaynak paketini şu kaynaktan alın InputStreamReader:

ResourceBundle rb = new PropertyResourceBundle(isr);

Umarım bu size yardımcı olabilir!


3
Buradaki asıl soru ResourceBundlebununla ilgili.
Nyerguds

1
Doğru, bu kullanıyorsanız cevap kabul edilmelidir Propertiesve UTF-8String almak istiyorsanız o zaman bu bir cazibe gibi çalışır. Bununla birlikte ResourceBundle, dil kaynakları gibi bir şey için kabul edilen cevap zariftir. Buna rağmen cevap oy verdi.
Ilgıt Yıldırım

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

23

ResourceBundle.Control UTF-8 ile ve yeni String yöntemleri ile çalışmaz, örneğin özellikler dosyası cp1251 karakter kümesi kullanıyorsa.

Bu yüzden ortak bir yöntem kullanarak tavsiye: unicode sembollerde yazmak . Bunun için:

IDEA - özel bir " Şeffaf yerelden ASCII'ye dönüştürme " seçeneğine sahiptir (Ayarlar> Dosya Kodlama).

Eclipse - " Özellikler Düzenleyicisi " eklentisine sahiptir . Ayrı bir uygulama olarak çalışabilir.


4
IntelliJ IDEA 14'te bu, Ayarlar -> Editör -> Dosya Kodlamaları bölümünde bulunur. Ayrıca, mevcut özellik dosyalarını silmek ve bu seçeneğin etkili olması için yeniden oluşturmak zorunda kaldım.
Cypher

IDE'ler özellikle cevapla ilgili değil, sadece UTF-8 karakter setinde içerik saklamamanın altında yatan problemi gerçekten ele almayan araçlar .... Bu, dönüştürme veya yazma özellikleri gibi hackery olmadan sorunu hemen çözecek farklı bir karakter kümesiyle tanımlanan bir dosyanın içindeki unicode sembollerinde.
Darrell Teague

21

Bu sorun nihayet Java 9'da giderildi: https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9

Özellikler dosyaları için varsayılan kodlama artık UTF-8'dir.

Mevcut özellik dosyalarının çoğu etkilenmemelidir: UTF-8 ve ISO-8859-1, ASCII karakterleri için aynı kodlamaya sahiptir ve insan tarafından okunabilen ASCII olmayan ISO-8859-1 kodlaması geçerli UTF-8 değildir. Geçersiz bir UTF-8 bayt dizisi algılanırsa, Java çalışma zamanı dosyayı ISO-8859-1'de otomatik olarak yeniden okur.


19

UTF-8'deki kaynakları içeren bir resources.utf8 dosyası oluştururuz ve aşağıdakileri çalıştırmak için bir kuralı vardır:

native2ascii -encoding utf8 resources.utf8 resources.properties

Nereden geliyoruz native2ascii? Sadece yaptım find / -name native2ascii*ben ... JDK sadece parçası değil varsayalım öylesine ve hiçbir sonuç var
ArtOfWarfare

Hm. IBM JDK'nın bir parçası değil, ancak Oracle JDK'ya dahil edilmiş gibi görünüyor jdk1.*.0_*/bin.
ArtOfWarfare

IBM JDK'nın bir parçası gibi görünüyor, en azından JDK 6'da.
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);  
            }  
        }  
    }  
}  


Bu çok iyi çalışıyor. Sadece UTF8 bir Çince Çeviri özellikleri dosyası ekledi ve herhangi bir sorun olmadan yüklenir.
tresf

9

Dikkat: java özellik dosyaları ISO 8859-1'de kodlanmalıdır!

ISO 8859-1 karakter kodlaması. Bu kodlamada doğrudan temsil edilemeyen karakterler Unicode çıkış karakterleri kullanılarak yazılabilir; kaçış sırasında sadece tek bir 'u' karakterine izin verilir.

@see Özellikler Java Belgesi

Bunu hala gerçekten yapmak istiyorsanız: bir göz atın: Eclipse'de Java özellikleri UTF-8 kodlaması - bazı kod örnekleri var


1
Java! = Eclipse ... ikincisi bir IDE. Diğer veriler! = Java. Java, uluslararasılaştırma için (sonuçta ResourceBundles ile ilgili) çok sayıda karakter seti kullanarak akış işlemeyi destekler ... UTF-8'i en basit yanıt olarak kullanmayı çözer. Özellik dosyalarını hedef dil tarafından desteklenmeyen bir karakter kümesinde yazmak gereksiz yere sorun yaratır.
Darrell Teague

@Darell Teague: Bir ResouceBundle için yüklenmiş bir dosyanın ISO 8859-1 olması gereken "ipucu" bir java ifadesidir: docs.oracle.com/javase/8/docs/api/java/util/… .. Cevabımın ikinci kısmı, şapka problemiyle nasıl başa çıkılacağına dair bir ipucu.
Ralph


3

İşte Guava'nın mükemmel destek kütüphanesini ve kaynaklarla deneme yapısını kullanan bir Java 7 çözümü. En basit genel deneyim için özellikler dosyalarını UTF-8 kullanarak okur ve yazar.

Bir özellikler dosyasını UTF-8 olarak okumak için:

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

Bir özellikler dosyasını UTF-8 olarak yazmak için:

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
}

Bu cevap faydalıdır. Buradaki çeşitli cevapların temel sorunu, veri ve karakter kümeleri hakkında bir yanlış anlama gibi görünüyor. Java, yukarıda gösterildiği gibi saklandığı karakter kümesini belirterek herhangi bir veriyi (doğru) okuyabilir. UTF-8, gezegendeki her dil olmasa bile çoğunu desteklemek için yaygın olarak kullanılır ve bu nedenle ResourceBundle tabanlı özelliklere çok uygulanabilir.
Darrell Teague

@DarrellTeague: Eh, "UTF-8 yaygın ... destek için kullanılan" - ziyade olmalıdır " Unicode yaygın destek için kullanılır ..." :) UTF-8 Unicode (sadece bir karakter kodlaması olarak tr .wikipedia.org / wiki / UTF-8 ).
Honza Zidek

Aslında UTF-8'in özellikle "karakter kümesi" olarak adlandırılması (bu bağlamda (veri) UTF-8'in internette kullanımı, % 67. Referans: stackoverflow.com/questions/8509339/…
Darrell Teague

3

Bir önerildiği gibi, ben kaynak paketinin uygulanması geçti .. ama bu yardımcı olmadı .. paket her zaman en_US yerel ayar altında çağrıldığından ... ben varsayılan yerel ayarımı farklı bir dile ve hala benim kaynak paketini uygulama ayarlamaya çalıştım en_US ile kontrol deniyordu ... ben günlük mesajları koymak ve hata ayıklama yoluyla bir adım yapmak ve xhtml ve JSF çağrıları aracılığıyla çalışma zamanında yerel ayar değiştirdikten sonra farklı bir yerel çağrı yapılıp yapılmadığını görmek çalıştı ... bu olmadı ... sonra sunucum (tomcat sunucusu) tarafından dosyaları okumak için bir utf8 varsayılan olarak ayarlanan bir sistem yapmaya çalıştım .. ama tüm sınıf kütüphaneler utf8 altında derlenmiş ve tomcat sonra utf8 formatında okumaya başladı gibi pronlem neden oldu ve sunucu düzgün çalışma değildi ... sonra xhtml dosyalarından çağrılacak benim java denetleyicisi bir yöntem uygulamak ile sona erdi ..bu yöntemde aşağıdakileri yaptım:

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

Bu benim uygulamamın performansını yavaşlatabilir gibi özellikle gergindim ... ancak, bunu uyguladıktan sonra, benim uygulama şimdi daha hızlı gibi görünüyor .. Bence, çünkü şimdi izin yerine özellikleri doğrudan erişiyorum JSF özelliklere erişme yolunu ayrıştırmak ... i bazı özellikleri çevrilmiş ve utf8 formatında olması gerekmez biliyorum çünkü ben özellikle bu çağrı Boole argüman geçmek ...

Şimdi özellikler dosyamı UTF8 biçiminde kaydettim ve uygulamamdaki her kullanıcının bir başvuru yerel ayarı tercihi olduğu için iyi çalışıyor.


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

Benim sorunum için ne dosyaların kendilerini yanlış kodlama vardı. İconv kullanmak benim için çalıştı

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

Bahsetmek için +1 iconv. Daha önce hiç duymadım ama konsola yazdım ve baktım, var olan bir şey (CentOS 6'da, zaten.)
ArtOfWarfare

Şimdi gerçekten kullanmayı denedim, işe yaramadı: ISO-8559-1'e dönüştürülemeyen ilk karakter üzerine attı.
ArtOfWarfare

1

Rod tarafından sağlanan yaklaşımı kullanmaya çalıştım, ancak BalusC'nin tüm uygulamada aynı çözümü tekrar etmeme konusundaki endişesini göz önünde bulundurarak bu sınıfla geldim:

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

Bunu kullanmanın yolu, normal ResourceBundle kullanımından çok benzer:

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

Veya varsayılan olarak UTF-8 kullanan alternatif yapıcıyı kullanabilirsiniz:

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

0

Ayarlar / Tercihler iletişim kutusunu ( Ctrl+ Alt+ S) açın, ardından Editör ve Dosya Kodlamaları'nı tıklayın.

Gösterilen pencerenin ekran görüntüsü

Ardından, altta, özellikler dosyaları için varsayılan kodlamaları parmaklarınızda göreceksiniz. Kodlama türünüzü seçin.

Alternatif olarak, kaynak paketinizdeki metin yerine unicode sembolleri kullanabilirsiniz (örneğin, "ів"eşittir \u0456\u0432)


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.