ドキュメント

§メッセージによる国際化

§アプリケーションでサポートされる言語の指定

アプリケーションの言語は、言語タグを使用して指定します。言語タグは、特定の言語を識別する特別な形式の文字列です。言語タグでは、英語の場合は「en」のように単純な言語、オーストラリアで使用される英語の場合は「en-AU」のように特定の地域の方言、ラテン文字で書かれたアゼルバイジャン語の場合は「az-Latn」のように言語と文字、中国で使用される中国語、マンダリン、簡体字の場合は「zh-cmn-Hans-CN」のようにこれらの組み合わせを指定できます。

まず、conf/application.confファイルにアプリケーションでサポートされる言語を指定する必要があります。

play.i18n.langs = [ "en", "en-US", "fr" ]

これらの言語タグは、play.api.i18n.Langインスタンスの作成に使用されます。アプリケーションでサポートされている言語にアクセスするには、play.api.i18n.Langsコンポーネントをクラスに注入できます。

import javax.inject.Inject

import play.api.i18n.Lang
import play.api.i18n.Langs
import play.api.mvc.Action
import play.api.mvc.AnyContent
import play.api.mvc.BaseController
import play.api.mvc.ControllerComponents

class ScalaI18nService @Inject() (langs: Langs) {
  val availableLangs: Seq[Lang] = langs.availables
}

個々のplay.api.i18n.Langは、lang.toLocaleを使用することでjava.util.Localeオブジェクトに変換できます。

val locale: java.util.Locale = lang.toLocale

§メッセージの外部化

メッセージは、conf/messages.xxxファイルに外部化できます。

デフォルトのconf/messagesファイルは、すべての言語に一致します。さらに、`conf/messages.fr`や`conf/messages.en-US`などの言語固有のメッセージファイルを指定することもできます。

メッセージは、インジェクションを介して追加できるMessagesApiインスタンスを介して利用できます。play.api.i18n.MessagesApiオブジェクトを使用してメッセージを取得できます。

import play.api.i18n.MessagesApi

class MyService @Inject() (langs: Langs, messagesApi: MessagesApi) {
  val lang: Lang = langs.availables.head

  val title: String = messagesApi("home.title")(lang)
}

言語を宣言するのではなく、暗黙的にすることもできます。

class MyOtherService @Inject() (langs: Langs, messagesApi: MessagesApi) {
  implicit val lang: Lang = langs.availables.head

  lazy val title: String = messagesApi("home.title")
}

Playは、フォーム検証用に定義済みのメッセージを提供しています。これらのメッセージは、デフォルトのメッセージファイルまたは言語固有のメッセージファイルで上書きできます。以下に、上書きできるメッセージを示します。

# Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>

# Default messages

# --- Constraints
constraint.required=Required
constraint.min=Minimum value: {0}
constraint.max=Maximum value: {0}
constraint.minLength=Minimum length: {0}
constraint.maxLength=Maximum length: {0}
constraint.email=Email
constraint.pattern=Required pattern: {0}

# --- Formats
format.date=Date (''{0}'')
format.numeric=Numeric
format.real=Real
format.uuid=UUID

# --- Patterns for Formats
formats.date=yyyy-MM-dd

# --- Errors
error.invalid=Invalid value
error.invalid.java.util.Date=Invalid date value
error.required=This field is required
error.number=Numeric value expected
error.real=Real number value expected
error.real.precision=Real number value with no more than {0} digit(s) including {1} decimal(s) expected
error.min=Must be greater or equal to {0}
error.min.strict=Must be strictly greater than {0}
error.max=Must be less or equal to {0}
error.max.strict=Must be strictly less than {0}
error.minLength=Minimum length is {0}
error.maxLength=Maximum length is {0}
error.email=Valid email required
error.pattern=Must satisfy {0}
error.date=Valid date required
error.uuid=Valid UUID required

error.expected.date=Date value expected
error.expected.date.isoformat=Iso date value expected
error.expected.time=Time value expected
error.expected.jsarray=Array value expected
error.expected.jsboolean=Boolean value expected
error.expected.jsnumber=Number value expected
error.expected.jsobject=Object value expected
error.expected.jsstring=String value expected
error.expected.jsnumberorjsstring=String or number expected
error.expected.keypathnode=Node value expected
error.expected.uuid=UUID value expected
error.expected.validenumvalue=Valid enumeration value expected
error.expected.enumstring=String value expected

error.path.empty=Empty path
error.path.missing=Missing path
error.path.result.multiple=Multiple results for the given path

§メッセージとMessagesProviderの使用

引数を指定せずにメッセージを使用することがよくあるので、特定のLangMessagesApiと一緒にラップして、play.api.i18n.Messagesインスタンスを作成できます。 play.api.i18n.MessagesImplケースクラスは、直接作成する場合にMessagesトレイトを実装します。

val messages: Messages = MessagesImpl(lang, messagesApi)
val title: String      = messages("home.title")

暗黙的なplay.api.i18n.MessagesProviderを使用して、シングルトンオブジェクトメソッドを使用することもできます。

implicit val messagesProvider: MessagesProvider = {
  MessagesImpl(lang, messagesApi)
}
// uses implicit messages
val title2 = Messages("home.title")

play.api.i18n.MessagesProviderは、必要に応じてMessagesオブジェクトを提供できるトレイトです。MessagesのインスタンスはMessagesProviderを拡張し、自身を返します。

MessagesProviderは、Messagesではない何かによって拡張される場合に最も役立ちます。

