Programming for fun and profit

Programming tutorials, problems, solutions. Always with code.

Java programmatic ResourceBundle – dynamic translations

Java programmatic ResourceBundle – dynamic translations

In this tutorial we’re going to implement own ResourceBundle to show how to provide locale-sensitive values dynamically.

Own ResourceBundle implementation

ResourceBundle can be extended in a couple of ways. Here we extend it directly and supply Spanish language translations for a Map:


import java.util.*;

public class Words_es extends ResourceBundle {

    private static final Map<String, String> WORDS = new HashMap<>();
    static {
        // Load from some data source
        WORDS.put("book", "el libro");
        WORDS.put("date-format", "yyyy.MM.dd");

    protected Object handleGetObject(String key) {
        // Return null for unknown key to allow
        // check other Resource Bundles
        return WORDS.get(key);

    protected Set<String> handleKeySet() {
        // Faster implementation than default one
        return WORDS.keySet();

    public Enumeration<String> getKeys() {
        return Collections.enumeration(WORDS.keySet());

Return null from handleGetObject(String key) if your ResourceBundle doesn’t know that key. This will make further searching of the key in more general bundles – like shown in Intro to ResourceBundles.

Overriding Set<String> handleKeySet() is optional and done only for performance reasons, because here we’ve got complete set of keys.

Default properties file, located also in path:


Precedence of ResourceBundles – class vs properties file

If there are two resources with the same name of which one is a class and the other is properties file, for instance Words_es.class and, then the class has higher precedence and properties file will not be loaded!

Using custom ResourceBundle

The above ResourceBundle is located in package, so it has to be loaded from that path:

public static void main(String[] args) {
    displayWord("book", Locale.getDefault());
    displayWord("book", new Locale("es", "ES"));

    displayDate(new Locale("es", "ES"));

private static final String BUNDLE_NAME
        = "";

private static void displayDate(Locale locale) {
    ResourceBundle bundle = getBundle(BUNDLE_NAME, locale);
    String localeDate = formatDate(bundle.getString("date-format"));
    System.out.printf("19th May 2015 in locale '%s': %s%n",
            locale, localeDate);

private static String formatDate(String dateFormat) {
    Date date = Date.from(Instant.parse("2015-05-19T00:00:00.00Z"));
    return new SimpleDateFormat(dateFormat).format(date);

private static void displayWord(String word, Locale locale) {
    ResourceBundle words = getBundle(BUNDLE_NAME, locale);
    System.out.printf("'%s' in locale '%s': %s%n",
            word, locale, words.getString(word));

We get locale version of “date format” string and format the date accordingly. Notice that we use Instant from Java 8 to parse date string and then the new Date.from(Instant) to create old-style java.util.Date from it.

Now, when we run the above program it will print:

'book' in locale 'en': book
'book' in locale 'es_ES': el libro
19th May 2015 in locale 'en_CA': 05/19/2015
19th May 2015 in locale 'es_ES': 2015.05.19
Share with the World!