Как использовать UTF-8 в свойствах ресурса с ResourceBundle

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

Мое приложение работает на Google App Engine.

Кто-нибудь может дать мне пример? Я не могу получить эту работу.

17 ответов

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());

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

Учитывая, что у вас есть экземпляр ResourceBundle, и вы можете получить String:

String val = bundle.getString(key); 

Я решил проблему с отображением на японском языке:

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

Посмотрите на это: http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html

свойства принимают объект Reader в качестве аргументов, который вы можете создать из InputStream.

во время создания вы можете указать кодировку Reader:

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

затем примените этот Reader к методу загрузки:

prop.load(isr);

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

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

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

Эта проблема была наконец исправлена ​​в Java 9: https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9.htm

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

На большинство существующих файлов свойств не следует воздействовать: UTF-8 и ISO-8859-1 имеют одинаковую кодировку для символов ASCII, а читаемая человеком кодировка не-ASCII ISO-8859-1 недопустима в UTF-8. Если обнаружена неверная последовательность байтов UTF-8, среда выполнения Java автоматически перечитывает файл в ISO-8859-1.

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

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

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

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

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

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

native2ascii -encoding utf8 resources.utf8 resources.properties

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

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

@see Properties Java Doc

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

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

как уже указано, файлы свойств должны быть закодированы в ISO 8859-1

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

Вот решение 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
}

Как было предложено, я прошел реализацию пакета ресурсов... но это не помогло... так как пакет всегда вызывался в 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, и он работает нормально, поскольку у каждого пользователя в моем приложении есть предпочтительный языковой стандарт.

Моя проблема заключалась в том, что сами файлы были в неправильной кодировке. Использование iconv сработало для меня

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

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

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

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

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

Я попытался использовать подход, предложенный Родом, но принимая во внимание беспокойство 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");
Properties prop = new Properties();
String fileName = "./src/test/resources/predefined.properties";
FileInputStream inputStream = new FileInputStream(fileName);
InputStreamReader reader = new InputStreamReader(inputStream,"UTF-8");

Начиная с Java 9, значение по умолчанию для загрузки файла свойств было изменено на UTF-8. https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9.htm

Что касается текущих (2021-2) версий Java, все еще существует старая функция ISO-8859-1 utils.Properties# load .

  • Если вы используете Properties.load, вы должны использовать ISO-8859-1.
  • Если вы используете ResourceBundle, то UTF-8 подойдет.

Разрешите процитировать официальный документ.

PropertyResourceBundle

PropertyResourceBundle может быть создан либо из InputStream, либо из Reader, который представляет файл свойств. Для создания экземпляра PropertyResourceBundle из InputStream требуется, чтобы входной поток был закодирован в UTF-8. По умолчанию, если при чтении входного потока возникает MalformedInputException или UnmappableCharacterException, то экземпляр PropertyResourceBundle сбрасывается в состояние до исключения, повторно считывает входной поток в ISO-8859-1 и продолжает чтение. Если для системного свойства java.util.PropertyResourceBundle.encoding установлено значение «ISO-8859-1» или «UTF-8», входной поток считывается только в этой кодировке и выдает исключение, если обнаруживает недопустимую последовательность.. Если указан "ISO-8859-1", символы, которые не могут быть представлены в кодировке ISO-8859-1, должны быть представлены с помощью экранирования Unicode, как определено в разделе 3.3 Спецификации языка Java™, тогда как другой конструктор, который принимает Reader, не есть это ограничение. Другие значения кодировки игнорируются для этого системного свойства. Системное свойство читается и оценивается при инициализации этого класса. Изменение или удаление свойства не имеет никакого эффекта после инициализации.

https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/PropertyResourceBundle.html

Свойства # load

Считывает список свойств (пары ключей и элементов) из входного потока байтов. Входной поток имеет простой строчно-ориентированный формат, как указано в load (Reader), и предполагается, что он использует кодировку символов ISO8859-1; то есть каждый байт - это один символ Latin1. Символы не в Latin1 и некоторые специальные символы представлены в ключах и элементах с помощью экранирования Unicode, как определено в разделе 3.3 Спецификации языка Java™.

https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/Properties.html#load(java.io.InputStream)

Другие вопросы по тегам