ResourceBundle을 사용하여 자원 등록 정보에서 UTF-8을 사용하는 방법


259

Java를 사용하여 리소스 속성에 UTF-8을 사용해야 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 프로그래머 비보이

6
JDK9 볼, 기본적으로 UTF-8을 지원해야 JEP 226
파올로 Fulgoni

답변:


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 문자입니다. Latin1이 아닌 문자 및 특정 특수 문자는 Java ™ 언어 스펙의 3.3 절에 정의 된대로 유니 코드 이스케이프를 사용하여 키 및 요소로 표시됩니다.

따라서 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

Eclipse와 같은 정상 IDE를 사용하는 경우 .propertiesJava 기반 프로젝트에서 파일 을 작성하고 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는 FORMAT_PROPERTIES를 반환하기 위해 getFormats를 재정의하는 것이 좋습니다.
Flávio Etrusco

getFormats ()를 재정의하기 위해이 제안을 자세히 설명해 주시겠습니까?
Mark Roper

1
@ imgx64 : 알려 주셔서 감사합니다. 답변이 수정되었습니다.
BalusC

10
StandardCharsets.UTF_8Java 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

@ 발 루스 C; 이 방법의 단점은 무엇입니까? (추가 문자열을 만드는 것 이외)
Paaske

8
@Paaske : 해결책이 아니라 해결책입니다. 코드베이스 전체의 모든 문자열 변수에서 모든 위치에 대안을 다시 적용해야합니다. 이것은 순수한 말도 안됩니다. 문자열 변수가 즉시 올바른 값을 포함하도록 올바른 위치에서 한 곳에 수정하십시오. 클라이언트를 수정하지 않아도됩니다.
BalusC

3
예, 전체 응용 프로그램을 수정해야하는 경우에는 물론 좋지 않습니다. 그러나 이미 ResourceBundle을 싱글 톤으로 사용하고 있다면 한 번만 수정하면됩니다. Singleton 방식이 ResourceBundle을 사용하는 가장 일반적인 방법이라는 인상을 받았습니다.
Paaske

50

이것을보십시오 : http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)

이 속성은 Reader 개체를 인수로 허용 하며 InputStream에서 만들 수 있습니다.

작성시 리더의 인코딩을 지정할 수 있습니다.

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

그런 다음이 리더를로드 방법에 적용하십시오.

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"))
dedek

22

ResourceBundle.Control 예를 들어 속성 ​​파일이 cp1251 문자 세트를 사용하는 경우 UTF-8 및 새로운 문자열 메소드가 작동하지 않습니다.

그래서 나는 일반적인 방법 으로 유니 코드 기호로 쓰는 것이 좋습니다. 이를 위해 :

IDEA는 - 특별한있다 " 투명 기본 - 투 - ASCII 변환 " 옵션 (설정> 파일 인코딩).

Eclipse – 플러그인 " 속성 편집기 "가 있습니다. 별도의 응용 프로그램으로 작동 할 수 있습니다.


3
IntelliJ IDEA 14의 경우 설정-> 편집기-> 파일 인코딩에 있습니다. 또한 기존 속성 파일을 삭제하고이 옵션을 적용하기 위해 다시 만들어야했습니다.
Cypher

IDE는 대답과 특별히 관련이 없지만 UTF-8 문자 세트로 내용을 저장하지 않는 근본적인 문제를 실제로 해결하지 않는 도구입니다. 다른 문자 집합으로 정의 된 파일 내부의 유니 코드 기호
Darrell Teague

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

자원을 UTF-8로 포함하는 resources.utf8 파일을 작성하고 다음을 실행하는 규칙이 있습니다.

native2ascii -encoding utf8 resources.utf8 resources.properties

우리는 어디 native2ascii에서 얻 습니까? 방금 find / -name native2ascii*결과를
얻지 못해서

흠. IBM JDK의 일부는 아니지만 Oracle JDK에 포함 된 것 같습니다 jdk1.*.0_*/bin.
ArtOfWarfare

최소한 JDK 6에서는 IBM JDK의 일부인 것으로 보입니다.
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);  
            }  
        }  
    }  
}  

