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 languages and their codes.

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: English (en) and Deutsch (de), you have the following file structure in the project (provided that the base package is com.company.demo):

src/main/resources/
    com/company/demo/
        messages_en.properties
        messages_de.properties
    application.properties

And the files have the following contents:

messages_en.properties
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, com.company.demo.view.main is the group and applicationTitle.text is the message key:

com.company.demo.view.main/applicationTitle.text=Demo App

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
com.company.demo.entity/User=User
# attribute names
com.company.demo.entity/User.id=ID
com.company.demo.entity/User.username=Username
com.company.demo.entity/User.firstName=First name
com.company.demo.entity/User.lastName=Last name
com.company.demo.entity/User.password=Password
com.company.demo.entity/User.email=Email

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

# enumeration name
com.company.demo.entity/Status=Status
# enumeration values
com.company.demo.entity/Status.ACTIVE=Active
com.company.demo.entity/Status.INACTIVE=Inactive
com.company.demo.entity/Status.SUSPENDED=Suspended
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/com/company/demo/
        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({"com/company/demo/additional_messages"})
    public class DemoApplication implements AppShellConfigurator {
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
com.company.demo/someNotification = Something has happened
String message1 = messages.getMessage("com.company.demo/someNotification"); (1)

String message2 = messages.getMessage("com.company.demo", "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 UI view controllers. It provides methods to get localized messages of a single group associated with the view. It differs from the Messages bean in that it can obtain the message group implicitly from the view class, so there is no need to pass the group key or the class name.

To use MessageBundle, inject it in the view:

@ViewComponent
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 view class 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
com.company.demo.view.user/userInfo=User name: %s
String formattedMessage = messageBundle.formatMessage("userInfo", user.getUsername());

In View XML Descriptors

Jmix UI view 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 view’s package.

    For example, if you have defined a message with com.company.demo.view.user group:

    messages_en.properties
    com.company.demo.view.user/someMessage = Some message

    you can retrieve it in a view descriptor located in the com.company.demo.view.user package simply by specifying the message key without the group:

    com/company/demo/view/user/user-list-view.xml
    <span text="msg://someMessage"/>
  2. Getting a message with an arbitrary group.

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

    com/company/demo/view/main/main-view.xml
    <span text="msg://com.company.demo.view.user/someMessage"/>

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

    com/company/demo/view/sample/sample-view.xml
    <view xmlns="http://jmix.io/schema/flowui/view"
          messagesGroup="some.common.messages"
          title="msg://sampleView.title">
  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 view descriptor as follows:

    <span text="msg:///messageWithoutGroup"/>