I18n questions · Localization files
On Java 9 and later, save the file as UTF-8 and nothing else is needed: PropertyResourceBundle reads .properties files as UTF-8 by default. On Java 8 and older the file is read as ISO-8859-1, so every character outside Latin-1 must be a \uXXXX escape, or you load the file yourself with a UTF-8 reader.
JEP 226 changed the default in JDK 9. The stream is decoded as UTF-8; if a MalformedInputException or UnmappableCharacterException occurs, the bundle resets and re-reads the whole stream as ISO-8859-1. That fallback keeps old escaped files working, and it is also why a file saved as Windows-1252 loads without an error but shows wrong characters: its bytes are not valid UTF-8, so the whole file is read as Latin-1. To make decoding strict, start the JVM with -Djava.util.PropertyResourceBundle.encoding=UTF-8 (or ISO-8859-1); the file is then read in that encoding only and an invalid sequence throws. Other values are ignored, and the property is read once when the class initializes, so set it on the command line. Details are in the PropertyResourceBundle Javadoc.
On Java 8 the PropertyResourceBundle(InputStream) constructor requires ISO-8859-1. The native2ascii tool turned UTF-8 files into \uXXXX escapes at build time; it was removed in JDK 9. The alternative that keeps the files readable is a ResourceBundle.Control that opens the resource with a UTF-8 reader and uses the PropertyResourceBundle(Reader) constructor, which has no encoding limitation:
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
public class Utf8Control extends ResourceBundle.Control {
@Override
public ResourceBundle newBundle(String baseName, Locale locale, String format,
ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
if (!"java.properties".equals(format)) {
return super.newBundle(baseName, locale, format, loader, reload);
}
String resourceName = toResourceName(toBundleName(baseName, locale), "properties");
try (InputStream stream = loader.getResourceAsStream(resourceName)) {
if (stream == null) {
return null;
}
return new PropertyResourceBundle(new InputStreamReader(stream, StandardCharsets.UTF_8));
}
}
}
ResourceBundle bundle = ResourceBundle.getBundle("messages", Locale.GERMAN, new Utf8Control());
Drop the control once you are on Java 9 or later: the default already does the right thing, and getBundle with a Control throws UnsupportedOperationException in named modules.
Related settings:
java.util.Properties.load(InputStream) still reads ISO-8859-1 on every Java version. If you read properties files directly, use load(Reader) with a UTF-8 InputStreamReader.ResourceBundleMessageSource and ReloadableResourceBundleMessageSource default to ISO-8859-1; call setDefaultEncoding("UTF-8"). Spring Boot's auto-configured MessageSource uses spring.messages.encoding, which defaults to UTF-8..properties files as UTF-8. In IntelliJ IDEA that is Settings | Editor | File Encodings | Default encoding for properties files, which defaults to ISO-8859-1.More on the format itself: Java properties file.
Published Oct 9, 2026 · Markdown version