ドキュメント

§I18N APIの移行

メッセージや言語の扱いを、特にフォームやテンプレートでより使いやすくするために、I18N APIにはいくつかの変更が加えられています。

§Java API

§インターフェースへのMessages APIのリファクタリング

Messagesへのアクセスを容易にするために、play.i18nパッケージが変更されました。これらの変更はユーザーには透過的であるはずですが、I18N APIを拡張するチームのためにここに提供されています。

Messagesはインターフェースとなり、そのインターフェースを実装するMessagesImplクラスがあります。

§非推奨/削除されたメソッド

play.i18n.Messagesの静的非推奨メソッドは2.6.xで削除されました。これは、MessagesApiインスタンスに同等のメソッドがあるためです。

§Scala API

§暗黙のデフォルト言語の削除

Langシングルトンオブジェクトには、JVMのデフォルトロケールを指すdefaultLangがあります。2.6.xより前のバージョンでは、defaultLangは暗黙の値であり、ローカルスコープでLangが見つからない場合に、暗黙のスコープ解決で使用される可能性がありました。この設定は一般的すぎて、リクエストが暗黙的に宣言されていない場合、リクエストのロケールの代わりにdefaultLangが使用されるというバグが発生しました。

その結果、暗黙の設定は削除され、

object Lang {
  implicit lazy val defaultLang: Lang = Lang(java.util.Locale.getDefault)
}

は次のようになりました。

object Lang {
  lazy val defaultLang: Lang = Lang(java.util.Locale.getDefault)
}

この暗黙の設定に依存していたコードは、Lang.defaultLangを明示的に使用する必要があります。

§トレイトへのMessages APIのリファクタリング

Messagesインスタンスへのアクセスを容易にし、Play内の暗黙の設定の数を減らすために、play.api.i18nパッケージが変更されました。これらの変更はユーザーには透過的であるはずですが、I18N APIを拡張するチームのためにここに提供されています。

Messagesは(ケースクラスではなく)トレイトになりました。ケースクラスはMessagesImplになり、Messagesを実装しています。

§I18nSupportの暗黙の変換

Play 2.5からPlay 2.6に直接アップグレードする場合は、2.6.xでI18nSupportのサポートが変更されたことを知っておく必要があります。2.5.xでは、リクエストが暗黙のスコープで宣言されていない場合、一連の暗黙の設定を通じて「言語のデフォルト」のMessagesインスタンスを使用することが可能でした。

  def listWidgets = Action {
    val lang = implicitly[Lang] // Uses Lang.defaultLang
    val messages = implicitly[Messages] // Uses I18nSupport.lang2messages(Lang.defaultLang)
    // implicit parameter messages: Messages in requiresMessages template, but no request!
    val content = views.html.requiresMessages(form)
    Ok(content)
  }

I18nSupportの暗黙の変換では、リクエストの優先ロケールと言語を正しく判断するために、スコープ内に暗黙のリクエストまたはリクエストヘッダーが必要になりました。

これは、以下のような場合、

def index = Action {

}

次のように変更する必要があることを意味します。

def index = Action { implicit request =>

}

これにより、i18nサポートはリクエストのロケールを認識し、ユーザーの言語でエラーメッセージと検証アラートを提供できます。

§よりスムーズなI18nSupport

コントローラー内でのフォームの使用は、2.6.xではよりスムーズなエクスペリエンスになります。ControllerComponentsには、MessagesApiインスタンスが含まれており、これはAbstractControllerによって公開されています。これは、I18nSupportトレイトが、Play 2.5.xで行っていたように、明示的なval messagesApi: MessagesApi宣言を必要としないことを意味します。

