Як використовувати UTF-8 у ресурсах ресурсів за допомогою ResourceBundle


259

Мені потрібно використовувати UTF-8 у своїх ресурсах ресурсів за допомогою Java ResourceBundle. Коли я ввожу текст безпосередньо у файл властивостей, він відображається як mojibake.

Мій додаток працює на Google App Engine.

Хтось може мені навести приклад? Я не можу отримати цю роботу.


1
Java 1.6 Виправлено це під час передачі у програмі Reader. Дивіться відповідь @Chinaxing нижче внизу
буде

1
@Will: питання стосується передусім читання їх через java.util.ResourceBundle, а не java.util.Properties.
BalusC

1
Перевірте це питання відповів ,,, сподіваюся , це допоможе вам [ stackoverflow.com/questions/863838 / ... [1]: stackoverflow.com/questions/863838 / ...
Majdy програміст Bboy

6
JDK9 повинен підтримувати UTF-8 на власній основі, дивіться JEP 226
Паоло Фулгоні

Відповіді:


375

В ResourceBundle#getBundle()використання під ковдрою , PropertyResourceBundleколи .propertiesв зазначений файл. Це, в свою чергу, використовує за замовчуванням Properties#load(InputStream)для завантаження файлів цих властивостей. Відповідно до javadoc , вони за замовчуванням читаються як ISO-8859-1.

public void load(InputStream inStream) throws IOException

Читає список властивостей (пари ключів та елементів) з потоку вхідних байтів. Вхідний потік у простому лінійно-орієнтованому форматі, як зазначено в завантаженні (Reader), і передбачається використовувати кодування символів ISO 8859-1 ; тобто кожен байт є одним символом Latin1. Символи, що не є латинським1, та певні спеціальні символи, представлені ключами та елементами, що використовують уникнення Unicode, як визначено в розділі 3.3 Спеціалізації мови Java ™.

Отже, вам потрібно буде зберегти їх як ISO-8859-1. Якщо у вас є будь-які символи, що перебувають за межами діапазону ISO-8859-1, і ви не можете використовувати \uXXXXверхню частину голови, і, таким чином, ви змушені зберегти файл як UTF-8, тоді вам потрібно буде використовувати інструмент native2ascii для перетворення Файл збережених властивостей UTF-8 у файл збережених властивостей ISO-8859-1, де всі непокриті символи перетворюються у \uXXXXформат. Наведений нижче приклад перетворює закодований файл властивостей UTF-8 text_utf8.propertiesу допустимий закодований файл властивостей ISO-8859-1 text.properties.

native2ascii -кодування UTF-8 text_utf8.properties text.properties

При використанні здорового IDE, такого як Eclipse, це вже робиться автоматично, коли ви створюєте .propertiesфайл у проекті на базі Java та використовуєте власний редактор Eclipse. Eclipse прозоро перетворить символи за межі діапазону ISO-8859-1 у \uXXXXформат. Дивіться також нижче скріншоти (зверніть увагу на вкладки "Властивості" та "Джерело" внизу, натисніть для великих):

Вкладка "Властивості" Вкладка "Джерело"

Крім того, ви також можете створити власну ResourceBundle.Controlреалізацію, в якій ви явно читаєте файли властивостей як UTF-8 за допомогою InputStreamReader, так що ви можете просто зберегти їх як UTF-8 без зайвих проблем native2ascii. Ось приклад відмови:

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

Це можна використовувати так:

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

Дивитися також:


Дякую. BTW, здається, було б гарною ідеєю замінити getFormats, щоб повернути FORMAT_PROPERTIES.
Flávio Etrusco

Не могли б ви детальніше розробити цю пропозицію, щоб замінити getFormats ()?
Марк Ропер

1
@ imgx64: Дякую за повідомлення. Відповідь виправлено.
BalusC

10
Не соромтеся використовувати, StandardCharsets.UTF_8якщо ви використовуєте Java 7+
Niks

1
@Nyerguds: якщо ви бачите причини, щоб коли-небудь програмно змінити це (я все життя не можу уявити), сміливо робіть це. Усі фрагменти коду, які я публікую, - це лише приклади початкового ставлення.
BalusC

131

З огляду на те, що у вас є примірник ResourceBundle, і ви можете отримати String:

String val = bundle.getString(key); 

Я вирішив свою проблему з японським дисплеєм:

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

36
Для всіх наївних прихильників / коментаторів тут: це не рішення, а обхід. Справжня основна проблема все ще стоїть і потребує вирішення.
BalusC

2
Це виправило мою ситуацію. Рішення полягало б у тому, щоб Java почала обробляти UTF-8 спочатку в наборах ресурсів та у файлах властивостей. Поки цього не стане, я скористаюся способом вирішення.
JohnRDOrazio

@BalusC; у чому недолік такого підходу? (крім створення додаткової рядки?)
Paaske

8
@Paaske: це рішення, а не рішення. Вам потрібно буде повторно застосувати вирішення місця на всіх місцях на всіх змінних рядків у всій базі коду. Це чиста дурниця. Просто зафіксуйте його в одному місці, в потрібному місці, щоб змінні рядка негайно містили потрібне значення. Не слід абсолютно змінювати клієнта.
BalusC

3
Так, якщо вам доведеться змінити всю програму, звичайно це погано. Але якщо ви вже використовуєте ResourceBundle як сингл, вам доведеться виправити це лише один раз. У мене було враження, що синтонічний підхід є найпоширенішим способом використання ResourceBundle.
Paaske

50

подивіться на це: http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)

