Skip to content

Commit

Permalink
Play integration module, but without tests yet.
Browse files Browse the repository at this point in the history
  • Loading branch information
makkarpov committed Apr 20, 2016
1 parent c9da1f0 commit 8e2f059
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ sudo: false
scala:
- 2.11.7

# Play 2.5.0 requires Java 8
jdk:
- oraclejdk8

script:
- sbt +test
- sbt "set version += \"-SNAPSHOT\"" +publishLocal scripted
Expand Down
18 changes: 18 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,24 @@ lazy val scalingua = project
)
.dependsOn(core)

lazy val play = project
.settings(common:_*)
.settings(
name := "Scalingua Play! module",
normalizedName := "scalingua-play",
description := "An integration module for Play! Framework",

// Recent versions of Play! supports only recent version of Scala.
// We should keep `crossPath` to keep naming consistent
scalaVersion := "2.11.7",
crossScalaVersions := Seq(scalaVersion.value),

libraryDependencies ++= Seq(
"com.typesafe.play" %% "twirl-api" % "1.1.1",
"com.typesafe.play" %% "play" % "2.5.0"
)
).dependsOn(scalingua)

lazy val plugin = project
.in(file("sbt-plugin"))
.settings(common:_*)
Expand Down
122 changes: 122 additions & 0 deletions play/src/main/scala/ru/makkarpov/scalingua/play/I18n.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/******************************************************************************
* Copyright © 2016 Maxim Karpov *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/

package ru.makkarpov.scalingua.play

import play.api.http.HeaderNames
import play.api.mvc.RequestHeader
import play.twirl.api.Html
import ru.makkarpov.scalingua
import ru.makkarpov.scalingua._

import scala.language.experimental.macros
import scala.language.implicitConversions

trait I18n extends scalingua.I18n {
type LHtml = LValue[Html]

implicit val htmlOutputFormat = new OutputFormat[Html] {
override def convert(s: String): Html = Html(s)

override def escape(s: String): String = {
val ret = new StringBuilder
var i = 0
ret.sizeHint(s.length)
while (i < s.length) {
s.charAt(i) match {
case '<' => ret.append("&lt;")
case '>' => ret.append("&gt;")
case '"' => ret.append("&quot;")
case '\'' => ret.append("&#x27;")
case '&' => ret.append("&amp;")
case c => ret += c
}
i += 1
}
ret.result()
}
}

// The conversion from `RequestHeader` to `LanguageId` will imply information loss:
// Suppose browser sent the following language precedence list `xx_YY`, `zz_WW`, `en_US`.
// This conversion will have no information about available languages, so it will result in
// `LanguageId("xx", "YY")` even if this language is absent. By supplying implicit `Messages`
// too, we will have enough information to skip unsupported languages and return supported
// one with respect to priority.

implicit def requestHeader2Language(implicit rq: RequestHeader, msg: Messages): Language = {
val langs = (for {
value0 <- rq.headers(HeaderNames.ACCEPT_LANGUAGE).split(',')
value = value0.trim
} yield {
RequestHeader.qPattern.findFirstMatchIn(value) match {
case Some(m) => (m.group(1).toDouble, m.before.toString)
case None => (1.0, value) // “The default value is q=1.”
}
}).sortBy(_._1).iterator

while (langs.hasNext) {
val (_, id) = langs.next()
val lng = LanguageId.get(id)

if (lng.isDefined && msg.contains(lng.get))
return msg(lng.get)
}

Language.English
}

implicit def stringContext2Interpolator1(sc: StringContext): I18n.StringInterpolator =
new I18n.StringInterpolator(sc)

def th(msg: String, args: (String, Any)*)(implicit lang: Language, outputFormat: OutputFormat[Html]): Html =
macro Macros.singular[Html]

def lth(msg: String, args: (String, Any)*)(implicit outputFormat: OutputFormat[Html]): LHtml =
macro Macros.lazySingular[Html]

def tch(ctx: String, msg: String, args: (String, Any)*)(implicit lang: Language, outputFormat: OutputFormat[Html]): Html =
macro Macros.singularCtx[Html]

def ltch(ctx: String, msg: String, args: (String, Any)*)(implicit outputFormat: OutputFormat[Html]): LHtml =
macro Macros.lazySingularCtx[Html]

def p(msg: String, msgPlural: String, n: Long, args: (String, Any)*)
(implicit lang: Language, outputFormat: OutputFormat[Html]): Html =
macro Macros.plural[Html]

def lp(msg: String, msgPlural: String, n: Long, args: (String, Any)*)
(implicit outputFormat: OutputFormat[Html]): LHtml =
macro Macros.lazyPlural[Html]

def pc(ctx: String, msg: String, msgPlural: String, n: Long, args: (String, Any)*)
(implicit lang: Language, outputFormat: OutputFormat[Html]): Html =
macro Macros.pluralCtx[Html]

def lpc(ctx: String, msg: String, msgPlural: String, n: Long, args: (String, Any)*)
(implicit outputFormat: OutputFormat[Html]): LHtml =
macro Macros.lazyPluralCtx[Html]
}

object I18n extends I18n {
class StringInterpolator(val sc: StringContext) extends AnyVal {
def h(args: Any*)(implicit lang: Language, outputFormat: OutputFormat[Html]): Html =
macro Macros.interpolate[Html]

def lh(args: Any*)(implicit outputFormat: OutputFormat[Html]): LHtml =
macro Macros.lazyInterpolate[Html]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/******************************************************************************
* Copyright © 2016 Maxim Karpov *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/

package ru.makkarpov.scalingua.play

import javax.inject.{Inject, Provider}

import play.api.{Application, Environment}
import ru.makkarpov.scalingua.Messages

class MessagesProvider @Inject()(conf: ScalinguaConfig, env: Environment) extends Provider[Messages] {
override def get(): Messages = Messages.compiledContext(env.classLoader, conf.localePackage)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/******************************************************************************
* Copyright © 2016 Maxim Karpov *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/

package ru.makkarpov.scalingua.play

import play.api.Configuration

class ScalinguaConfig(cfg: Configuration) {
val localePackage: String = cfg.getString("scalingua.package").getOrElse("locales")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/******************************************************************************
* Copyright © 2016 Maxim Karpov *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/

package ru.makkarpov.scalingua.play

import javax.inject.{Inject, Provider}

import play.api.Configuration

class ScalinguaConfigProvider @Inject() (cfg: Configuration) extends Provider[ScalinguaConfig] {
override def get(): ScalinguaConfig = new ScalinguaConfig(cfg)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/******************************************************************************
* Copyright © 2016 Maxim Karpov *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/

package ru.makkarpov.scalingua.play

import javax.inject.Inject

import play.api.{Application, BuiltInComponentsFromContext, Configuration, Environment}
import play.api.inject.{Binding, Module}
import ru.makkarpov.scalingua.Messages

trait ScalinguaComponents {
def configuration: Configuration
def environment: Environment

lazy val messages = new MessagesProvider(new ScalinguaConfigProvider(configuration).get(), environment).get()
}

class ScalinguaModule extends Module {
override def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]] = Seq(
bind[ScalinguaConfig].toProvider[ScalinguaConfigProvider],
bind[Messages].toProvider[MessagesProvider]
)
}

0 comments on commit 8e2f059

Please sign in to comment.