web-dev-qa-db-ja.com

カスタムスプリングブートスターター:i18nメッセージをMessageSourceにどのように提供しますか?

私は他の開発者が自分のアプリケーションにドロップするカスタムの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.propertiesファイルをスターター.jarのclasspath:/messages.propertiesファイルに移動すると思いますが、それは適切な解決策:アプリ開発者が独自のmessages.propertiesファイルを持っている場合、そのうちの1つだけが読み取られ、メッセージ値が失われます。

Spring Boot MessageSourceAutoConfiguration は、利用可能な1つ以上のCompositeMessageSourceインスタンス(およびMessageSourceedを反復処理するOrderの概念を持つ必要があるようです)Spring ApplicationContext内で、thatがDispatcherServletによって使用されます。これにより、スターターは、自動構成でMessageSourceを宣言するだけで、使用可能なメッセージに貢献できます。

私が求めることを行うことは可能ですか?アプリ開発者にとって最も手助けになるソリューションは何ですか?

9
Les Hazlewood

多分それは長いショットですが、あなたは BeanFactoryPostProcessor を使用してみることができます。

アイデアは次のとおりです:

  1. 「messageSource」Beanをアプリケーションコンテキストから取り出します。スプリングブーツの場合もありますが、そうである必要はありません。開発者は独自の実装を使用し、スプリングブート自動構成を使用しないことを望んでいます。

  2. 「キー」を解決しようとする独自の実装に置き換え、残りを元のメッセージソースに委任します。または、開発者による翻訳のオーバーライドを可能にしたい場合はその逆です(元のメッセージソースが不明なキーに対して例外をスローしない場合、問題が発生する可能性があります)。

しかし、そのためのより良い方法があるかもしれません。

2
sodik

以下のように設定しました。現在en_USのみをサポートしていますが、国際化(i18n)を使用して任意の数の言語を処理するように設定されています。

ここにコードを表示

ここでコードGistを表示: github Gist上のコード

メッセージソースとデフォルトロケールBeanを追加する

これらのBeanをApplication.Javaに追加して、デフォルトのロケールを設定し、メッセージプロップの場所を構成します。

Message source and default locale


メッセージサービスの作成

サービスはセッションからデフォルトのロケールを取得し、小道具からメッセージテキストを取得します

Setup Message svc


コントローラーでメッセージサービスを使用する

メッセージsvcを挿入し、IDを渡して、propsファイルから値を取得します

Use svc in controller


ロケールにmessage.propertiesファイルを追加します

/リソースに移動:

  • ロケールフォルダを作成する
  • messages_en_US.propertiesというファイルを作成します

message props


続きを読む

このテーマに関するより完全な記事は、こちらで確認できます。 Spring Boot Internationalization i18n using Message Properties


コードを見る

ここでコードGistを表示: github Gist上のコード

17
anataliocs

これは古くて回答済みの質問であることに気づきましたが、先日同じ問題に遭遇し、それを解決することに決めた方法についてブログに投稿しました。このスレッドから自分の解決策にインスピレーションを得たので、ここで共有する必要があると思いました。

要するに、MessageSource Beanの作成をインターセプトするというsodikのアイデアが必要ですが、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のインスタンスが早まってしまい、コンテナに違反して、意図しない副作用が発生する可能性があります。 Beanインスタンスの相互作用が必要な場合は、代わりにBeanPostProcessorの実装を検討してください。

上記の例を更新して、代わりにBeanPostProcessorを使用します。これにより、Bean定義ではなくBeanインスタンスが変更されます。

2
Thomas Kåsene