властивості приймають об’єкт Reader як аргументи, які ви можете створити з InputStream.

під час створення можна вказати кодування Reader:

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

потім застосуйте цей Reader до методу завантаження:

prop.load(isr);

BTW: отримати потік з .properties файла:

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

BTW: отримати пакет ресурсів від InputStreamReader:

ResourceBundle rb = new PropertyResourceBundle(isr);

сподіваюся, що це може вам допомогти!


3
Але власне питання тут про ResourceBundle, однак.
Nyerguds

1
Правда, на це слід прийняти відповідь, якщо ви використовуєте, Propertiesі ви хочете отримати UTF-8рядок, то це працює як шарм. Однак для ResourceBundleтаких, як мовні ресурси, тоді прийнята відповідь є елегантною. Проте нагору проголосували відповідь.
Ilgıt Yıldırım

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

22

ResourceBundle.Control з UTF-8 і новими методами String не працюють, наприклад, якщо файл властивостей використовує cp1251 charset.

Тому я рекомендував використовувати звичайний метод: писати символами unicode . Для цього:

IDEA - має спеціальну " Прозору конверсію з нативного в ASCII " опцію (Налаштування> Кодування файлів).

Eclipse - має плагін " Редактор властивостей " . Він може працювати як окремий додаток.


3
У IntelliJ IDEA 14 це розміщено в Налаштуваннях -> Редактор -> Кодування файлів. Я також повинен був видалити будь-які наявні файли властивостей і заново створити їх, щоб ця опція набула чинності.
Сайфер

IDE не особливо стосуються відповіді, але просто інструменти, які дійсно не вирішують основної проблеми не збереження вмісту в наборі символів UTF-8 .... що вирішить проблему відразу без перетворення або хакерства, як властивості запису в символах unicode всередині файлу, визначеного з іншим набором символів.
Даррелл Тіг

21

Ця проблема остаточно виправлена ​​в Java 9: https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9

Кодування за файлами властивостей за замовчуванням тепер UTF-8.

Більшість існуючих файлів властивостей не повинні впливати: UTF-8 та ISO-8859-1 мають однакове кодування для символів ASCII, а кодируемое для людини кодування, що не читається ASCII ISO-8859-1, недійсне UTF-8. Якщо виявлена ​​недійсна послідовність байтів UTF-8, час виконання Java автоматично перечитує файл у ISO-8859-1.


19

Ми створюємо файл resource.utf8, який містить ресурси в UTF-8 і має правило для виконання наступного:

native2ascii -encoding utf8 resources.utf8 resources.properties

Звідки ми беремось native2ascii? Я щойно зробив find / -name native2ascii*і не отримав результатів, тому я припускаю, що це не просто частина JDK ...
ArtOfWarfare

Гм. Він не є частиною IBM JDK, але він, здається, включений в JDK Oracle, в jdk1.*.0_*/bin.
ArtOfWarfare

Здається, вона є частиною IBM JDK, принаймні, у JDK 6.
Ерік Фін

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
Мені подобається це рішення, і я публікую його як Gist gist.github.com/enginer/3168dd4a374994718f0e
Sllouyssgort,

Це працює дуже добре. Щойно доданий файл властивостей китайського перекладу в UTF8, і він завантажується без проблем.
tresf

9

Увага: файли властивостей Java повинні бути закодовані в ISO 8859-1!

Кодування символів ISO 8859-1. Символи, які не можуть бути безпосередньо представлені в цьому кодуванні, можна записати, використовуючи уникнення Unicode; у послідовності відпуску допускається лише один символ 'u'.

@see Властивості Java Doc

Якщо ви все ще хочете це зробити: подивіться на: кодування властивостей Java UTF-8 кодування в Eclipse - є кілька зразків коду


1
Java! = Затемнення ... останній - IDE. Подальші дані! = Java. Java підтримує обробку потоків, використовуючи широкий набір символьних наборів, який для інтернаціоналізації (питання стосується ResourceBundles зрештою) ... вирішує використовувати UTF-8 як найбільш пряму відповідь. Запис файлів властивостей у набір символів, які не підтримуються цільовою мовою, зайво ускладнює проблему.
Darrell Teague

