自定义 Spring 引导程序:您如何向 MessageSource 提供国际化消息?
Custom Spring Boot starter: how do you contribute i18n messages to the MessageSource?
我正在编写一个自定义 Spring 引导启动器,其他开发人员会将其放入他们的应用程序中,并且该启动器包含开箱即用的控制器和 UI 屏幕。
这些 UI 屏幕是国际化的,i18n keys/values 在包文件中:com/foo/wherever/i18n.properties
.
我想确保当我的启动器在启动时加载时,这些 i18n.properties 在应用程序的 MessageSource
中自动可用,以便我的 UI 页面工作(通过正常呈现Spring Controller + ViewResolver + View implementations) 无需应用开发者自己指定此文件.
换句话说,他们应该能够将我的启动器添加到他们的运行时类路径和所有内容 'just works' 而无需配置任何东西。
现在,我发现应用程序开发人员可以创建自己的 src/main/resources/messages.properties
文件 和 手动配置 application.properties
中的附加消息文件:
spring.messages.basename = messages, com.foo.wherever.i18n
这会起作用。
但是,这需要满足以下两个条件:
- 他们必须手动配置
spring.messages.basename
属性 - 它不是自动的。和
- 他们的应用程序类路径中必须有自己的
messages.properties
文件。如果 messages.properties
文件不存在,spring.messages.basename
甚至不起作用。即使他们不关心 i18n,这仍然是必需的 - 不可取。
我想我 可以 将我的 i18n.properties 文件移动到 classpath:/messages.properties starter .jar 中的文件,但这似乎不是一个很好的解决方案:如果应用程序开发者有自己的 messages.properties 文件,则只会读取其中一个文件,从而导致消息值丢失。
似乎 Spring Boot MessageSourceAutoConfiguration 应该有一个 CompositeMessageSource
的概念,它迭代一个或多个可用的 MessageSource
实例(和 Order
ed) 在 Spring ApplicationContext 和 that 被 DispatcherServlet 使用。这将允许任何初学者只需在其自动配置
中声明 MessageSource
即可为可用消息做出贡献
可以按照我的要求做吗?对于应用程序开发人员来说,最 'hands off' 的解决方案是什么?
也许这是不可能的,但你可以尝试使用 BeanFactoryPostProcessor。
思路如下:
从应用程序上下文中取出 "messageSource" bean。请注意,它可能但不一定是 spring 引导程序,例如开发人员希望使用自己的实现并且不使用 spring 引导自动配置。
将其替换为您自己的尝试解析 "your keys" 的实现,其余委托给原始消息源。反之亦然,如果您希望开发人员可以覆盖您的翻译(如果原始消息源不为未知键抛出异常,则可能会出现问题)。
但可能有更好的方法。
我是这样设置的。我目前仅支持 en_US,但它已设置为使用国际化 (i18n) 处理任意数量的语言。
在此处查看代码
在此处查看代码要点:Code on github gist
添加消息源和默认语言环境 Bean
将这些 bean 添加到您的 Application.java 以设置您的默认语言环境并配置消息道具的位置
创建消息服务
服务将从会话中获取默认语言环境,然后从您的道具中获取消息文本
在控制器中使用消息服务
注入消息svc然后传入id从props文件中获取值
在语言环境中添加 message.properties 文件
转到/资源:
- 创建语言环境文件夹
- 创建一个名为 messages_en_US.properties
的文件
阅读更多
您可以在此处查看有关此主题的更完整的文章:
Spring Boot Internationalization i18n using Message Properties
看代码
在此处查看代码要点:
Code on github gist
我意识到这是一个老问题,但我前几天遇到了同样的问题,并写了一篇博客 post 关于我决定如何解决它。我想我应该在这里分享它,因为我从这个帖子中获得了一些解决方案的灵感。
简而言之,它采用了 sodik 的想法来拦截 MessageSource
bean 的创建,但我没有使用 BeanFactoryPostProcessor
,而是使用 BeanPostProcessor
,而不是替换应用程序上下文中的原始 MessageSource
,我只是添加自己的作为其父级:
@Bean
BeanPostProcessor messageSourceCustomExtender() {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof HierarchicalMessageSource && beanName.equals("messageSource")) {
ResourceBundleMessageSource parent = new ResourceBundleMessageSource();
parent.setBasename("custom");
((HierarchicalMessageSource) bean).setParentMessageSource(parent);
}
return bean;
}
};
}
您可以阅读完整的博客 post,其中我解释了有关我的解决方案的一些注意事项:http://www.thomaskasene.com/2016/08/20/custom-spring-boot-starter-messagesource/
更新
经过一番修改后,我意识到使用 BeanFactoryPostProcessor
是错误的,因为它会导致过早创建原始 MessageSource
bean 并忽略应用程序属性(最重要的是,spring.messages.basename
) .这意味着应用程序将无法配置这些属性。请参阅下面 BeanFactoryPostProcessor 文档的摘录。
A BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances. Doing so may cause premature bean instantiation, violating the container and causing unintended side-effects. If bean instance interaction is required, consider implementing BeanPostProcessor instead.
我已将上面的示例更新为使用 BeanPostProcessor
,这会更改 bean 实例而不是 bean 定义。
我正在编写一个自定义 Spring 引导启动器,其他开发人员会将其放入他们的应用程序中,并且该启动器包含开箱即用的控制器和 UI 屏幕。
这些 UI 屏幕是国际化的,i18n keys/values 在包文件中:com/foo/wherever/i18n.properties
.
我想确保当我的启动器在启动时加载时,这些 i18n.properties 在应用程序的 MessageSource
中自动可用,以便我的 UI 页面工作(通过正常呈现Spring Controller + ViewResolver + View implementations) 无需应用开发者自己指定此文件.
换句话说,他们应该能够将我的启动器添加到他们的运行时类路径和所有内容 'just works' 而无需配置任何东西。
现在,我发现应用程序开发人员可以创建自己的 src/main/resources/messages.properties
文件 和 手动配置 application.properties
中的附加消息文件:
spring.messages.basename = messages, com.foo.wherever.i18n
这会起作用。
但是,这需要满足以下两个条件:
- 他们必须手动配置
spring.messages.basename
属性 - 它不是自动的。和 - 他们的应用程序类路径中必须有自己的
messages.properties
文件。如果messages.properties
文件不存在,spring.messages.basename
甚至不起作用。即使他们不关心 i18n,这仍然是必需的 - 不可取。
我想我 可以 将我的 i18n.properties 文件移动到 classpath:/messages.properties starter .jar 中的文件,但这似乎不是一个很好的解决方案:如果应用程序开发者有自己的 messages.properties 文件,则只会读取其中一个文件,从而导致消息值丢失。
似乎 Spring Boot MessageSourceAutoConfiguration 应该有一个 CompositeMessageSource
的概念,它迭代一个或多个可用的 MessageSource
实例(和 Order
ed) 在 Spring ApplicationContext 和 that 被 DispatcherServlet 使用。这将允许任何初学者只需在其自动配置
MessageSource
即可为可用消息做出贡献
可以按照我的要求做吗?对于应用程序开发人员来说,最 'hands off' 的解决方案是什么?
也许这是不可能的,但你可以尝试使用 BeanFactoryPostProcessor。
思路如下:
从应用程序上下文中取出 "messageSource" bean。请注意,它可能但不一定是 spring 引导程序,例如开发人员希望使用自己的实现并且不使用 spring 引导自动配置。
将其替换为您自己的尝试解析 "your keys" 的实现,其余委托给原始消息源。反之亦然,如果您希望开发人员可以覆盖您的翻译(如果原始消息源不为未知键抛出异常,则可能会出现问题)。
但可能有更好的方法。
我是这样设置的。我目前仅支持 en_US,但它已设置为使用国际化 (i18n) 处理任意数量的语言。
在此处查看代码
在此处查看代码要点:Code on github gist
添加消息源和默认语言环境 Bean
将这些 bean 添加到您的 Application.java 以设置您的默认语言环境并配置消息道具的位置
创建消息服务
服务将从会话中获取默认语言环境,然后从您的道具中获取消息文本
在控制器中使用消息服务
注入消息svc然后传入id从props文件中获取值
在语言环境中添加 message.properties 文件
转到/资源:
- 创建语言环境文件夹
- 创建一个名为 messages_en_US.properties 的文件
阅读更多
您可以在此处查看有关此主题的更完整的文章: Spring Boot Internationalization i18n using Message Properties
看代码
在此处查看代码要点: Code on github gist
我意识到这是一个老问题,但我前几天遇到了同样的问题,并写了一篇博客 post 关于我决定如何解决它。我想我应该在这里分享它,因为我从这个帖子中获得了一些解决方案的灵感。
简而言之,它采用了 sodik 的想法来拦截 MessageSource
bean 的创建,但我没有使用 BeanFactoryPostProcessor
,而是使用 BeanPostProcessor
,而不是替换应用程序上下文中的原始 MessageSource
,我只是添加自己的作为其父级:
@Bean
BeanPostProcessor messageSourceCustomExtender() {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof HierarchicalMessageSource && beanName.equals("messageSource")) {
ResourceBundleMessageSource parent = new ResourceBundleMessageSource();
parent.setBasename("custom");
((HierarchicalMessageSource) bean).setParentMessageSource(parent);
}
return bean;
}
};
}
您可以阅读完整的博客 post,其中我解释了有关我的解决方案的一些注意事项:http://www.thomaskasene.com/2016/08/20/custom-spring-boot-starter-messagesource/
更新
经过一番修改后,我意识到使用 BeanFactoryPostProcessor
是错误的,因为它会导致过早创建原始 MessageSource
bean 并忽略应用程序属性(最重要的是,spring.messages.basename
) .这意味着应用程序将无法配置这些属性。请参阅下面 BeanFactoryPostProcessor 文档的摘录。
A BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances. Doing so may cause premature bean instantiation, violating the container and causing unintended side-effects. If bean instance interaction is required, consider implementing BeanPostProcessor instead.
我已将上面的示例更新为使用 BeanPostProcessor
,这会更改 bean 实例而不是 bean 定义。