Where to place i18n key strings in Java

Viewed 5656

When doing internationalization in Java, you assign a string key to each message. What's the best practice, on where to place those string keys. Goal is to allow easy refactoring (eg. key name changes), clean and readable code, separation of concerns but still no duplication of keys/messages even if called from different parts of the code.

//bad way, strings directly in code
messages.getString("hello_key");

-

// better way, use String constants
public static final String HELLO_KEY = "hello_key";
...
messages.getString(HELLO_KEY);

-

// other (better?) way, put all keys in one huge central class
public class AllMessageKeys {
  public static final String HELLO_KEY = "hello_key";
  ...
}

public class Foo {
  ...
  messages.getString(AllMessageKeys.HELLO_KEY);
}

-

// other (better?) way, put all keys in neighbor class
public class FooMessageKeys {
  public static final String HELLO_KEY = "hello_key";
}

public class Foo {
  ...
  messages.getString(FooMessageKeys.HELLO_KEY);
}

Any other proposals? Which is best? I'm on Eclipse IDE, if that makes the refactoring part any clearer.

Clarification: in the above examples "messages" is of type ResourceBundle.

8 Answers

I've devised this method long time ago for the shortest amount of typing necessary to get localized version of given strings and to easily add new entries as well. I think it is useful for many type of Java projects as well.

  1. An enum class ME (Short for MessagesEnum) has all the definitions and its constructor converts LOREM_IPSUM to lorem.ipsum so that you don't have to type constructor parameter.

  2. An I18NManager class which is responsible for getting the Locale from the context and constructing/caching ResourceBundles, preferably a Spring @Component.

  3. An optional ServiceRegistry class for static access to I18NManager instance, from ME.get() method to complete the cycle.

The enum:

public enum ME {

    USERNAME,
    PASSWORD,
    LOGIN,
    LOGOUT,
    DASHBOARD,
    MENU,
    OK,
    CANCEL,
    YES,
    NO,
    CONFIRM,
    DELETE,
    CREATE,
    NEW,
    EDIT,
    ONLINE,
    OFFLINE,
    SALES_REPORTS,
    ...;

    private String key;

    ME(String key){
        this.key = key;
    }

    ME(){
        this.key = name().toLowerCase(Locale.ENGLISH).replace("_", ".");
    }

    public String get(){
        return ServiceRegistry.get().getI18nManager().getMessage(key);
    }

    public String get(Object ...args ){
        return ServiceRegistry.get().getI18nManager().getMessage(key, args);
    }

    public String key(){
        return key;
    }

}

I18NManager:

import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;

public interface I18nManager {

    Locale getCurrentUserLocale();

    ResourceBundle getResourceBundle(Locale locale);

    default String getMessage(String key) {
        return getResourceBundle(getCurrentUserLocale()).getString(key); //FIXME add error handling
    }

    default String getMessage(String key, Object... args) {
        return MessageFormat.format(getMessage(key), args);
    }
}

Usage is simply:

ME.SALES_REPORTS.get()
Related