Playframework 2.x でタイプセーフに国際化対応のメール送信

この記事は Play framework 2.x Scala Advent Calendar 2013 の 5 日目です。

http://www.adventar.org/calendars/114

メールテンプレート

これまで Java のプロジェクトでは Velocity/FreeMarker/ozacc-mail といったライブラリを私はメールテンプレートに使ってきましたが、これらのライブラリに依存せず Play のテンプレート機能を使ってメール送信をしてみます。

最近ではクライアントサイド側でレンダリングすることも増え、サーバーサイド側のテンプレートの需要は減りつつありますが、メールは事前に書かれている必要がありますので、簡易なメールでなければ何かしらのテンプレートは欲しいところです。

Play の view テンプレート機能はタイプセーフに記述できるため、恐れずオブジェクトのプロパティ名を変更できます。また最近のフレームワークらしくデフォルトで HTML エスケープを有効にしてくれるため入力値を安全に埋め込められるメリットがあります。

メール機能の有効化

/build.sbt

libraryDependencies ++= Seq(
  jdbc,
  cache,
  "com.typesafe" %% "play-plugins-mailer" % "2.2.0"
)

まず play-plugins (https://github.com/typesafehub/play-plugins/tree/master/mailer) にメールライブラリが公開されているので /build.sbt に依存ライブラリとして登録します。

/conf/play.plugins

1500:com.typesafe.plugin.CommonsMailerPlugin

このライブラリはプラグインとして動作するので /conf/play.plugins ファイルを作成し、 Play アプリケーションから呼び出せるように登録します。

/conf/application.conf

# Mailer
# ~~~~~
smtp.host=localhost
smtp.mock=true

メールを送信するための設定を /conf/application.conf に事前に入れておく必要があります。今回は開発用ということでコンソールにログ出力されるだけの Mock 機能を利用します。

プロダクション環境などで実際にメールを飛ばす場合は別途設定ファイルを設けて、実行時に設定を切り替えられるようにしておきましょう。

これでアプリケーションから呼び出す準備ができました。

メールテンプレート作成

今回は HTML 形式とテキスト形式両方を送信してみようと思うので、それぞれテンプレートを用意します。なお Play の view テンプレートは HTML/XML/TEXT 形式に対応しています。

http://www.playframework.com/documentation/2.2.x/ScalaTemplates

/app/views/mail/registeredAccount.scala.txt

@(account: Account)(implicit lang: Lang)
@lang match {
    case Lang("ja", _) => {
進捗どうですか? @account.name & @account.mailAddress
    }
    case _ => {
How is your progress? @account.name & @account.mailAddress
    }
}

/app/views/mail/registeredAccount.scala.html

@(account: Account)(implicit lang: Lang)
@lang match {
    case Lang("ja", _) => {  
        <html>
            <body>
                <h1>進捗どうですか? @account.name &amp; @account.mailAddress</h1>
            </body>
        </html>
    }
    case _ => {
        <html>
            <body>
                <h1>How is your progress? @account.name &amp; @account.mailAddress</h1>
            </body>
        </html>
    }
}

メール送信

先ほど記述した HTML メール、テキストメールを指定し、 body メソッドを介してメールの内容を取得することが出来ます。 trim はテキストメールで前後に無駄な改行が入ってしまい、ユーザーが表示した際に不要に改行されてしまうのを防止するために入れています。

import play.api.Play.current
import com.typesafe.plugin._

val mail = use[MailerPlugin].email
mail.setSubject("Registered account")
mail.setRecipient(s"${account.name} <${account.mailAddress}>")
mail.setFrom("Playframework <noreply@example.com>")
mail.send(
  views.txt.mail.registeredAccount(account).body.trim,
  views.html.mail.registeredAccount(account).body.trim
)

メールテンプレートファイルを移動、リネームしてしまってもコンパイルエラーになりますので、リソースがみつからないという実行時エラーを避けられるメリットがあります。

国際化

メッセージ API と多言語対応にも記述されていますが、 /application.conf に言語設定を最初に行っておく必要があります。

application.langs="en,ja"

Play は HTTP リクエストに含まれるロケール情報、またはレスポンスを返すときに withLang メソッドによって生成された cookie によって言語切替が行えるようになっています。

そのためテンプレートに Lang オブジェクトを暗黙的引数として入れておけば、コントローラーから渡される RequestHeader からユーザーの言語情報を自動で取得できるようになり、あとはその渡された言語ごとにメールテンプレートを切り替えれば国際化対応が行えます。

match でわざわざ分岐させず Play が提供している Messages オブジェクトを介して外部ファイルによる国際化対応もできるでしょう。

まとめ

Scala 2.10 からは String Interpolation 機能によってタイプセーフに文字列を生成できますが、 今回は Play の view テンプレートを使うことで得られるいくつかのメリットを紹介してみました。

サンプルは Github に公開しています。

https://github.com/tsuyoshizawa/play-mail-app

明日は kyu999 さんです。お楽しみに。