Как использовать 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/…
Майди, программист, 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

Считывает список свойств (пары ключей и элементов) из входного байтового потока. Входной поток имеет простой линейно-ориентированный формат, как указано в load (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());

Смотрите также:

BalusC
источник
Спасибо. Кстати, кажется хорошей идеей переопределить getFormats для возврата FORMAT_PROPERTIES.
Флавио Этруско
Не могли бы вы уточнить это предложение переопределить 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");
прут
источник
37
Для всех наивных пользователей / комментаторов здесь: это не решение, а обходной путь. Истинная основная проблема все еще стоит и требует решения.
BalusC
2
Это исправило мою ситуацию. Решение было бы для Java начать обработку UTF-8 изначально в пакетах ресурсов и в файлах свойств. Пока это не произойдет, я буду использовать обходной путь.
JohnRDOrazio
@BalusC; в чем недостаток этого подхода? (кроме создания дополнительной строки?)
Пааске
8
@Paaske: это обходной путь, а не решение. Вам нужно будет повторно применить обходной путь для всех строковых переменных в базе кода. Это полная чушь. Просто исправьте это в одном месте, в нужном месте, чтобы строковые переменные сразу содержали правильное значение. Не должно быть абсолютно никакой необходимости модифицировать клиента.
BalusC
3
Да, если вам нужно изменить все приложение, конечно, это плохо. Но если вы уже используете ResourceBundle в качестве одиночного, вам нужно исправить это только один раз. У меня сложилось впечатление, что одноэлементный подход является наиболее распространенным способом использования ResourceBundle.
Пааске
51

посмотрите на это: 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);

Кстати: получить поток из файла .properties :

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

Кстати: получить пакет ресурсов от InputStreamReader:

ResourceBundle rb = new PropertyResourceBundle(isr);

надеюсь, это поможет вам!

Chinaxing
источник
3
Фактический вопрос здесь о ResourceBundle, хотя.
Nyerguds
1
Правда, это должен быть принятый ответ, если вы используете Propertiesи хотите получить UTF-8строку, тогда это работает как талисман. Однако для ResourceBundleтаких языковых ресурсов принятый ответ является элегантным. Тем не менее, до проголосовал за ответ.
Ильгит Йылдырым
ResourceBundle rb = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"))
Dedek
23

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

Поэтому я рекомендовал использовать общий метод: писать в юникоде символах . Для этого:

IDEA - имеет специальное « Прозрачное преобразование из нативного в ASCII » опцию (Настройки> Кодировка файла).

Eclipse - имеет плагин « Редактор свойств » . Может работать как отдельное приложение.

Kinjeiro
источник
4
В IntelliJ IDEA 14 это находится в Настройки -> Редактор -> Кодировки файлов. Мне также пришлось удалить все существующие файлы свойств и заново создать их, чтобы эта опция вступила в силу.
Cypher
IDE не имеют особого отношения к ответу, а просто инструменты, которые на самом деле не решают основную проблему отсутствия хранения содержимого в наборе символов UTF-8 .... что решит проблему сразу же без преобразования или хакерских атак, таких как написание свойств в символах Юникода внутри файла, определенного с другим набором символов.
Даррелл Тиг
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.

stenix
источник
19

Мы создаем файл resources.utf8, который содержит ресурсы в UTF-8, и имеем правило для запуска следующего:

native2ascii -encoding utf8 resources.utf8 resources.properties
andykellr
источник
Откуда мы получаем native2ascii? Я просто сделал find / -name native2ascii*и не получил результатов, поэтому я предполагаю, что это не просто часть JDK ...
ArtOfWarfare
Гектометр Это не часть IBM JDK, но, похоже, она включена в Oracle JDK, в 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);  
            }  
        }  
    }  
}  
marcolopes
источник
1
Мне нравится это решение, и я публикую
Sllouyssgort
Это работает очень хорошо. Просто добавил файл свойств китайского перевода в UTF8, и он загружается без проблем.
tresf
9

Внимание: файлы свойств java должны быть закодированы в ISO 8859-1!

Кодировка ISO 8859-1. Символы, которые не могут быть непосредственно представлены в этой кодировке, могут быть написаны с использованием экранирования Unicode; в escape-последовательности допускается только один символ 'u'.

@see Properties Java Doc

Если вы все еще действительно хотите сделать это: взгляните на: Свойства Java Кодировка UTF-8 в Eclipse - есть несколько примеров кода

Ральф
источник
1
Java! = Eclipse ... последняя является IDE. Дополнительные данные! = Java. Java поддерживает потоковую обработку с использованием огромного массива наборов символов, который для интернационализации (в конце концов, вопрос касается ResourceBundles) ... разрешает использовать UTF-8 в качестве наиболее простого ответа. Запись файлов свойств в наборе символов, не поддерживаемом целевым языком, излишне усложняет проблему.
Даррелл Тиг
@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

Вы можете использовать вышеуказанный плагин для Eclipse IDE, чтобы сделать преобразование Unicode для вас.

fmucar
источник
3

Вот решение Java 7, которое использует отличную библиотеку поддержки Guava и конструкцию try-with-resources. Он читает и записывает файлы свойств, используя 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 ).
Хонза Зидек
На самом деле UTF-8 должен был быть специально назван как «набор символов» (а не просто ссылаться на «любой набор символов UniCode»), поскольку UTF-8 в этом контексте (данные) преобладает в Интернете по некоторым показателям до 67%. Ссылка: stackoverflow.com/questions/8509339/…
Даррелл Тиг
3

Как было предложено, я прошел реализацию пакета ресурсов ... но это не помогло ... так как пакет всегда вызывался в en_US locale ... я пытался установить свой язык по умолчанию на другой язык, и все же моя реализация пакета ресурсов элемент управления вызывался с помощью en_US ... я попытался поместить сообщения журнала и выполнить шаг отладки и посмотреть, был ли сделан другой локальный вызов после того, как я изменил локаль во время выполнения через вызовы xhtml и JSF ... которые не произошли ... затем я попытался сделать системный набор по умолчанию utf8 для чтения файлов моим сервером (tomcat server) .. но это вызвало pronlem, так как все мои библиотеки классов не были скомпилированы под 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)
ArtOfWarfare,
Теперь, когда я действительно попытался использовать его, он не сработал: он вырвал первый символ, который не удалось преобразовать в ISO-8559-1.
ArtOfWarfare
1

Я попытался использовать подход, предложенный Родом, но с учетом озабоченности 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");
carlossierra
источник
0

Откройте диалоговое окно «Настройки / Настройки» ( Ctrl+ Alt+ S), затем нажмите «Редактор» и «Кодировки файлов».

Скриншот показанного окна

Затем в нижней части вы найдете кодировки по умолчанию для файлов свойств. Выберите тип кодировки.

В качестве альтернативы вы можете использовать символы юникода вместо текста в вашем наборе ресурсов (например, "ів"равно \u0456\u0432)

Юра Чорнота
источник