消息包

本地化的最常见工作就是根据用户所在的语言环境提供本地化消息。因此,必须从代码中将消息抽取出来,并放置到特殊的属性文件内,一种语言建立一个文件。这样的一组文件被称为 消息包

除了消息之外,消息包还可以包含 本地化数据格式字符串

项目中默认的消息包由一组 messages_<language>.properties 文件组成,位于 src/main/resources 目录下的根包目录内。

消息包文件必须是 UTF-8 编码。

配置语言环境

当使用 Studio 创建新项目时,可以在项目创建向导中的 Locales 字段配置支持的语言环境。该字段支持选择语言编码并设置名称。

Studio 将语言编码列表写入 jmix.core.available-locales 应用程序属性,将语言名称用 localeDisplayName.<language> 键值写入对应的 messages_<language>.properties 文件中。之后可以通过 Studio Project Properties 窗口的 Locales 标签页修改,或者手动修改。

例如,如果为应用程序定义了两种语言:en - Englishde - Deutsch,项目中则会有如下文件结构(假设根包是 localization_demo.ex1):

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

文件有如下内容:

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

创建消息

分组和键值

通常,应用程序只包含单一消息包。即使一个简单的应用程序也可能包含大量的消息,因此我们推荐属性键值由两部分组成:分组和消息键值,用 / 分隔。这样可以对相关的消息进行分组避免命名冲突。下面例子中,localization_demo.ex1.screen.main 是分组,application.caption 是消息键值:

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

也可以定义无分组的消息,示例:

messageWithoutGroup = Message without a group

数据模型本地化

Jmix 引入了一些关于数据模型元素本地化的约定:实体、属性名称和枚举值。这些约定支持框架在 UI 组件中显示实体和枚举时方便查找本地化名称。

实体名称使用 <package>/<class> 格式本地化,属性名称使用 <package>/<class>.<attribute> 格式。示例:

# 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

枚举类使用 <package>/<class> 格式本地化,枚举值使用 <package>/<class>.<value> 格式。示例:

# 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
用 Studio 可视化设计器可以很容易创建数据模型元素的本地化名称。点击元素名称旁边的 🌐(地球)按钮,然后在 Localized Message 对话框中分别输入本地化值即可。

添加消息包

在带有很多消息的大应用程序中,你可以定义附加消息包以保持合理的属性文件大小:

  1. src/main/resources 下面的任意文件夹创建一组任意命名的属性文件。下面例子中,我们在主消息包的同级目录中创建了一组 additional_messages 文件:

     src/
        main/
            resources/
                localization_demo/
                    ex1/
                        additional_messages_en.properties
                        additional_messages_de.properties
                        messages_en.properties
                        messages_de.properties
  2. 在附加消息包中按照主消息包一样的格式写入本地化消息。

  3. 为应用程序类添加 @MessageSourceBasenames 注解,并设置附加包的路径和名称:

    @SpringBootApplication
    @MessageSourceBasenames({"localization_demo/ex1/additional_messages"})
    public class LocalizationExampleApplication {
所有消息包中的键值加载至同一个列表中,因此需要保证所有包中键值的唯一性。

使用消息

在 Java 中使用

Messages 接口

使用 Messages bean 从消息包获取本地化消息。最常见用法是调用 getMessage() 方法,为其提供消息分组和消息键值。方法返回当前用户语言环境的消息;如果无此消息,则返回传入的键值本身。

下面示例中,我们在消息包中定义了一条消息,并使用 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)

三个方法返回同一个值:Something has happened

1 指定完整的属性键值。
2 分组和键值分别指定。
3 如果第一个参数是 Class,方法使用类所在的包名作为分组。

getMessage() 方法还有一个接收 Enum 参数的重载,用于获取枚举值的本地化消息,示例:

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

MessageBundle 接口

MessageBundle 接口可以在界面控制器中使用。该接口提供获取与界面控制器关联的单一消息组内本地化消息的方法。功能与 Messages bean 是有差别的,MessageBundle 直接获取与界面控制器相关的消息组,因此无需传递组键值或类名。

如需使用 MessageBundle,将其注入控制器:

@Autowired
private MessageBundle messageBundle;

然后可以通过键值获取消息:

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

消息的组名从界面控制器包名推断,与使用 Messages.getMessage(getClass(), "someMessage") 类似。

可以使用下面描述的 messagesGroup XML 属性定义任意消息组,也可以使用 setMessageGroup() 方法在控制器中设置:

messageBundle.setMessageGroup("some.group");

MessageBundleformatMessage() 方法通过键键值检索本地化消息,然后使用消息来格式化输入参数。格式是根据 String.format() 方法规则定义的。例如:

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

在 XML 描述中使用

Jmix UI 界面描述和菜单配置文件都能识别 msg:// 前缀并从消息包加载消息。

我们通过示例看看使用 msg:// 的不同情况。

  1. 消息分组通过界面的包名推断。

    例如,如果定义了 localization_demo.ex1.screen.user 分组的消息:

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

    localization_demo.ex1.screen.user 包内的界面描述文件中可以直接指定消息键值获取消息,而无需指定分组:

    localization_demo/ex1/screen/user/user-browse.xml
    <label value="msg://someMessage"/>
  2. 获取任意分组内的消息。

    通过在 msg:// 后指定分组和键值,可以从其他界面获取上面示例中的消息,示例:

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

    界面级别还可以使用 messagesGroup 属性设置任意消息包,这样的话,所有消息都默认从设置的包中获取:

    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. 获取无分组的消息。

    如需获取无分组的消息,可以在前缀中使用三个斜杠 msg:///,然后直接跟消息键值。

    例如,定义消息:

    messageWithoutGroup = Message without a group

    界面控制器中可这样获取:

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