序文
同社は、プロジェクトをStruts2からSpringMVCに転送しました。会社のビジネスは海外サービスであるため、国際的な機能の需要は非常に高くなっています。 Struts2の国際化機能はSpringMVCよりも完璧ですが、Springの大きな特徴はカスタマイズ可能でカスタマイズされていることです。そのため、会社のプロジェクトがSpringMVCに移植されると、国際化機能が追加されます。ここでレコードをまとめて改善しました。
この記事で実装されている主な機能:
フォルダーから複数の国際ファイルを直接ロードします。バックグラウンド設定のフロントエンドページには、国際情報が表示されます。国際情報を表示するファイルは、インターセプターと注釈を使用して自動的に設定されます。フロントエンドページに国際情報を表示するファイルには、国際情報が表示されます。
注:この記事では、国際化、地域パーサーなどの構成方法を詳細に紹介していません。
成し遂げる
国際プロジェクトの初期化
最初に、基本的なスプリングブート+Thymeleaf+Internationalization Information(message.Properties)プロジェクトを作成します。必要な場合は、githubからダウンロードできます。
プロジェクトのディレクトリとファイルを簡単に見る
その中で、i18napplication.javaは、 CookieLocaleResolverを設定します。これは、Cookieを使用して国際言語を制御します。また、国際言語の変化を傍受するために、 LocaleChangeInterceptorインターセプターをセットアップします。
@SpringBootApplication@configurationPublic class i18napplication {public static void main(string [] args){springApplication.run(i18napplication.class、args); } @Bean Public LocalEresolver Localeresolver(){cookielocaleresolver slr = new cookielocaleresolver(); slr.setCookieMaxage(3600); slr.setCookiename( "言語"); //保存されたクッキーの名前を言語返品slrに設定します。 } @bean public webmvcconfigurer webmvcconfigurer(){return new webmvcconfigurer(){// interceptor @override public void addInterceptors(interceptorregistry registry){registry.addInterceptor(new localechangeinterceptor())。 }}; }}hello.htmlで書かれているものを見てみましょう:
<!doctype html> <html xmlns = "http://www.w3.org/1999/xhtml" xmlns:th = "http://www.thymeleaf.org"> <head> <title> hello world! Th:text = "#{i18n_page}"> </h1> <h3 th:text = "#{hello}"> </h3> </body> </html>ここでプロジェクトを開始し、 http://localhost:9090/hello (application.propertiesでポートを9090に設定します)にアクセスします。
ブラウザのデフォルト言語は中国語であるため、デフォルトでmessages_zh_cn.propertiesで検索します。そうでない場合は、messages.propertiesで国際化された単語を検索します。
次に、 http://localhost:9090/hello?locale=en_USブラウザーに入力すると、言語は英語にカットされます。同様に、URLの後のパラメーターがlocale=zh_CHに設定されている場合、言語は中国語にカットされます。
複数の国際ファイルをフォルダーから直接読み込みます
hello.htmlページには、「i18n_page」と「hello」という国際情報が2つしかありません。ただし、実際のプロジェクトでは、いくつかの国際情報、通常は数百の国際情報ほど小さいことはありません。次に、 messages.propertiesを備えたファイルにそれほど多くの国際情報を配置してはなりません。通常、国際情報はいくつかのファイルに分類および保存されます。ただし、プロジェクトが大きくなると、これらの国際ファイルはますます大きくなります。現時点では、 application.propertiesファイルでこのファイルを1つずつ構成することは不便です。したがって、すべての国際ファイルを定式化ディレクトリに自動的にロードする関数を実装します。
ResourceBundleMessageSourceを継承します
ResourceBundleMessageSourceまたはReloadableResourceBundleMessageSource継承するプロジェクトの下でクラスを作成し、 MessageResourceExtensionという名前を付けます。そして、それをBeanに注入し、名前付きmessageSourceで、ResourceBundLemessagesOurceを継承します。
@Component( "Messagesource")Public Class MessageresourceExtensions Extends ResourceBundLemessagesource {} ApplicationContext初期化するときに、「Messagesource」という名前のBeanのBeanが検索されるため、コンポーネント名は「Messagesource」でなければならないことに注意してください。このプロセスは、 AbstractApplicationContext.javaにあります。ソースコードを見てみましょう
/***メッセージソースを初期化します。*このコンテキストで定義されていない場合は親のを使用します。 if(beanfactory.containslocalbean(message_source_bean_name)){this.messageource = beanfactory.getbean(message_source_bean_name、massionsource.class); ...}} ... Messagesourceを初期化するこの方法では、beanFactoryは、名前MESSAGE_SOURCE_BEAN_NAME(messageSource)を注入した豆を探します。見つからない場合は、親クラスに名前が付いた豆があるかどうかを探します。
ファイルの読み込みを実装します
これで、作成したMessageResourceExtensionを開始できます
ファイルをロードする方法が記述されています。
@Component( "Messagesource")Public Class MessagerESourceExtensionは、ResourceBundLemessageSourceを拡張します{private final static Logger = oggerFactory.getLogger(MessageresourceExtension.class); / ***指定された国際化ファイルディレクトリ*/ @value(value = "$ {spring.messages.basefolder:i18n}")private string basefolder; / *** Parent Messagesourceによって指定された国際化ファイル*/ @value(value = "$ {spring.messages.basename:message}")private string basename; @postconstruct public void init(){logger.info( "init messageresourceextension ..."); if(!stringutils.isempty(basefolder)){try {this.setBasenames(getallBasenames(baseFolder)); } catch(ioException e){logger.error(e.getmessage()); }} // parent messagesource resourcebundlemessagesource parent = new ResourceBundLemessageSource(); parent.setBasename(Basename); this.setParentMessageSource(親); } / ** *フォルダー内のすべての国際ファイル名を取得 * * @param foldernameファイル名 * @return * @throws ioexception * / private string [] getallbasenames(string foldername)throws ioexception {resource resource = new classpathresource(foldername); file file = resource.getFile(); List <String> Basenames = new ArrayList <>(); if(file.exists()&& file.isdirectory()){this.getallfile(basenames、file、 ""); } else {logger.error( "指定されたベースファイルは存在しないか、フォルダーではありません"); } return basenames.toarray(new String [basenames.size()]); } / ** * traverseすべてのファイル * * @param basenames * @param folder * @param path * / private void getallfile(list <string> basenames、file folder、string path){if(folder.isdirectory()){for(file:file:folder.listfiles()){thisenames()basenames() + file、path + + foat file.separator); }} else {string i18name = this.geti18FileName(path + folder.getName()); if(!basenames.contains(i18name)){basenames.add(i18name); }}} / ** *通常のファイル名をInternational File namesに変換 * * @param filename * @return * / private string geti18FileName(string filename){filename = filename.replace( "。プロパティ"、 ""); for(int i = 0; i <2; i ++){int index = filename.lastindexof( "_"); if(index!= -1){filename = filename.substring(0、index); }} filenameを返します。 }}いくつかの方法を順番に説明してください。
init()メソッドには@PostConstructアノテーションがあります。これは、messageresourceextensionクラスがインスタンス化された後、自動的にinit()メソッドを呼び出します。このメソッドはbaseFolderディレクトリ内のすべての国際化ファイルを取得し、それらをbasenameSetに設定します。 ParentMessageSourceを設定します。これは、Parent Messagesourceに電話して、国際情報が見つからない場合に国際情報を見つけます。getAllBaseNames()メソッドは、 baseFolderへのパスを取得し、 getAllFile()メソッドを呼び出して、ディレクトリ内のすべての国際ファイルのファイル名を取得します。getAllFile() 、ディレクトリをトラバースします。フォルダーの場合、ファイルの場合はトラバースを継続し、 getI18FileName()を呼び出して、ファイル名を「I18N/BASENAME/」の形式で国際リソース名に変換します。 MessageResourceExtensionがインスタンス化された後、「i18n」フォルダーの下のリソースファイルの名前がBasenamesにロードされた後、簡単に言えば簡単に言えば。それでは、効果を見てみましょう。
最初に、 spring.messages.baseFolder=i18n baseFolder MessageResourceExtensionファイルに追加します。
開始後、init情報がコンソールに印刷されていることがわかり、 @PostConstructによって注釈されたinit()メソッドが実行されたことを示しています。
次に、「ダッシュボード」と「マーチャント」という2つの国際情報ファイルのセットを作成します。これは、それぞれ「Dashboard.hello」と「Merchant.hello」という国際情報が1つしかありません。
次に、hello.htmlファイルを変更してから、helloページにアクセスします。
... <body> <h1>国際化ページ!</h1> <p th:text = "#{hello}"> </p> <p th:text = "#{merchant.hello}"> </p> <p th:text = "#{dashboard.hello}"> </p> </> ...「メッセージ」、「ダッシュボード」、「マーチャント」の国際化情報がWebページにロードされていることがわかり、「i18n」フォルダーの下にファイルが一度に正常にロードされたことがわかります。
背景設定のフロントエンドページに国際情報を表示するファイル
前のセクションでは、複数の国際化ファイルを正常にロードし、国際化情報を表示しました。しかし、「Dashboard.Properties」の国際情報は「Dashboard.hello」と「merchant.properties」は「merchant.hello」です。そのため、それぞれにプレフィックスを書くのは非常に面倒です。今、私は「ダッシュボード」と「商人」の国際化ファイルに「Hello」のみを書きたいと思っていますが、「ダッシュボード」または「商人」の国際化情報が表示されます。
MessageResourceExtensionでresolveCodeWithoutArgumentsメソッドを書き換えます(文字のフォーマットが必要な場合はresolveCode書き直します)。
@Component( "Messagesource")Public Class MessageresourceExtensionは、ResourceBundLemessageSource {... public Static String i18n_attribute = "i18n_attribute"; @Override保護された文字列ResolveCodewithoutArguments(String Code、Locale Locale){// Request ServletRequestattributes attr =(servletRequestattributes)requestContextholder.CurrentRequestattributes()で指定された国際ファイル名を取得します。最終文字列i18file =(string)attr.getattribute(i18n_attribute、requestattributes.scope_request); if(!stringutils.isempty(i18file)){// basenameset string basename = getbasenameset()。firet().filter(name-> stringutils.swithignorecase(name-> stringutils.swithignorecase(name、i18file)).findfirst()。 if(!stringutils.isempty(basename)){//指定された国際ファイルリソースバンドルbundle = getResourceBundle(basename、locale)を取得します。 if(bundle!= null){return getStringornull(bundle、code); }}} //指定されたi18フォルダーに国際化フィールドがない場合、nullを返すと、parentmessageSourceでnullを返します。 } ...}書き換えたresolveCodeWithoutArgumentsメソッドでは、表示する国際ファイル名に対応するhttpservletrequest(これをどこに設定するかについて説明します)から「i18n_attribute」を取得します。次に、 BasenameSetのファイルを調べ、 getResourceBundleを介してリソースを取得し、最後に対応する国際情報を取得するためにgetStringOrNull取得します。
次に、 HelloControllerに2つの方法を追加しましょう。
@controllerpublic class hellocontroller {@getmapping( "/hello")public string index(httpservletrequest request){request.setattribute(messageresourceextension.i18n_attribute、 "hello"); "system/hello"を返します。 } @getMapping( "/dashboard")public string dashboard(httpservletrequest request){request.setattribute(messageresourceextension.i18n_attribute、 "dashboard"); 「ダッシュボード」を返します。 } @getMapping( "/merchant")public string merchant(httpservletrequest request){request.setattribute(messageresourceextension.i18n_attribute、 "merchant"); 「商人」を返します。 }}各メソッドに対応する「i18n_attribute」を設定します。これにより、各リクエストに対応する国際化ファイルが設定され、 MessageResourceExtensionで取得されます。
現時点では、国際化ファイルを見て、すべてのキーワードが「こんにちは」であることがわかりますが、情報は異なります。
同時に、2つの新しいHTMLファイルは「dashboard.html」と「merchant.html」であり、「hello」の国際情報と区別のタイトルしかありません。
<! - これはhello.html-> <body> <h1>国際化ページ!</h1> <p th:text = "#{hello}"> </p> </body> <! - これはdashboard.htmlです - > <body> <h1>国際化ページ(ダッシュボード)!</h1> <p th:text = "#{hello}"> </p> </body> <! - これはmerchant.htmlです - > <body> <h1>国際化ページ(商人)!</h1> <p th:text = "#{hello}"> </p> </body>この時点で、プロジェクトを始めて、見てみましょう。
各ページの国際化ワードは「Hello」ですが、対応するページに表示する情報が表示されます。
インターセプターと注釈を使用して、フロントエンドページに国際情報を表示するファイルを自動的に設定します
対応する国際化情報を指定できますが、各コントローラーのhttpservletrequestに国際化ファイルを設定するのは面倒です。したがって、対応するファイルを表示するために自動判断を実装しました。
最初に、クラスまたはメソッドに配置できる注釈を作成します。
@target({elementType.Type、elementType.Method}) @retention(retentionPolicy.runtime)public @interface i18n { / ***国際化されたファイル名* /文字列値();}次に、コントローラーメソッドに作成されたI18nアノテーションを今すぐ配置します。その効果を表示するために、 ShopControllerとUserControllerを作成し、対応する「ショップ」と「ユーザー」の国際ファイルも作成します。コンテンツも「Hello」です。
@controllerpublic class hellocontroller {@getmapping( "/hello")public string index(){return "system/hello"; } @i18n( "dashboard")@getMapping( "/dashboard")public string dashboard(){return "dashboard"; } @i18n( "merchant")@getmapping( "/merchant")public string merchant(){return "merchant"; }} @i18n( "shop") @ControllPublic Class ShopController {@getMapping( "Shop")Public String Shop(){return "Shop"; }} @controllerpublic class usercontroller {@getmapping( "user")public string user(){return "user"; }} I18nアノテーションは、それぞれHelloControllerの下に、およびShopControllerクラスの下にdashboardとmerchantメソッドの下に配置します。また、元のdashboardとmerchantメソッドの下に「i18n_attribute」を設定するステートメントが削除されます。
準備はすべて行われます。これらの注釈に基づいて国際化ファイルを自動的に指定する方法を確認します。
Public Class MessageresourceInterceptorは、HandlerInterceptor {@Override public void posthandle(httpservletrequest req、httpservletresponse rep、object handler、modelandview modelandview){//メソッドのi18パスを設定するif(null!= req.gettribute(mesagereSourceextions(mesagereSource))を設定します。 } handlermethod method =(handlermethod)ハンドラー; //メソッドI18 I18N I18NMETHOD = method.getMethodAnnotation(i18n.class); if(null!= i18nmethod){req.setattribute(messageresourceextension.i18n_attribute、i18nmethod.value());戻る; } //コントローラーでのアノテーションi18n i18ncontroller = method.getBeanType()。getAnnotation(i18n.class); if(null!= i18ncontroller){req.setattribute(messageresourceextension.i18n_attribute、i18ncontroller.value());戻る; } // I18 String Controller = method.getBeanType()。getName(); int index = controller.lastindexof( "。"); if(index!= -1){controller = controller.substring(index + 1、controller.length()); } index = controller.touppercase()。indexof( "controller"); if(index!= -1){controller = controller.substring(index + 1、controller.length()); } index = controller.touppercase()。indexof( "controller"); if(index!= -1){controller = controller.substring(0、index); } req.setattribute(messageresourceextension.i18n_attribute、controller); } @Override public boolean prehandle(httpservletrequest req、httpservletresponse rep、object handler){//このメソッドにジャンプするとき、リクエストのreq.removeattribute(messageresourceextension.i18n_attribute); trueを返します。 }}このインターセプターを簡単に説明させてください。
まず、リクエストに「i18n_attribute」が既にある場合、設定がコントローラーメソッドで指定され、判断が行われなくなることを意味します。
次に、インターセプターを入力する方法にI18nアノテーションがあるかどうかを判断します。ある場合は、「i18n_attribute」をリクエストに設定し、インターセプターを終了します。いない場合は、続行します。
次に、インターセプトに入ったクラスにI18nアノテーションがあるかどうかを判断します。ある場合は、「i18n_attribute」をリクエストに設定し、インターセプターを終了します。いない場合は、続行します。
最後に、メソッドとクラスにI18nアノテーションがない場合、コントローラー名に従って指定された国際化ファイルを自動的に設定できます。たとえば、「usercontroller」では、「ユーザー」国際化ファイルを探します。
次に、再度実行して効果を確認し、各リンクに表示される対応する国際情報のコンテンツを表示しましょう。
やっと
国際化の強化全体の基本的な機能を完了しました。最後に、関数の実装効果を示すために、すべてのコードと統合されたbootstrap4を整理しました。
詳細なコードについては、githubのspring-boot-i18n-proのコードを参照してください
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。