implicit val customMessagesProvider: MessagesProvider = new MessagesProvider {
  // resolve messages at runtime
  override def messages: Messages = { ... }
}
// uses implicit messages
val title3: String = Messages("home.title")

§コントローラーでのメッセージの使用

MessagesAbstractControllerまたは`MessagesBaseController`を拡張することで、リクエストに`Messages`サポートを追加できます。

import javax.inject.Inject

import play.api.i18n._

class MyMessagesController @Inject() (mcc: MessagesControllerComponents) extends MessagesAbstractController(mcc) {
  def index = Action { implicit request: MessagesRequest[AnyContent] =>
    val messages: Messages = request.messages
    val message: String    = messages("info.error")
    Ok(message)
  }

  def messages2 = Action { implicit request: MessagesRequest[AnyContent] =>
    val lang: Lang      = request.messages.lang
    val message: String = messagesApi("info.error")(lang)
    Ok(message)
  }

  def messages4 = Action { implicit request: MessagesRequest[AnyContent] =>
    // MessagesRequest is an implicit MessagesProvider
    Ok(views.html.formpage())
  }
}

または、コントローラーにplay.api.i18n.I18nSupportトレイトを追加し、MessagesApiのインスタンスがスコープ内にあることを確認することで、暗黙的にリクエストを変換できます。

import javax.inject.Inject

import play.api.i18n._

class MySupportController @Inject() (val controllerComponents: ControllerComponents)
    extends BaseController
    with I18nSupport {
  def index: Action[AnyContent] = Action { implicit request =>
    // type enrichment through I18nSupport
    val messages: Messages = request.messages
    val message: String    = messages("info.error")
    Ok(message)
  }

  def messages2: Action[AnyContent] = Action { implicit request =>
    // type enrichment through I18nSupport
    val lang: Lang      = request.lang
    val message: String = messagesApi("info.error")(lang)
    Ok(message)
  }

  def messages3: Action[AnyContent] = Action { request =>
    // direct access with no implicits required
    val messages: Messages = messagesApi.preferred(request)
    val lang               = messages.lang
    val message: String    = messages("info.error")
    Ok(message)
  }

  def messages4: Action[AnyContent] = Action { implicit request =>
    // takes implicit Messages, converted using request2messages
    // template defined with @()(implicit messages: Messages)
    Ok(views.html.formpage())
  }
}

TwirlテンプレートのすべてのフォームヘルパーはMessagesProviderを受け取り、フォームの処理時にMessagesProviderが暗黙的なパラメーターとしてテンプレートに渡されると想定されています。

@(form: Form[Foo])(implicit messages: MessagesProvider)

@helper.inputText(field = form("name")) @* <- takes MessagesProvider *@

§HTTPリクエストからサポートされている言語を取得する

特定のHTTPリクエストでサポートされている言語を取得できます。

def index: Action[AnyContent] = Action { request =>
  Ok("Languages: " + request.acceptLanguages.map(_.code).mkString(", "))
}

§リクエストタイプ

I18nSupportトレイトは、Requestに次のメソッドを追加します。

優先言語は、Accept-Languageヘッダー(およびオプションで言語クッキー)から抽出され、messagesApi.preferredを使用してMessagesApiでサポートされている言語のいずれかと照合されます。

I18nSupportは、Resultに2つの便利なメソッドも追加します。

例えば、

def homePageInFrench: Action[AnyContent] = Action {
  Redirect("/user/home").withLang(Lang("fr"))
}

def homePageWithDefaultLang: Action[AnyContent] = Action {
  Redirect("/user/home").withoutLang
}

withLangメソッドは、将来のリクエストのためにPLAY_LANGという名前のクッキーを設定しますが、withoutLangはクッキーを破棄し、PlayはクライアントのAccept-Languageヘッダーに基づいて言語を選択します。

クッキー名は、設定パラメーター:play.i18n.langCookieNameを変更することで変更できます。

§暗黙的なLang変換

LangImplicitsトレイトは、暗黙的なLangインスタンスが与えられた場合に、リクエストをMessagesに暗黙的に変換するために、コントローラーで宣言できます。

import play.api.i18n.LangImplicits

class MyClass @Inject() (val messagesApi: MessagesApi) extends LangImplicits {
  def convertToMessage: Unit = {
    implicit val lang: Lang = Lang("en")
    val messages: Messages  = lang2Messages // implicit conversion
  }
}

§メッセージのフォーマット

メッセージは、java.text.MessageFormatライブラリを使用してフォーマットされます。たとえば、次のようなメッセージが定義されているとします。

files.summary=The disk {1} contains {0} file(s).

パラメーターは次のように指定できます。

Messages("files.summary", d.files.length, d.name)

§アポストロフィに関する注意事項

メッセージはjava.text.MessageFormatを使用しているため、単一引用符はパラメーター置換をエスケープするためのメタ文字として使用されることに注意してください。

たとえば、次のようなメッセージが定義されている場合、

info.error=You aren''t logged in!
example.formatting=When using MessageFormat, '''{0}''' is replaced with the first parameter.

次のような結果が期待されます。

messagesApi("info.error") == "You aren't logged in!"
messagesApi("example.formatting") == "When using MessageFormat, '{0}' is replaced with the first parameter."

§明示的なMessagesApi

MessagesApiのデフォルトの実装はDefaultMessagesApiです。ドキュメントのテストセクションで単体テスト機能テストの例を参照できます。

また、テストではHelpers.stubMessagesApi()を使用して、事前に作成された空のMessagesApiを提供することもできます。

次へ:依存性注入


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