class FormController @Inject()(components: ControllerComponents)
  extends AbstractController(components) with I18nSupport {

  import play.api.data.validation.Constraints._

  val userForm = Form(
    mapping(
      "name" -> text.verifying(nonEmpty),
      "age" -> number.verifying(min(0), max(100))
    )(UserData.apply)(UserData.unapply)
  )

  def index = Action { implicit request =>
    // use request2messages implicit conversion method
    Ok(views.html.user(userForm))
  }

  def showMessage = Action { request =>
    // uses type enrichment
    Ok(request.messages("hello.world"))
  }

  def userPost = Action { implicit request =>
    userForm.bindFromRequest.fold(
      formWithErrors => {
        BadRequest(views.html.user(formWithErrors))
      },
      user => {
        Redirect(routes.FormController.index()).flashing("success" -> s"User is ${user}!")
      }
    )
  }
}

また、I18nSupportには型エンリッチメントがあり、request.messagesおよびrequest.langを追加することにも注意してください。これは、I18nSupportから拡張するか、import I18nSupport._によって追加できます。インポートバージョンには、request2messages暗黙の変換は含まれていません。

§MessagesProviderとの統合されたメッセージ

新しいMessagesProviderトレイトが利用可能になり、Messagesインスタンスを公開します。

trait MessagesProvider {
  def messages: Messages
}

MessagesImplMessagesMessagesProviderを実装し、デフォルトで自身を返します。

すべてのテンプレートヘルパーは、ストレートなMessagesオブジェクトではなく、暗黙のパラメーターとしてMessagesProviderを受け取るようになりました。例えば、inputText.scala.htmlは次のようになります。

@(field: play.api.data.Field, args: (Symbol,Any)*)(implicit handler: FieldConstructor, messagesProvider: play.api.i18n.MessagesProvider)

MessagesProviderを使用する利点は、それ以外の場合、暗黙のMessagesを使用すると、それらの暗黙の設定が混乱する可能性のある場所で、Requestのような他の型からの暗黙の変換を導入する必要があることです。

§MessagesRequestとMessagesAbstractController

支援のために、MessagesRequestがあります。これは、MessagesProviderを実装し、優先言語を提供するWrappedRequestです。

MessagesActionBuilderを使用することで、MessagesRequestにアクセスできます。


