我正在编写一个自定义的Spring Boot启动程序,其他开发人员将放入其应用程序中,此启动程序包含开箱即用的控制器和UI屏幕。
这些UI屏幕已国际化,i18n键/值位于包文件中:com/foo/wherever/i18n.properties
。
我想确保在启动时加载启动器时,这些i18n.properties会自动在应用程序的MessageSource
中提供,以便我的UI页面可以工作(通过普通的Spring Controller + ViewResolver + View实现呈现) 没有应用开发者必须自己指定此文件。
换句话说,他们应该能够将我的启动器添加到他们的运行时类路径中,并且一切“正常工作”而无需配置任何东西。
现在,我发现应用开发者可以创建自己的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文件移动到启动器.jar中的类路径:/messages.properties文件中,但这似乎不是一个好的解决方案:如果应用程序开发有自己的messages.properties文件,只会读取其中一个,导致缺少消息值。
好像Spring Boot MessageSourceAutoConfiguration应该有一个CompositeMessageSource
的概念,它迭代一个或多个可用的MessageSource
个实例(和Order
ed) DispatcherServlet使用Spring ApplicationContext和 。这将允许任何首发只通过在其自动配置中声明MessageSource
来为可用消息做出贡献
有可能做我的要求吗?应用程序开发人员最“亲切”的解决方案是什么?
答案 0 :(得分:17)
我按照以下方式设置了它。我目前只支持en_US,但它设置为使用国际化(i18n)处理任意数量的语言。
在此处查看代码要点:Code on github gist
将这些bean添加到Application.java中以设置默认语言环境并配置消息道具的位置
服务将从会话中获取默认语言环境,然后从您的道具中获取消息文本
注入消息svc,然后传入id以从props文件中获取值
转到/资源:
您可以在此处查看有关此主题的更完整的文章: Spring Boot Internationalization i18n using Message Properties
在此处查看代码要点: Code on github gist
答案 1 :(得分:2)
也许是远景,但你可以尝试使用BeanFactoryPostProcessor。
想法如下:
从应用程序上下文中取出“messageSource”bean。请注意,它可能但不一定是弹簧靴的,例如,如果开发人员想要使用自己的实现,不要使用spring boot autoconfiguration。
将其替换为您自己的实现,尝试解析“您的密钥”,其余委托给原始邮件源。反之亦然如果您希望开发人员覆盖您的翻译(如果原始消息源不会因未知密钥而异常,则可能会出现问题)。
但是可能有更好的方法来做到这一点。
答案 2 :(得分:2)
我现在意识到这是一个陈旧且回答的问题,但是前几天我遇到了同样的问题,写了一篇关于我如何决定解决它的博客文章。我想我应该在这里分享,因为我从这个帖子中得到了一些灵感来解决我的问题。
简而言之,它需要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;
}
};
}
,我只是添加自己的父作为其父:
BeanFactoryPostProcessor
您可以阅读完整的博客文章,其中我解释了有关我的解决方案的一些注意事项:http://www.thomaskasene.com/2016/08/20/custom-spring-boot-starter-messagesource/
经过一些修补后,我意识到使用MessageSource
是错误的,因为它会导致原始spring.messages.basename
bean过早地创建并忽略应用程序属性(最重要的是,BeanPostProcessor
)。这意味着应用程序将无法配置这些属性。请参阅下面BeanFactoryPostProcessor文档中的摘录。
BeanFactoryPostProcessor可以与bean定义交互并修改bean定义,但绝不能与bean实例交互。这样做可能会导致bean过早实例化,违反容器并导致意外的副作用。如果需要bean实例交互,请考虑实现BeanPostProcessor。
我已经更新了上面的示例,改为使用[Error] Runtime error: Error: Cannot find module "./app.component"
[Error] Runtime error: Error: Module parse failed: .\src\app\app.component.spec.ts Unexpected token (12:31)
[Error] You may need an appropriate loader to handle this file type.
[Error] |
[Error] | it('should create the app',
[Error] | inject([AppComponent], (app: AppComponent) => {
[Error] | expect(app).toBeTruthy();
[Error] | }));
[Error] at http://localhost:58135/__modules/926.js?1471691462032:1
[Error] Runtime error: Error: Cannot find module "./app.component"
[Error] at http://localhost:58135/src/app/app.component.spec.ts.compiled.js.wbp.js?1471691462037&wallabyFileId=12:4
,这改变了bean实例而不是bean定义。