Java 自定义 Spring Boot starter:如何向 MessageSource 贡献 i18n 消息?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28750292/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 06:44:15  来源:igfitidea点击:

Custom Spring Boot starter: how do you contribute i18n messages to the MessageSource?

javaspringinternationalizationspring-boot

提问by Les Hazlewood

I'm writing a custom Spring Boot starter that other developers will drop into their applications, and this starter contains out-of-the-box controllers and UI screens.

我正在编写一个自定义 Spring Boot 启动器,其他开发人员会将其放入他们的应用程序中,这个启动器包含开箱即用的控制器和 UI 屏幕。

These UI screens are internationalized and the i18n keys/values are in a package file: com/foo/wherever/i18n.properties.

这些 UI 屏幕是国际化的,i18n 键/值位于包文件中:com/foo/wherever/i18n.properties.

I want to ensure that when my starter is loaded at startup, that these i18n.properties are available in the application's MessageSourceautomatically so that my UI pages work (rendered via normal Spring Controller + ViewResolver + View implementations) without the app developer having to specify this file themselves.

我想确保在启动时加载我的 starter 时,这些 i18n.properties 在应用程序中MessageSource自动可用,以便我的 UI 页面工作(通过普通的 Spring Controller + ViewResolver + View 实现呈现)而无需应用程序开发人员指定文件自己

In other words, they should be able to add my starter to their runtime classpath and everything 'just works' without the need to configure anything.

换句话说,他们应该能够将我的 starter 添加到他们的运行时类路径中,并且一切“正常工作”而无需进行任何配置。

Now, I have discovered that the app developer can create their own src/main/resources/messages.propertiesfile andmanually configure the additional messages file in application.properties:

现在,我发现应用程序开发人员可以创建自己的src/main/resources/messages.properties文件手动配置附加消息文件application.properties

spring.messages.basename = messages, com.foo.wherever.i18n

And this will work.

这将起作用。

However, this requires both of the following:

但是,这需要以下两项:

  1. They must manually configure the spring.messages.basenameproperty - it's not automatic. and
  2. They must have their own messages.propertiesfile in their application classpath. If a messages.propertiesfile does not exist, spring.messages.basenamedoesn't even work. Even if they don't care about i18n, this is still required - not desirable.
  1. 他们必须手动配置spring.messages.basename属性 - 这不是自动的。和
  2. 他们messages.properties的应用程序类路径中必须有自己的文件。如果messages.properties文件不存在,spring.messages.basename甚至不起作用。即使他们不关心 i18n,这仍然是必需的 - 不可取。

I suppose I couldmove my i18n.properties file to a classpath:/messages.properties file in the starter .jar, but that doesn't seem like a good solution: if the app dev has their own messages.properties file only one of them would be read, resulting in missing message values.

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

It seems as if Spring Boot MessageSourceAutoConfigurationshould have a concept of a CompositeMessageSourcethat iterates over one or more MessageSourceinstances that are available (and Ordered) in the Spring ApplicationContext and thatis used by the DispatcherServlet. This would allow any starter to contribute to the available messages just by declaring a MessageSourcein their auto config

看起来好像春天启动MessageSourceAutoConfiguration应该有一个概念CompositeMessageSource是在一个或多个迭代MessageSource实例可用(并且Order在Spring的ApplicationContext ED)和所使用的DispatcherServlet的。这将允许任何启动器仅通过MessageSource在其自动配置中声明 a 来为可用消息做出贡献

Is it possible to do what I ask? What is the most 'hands off' solution for the app developer?

可以按我的要求做吗?对于应用程序开发人员来说,最“放手”的解决方案是什么?

采纳答案by sodik

Maybe it is long shot but you can try to use BeanFactoryPostProcessor.

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

Idea is following:

思路如下:

  1. Take "messageSource" bean out of the application context. Note that it might but does not have to be a spring boot's one if e.g. developer wants to use its own implementation and don't use spring boot autoconfiguration.

  2. Replace it with your own implementation that tries to resolve "your keys" and the rest delegate to original message source. Or vice versa if you want to make possible to override your translations by developer (there can be problems if original message source does not throw exception for unknown keys).

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

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

But there might be a better way to do that.

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

回答by anataliocs

I set this up in the following way. I'm only currently supporting en_US but it is setup to handle any number of languages using internationalization(i18n).

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

View the Code here

在此处查看代码

View the code gist here: Code on github gist

在此处查看代码要点:github gist 上的代码

Add Message Source and Default locale Beans

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

Add these beans to your Application.java to set your default locale and configure the location of your message props

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

Message source and default locale

消息源和默认语言环境



Create Message Service

创建消息服务

Service will get the default locale from the session and then get the message text from your props

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

Setup Message svc

设置消息 svc



Use the Message service in Controller

使用 Controller 中的 Message 服务

Inject the message svc and then pass in the id to get the value from the props file

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

Use svc in controller

在控制器中使用 svc



Add message.properties file in the locale

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

Goto /resources:

转到/资源:

  • create the locale folder
  • create a file called messages_en_US.properties
  • 创建语言环境文件夹
  • 创建一个名为 messages_en_US.properties 的文件

message props

留言道具



Read more

阅读更多

You can view a more complete article on this subject here: Spring Boot Internationalization i18n using Message Properties

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



Look at the Code

看代码

View the code gist here: Code on github gist

在此处查看代码要点:github gist 上的代码

回答by Thomas K?sene

I realize this is an old and answered question by now, but I encountered the same problem the other day, and wrote a blog post about how I've decided to solve it. I thought I should share it here since I got some inspiration for my solution from this thread.

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

In short, it takes sodik's idea of intercepting the creation of the MessageSourcebean, but instead of using a BeanFactoryPostProcessorI'm using a BeanPostProcessor, and rather than replacing the original MessageSourcein the application context, I just add my own as its parent:

简而言之,它采用了 sodik 拦截MessageSourcebean创建的想法,但是BeanFactoryPostProcessor我没有使用 a我正在使用 a 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;
        }
    };
}

You can read the full blog post where I explain some caveats about my solution: http://www.thomaskasene.com/2016/08/20/custom-spring-boot-starter-messagesource/

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

Update

更新

After some tinkering I realized that using a BeanFactoryPostProcessorwas wrong as it will cause the original MessageSourcebean to be created prematurely and ignore application properties (most importantly, spring.messages.basename). That means the application won't be able to configure these properties. See the excerpt from the BeanFactoryPostProcessordocumentation below.

经过一些修补后,我意识到使用 aBeanFactoryPostProcessor是错误的,因为它会导致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.

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

I've updated the example above to use BeanPostProcessorinstead, which alters the bean instance rather than the bean definition.

我已经更新了上面的例子来使用BeanPostProcessor,它改变了 bean 实例而不是 bean 定义。