1
나는이 솔루션을 추천하고 나는 요점처럼 게시 gist.github.com/enginer/3168dd4a374994718f0e
Sllouyssgort

이것은 매우 잘 작동합니다. UTF8로 중국어 번역 속성 파일을 추가하면 아무 문제없이로드됩니다.
tresf February

9

주의 : java 특성 파일은 ISO 8859-1로 인코딩해야합니다!

ISO 8859-1 문자 인코딩 이 인코딩에서 직접 표현할 수없는 문자는 유니 코드 이스케이프를 사용하여 작성할 수 있습니다. 이스케이프 시퀀스에는 단일 'u'문자 만 허용됩니다.

@ 참조 속성 Java Doc

여전히 정말로하고 싶다면 : Eclipse의 Java 속성 UTF-8 인코딩을 살펴보십시오. 일부 코드 샘플이 있습니다.


1
Java! = Eclipse ... 후자는 IDE입니다. 추가 데이터! = Java. Java는 국제화를위한 광범위한 문자 집합을 사용하여 스트림 처리를 지원합니다 (물음은 ResourceBundles에 대한 질문입니다) ... UTF-8을 가장 간단한 대답으로 사용합니다. 대상 언어에서 지원하지 않는 문자 집합으로 속성 파일을 쓰면 문제가 복잡해집니다.
Darrell Teague

@Darell Teague : ResouceBundle에로드 된 속성 파일이 ISO 8859-1 인 "힌트"는 Java 문입니다 : docs.oracle.com/javase/8/docs/api/java/util/… .. 내 대답의 두 번째 부분은 모자 문제를 다루는 방법에 대한 "힌트"입니다.
Ralph


3

다음은 Guava의 우수한 지원 라이브러리 및 리소스와 함께 사용하는 구성을 사용하는 Java 7 솔루션입니다. 가장 간단한 전반적인 경험을 위해 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 기반 속성에 매우 많이 적용됩니다.
Darrell Teague

@DarrellTeague : 음은, "UTF-8은 일반적으로 ... 지원하는 데 사용됩니다"- 오히려이 있어야한다 " 유니 코드가 일반적으로 지원하는 데 사용됩니다 ...": UTF-8 유니 코드 (단지 문자 인코딩 같이 엉 .wikipedia.org / wiki / UTF-8 ).
Honza Zidek

실제로 UTF-8은이 컨텍스트 (데이터)에서 UTF-8이 인터넷에서 사용률이 우세한 것처럼 "문자 세트"( '모든 유니 코드 문자 세트'를 참조하는 것과는 달리)라고 구체적으로 부릅니다. 67 %. 참조 : stackoverflow.com/questions/8509339/…
Darrell Teague 2012 년

3

제안한 것처럼 리소스 번들의 구현을 겪었지만 도움이되지 않았습니다. 번들이 항상 en_US 로캘에서 호출되었으므로 기본 로캘을 다른 언어로 설정하고 여전히 리소스 번들의 구현을 시도했습니다. 제어가 en_US로 호출되었습니다 ... 로그 메시지를 넣고 디버그를 통해 단계를 시도했으며 xhtml 및 JSF 호출을 통해 런타임에 로케일을 변경 한 후 다른 로컬 호출이 수행되었는지 확인했습니다 ... ... 그런 다음 서버 (tomcat 서버)가 파일을 읽을 수 있도록 시스템 설정 기본값을 utf8로 설정하려고 시도했지만 모든 클래스 라이브러리가 utf8에서 컴파일되지 않고 Tomcat이 utf8 형식으로 읽히기 시작했을 때 문제가 발생했습니다. 서버가 제대로 실행되지 않았습니다 ... 그런 다음 xhtml 파일에서 호출 할 Java 컨트롤러의 메소드를 구현했습니다.그 방법으로 다음을 수행했습니다.

        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

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)를 연 다음 편집기 및 파일 인코딩을 클릭하십시오.

표시된 창의 스크린 샷

그런 다음 하단에 속성 파일의 기본 인코딩을 핑합니다. 인코딩 유형을 선택하십시오.

또는 자원 번들에서 텍스트 대신 유니 코드 기호를 사용할 수 있습니다 (예 : "ів"equals \u0456\u0432)


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.