make perf keys translatable; only rapid & classical for now

perfI18n
Thibault Duplessis 2020-02-13 12:20:19 -06:00
parent ad13414fbe
commit 5b006658da
27 changed files with 78 additions and 65 deletions

View File

@ -20,7 +20,7 @@ final class Challenge(
def all = Auth { implicit ctx => me =>
XhrOrRedirectHome {
api allFor me.id map { all =>
Ok(env.challenge.jsonView(all, ctx.lang)) as JSON
Ok(env.challenge.jsonView(all)) as JSON
}
}
}
@ -183,7 +183,7 @@ final class Challenge(
}
def apiCreate(userId: String) = ScopedBody(_.Challenge.Write, _.Bot.Play) { implicit req => me =>
implicit val lang = lila.i18n.I18nLangPicker(req, me.some)
implicit val lang = lila.i18n.I18nLangPicker(req, me.lang)
env.setup.forms.api.bindFromRequest.fold(
jsonFormErrorDefaultLang,
config =>

View File

@ -471,7 +471,7 @@ abstract private[controllers] class LilaController(val env: Env)
}
private def getAndSaveLang(req: RequestHeader, user: Option[UserModel]): Lang = {
val lang = lila.i18n.I18nLangPicker(req, user)
val lang = lila.i18n.I18nLangPicker(req, user.flatMap(_.lang))
user.filter(_.lang.fold(true)(_ != lang.code)) foreach { env.user.repo.setLang(_, lang) }
lang
}
@ -616,7 +616,7 @@ abstract private[controllers] class LilaController(val env: Env)
jsonFormError(err)(lila.i18n.defaultLang)
protected def jsonFormErrorFor(err: Form[_], req: RequestHeader, user: Option[UserModel]) =
jsonFormError(err)(lila.i18n.I18nLangPicker(req, user))
jsonFormError(err)(lila.i18n.I18nLangPicker(req, user.flatMap(_.lang)))
protected def pageHit(req: RequestHeader): Unit =
if (HTTPRequest isHuman req) lila.mon.http.path(req.path).increment()

View File

@ -5,6 +5,7 @@ import controllers.routes
import lila.app.ui.ScalatagsTemplate._
import lila.tournament.{ Schedule, Tournament }
import lila.user.User
import lila.rating.PerfType
import play.api.libs.json.Json
@ -46,11 +47,12 @@ trait TournamentHelper { self: I18nHelper with DateHelper with UserHelper =>
private val replacements = List(
"Lichess " -> "",
"Marathon" -> icon('\\'),
"HyperBullet" -> s"H${icon(lila.rating.PerfType.Bullet.iconChar)}",
"SuperBlitz" -> s"S${icon(lila.rating.PerfType.Blitz.iconChar)}"
) ::: lila.rating.PerfType.leaderboardable.map { pt =>
"HyperBullet" -> s"H${icon(PerfType.Bullet.iconChar)}",
"SuperBlitz" -> s"S${icon(PerfType.Blitz.iconChar)}"
) ::: PerfType.leaderboardable.filterNot(PerfType.translated.contains).map { pt =>
pt.name -> icon(pt.iconChar)
}
def apply(name: String): Frag = raw {
replacements.foldLeft(name) {
case (n, (from, to)) => n.replace(from, to)

View File

@ -46,7 +46,7 @@ trait UserHelper { self: I18nHelper with StringHelper with NumberHelper =>
)
def showPerfRating(perfType: PerfType, perf: Perf)(implicit lang: Lang): Frag =
showPerfRating(perf.intRating, perfType.name, perf.nb, perf.provisional, perfType.iconChar)
showPerfRating(perf.intRating, perfType.trans, perf.nb, perf.provisional, perfType.iconChar)
def showPerfRating(u: User, perfType: PerfType)(implicit lang: Lang): Frag =
showPerfRating(perfType, u perfs perfType)

View File

@ -90,7 +90,7 @@ object activity {
iconTag(pt.iconChar),
scoreFrag(score),
div(
trans.activity.playedNbGames.plural(score.size, score.size, pt.name),
trans.activity.playedNbGames.plural(score.size, score.size, pt.trans),
score.rp.filterNot(_.isEmpty).map(ratingProgFrag)
)
)

View File

@ -31,7 +31,7 @@ object bits {
if (c.variant.exotic)
views.html.game.bits.variantLink(c.variant, variantName(c.variant))
else
c.perfType.name,
c.perfType.trans,
br,
span(cls := "clock")(
c.daysPerTurn map { days =>

View File

@ -101,7 +101,7 @@ object teacherDashboard {
a(
cls := progress.perfType.key.active(pt.key),
href := routes.Clas.progress(c.id.value, pt.key, progress.days)
)(pt.name),
)(pt.trans),
}
)
),
@ -122,7 +122,7 @@ object teacherDashboard {
thead(
tr(
th(attr("data-sort-default") := "1")(
trans.clas.variantXOverLastY(progress.perfType.name, trans.nbDays.txt(progress.days)),
trans.clas.variantXOverLastY(progress.perfType.trans, trans.nbDays.txt(progress.days)),
sortNumberTh(trans.rating()),
sortNumberTh(trans.clas.progress()),
sortNumberTh(if (progress.isPuzzle) trans.puzzles() else trans.games()),

View File

@ -159,7 +159,7 @@ object forms {
raw(s"""<strong data-icon="${perfType.iconChar}">${me
.perfs(perfType.key)
.map(_.intRating)
.getOrElse("?")}</strong> ${perfType.name}""")
.getOrElse("?")}</strong> ${perfType.trans}""")
)
)
}

View File

@ -65,7 +65,7 @@ object variant {
cls := List("text" -> true, "active" -> active.has(pt)),
href := routes.Page.variant(pt.key),
dataIcon := pt.iconChar
)(pt.name)
)(pt.trans)
}
),
div(cls := s"page-menu__content box $klass")(body)

View File

@ -15,7 +15,7 @@ object ratingDistribution {
def apply(perfType: PerfType, data: List[Int])(implicit ctx: Context) =
views.html.base.layout(
title = trans.weeklyPerfTypeRatingDistribution.txt(perfType.name),
title = trans.weeklyPerfTypeRatingDistribution.txt(perfType.trans),
moreCss = cssTag("user.rating.stats"),
wrapClass = "full-screen-force",
moreJs = frag(
@ -36,13 +36,13 @@ object ratingDistribution {
trans.weeklyPerfTypeRatingDistribution(
views.html.base.bits.mselect(
"variant-stats",
span(perfType.name),
span(perfType.trans),
PerfType.leaderboardable map { pt =>
a(
dataIcon := pt.iconChar,
cls := (perfType == pt).option("current"),
href := routes.Stat.ratingDistribution(pt.key)
)(pt.name)
)(pt.trans)
}
)
)
@ -53,21 +53,21 @@ object ratingDistribution {
case (under, sum) =>
div(
trans
.nbPerfTypePlayersThisWeek(raw(s"""<strong>${sum.localize}</strong>"""), perfType.name),
.nbPerfTypePlayersThisWeek(strong(sum.localize), perfType.trans),
br,
trans.yourPerfTypeRatingIsRating(perfType.name, raw(s"""<strong>$rating</strong>""")),
trans.yourPerfTypeRatingIsRating(perfType.trans, strong(rating)),
br,
trans.youAreBetterThanPercentOfPerfTypePlayers(
raw(s"""<strong>${(under * 100.0 / sum).round}%</strong>"""),
perfType.name
strong((under * 100.0 / sum).round),
perfType.trans
)
)
}
} getOrElse div(
trans.nbPerfTypePlayersThisWeek
.plural(data.sum, raw(s"""<strong>${data.sum.localize}</strong>"""), perfType.name),
.plural(data.sum, strong(data.sum.localize), perfType.trans),
br,
trans.youDoNotHaveAnEstablishedPerfTypeRating(perfType.name)
trans.youDoNotHaveAnEstablishedPerfTypeRating(perfType.trans)
)
),
div(id := "rating_distribution_chart")(spinner)

View File

@ -86,7 +86,7 @@ object list {
private def userTopPerf(users: List[User.LightPerf], perfType: PerfType)(implicit lang: Lang) =
st.section(cls := "user-top")(
h2(cls := "text", dataIcon := perfType.iconChar)(
a(href := routes.User.topNb(200, perfType.key))(perfType.name)
a(href := routes.User.topNb(200, perfType.key))(perfType.trans)
),
ol(users map { l =>
li(

View File

@ -24,14 +24,14 @@ object perfStat {
ratingChart: Option[String]
)(implicit ctx: Context) =
views.html.base.layout(
title = s"${u.username} ${perfStats.txt(perfType.name)} stats",
title = s"${u.username} - ${perfStats.txt(perfType.trans)}",
robots = false,
moreJs = frag(
jsAt("compiled/user.js"),
ratingChart.map { rc =>
frag(
jsTag("chart/ratingHistory.js"),
embedJsUnsafe(s"lichess.ratingHistoryChart($rc,'${perfType.name}');")
embedJsUnsafe(s"lichess.ratingHistoryChart($rc,'${perfType.trans}');")
)
}
),
@ -43,7 +43,7 @@ object perfStat {
div(cls := "box__top")(
h1(
a(href := routes.User.show(u.username))(u.username),
span(perfStats(perfType.name))
span(perfStats(perfType.trans))
),
div(cls := "box__top__actions")(
u.perfs(perfType).nb > 0 option a(
@ -86,7 +86,7 @@ object perfStat {
span(cls := "details")(
trans.youAreBetterThanPercentOfPerfTypePlayers(
a(href := routes.Stat.ratingDistribution(perfType.key))(strong(percentile, "%")),
a(href := routes.Stat.ratingDistribution(perfType.key))(perfType.name)
a(href := routes.Stat.ratingDistribution(perfType.key))(perfType.trans)
)
)
}

View File

@ -19,7 +19,7 @@ object side {
def showNonEmptyPerf(perf: lila.rating.Perf, perfType: PerfType) =
perf.nonEmpty option showPerf(perf, perfType)
def showPerf(perf: lila.rating.Perf, perfType: PerfType, name: Option[String] = none) = {
def showPerf(perf: lila.rating.Perf, perfType: PerfType) = {
val isGame = lila.rating.PerfType.isGame(perfType)
a(
dataIcon := perfType.iconChar,
@ -31,7 +31,7 @@ object side {
),
href := isGame option routes.User.perfStat(u.username, perfType.key).url,
span(
h3(name.getOrElse(perfType.name).toUpperCase),
h3(perfType.trans),
st.rating(
strong(
perf.glicko.intRating,

View File

@ -12,16 +12,16 @@ object top {
def apply(perfType: lila.rating.PerfType, users: List[User.LightPerf])(implicit ctx: Context) = {
val title = s"${perfType.name} top 200"
val title = s"${perfType.trans} top 200"
views.html.base.layout(
title = title,
moreCss = cssTag("slist"),
openGraph = lila.app.ui
.OpenGraph(
title = s"Leaderboard of ${perfType.name}",
title = s"Leaderboard of ${perfType.trans}",
url = s"$netBaseUrl${routes.User.topNb(200, perfType.key).url}",
description = s"The 200 best chess players in ${perfType.name}, sorted by rating"
description = s"The 200 best chess players in ${perfType.trans}, sorted by rating"
)
.some
)(

View File

@ -41,7 +41,7 @@ object chart {
data.perfResults.map {
case (pt, res) => {
tr(
th(iconTag(pt.iconChar, pt.name)),
th(iconTag(pt.iconChar, pt.trans)),
td(res.nb.localize),
td(res.points.median.map(_.toInt)),
td(res.points.sum.localize),

View File

@ -69,7 +69,7 @@ lazy val api = module("api",
) aggregate (moduleRefs: _*)
lazy val i18n = module("i18n",
Seq(common, db, user, hub),
Seq(common, db, hub),
Seq(scalatags)
).settings(
sourceGenerators in Compile += Def.task {
@ -129,7 +129,7 @@ lazy val common = module("common",
)
lazy val rating = module("rating",
Seq(common, db, memo),
Seq(common, db, memo, i18n),
reactivemongo.bundle
)
@ -174,7 +174,7 @@ lazy val timeline = module("timeline",
)
lazy val event = module("event",
Seq(common, db, memo, i18n),
Seq(common, db, memo, i18n, user),
Seq(scalatags) ++ reactivemongo.bundle
)

View File

@ -97,6 +97,6 @@ final class EventStream(
)
private def toJson(c: Challenge) = Json.obj(
"type" -> "challenge",
"challenge" -> challengeJsonView(none)(c)
"challenge" -> challengeJsonView(none)(c)(lila.i18n.defaultLang)
)
}

View File

@ -4,7 +4,7 @@ import play.api.libs.json.{ JsArray, JsObject, Json }
import lila.game.Pov
import lila.lobby.SeekApi
import lila.pool.JsonView.poolConfigJsonWriter
import lila.pool.PoolConfig.poolConfigJsonWriter
import lila.setup.FilterConfig
import lila.user.UserContext

View File

@ -127,7 +127,7 @@ final class ChallengeApi(
_ flatMap lila.i18n.I18nLangPicker.byStr getOrElse lila.i18n.defaultLang
}
} yield Bus.publish(
SendTo(userId, lila.socket.Socket.makeMessage("challenges", jsonView(all, lang))),
SendTo(userId, lila.socket.Socket.makeMessage("challenges", jsonView(all)(lang))),
"socketUsers"
)

View File

@ -30,18 +30,20 @@ final class JsonView(
.add("lag" -> UserLagCache.getLagRating(r.id))
}
def apply(a: AllChallenges, lang: Lang): JsObject = Json.obj(
def apply(a: AllChallenges)(implicit lang: Lang): JsObject = Json.obj(
"in" -> a.in.map(apply(Direction.In.some)),
"out" -> a.out.map(apply(Direction.Out.some)),
"i18n" -> lila.i18n.JsDump.keysToObject(i18nKeys, lang)
)
def show(challenge: Challenge, socketVersion: SocketVersion, direction: Option[Direction]) = Json.obj(
def show(challenge: Challenge, socketVersion: SocketVersion, direction: Option[Direction])(
implicit lang: Lang
) = Json.obj(
"challenge" -> apply(direction)(challenge),
"socketVersion" -> socketVersion
)
def apply(direction: Option[Direction])(c: Challenge): JsObject =
def apply(direction: Option[Direction])(c: Challenge)(implicit lang: Lang): JsObject =
Json
.obj(
"id" -> c.id,
@ -69,7 +71,7 @@ final class JsonView(
"color" -> c.colorChoice.toString.toLowerCase,
"perf" -> Json.obj(
"icon" -> iconChar(c).toString,
"name" -> c.perfType.name
"name" -> c.perfType.trans
)
)
.add("direction" -> direction.map(_.name))

View File

@ -691,6 +691,8 @@ val `agreementAccount` = new I18nKey("agreementAccount")
val `agreementPolicy` = new I18nKey("agreementPolicy")
val `searchOrStartNewDiscussion` = new I18nKey("searchOrStartNewDiscussion")
val `edit` = new I18nKey("edit")
val `rapid` = new I18nKey("rapid")
val `classical` = new I18nKey("classical")
val `opponentLeftCounter` = new I18nKey("opponentLeftCounter")
val `mateInXHalfMoves` = new I18nKey("mateInXHalfMoves")
val `nextCaptureOrPawnMoveInXHalfMoves` = new I18nKey("nextCaptureOrPawnMoveInXHalfMoves")

View File

@ -3,13 +3,10 @@ package lila.i18n
import play.api.mvc.RequestHeader
import play.api.i18n.Lang
import lila.user.User
object I18nLangPicker {
def apply(req: RequestHeader, user: Option[User]): Lang =
user
.flatMap(_.lang)
def apply(req: RequestHeader, userLang: Option[String]): Lang =
userLang
.orElse(req.session get "lang")
.flatMap(Lang.get)
.flatMap(findCloser)

View File

@ -1,15 +0,0 @@
package lila.pool
import play.api.libs.json._
object JsonView {
implicit val poolConfigJsonWriter = OWrites[PoolConfig] { p =>
Json.obj(
"id" -> p.id.value,
"lim" -> p.clock.limitInMinutes,
"inc" -> p.clock.incrementSeconds,
"perf" -> p.perfType.name
)
}
}

View File

@ -22,4 +22,14 @@ object PoolConfig {
case class Wave(every: FiniteDuration, players: NbPlayers)
def clockToId(clock: chess.Clock.Config) = Id(clock.show)
import play.api.libs.json._
implicit val poolConfigJsonWriter = OWrites[PoolConfig] { p =>
Json.obj(
"id" -> p.id.value,
"lim" -> p.clock.limitInMinutes,
"inc" -> p.clock.incrementSeconds,
"perf" -> p.perfType.name
)
}
}

View File

@ -1,8 +1,8 @@
package lila.rating
import chess.Centis
import chess.Speed
import play.api.i18n.Lang
sealed abstract class PerfType(
val id: Perf.ID,
@ -15,6 +15,8 @@ sealed abstract class PerfType(
def shortName = name
def iconString = iconChar.toString
def trans(implicit lang: Lang): String = PerfType.trans(this)
}
object PerfType {
@ -283,4 +285,14 @@ object PerfType {
def iconByVariant(variant: chess.variant.Variant): Char =
byVariant(variant).fold('C')(_.iconChar)
import lila.i18n.I18nKeys
def trans(pt: PerfType)(implicit lang: Lang): String = pt match {
case PerfType.Rapid => I18nKeys.rapid.txt()
case PerfType.Classical => I18nKeys.classical.txt()
case PerfType.Puzzle => I18nKeys.puzzles.txt()
case pt => pt.name
}
val translated: Set[PerfType] = Set(PerfType.Rapid, PerfType.Classical, PerfType.Puzzle)
}

View File

@ -840,4 +840,6 @@ computer analysis, game chat and shareable URL.</string>
<string name="agreementPolicy">I agree that I will follow all Lichess policies.</string>
<string name="searchOrStartNewDiscussion">Search or start new conversation</string>
<string name="edit">Edit</string>
<string name="rapid">Rapid</string>
<string name="classical">Classical</string>
</resources>

View File

@ -63,6 +63,7 @@
h3 {
@extend %roboto;
font-size: 1.1em;
text-transform: uppercase;
letter-spacing: .05em;
margin: 0;
display: inline-block;