消息包

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

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

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

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

配置语言环境

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

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

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

src/main/resources/
    com/company/demo/
        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

创建消息

分组和键值

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

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

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

messageWithoutGroup = Message without a group

数据模型本地化

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

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

# 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

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

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

添加消息包

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

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

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

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

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

使用消息

在 Java 中使用

Messages 接口

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

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

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

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

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

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

MessageBundle 接口

MessageBundle 接口可以在 UI 视图控制器中使用。该接口提供获取与视图关联的单一消息组内本地化消息的方法。功能与 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
com.company.demo.view.user/userInfo=User name: %s
String formattedMessage = messageBundle.formatMessage("userInfo", user.getUsername());

在 XML 描述中使用

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

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

  1. 消息分组通过视图的包名推断。

    例如,如果定义了 com.company.demo.view.user 分组的消息:

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

    com.company.demo.view.user 包内的视图描述文件中可以直接指定消息键值获取消息,而无需指定分组:

    com/company/demo/view/user/user-list-view.xml
    <span text="msg://someMessage"/>
  2. 获取任意分组内的消息。

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

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

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

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

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

    例如,定义消息:

    messageWithoutGroup = Message without a group

    视图控制器中可这样获取:

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