class MyController @Inject()( messagesAction: MessagesActionBuilder, cc: ControllerComponents ) extends AbstractController(cc) { def index = messagesAction { implicit request: MessagesRequest[AnyContent] => Ok(views.html.formTemplate(form)) // twirl template with form builders } }

または、ブロック内でRequestの代わりにMessagesRequestを提供するデフォルトのActionを入れ替えるMessagesAbstractControllerを使用することもできます。


class MyController @Inject() ( mcc: MessagesControllerComponents ) extends MessagesAbstractController(mcc) { def index = Action { implicit request: MessagesRequest[AnyContent] => Ok(s"The messages are ${request.messages}") } }

以下は、CSRFアクションを使用するフォームの完全な例です(CSRFフィルターが無効になっていると仮定します)。


class MyController @Inject() ( addToken: CSRFAddToken, checkToken: CSRFCheck, mcc: MessagesControllerComponents ) extends MessagesAbstractController(mcc) { import play.api.data.Form import play.api.data.Forms._ val userForm = Form( mapping( "name" -> text, "age" -> number )(UserData.apply)(UserData.unapply) ) def index = addToken { Action { implicit request => Ok(views.html.formpage(userForm)) } } def userPost = checkToken { Action { implicit request => userForm.bindFromRequest.fold( formWithErrors => { play.api.Logger.info(s"unsuccessful user submission") BadRequest(views.html.formpage(formWithErrors)) }, user => { play.api.Logger.info(s"successful user submission ${user}") Redirect(routes.MyController.index()).flashing("success" -> s"User is ${user}!") } ) } } }

MessagesRequestMessagesProviderであるため、リクエストを暗黙的に定義するだけで、テンプレートに引き継がれます。これは、CSRFチェックが関係している場合に特に役立ちます。formpage.scala.htmlページは次のとおりです。


@(userForm: Form[UserData])(implicit request: MessagesRequestHeader) @helper.form(action = routes.MyController.userPost()) { @views.html.helper.CSRF.formField @helper.inputText(userForm("name")) @helper.inputText(userForm("age")) <input type="submit" value="SUBMIT"/> }

MessageRequestの本文はテンプレートには関係ないため、ここではMessageRequest[_]の代わりにMessagesRequestHeaderを使用できることに注意してください。

詳細については、フォームヘルパーへのメッセージの渡し方を参照してください。

§DefaultMessagesApiコンポーネント

MessagesApiのデフォルトの実装はDefaultMessagesApiです。DefaultMessagesApiは、以前はConfigurationEnvironmentを直接受け取っていたため、フォームでの扱いが面倒でした。ユニットテストの目的のために、DefaultMessagesApiは引数なしでインスタンス化でき、未加工のマップを受け取ります。


import play.api.data.Forms._ import play.api.data._ import play.api.i18n._ val messagesApi = new DefaultMessagesApi( Map("en" -> Map("error.min" -> "minimum!") ) ) implicit val request = { play.api.test.FakeRequest("POST", "/") .withFormUrlEncodedBody("name" -> "Play", "age" -> "-1") } implicit val messages = messagesApi.preferred(request) def errorFunc(badForm: Form[UserData]) = { BadRequest(badForm.errorsAsJson) } def successFunc(userData: UserData) = { Redirect("/").flashing("success" -> "success form!") } val result = Future.successful(form.bindFromRequest().fold(errorFunc, successFunc)) Json.parse(contentAsString(result)) must beEqualTo(Json.obj("age" -> Json.arr("minimum!")))

構成に関わる機能テストの場合、最良の選択肢は、WithApplicationを使用して、注入されたMessagesApiを取り込むことです。


import play.api.test.{ PlaySpecification, WithApplication } import play.api.i18n._ class MessagesSpec extends PlaySpecification { sequential implicit val lang = Lang("en-US") "Messages" should { "provide default messages" in new WithApplication(_.requireExplicitBindings()) { val messagesApi = app.injector.instanceOf[MessagesApi] val javaMessagesApi = app.injector.instanceOf[play.i18n.MessagesApi] val msg = messagesApi("constraint.email") val javaMsg = javaMessagesApi.get(new play.i18n.Lang(lang), "constraint.email") msg must ===("Email") msg must ===(javaMsg) } "permit default override" in new WithApplication(_.requireExplicitBindings()) { val messagesApi = app.injector.instanceOf[MessagesApi] val msg = messagesApi("constraint.required") msg must ===("Required!") } } }

構成をカスタマイズする必要がある場合は、DefaultMessagesApiProviderを直接使用するよりも、構成値をGuiceApplicationBuilderに追加することをお勧めします。

§非推奨のメソッド

play.api.i18n.Messages.Implicits.applicationMessagesApiplay.api.i18n.Messages.Implicits.applicationMessages は、暗黙的な Application インスタンスに依存しているため、非推奨となりました。

play.api.mvc.Controller.request2lang メソッドは、内部でグローバルな Application を使用していたため、非推奨となりました。

play.api.i18n.I18nSupport.request2Messages 暗黙的な変換メソッドは、I18NSupportLowPriorityImplicits.request2Messages に移動され、より明確な request.messages 型の拡張に移行するため、非推奨となりました。

I18NSupportLowPriorityImplicits.lang2Messages 暗黙的な変換は、暗黙的な Request と Lang の両方がスコープ内にある場合の混乱を避けるため、LangImplicits.lang2Messages に移動されました。暗黙的な Lang から Messages を作成したい場合は、明示的に play.api.i18n.LangImplicits トレイトを拡張してください。

次: WS マイグレーション


このドキュメントにエラーを見つけましたか? このページのソースコードは こちら にあります。 ドキュメントガイドライン を読んだ後、お気軽にプルリクエストを送信してください。質問や共有したいアドバイスがありますか? コミュニティフォーラム にアクセスして、コミュニティとの会話を始めましょう。