Message Bundles

The most common task of localization is to provide messages depending on the current user locale. To achieve this, messages must be extracted from code and placed in special property files, one file per language. A collection of such files is called a message bundle.

Apart from messages, the message bundle can also contain localized data format strings.

The default message bundle of your project is a set of messages_<language>.properties files located in the base package under the src/main/resources folder.

Message bundle files must have the UTF-8 encoding.

Setting Up Locales

When you create a new project using Studio, you can set up the list of supported locales in the Locales field of the project wizard. This field allows you to select a language code and set its name.

Studio writes the list of language codes to the jmix.core.available-locales application property and language names to the corresponding messages_<language>.properties files with localeDisplayName.<language> key. You can later edit these properties manually or use the Locales tab of the Project Properties window in Studio.

For example, if you have defined two languages for your application: en - English and de - Deutsch, you have the following file structure in the project (provided that the base package is localization_demo.ex1):

 src/
    main/
        resources/
            localization_demo/
                ex1/
                    messages_en.properties
                    messages_de.properties
            application.properties

And the files have the following contents:

messages_en.properties
localeDisplayName.en = English
messages_de.properties
localeDisplayName.de = Deutsch
application.properties
jmix.core.available-locales = en,de

Creating Messages

Group and Key

Usually, an application contains a single message bundle. Because the number of messages even in a simple application can be quite large, we recommend making property keys of two parts: the group and the message key, separated by the forward slash (/). It allows you to group related messages and avoid naming conflicts. In the following example, localization_demo.ex1.screen.main is the group and application.caption is the message key:

localization_demo.ex1.screen.main/application.caption = Sample application

But you can also define a message without a group, for example:

messageWithoutGroup = Message without a group

Localizing Data Model

Jmix introduces some conventions on localization of the data model elements: entity and attribute names, enumeration values. It allows the framework to find the localized names when displaying entities and enumerations in UI components.

Entity name is localized using the <package>/<class> format, attribute name - using the <package>/<class>.<attribute> format. For example:

# entity name
localization_demo.ex1.entity/Customer = Customer
# attribute names
localization_demo.ex1.entity/Customer.id = Id
localization_demo.ex1.entity/Customer.version = Version
localization_demo.ex1.entity/Customer.name = Name

Enumeration name is localized using the <package>/<class> format, enumeration value - using the <package>/<class>.<value> format. For example:

# enumeration name
localization_demo.ex1.entity/Status = Status
# enumeration values
localization_demo.ex1.entity/Status.ACTIVE = Active
localization_demo.ex1.entity/Status.SUSPENDED = Suspended
localization_demo.ex1.entity/Status.INACTIVE = Inactive
You can easily create localized names of the data model elements in Studio visual designers. Click the 🌐 (globe) button next to the element name and enter localized values for available locales in the Localized Message dialog.

Additional Bundles

In a big application with a lot of messages you can maintain a reasonable size of the property files by defining additional message bundles:

  1. Create a set of property files with arbitrary name in any directory under src/main/resources. In the example below, we created additional_messages files in the same base package as the main message bundle:

     src/
        main/
            resources/
                localization_demo/
                    ex1/
                        additional_messages_en.properties
                        additional_messages_de.properties
                        messages_en.properties
                        messages_de.properties
  2. Write localized messages in the additional files in the same way as in the main bundle.

  3. Add the @MessageSourceBasenames annotation to the application class and set the path and name of the additional bundle in it:

    @SpringBootApplication
    @MessageSourceBasenames({"localization_demo/ex1/additional_messages"})
    public class LocalizationExampleApplication {
Messages from all bundles of the application are loaded into a single list, so property keys must be unique among all your bundles.

Using Messages

In Java Code

Messages Interface

To get localized messages from the message bundle, use the Messages bean. The most common use case is to invoke its getMessage() method and provide the message group and key. The method will return a message for the current user locale, or the given message key if the message is not found.

In the following example, we define a message in a message bundle and get it using different methods of the Messages bean:

messages_en.properties
localization_demo.ex1/someNotification = Something has happened
String message1 = messages.getMessage("localization_demo.ex1/someNotification"); (1)

String message2 = messages.getMessage("localization_demo.ex1", "someNotification"); (2)

String message3 = messages.getMessage(getClass(), "someNotification"); (3)

All three methods return the same value: Something has happened.

1 The whole property key is specified.
2 The group and the message key are specified separately.
3 If the first argument is Class, the method uses the package of the class as the group.

There is also an overloaded getMessage() method accepting Enum. Use it to retrieve a localized enum value, for example:

String message = messages.getMessage(Status.ACTIVE);

MessageBundle Interface

The MessageBundle interface is available in screen controllers. It provides methods to get localized messages of a single group associated with the screen controller. It differs from the Messages bean in that it can obtain the message group implicitly from the screen controller, so there is no need to pass the group key or the class name.

To use MessageBundle, inject it in the controller:

@Autowired
private MessageBundle messageBundle;

Now you can use it to get the message by the key:

String someMessage = messageBundle.getMessage("someMessage");

The group of the message is inferred from the screen controller package, as if using Messages.getMessage(getClass(), "someMessage").

An arbitrary group can be either defined using the messagesGroup XML attribute described below, or you can set it in the controller using the setMessageGroup() method:

messageBundle.setMessageGroup("some.group");

The formatMessage() method of MessageBundle retrieves a localized message by the key and then uses it to format the input parameters. The format is defined according to String.format() method rules. For example:

messages_en.properties
localization_demo.ex1.screen.user/userInfo=User name: %s
String formattedMessage = messageBundle.formatMessage("userInfo", user.getUsername());

In Screen XML Descriptors

Jmix UI screen descriptors and menu configuration files recognize the msg:// prefix in messages and load such messages from the message bundle.

Let’s consider different options of using msg:// with examples.

  1. The group of the message is inferred from the screen’s package.

    For example, if you have defined a message with localization_demo.ex1.screen.user group:

    messages_en.properties
    localization_demo.ex1.screen.user/someMessage = Some message

    you can retrieve it in a screen descriptor located in the localization_demo.ex1.screen.user package simply by specifying the message key without the group:

    localization_demo/ex1/screen/user/user-browse.xml
    <label value="msg://someMessage"/>
  2. Getting a message with an arbitrary group.

    You can retrieve the message from the previous example in any screen by specifying both the group and the key after the msg:// prefix, for example:

    localization_demo/ex1/screen/main/main-screen.xml
    <label value="msg://localization_demo.ex1.screen.user/someMessage"/>

    You can also set an arbitrary message bundle at the screen level using the messagesGroup attribute, so that all messages will be retrieved from it by default:

    localization_demo/ex1/screen/user/user-browse.xml
    <window xmlns="http://jmix.io/schema/ui/window"
            caption="msg://UserBrowse.caption"
            messagesGroup="additional_messages"
            focusComponent="usersTable">
  3. Getting a message without a group.

    To retrieve a message without a group, use triple slash in the prefix msg:///, followed by the message key.

    For example, if you have defined a message like this:

    messageWithoutGroup = Message without a group

    you can get it in any screen descriptor as follows:

    <label value="msg:///messageWithoutGroup"/>