@Darell Teague: "Підказка" про те, що файл, який завантажується для ResouceBundle, має бути ISO 8859-1 - це заява Java: docs.oracle.com/javase/8/docs/api/java/util/… . Друга частина моєї відповіді - це лише "натяк", як боротися з проблемою капелюха.
Ральф

5

http://sourceforge.net/projects/eclipse-rbe/

як уже зазначені файли властивостей повинні бути закодовані в ISO 8859-1

Ви можете використовувати вищевказаний плагін для затемнення IDE, щоб зробити перетворення Unicode для вас.


3

Ось рішення Java 7, яке використовує чудову бібліотеку підтримки Guava та конструктивну систему ресурсів. Він читає і записує файли властивостей за допомогою UTF-8 для найпростішого загального досвіду.

Щоб прочитати файл властивостей як 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
  }
}

Щоб записати файл властивостей як 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
}

Ця відповідь корисна. Основною проблемою тут у різних відповідях є непорозуміння щодо даних та наборів символів. Java може читати будь-які дані (правильно), просто вказавши набір символів, у яких він зберігався, як показано вище. UTF-8 зазвичай використовується для підтримки більшості, якщо не кожної мови на планеті, а тому дуже застосовно до властивостей на основі ResourceBundle.
Даррелл Тіг

@DarrellTeague: Ну, "UTF-8 зазвичай використовується для підтримки ..." - швидше має бути " Unicode зазвичай використовується для підтримки ..." :), оскільки UTF-8 - це просто кодування символів Unicode ( en .wikipedia.org / wiki / UTF-8 ).
Honza Zidek

Насправді UTF-8 мав бути покликаний конкретно називатися "набором символів" (проти просто посилання на "будь-який набір символів UniCode"), оскільки UTF-8 у цьому контексті (дані) переважає використання в Інтернеті деякими заходами настільки ж високими. 67%. Посилання: stackoverflow.com/questions/8509339/…
Даррелл Тейг

3

Як було запропоновано, я пройшов реалізацію пакета ресурсів .. але це не допомогло .. так як пакет завжди викликався під en_US locale ... я намагався встановити мою локаль за замовчуванням на іншій мові, і все одно моя реалізація пакета ресурсів управління викликали за допомогою en_US ... Я намагався поставити повідомлення журналу і зробити крок через налагодження і побачити, чи робиться інший локальний дзвінок після того, як я міняю локаль під час виконання через виклики xhtml та JSF ... це не сталося ... тоді я намагався зробити системний набір за замовчуванням до utf8 для читання файлів на моєму сервері (сервер tomcat) .., але це спричинило заклик, оскільки всі мої бібліотеки класів не були складені під utf8, і tomcat почав читати потім у форматі utf8 і сервер не працює належним чином ... тоді я закінчив реалізацію методу в моєму контролері Java для виклику з файлів xhtml ..у цьому методі я зробив наступне:

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

Я був особливо нервовий, тому що це може сповільнити продуктивність моєї програми ... однак після впровадження цього виглядає так, ніби моя програма зараз швидша .. я думаю, що це тому, що я зараз безпосередньо отримую доступ до властивостей, а не дозволяю JSF проаналізує свій шлях до доступу до властивостей ... Я спеціально передаю булевий аргумент у цьому виклику, тому що я знаю, що деякі властивості не перекладаються і не мають бути у форматі utf8 ...

Тепер я зберегла свій файл властивостей у форматі UTF8, і він працює чудово, оскільки кожен користувач у моїй програмі має перевагу локального референту.


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

Оскільки це варто для мого питання, це те, що самі файли були в неправильному кодуванні. Використання iconv працювало на мене

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

+1 для згадки iconv. Я ніколи не чув про нього раніше, але я набрав його в консоль, і ось, ось це є річ, яка існує (у CentOS 6, все
одно

Тепер, коли я насправді спробував використовувати його, він не спрацював: він перекинувся на перший символ, який не вдалося перетворити на ISO-8559-1.
ArtOfWarfare

1

Я спробував використати підхід, запропонований Rod, але, враховуючи занепокоєння BalusC щодо того, щоб не повторювати однакову роботу у всіх програмах, і прийшов із цим класом:

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

Спосіб використання цього буде дуже подібний, ніж звичайне використання ResourceBundle:

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

Або ви можете використовувати альтернативний конструктор, який використовує UTF-8 за замовчуванням:

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

0

Відкрийте діалогове вікно "Налаштування / Налаштування" ( Ctrl+ Alt+ S), потім натисніть Редактор та Кодування файлів.

Знімок екрана

Потім, внизу, ви подасте кодування за замовчуванням для файлів властивостей. Виберіть тип кодування.

Крім того, ви можете використовувати символи unicode замість тексту у своєму пакеті ресурсів (наприклад, "ів"дорівнює \u0456\u0432)


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.