【问题标题】:Custom Spring Boot starter: how do you contribute i18n messages to the MessageSource?自定义 Spring Boot 启动器:如何向 MessageSource 贡献 i18n 消息?
【发布时间】:2015-04-29 07:01:37
【问题描述】:

我正在编写一个自定义 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

这会奏效。

但是,这需要以下两个条件:

  1. 他们必须手动配置spring.messages.basename 属性 - 这不是自动的。和
  2. 他们的应用程序类路径中必须有自己的messages.properties 文件。如果messages.properties 文件不存在,spring.messages.basename 甚至不起作用。即使他们不关心 i18n,这仍然是必需的 - 不可取。

我想我可以将我的 i18n.properties 文件移动到 starter .jar 中的 classpath:/messages.properties 文件,但这似乎不是一个好的解决方案:如果应用程序开发有自己的 messages.properties 文件,只会读取其中一个,导致消息值丢失。

似乎 Spring Boot MessageSourceAutoConfiguration 应该有一个 CompositeMessageSource 的概念,它遍历 Spring ApplicationContext 中可用的一个或多个 MessageSource 实例(和 Ordered), 由 DispatcherServlet 使用。这将允许任何初学者通过在其自动配置中声明 MessageSource 来为可用消息做出贡献

可以按照我的要求去做吗?对于应用开发者来说,最“不干涉”的解决方案是什么?

【问题讨论】:

  • 只是想知道你所说的“com/foo/wherever/i18n.properties”是否是消息属性文件应该与 JSP 视图位于同一目录中(或与视图位于同一包中,如果使用 JSP 以外的其他东西)?这是struts2中处理消息资源的方式。

标签: java spring internationalization spring-boot


【解决方案1】:

也许它是远射,但你可以尝试使用BeanFactoryPostProcessor

思路如下:

  1. 从应用程序上下文中取出“messageSource”bean。请注意,它可能但不一定是弹簧靴,例如开发者希望使用自己的实现,不要使用 spring boot 自动配置。

  2. 将其替换为您自己的实现,该实现尝试解析“您的密钥”并将其余部分委托给原始消息源。反之亦然,如果您希望开发人员能够覆盖您的翻译(如果原始消息源没有为未知键引发异常,则可能会出现问题)。

但可能有更好的方法来做到这一点。

【讨论】:

  • 只是重新审视这个 - Spring 目前不支持这个,所以这仍然是我能找到的最佳答案。答案已定!
【解决方案2】:

我通过以下方式进行设置。我目前只支持 en_US,但它设置为使用国际化(i18n)处理任意数量的语言。

在此处查看代码

在此处查看代码要点:Code on github gist

添加消息源和默认语言环境 Beans

将这些 bean 添加到您的 Application.java 以设置您的默认语言环境并配置您的消息道具的位置


创建消息服务

服务将从会话中获取默认语言环境,然后从您的道具中获取消息文本


在控制器中使用消息服务

注入消息svc然后传入id从props文件中获取值


在语言环境中添加 message.properties 文件

转到/资源:

  • 创建语言环境文件夹
  • 创建一个名为 messages_en_US.properties 的文件


了解更多

您可以在此处查看有关此主题的更完整文章: Spring Boot Internationalization i18n using Message Properties


看代码

在此处查看代码要点: Code on github gist

【讨论】:

  • 很高兴发布答案,但代码应该是可复制的。可以从发布的链接复制它。
  • @xdg Gist 是受版本控制的,所以如果我更新 Gist,复制到 Stack Overflow 上的代码将会过时。我会定期更新我的要点。
  • 更新了答案,因此代码链接位于答案的顶部并且更加突出
  • 这个答案没有解决这个问题 - 问题是关于如何让国际化在 Spring Boot 启动器中工作而不强制用户在他们的本地应用程序中定义这些概念.
【解决方案3】:

我现在意识到这是一个老问题,但我前几天遇到了同样的问题,并写了一篇关于我决定如何解决它的博客文章。我想我应该在这里分享它,因为我从这个帖子中获得了一些解决方案的灵感。

简而言之,它采用了 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;
        }
    };
}

您可以阅读完整的博客文章,其中我解释了我的解决方案的一些注意事项:http://www.thomaskasene.com/2016/08/20/custom-spring-boot-starter-messagesource/

更新

经过一番修改后,我意识到使用BeanFactoryPostProcessor 是错误的,因为它会导致原始MessageSource bean 过早创建并忽略应用程序属性(最重要的是spring.messages.basename)。这意味着应用程序将无法配置这些属性。请参阅下面BeanFactoryPostProcessor 文档的摘录。

BeanFactoryPostProcessor 可以与 bean 定义交互和修改,但不能与 bean 实例交互。这样做可能会导致过早的 bean 实例化,违反容器并导致意外的副作用。如果需要 bean 实例交互,请考虑实现 BeanPostProcessor。

我已将上面的示例更新为使用 BeanPostProcessor,这会改变 bean 实例而不是 bean 定义。

【讨论】:

    猜你喜欢
    • 2016-08-13
    • 1970-01-01
    • 2020-03-21
    • 2015-08-25
    • 2022-01-24
    • 2020-04-06
    • 1970-01-01
    • 2011-12-08
    • 2018-09-05
    相关资源
    最近更新 更多