parent
ed7ae6a48c
commit
4654058dcb
|
@ -23,19 +23,19 @@ final class Dasher(env: Env) extends LilaController(env) {
|
|||
trans.boardSize,
|
||||
trans.pieceSet,
|
||||
trans.preferences.zenMode
|
||||
)
|
||||
).map(_.key)
|
||||
|
||||
private val translationsAnon = List(
|
||||
trans.signIn,
|
||||
trans.signUp
|
||||
) ::: translationsBase
|
||||
).map(_.key) ::: translationsBase
|
||||
|
||||
private val translationsAuth = List(
|
||||
trans.profile,
|
||||
trans.inbox,
|
||||
trans.preferences.preferences,
|
||||
trans.logOut
|
||||
) ::: translationsBase
|
||||
).map(_.key) ::: translationsBase
|
||||
|
||||
private def translations(implicit ctx: Context) =
|
||||
lila.i18n.JsDump.keysToObject(
|
||||
|
@ -43,7 +43,7 @@ final class Dasher(env: Env) extends LilaController(env) {
|
|||
ctx.lang
|
||||
) ++ lila.i18n.JsDump.keysToObject(
|
||||
// the language settings should never be in a totally foreign language
|
||||
List(trans.language),
|
||||
List(trans.language.key),
|
||||
if (I18nLangPicker.allFromRequestHeaders(ctx.req).has(ctx.lang)) ctx.lang
|
||||
else I18nLangPicker.bestFromRequestHeaders(ctx.req) | enLang
|
||||
)
|
||||
|
|
|
@ -597,7 +597,7 @@ abstract private[controllers] class LilaController(val env: Env)
|
|||
.mapValues { errors =>
|
||||
JsArray {
|
||||
errors.map { e =>
|
||||
JsString(lila.i18n.Translator.txt.literal(e.message, lila.i18n.I18nDb.Site, e.args, lang))
|
||||
JsString(lila.i18n.Translator.txt.literal(e.message, e.args, lang))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import play.api.data._
|
|||
|
||||
import lila.api.Context
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.i18n.I18nDb
|
||||
|
||||
trait FormHelper { self: I18nHelper =>
|
||||
|
||||
|
@ -14,7 +13,7 @@ trait FormHelper { self: I18nHelper =>
|
|||
def errMsg(form: Form[_])(implicit ctx: Context): Frag = errMsg(form.errors)
|
||||
|
||||
def errMsg(error: FormError)(implicit ctx: Context): Frag =
|
||||
p(cls := "error")(transKey(error.message, I18nDb.Site, error.args))
|
||||
p(cls := "error")(transKey(error.message, error.args))
|
||||
|
||||
def errMsg(errors: Seq[FormError])(implicit ctx: Context): Frag =
|
||||
errors map errMsg
|
||||
|
@ -39,7 +38,7 @@ trait FormHelper { self: I18nHelper =>
|
|||
private def errors(errs: Seq[FormError])(implicit ctx: Context): Frag = errs map error
|
||||
private def errors(field: Field)(implicit ctx: Context): Frag = errors(field.errors)
|
||||
private def error(err: FormError)(implicit ctx: Context): Frag =
|
||||
p(cls := "error")(transKey(err.message, I18nDb.Site, err.args))
|
||||
p(cls := "error")(transKey(err.message, err.args))
|
||||
|
||||
private def validationModifiers(field: Field): Seq[Modifier] = field.constraints collect {
|
||||
/* Can't use constraint.required, because it applies to optional fields
|
||||
|
|
|
@ -5,22 +5,19 @@ import play.api.libs.json.JsObject
|
|||
import play.api.i18n.Lang
|
||||
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.i18n.{ I18nDb, I18nKey, JsDump, LangList, TimeagoLocales, Translator }
|
||||
import lila.i18n.{ I18nKey, JsDump, LangList, MessageKey, TimeagoLocales, Translator }
|
||||
import lila.user.UserContext
|
||||
|
||||
trait I18nHelper extends HasEnv with UserContext.ToLang {
|
||||
|
||||
def transKey(key: String, db: I18nDb.Ref, args: Seq[Any] = Nil)(implicit lang: Lang): Frag =
|
||||
Translator.frag.literal(key, db, args, lang)
|
||||
def transKey(key: MessageKey, args: Seq[Any] = Nil)(implicit lang: Lang): Frag =
|
||||
Translator.frag.literal(key, args, lang)
|
||||
|
||||
def i18nJsObject(keys: Seq[I18nKey])(implicit lang: Lang): JsObject =
|
||||
def i18nJsObject(keys: Seq[MessageKey])(implicit lang: Lang): JsObject =
|
||||
JsDump.keysToObject(keys, lang)
|
||||
|
||||
def i18nOptionJsObject(keys: Option[I18nKey]*)(implicit lang: Lang): JsObject =
|
||||
JsDump.keysToObject(keys.flatten, lang)
|
||||
|
||||
def i18nFullDbJsObject(db: I18nDb.Ref)(implicit lang: Lang): JsObject =
|
||||
JsDump.dbToObject(db, lang)
|
||||
JsDump.keysToObject(keys.collect { case Some(k) => k.key }, lang)
|
||||
|
||||
def timeagoLocaleScript(implicit ctx: lila.api.Context): String = {
|
||||
TimeagoLocales.js.get(ctx.lang.code) orElse
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package views.html.analyse
|
||||
|
||||
import lila.api.Context
|
||||
import play.api.i18n.Lang
|
||||
|
||||
import lila.app.templating.Environment._
|
||||
import lila.i18n.{ I18nKeys => trans }
|
||||
|
||||
private object jsI18n {
|
||||
|
||||
def apply()(implicit ctx: Context) = i18nJsObject(translations)
|
||||
def apply()(implicit lang: Lang) = i18nJsObject(i18nKeys)
|
||||
|
||||
private val translations = List(
|
||||
private val i18nKeys = List(
|
||||
trans.flipBoard,
|
||||
trans.gameAborted,
|
||||
trans.checkmate,
|
||||
|
@ -151,5 +152,5 @@ private object jsI18n {
|
|||
trans.opening,
|
||||
trans.middlegame,
|
||||
trans.endgame
|
||||
)
|
||||
).map(_.key)
|
||||
}
|
||||
|
|
|
@ -235,7 +235,7 @@ object layout {
|
|||
"playing" -> ctx.onlineFriends.playing,
|
||||
"patrons" -> ctx.onlineFriends.patrons,
|
||||
"studying" -> ctx.onlineFriends.studying,
|
||||
"i18n" -> i18nJsObject(List(trans.nbFriendsOnline))
|
||||
"i18n" -> i18nJsObject(i18nKeys)
|
||||
)
|
||||
)
|
||||
)(
|
||||
|
@ -329,4 +329,6 @@ object layout {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
private val i18nKeys = List(trans.nbFriendsOnline.key)
|
||||
}
|
||||
|
|
|
@ -29,10 +29,10 @@ object bits {
|
|||
"duration" -> ctx.pref.animationFactor * animationDuration.toMillis
|
||||
),
|
||||
"is3d" -> ctx.pref.is3d,
|
||||
"i18n" -> i18nJsObject(translations)(ctxLang(ctx))
|
||||
"i18n" -> i18nJsObject(i18nKeyes)
|
||||
)
|
||||
|
||||
private val translations = List(
|
||||
private val i18nKeyes = List(
|
||||
trans.setTheBoard,
|
||||
trans.boardEditor,
|
||||
trans.startPosition,
|
||||
|
@ -51,5 +51,5 @@ object bits {
|
|||
trans.playWithAFriend,
|
||||
trans.analysis,
|
||||
trans.toStudy
|
||||
)
|
||||
).map(_.key)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package views.html.board
|
|||
import play.api.i18n.Lang
|
||||
|
||||
import lila.app.templating.Environment._
|
||||
import lila.i18n.{ I18nKey, I18nKeys => trans }
|
||||
import lila.i18n.{ MessageKey, I18nKeys => trans }
|
||||
|
||||
object userAnalysisI18n {
|
||||
|
||||
|
@ -24,7 +24,7 @@ object userAnalysisI18n {
|
|||
}
|
||||
)
|
||||
|
||||
private val baseTranslations: Vector[I18nKey] = Vector(
|
||||
private val baseTranslations: Vector[MessageKey] = Vector(
|
||||
trans.analysis,
|
||||
trans.flipBoard,
|
||||
trans.backToGame,
|
||||
|
@ -100,9 +100,9 @@ object userAnalysisI18n {
|
|||
// gamebook
|
||||
trans.findTheBestMoveForWhite,
|
||||
trans.findTheBestMoveForBlack
|
||||
)
|
||||
).map(_.key)
|
||||
|
||||
private val cevalTranslations = Vector(
|
||||
private val cevalTranslations: Vector[MessageKey] = Vector(
|
||||
// also uses gameOver
|
||||
trans.depthX,
|
||||
trans.usingServerAnalysis,
|
||||
|
@ -122,9 +122,9 @@ object userAnalysisI18n {
|
|||
trans.multipleLines,
|
||||
trans.cpus,
|
||||
trans.memory
|
||||
)
|
||||
).map(_.key)
|
||||
|
||||
private val explorerTranslations = Vector(
|
||||
private val explorerTranslations: Vector[MessageKey] = Vector(
|
||||
// also uses gameOver, checkmate, stalemate, draw, variantEnding
|
||||
trans.openingExplorerAndTablebase,
|
||||
trans.openingExplorer,
|
||||
|
@ -156,21 +156,21 @@ object userAnalysisI18n {
|
|||
trans.winPreventedBy50MoveRule,
|
||||
trans.lossSavedBy50MoveRule,
|
||||
trans.allSet
|
||||
)
|
||||
).map(_.key)
|
||||
|
||||
private val forecastTranslations = Vector(
|
||||
private val forecastTranslations: Vector[MessageKey] = Vector(
|
||||
trans.conditionalPremoves,
|
||||
trans.addCurrentVariation,
|
||||
trans.playVariationToCreateConditionalPremoves,
|
||||
trans.noConditionalPremoves,
|
||||
trans.playX,
|
||||
trans.andSaveNbPremoveLines
|
||||
)
|
||||
).map(_.key)
|
||||
|
||||
private val advantageChartTranslations = Vector(
|
||||
private val advantageChartTranslations: Vector[MessageKey] = Vector(
|
||||
trans.advantage,
|
||||
trans.opening,
|
||||
trans.middlegame,
|
||||
trans.endgame
|
||||
)
|
||||
).map(_.key)
|
||||
}
|
||||
|
|
|
@ -11,16 +11,18 @@ import controllers.routes
|
|||
|
||||
object index {
|
||||
|
||||
import trans.learn.{ play => _, _ }
|
||||
|
||||
def apply(data: Option[play.api.libs.json.JsValue])(implicit ctx: Context) =
|
||||
views.html.base.layout(
|
||||
title = s"${trans.learn.learnChess.txt()} - ${trans.learn.byPlaying.txt()}",
|
||||
title = s"${learnChess.txt()} - ${byPlaying.txt()}",
|
||||
moreJs = frag(
|
||||
jsAt(s"compiled/lichess.learn${isProd ?? (".min")}.js"),
|
||||
embedJsUnsafe(s"""$$(function() {
|
||||
LichessLearn(document.getElementById('learn-app'), ${safeJsonValue(
|
||||
Json.obj(
|
||||
"data" -> data,
|
||||
"i18n" -> i18nFullDbJsObject(lila.i18n.I18nDb.Learn)
|
||||
"i18n" -> i18nJsObject(i18nKeys)
|
||||
)
|
||||
)})})""")
|
||||
),
|
||||
|
@ -28,8 +30,7 @@ LichessLearn(document.getElementById('learn-app'), ${safeJsonValue(
|
|||
openGraph = lila.app.ui
|
||||
.OpenGraph(
|
||||
title = "Learn chess by playing",
|
||||
description =
|
||||
"You don't know anything about chess? Excellent! Let's have fun and learn to play chess!",
|
||||
description = "You don't know much about chess? Excellent! Let's have fun and learn to play chess!",
|
||||
url = s"$netBaseUrl${routes.Learn.index}"
|
||||
)
|
||||
.some,
|
||||
|
@ -37,4 +38,177 @@ LichessLearn(document.getElementById('learn-app'), ${safeJsonValue(
|
|||
) {
|
||||
main(id := "learn-app")
|
||||
}
|
||||
|
||||
private val i18nKeys: List[lila.i18n.MessageKey] =
|
||||
List(
|
||||
menu,
|
||||
progressX,
|
||||
resetMyProgress,
|
||||
youWillLoseAllYourProgress,
|
||||
trans.learn.play,
|
||||
chessPieces,
|
||||
theRook,
|
||||
itMovesInStraightLines,
|
||||
rookIntro,
|
||||
rookGoal,
|
||||
grabAllTheStars,
|
||||
theFewerMoves,
|
||||
useTwoRooks,
|
||||
rookComplete,
|
||||
theBishop,
|
||||
itMovesDiagonally,
|
||||
bishopIntro,
|
||||
youNeedBothBishops,
|
||||
bishopComplete,
|
||||
theQueen,
|
||||
queenCombinesRookAndBishop,
|
||||
queenIntro,
|
||||
queenComplete,
|
||||
theKing,
|
||||
theMostImportantPiece,
|
||||
kingIntro,
|
||||
theKingIsSlow,
|
||||
lastOne,
|
||||
kingComplete,
|
||||
theKnight,
|
||||
itMovesInAnLShape,
|
||||
knightIntro,
|
||||
knightsHaveAFancyWay,
|
||||
knightsCanJumpOverObstacles,
|
||||
knightComplete,
|
||||
thePawn,
|
||||
itMovesForwardOnly,
|
||||
pawnIntro,
|
||||
pawnsMoveOneSquareOnly,
|
||||
mostOfTheTimePromotingToAQueenIsBest,
|
||||
pawnsMoveForward,
|
||||
captureThenPromote,
|
||||
useAllThePawns,
|
||||
aPawnOnTheSecondRank,
|
||||
grabAllTheStarsNoNeedToPromote,
|
||||
pawnComplete,
|
||||
pawnPromotion,
|
||||
yourPawnReachedTheEndOfTheBoard,
|
||||
itNowPromotesToAStrongerPiece,
|
||||
selectThePieceYouWant,
|
||||
fundamentals,
|
||||
capture,
|
||||
takeTheEnemyPieces,
|
||||
captureIntro,
|
||||
takeTheBlackPieces,
|
||||
takeTheBlackPiecesAndDontLoseYours,
|
||||
captureComplete,
|
||||
protection,
|
||||
keepYourPiecesSafe,
|
||||
protectionIntro,
|
||||
protectionComplete,
|
||||
escape,
|
||||
noEscape,
|
||||
dontLetThemTakeAnyUndefendedPiece,
|
||||
combat,
|
||||
captureAndDefendPieces,
|
||||
combatIntro,
|
||||
combatComplete,
|
||||
checkInOne,
|
||||
attackTheOpponentsKing,
|
||||
checkInOneIntro,
|
||||
checkInOneGoal,
|
||||
checkInOneComplete,
|
||||
outOfCheck,
|
||||
defendYourKing,
|
||||
outOfCheckIntro,
|
||||
escapeWithTheKing,
|
||||
theKingCannotEscapeButBlock,
|
||||
youCanGetOutOfCheckByTaking,
|
||||
thisKnightIsCheckingThroughYourDefenses,
|
||||
escapeOrBlock,
|
||||
outOfCheckComplete,
|
||||
mateInOne,
|
||||
defeatTheOpponentsKing,
|
||||
mateInOneIntro,
|
||||
attackYourOpponentsKing,
|
||||
mateInOneComplete,
|
||||
intermediate,
|
||||
boardSetup,
|
||||
howTheGameStarts,
|
||||
boardSetupIntro,
|
||||
thisIsTheInitialPosition,
|
||||
firstPlaceTheRooks,
|
||||
thenPlaceTheKnights,
|
||||
placeTheBishops,
|
||||
placeTheQueen,
|
||||
placeTheKing,
|
||||
pawnsFormTheFrontLine,
|
||||
boardSetupComplete,
|
||||
castling,
|
||||
theSpecialKingMove,
|
||||
castlingIntro,
|
||||
castleKingSide,
|
||||
castleQueenSide,
|
||||
theKnightIsInTheWay,
|
||||
castleKingSideMovePiecesFirst,
|
||||
castleQueenSideMovePiecesFirst,
|
||||
youCannotCastleIfMoved,
|
||||
youCannotCastleIfAttacked,
|
||||
findAWayToCastleKingSide,
|
||||
findAWayToCastleQueenSide,
|
||||
castlingComplete,
|
||||
enPassant,
|
||||
theSpecialPawnMove,
|
||||
enPassantIntro,
|
||||
blackJustMovedThePawnByTwoSquares,
|
||||
enPassantOnlyWorksImmediately,
|
||||
enPassantOnlyWorksOnFifthRank,
|
||||
takeAllThePawnsEnPassant,
|
||||
enPassantComplete,
|
||||
stalemate,
|
||||
theGameIsADraw,
|
||||
stalemateIntro,
|
||||
stalemateGoal,
|
||||
stalemateComplete,
|
||||
advanced,
|
||||
pieceValue,
|
||||
evaluatePieceStrength,
|
||||
pieceValueIntro,
|
||||
queenOverBishop,
|
||||
takeThePieceWithTheHighestValue,
|
||||
pieceValueComplete,
|
||||
checkInTwo,
|
||||
twoMovesToGiveCheck,
|
||||
checkInTwoIntro,
|
||||
checkInTwoGoal,
|
||||
checkInTwoComplete,
|
||||
whatNext,
|
||||
youKnowHowToPlayChess,
|
||||
register,
|
||||
getAFreeLichessAccount,
|
||||
practice,
|
||||
learnCommonChessPositions,
|
||||
puzzles,
|
||||
exerciseYourTacticalSkills,
|
||||
videos,
|
||||
watchInstructiveChessVideos,
|
||||
playPeople,
|
||||
opponentsFromAroundTheWorld,
|
||||
playMachine,
|
||||
testYourSkillsWithTheComputer,
|
||||
letsGo,
|
||||
stageX,
|
||||
awesome,
|
||||
excellent,
|
||||
greatJob,
|
||||
perfect,
|
||||
outstanding,
|
||||
wayToGo,
|
||||
yesYesYes,
|
||||
youreGoodAtThis,
|
||||
nailedIt,
|
||||
rightOn,
|
||||
stageXComplete,
|
||||
yourScore,
|
||||
next,
|
||||
backToMenu,
|
||||
puzzleFailed,
|
||||
retry
|
||||
).map(_.key)
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ object home {
|
|||
"remainingSeconds" -> (pb.remainingSeconds + 3)
|
||||
)
|
||||
},
|
||||
"i18n" -> i18nJsObject(translations)
|
||||
"i18n" -> i18nJsObject(i18nKeys)
|
||||
)
|
||||
)}"""
|
||||
)
|
||||
|
@ -186,7 +186,7 @@ object home {
|
|||
}
|
||||
}
|
||||
|
||||
private val translations = List(
|
||||
private val i18nKeys = List(
|
||||
trans.realTime,
|
||||
trans.correspondence,
|
||||
trans.nbGamesInPlay,
|
||||
|
@ -212,7 +212,7 @@ object home {
|
|||
trans.lobby,
|
||||
trans.custom,
|
||||
trans.anonymous
|
||||
)
|
||||
).map(_.key)
|
||||
|
||||
private val nbPlaceholder = strong("--,---")
|
||||
}
|
||||
|
|
|
@ -28,9 +28,9 @@ object msg {
|
|||
main(cls := "box msg-app")
|
||||
}
|
||||
|
||||
def jsI18n(implicit ctx: Context) = i18nJsObject(translations)
|
||||
def jsI18n(implicit ctx: Context) = i18nJsObject(i18nKeys)
|
||||
|
||||
private val translations = List(
|
||||
private val i18nKeys = List(
|
||||
trans.inbox,
|
||||
trans.challengeToPlay,
|
||||
trans.block,
|
||||
|
@ -44,5 +44,5 @@ object msg {
|
|||
trans.discussions,
|
||||
trans.today,
|
||||
trans.yesterday
|
||||
)
|
||||
).map(_.key)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package views
|
||||
package html.puzzle
|
||||
|
||||
import lila.api.Context
|
||||
import play.api.i18n.Lang
|
||||
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
|
||||
|
@ -20,9 +21,9 @@ object bits {
|
|||
dataLastmove := lastMove
|
||||
)(cgWrapContent)
|
||||
|
||||
def jsI18n()(implicit ctx: Context) = i18nJsObject(translations)
|
||||
def jsI18n()(implicit lang: Lang) = i18nJsObject(i18nKeys)
|
||||
|
||||
private val translations = List(
|
||||
private val i18nKeys = List(
|
||||
trans.yourPuzzleRatingX,
|
||||
trans.goodMove,
|
||||
trans.butYouCanDoBetter,
|
||||
|
@ -65,5 +66,5 @@ object bits {
|
|||
trans.gameOver,
|
||||
trans.inLocalBrowser,
|
||||
trans.toggleLocalEvaluation
|
||||
)
|
||||
).map(_.key)
|
||||
}
|
||||
|
|
|
@ -28,13 +28,10 @@ object show {
|
|||
analyseNvuiTag,
|
||||
embedJsUnsafe(s"""lichess=window.lichess||{};lichess.relay=${safeJsonValue(
|
||||
Json.obj(
|
||||
"relay" -> data.relay,
|
||||
"study" -> data.study,
|
||||
"data" -> data.analysis,
|
||||
"i18n" -> {
|
||||
board.userAnalysisI18n(withAdvantageChart = true) ++
|
||||
i18nFullDbJsObject(lila.i18n.I18nDb.Study)
|
||||
},
|
||||
"relay" -> data.relay,
|
||||
"study" -> data.study,
|
||||
"data" -> data.analysis,
|
||||
"i18n" -> views.html.study.jsI18n(),
|
||||
"tagTypes" -> lila.study.PgnTags.typesToString,
|
||||
"userId" -> ctx.userId,
|
||||
"chat" -> chatOption.map(c =>
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package views.html.round
|
||||
|
||||
import lila.api.Context
|
||||
import play.api.i18n.Lang
|
||||
|
||||
import lila.app.templating.Environment._
|
||||
import lila.i18n.{ I18nKeys => trans }
|
||||
|
||||
object jsI18n {
|
||||
|
||||
def apply(g: lila.game.Game)(implicit ctx: Context) = i18nJsObject {
|
||||
def apply(g: lila.game.Game)(implicit lang: Lang) = i18nJsObject {
|
||||
baseTranslations ++ {
|
||||
if (g.isCorrespondence) correspondenceTranslations
|
||||
else realtimeTranslations
|
||||
|
@ -21,21 +22,21 @@ object jsI18n {
|
|||
trans.oneDay,
|
||||
trans.nbDays,
|
||||
trans.nbHours
|
||||
)
|
||||
).map(_.key)
|
||||
|
||||
private val realtimeTranslations = Vector(trans.nbSecondsToPlayTheFirstMove)
|
||||
private val realtimeTranslations = Vector(trans.nbSecondsToPlayTheFirstMove).map(_.key)
|
||||
|
||||
private val variantTranslations = Vector(
|
||||
trans.kingInTheCenter,
|
||||
trans.threeChecks,
|
||||
trans.variantEnding
|
||||
)
|
||||
).map(_.key)
|
||||
|
||||
private val tournamentTranslations = Vector(
|
||||
trans.backToTournament,
|
||||
trans.viewTournament,
|
||||
trans.standing
|
||||
)
|
||||
).map(_.key)
|
||||
|
||||
private val baseTranslations = Vector(
|
||||
trans.flipBoard,
|
||||
|
@ -89,5 +90,5 @@ object jsI18n {
|
|||
trans.youPlayTheWhitePieces,
|
||||
trans.youPlayTheBlackPieces,
|
||||
trans.itsYourTurn
|
||||
)
|
||||
).map(_.key)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package views.html.simul
|
||||
|
||||
import play.api.i18n.Lang
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
|
@ -11,7 +13,7 @@ object bits {
|
|||
def link(simulId: lila.simul.Simul.ID): Frag =
|
||||
a(href := routes.Simul.show(simulId))("Simultaneous exhibition")
|
||||
|
||||
def jsI18n()(implicit ctx: Context) = i18nJsObject(baseTranslations)
|
||||
def jsI18n()(implicit lang: Lang) = i18nJsObject(baseTranslations)
|
||||
|
||||
def notFound()(implicit ctx: Context) =
|
||||
views.html.base.layout(
|
||||
|
@ -69,5 +71,5 @@ object bits {
|
|||
trans.by,
|
||||
trans.signIn,
|
||||
trans.mustBeInTeam
|
||||
)
|
||||
).map(_.key)
|
||||
}
|
||||
|
|
|
@ -22,18 +22,9 @@ object ratingDistribution {
|
|||
jsTag("chart/ratingDistribution.js"),
|
||||
embedJsUnsafe(s"""lichess.ratingDistributionChart(${safeJsonValue(
|
||||
Json.obj(
|
||||
"freq" -> data,
|
||||
"myRating" -> ctx.me.map { me =>
|
||||
me.perfs(perfType).intRating
|
||||
},
|
||||
"i18n" -> i18nJsObject(
|
||||
List(
|
||||
trans.players,
|
||||
trans.yourRating,
|
||||
trans.cumulative,
|
||||
trans.glicko2Rating
|
||||
)
|
||||
)
|
||||
"freq" -> data,
|
||||
"myRating" -> ctx.me.map(_.perfs(perfType).intRating),
|
||||
"i18n" -> i18nJsObject(i18nKeys)
|
||||
)
|
||||
)})""")
|
||||
)
|
||||
|
@ -84,4 +75,11 @@ object ratingDistribution {
|
|||
)
|
||||
}
|
||||
|
||||
private val i18nKeys =
|
||||
List(
|
||||
trans.players,
|
||||
trans.yourRating,
|
||||
trans.cumulative,
|
||||
trans.glicko2Rating
|
||||
).map(_.key)
|
||||
}
|
||||
|
|
|
@ -9,17 +9,117 @@ object jsI18n {
|
|||
|
||||
def apply()(implicit lang: Lang) =
|
||||
views.html.board.userAnalysisI18n(withAdvantageChart = true) ++
|
||||
i18nFullDbJsObject(lila.i18n.I18nDb.Study) ++
|
||||
i18nJsObject(translations)
|
||||
i18nJsObject(i18nKeys)
|
||||
|
||||
private val translations = Vector(
|
||||
trans.name,
|
||||
trans.white,
|
||||
trans.black,
|
||||
trans.variant,
|
||||
trans.clearBoard,
|
||||
trans.startPosition,
|
||||
trans.cancel,
|
||||
trans.chat
|
||||
)
|
||||
val i18nKeys: List[lila.i18n.MessageKey] = {
|
||||
import trans.study._
|
||||
List(
|
||||
trans.name,
|
||||
trans.white,
|
||||
trans.black,
|
||||
trans.variant,
|
||||
trans.clearBoard,
|
||||
trans.startPosition,
|
||||
trans.cancel,
|
||||
trans.chat,
|
||||
addNewChapter,
|
||||
addMembers,
|
||||
inviteToTheStudy,
|
||||
pleaseOnlyInvitePeopleYouKnow,
|
||||
searchByUsername,
|
||||
spectator,
|
||||
contributor,
|
||||
kick,
|
||||
leaveTheStudy,
|
||||
youAreNowAContributor,
|
||||
youAreNowASpectator,
|
||||
pgnTags,
|
||||
like,
|
||||
newTag,
|
||||
commentThisPosition,
|
||||
commentThisMove,
|
||||
annotateWithGlyphs,
|
||||
theChapterIsTooShortToBeAnalysed,
|
||||
onlyContributorsCanRequestAnalysis,
|
||||
getAFullComputerAnalysis,
|
||||
makeSureTheChapterIsComplete,
|
||||
allSyncMembersRemainOnTheSamePosition,
|
||||
shareChanges,
|
||||
playing,
|
||||
first,
|
||||
previous,
|
||||
next,
|
||||
last,
|
||||
shareAndExport,
|
||||
cloneStudy,
|
||||
studyPgn,
|
||||
chapterPgn,
|
||||
studyUrl,
|
||||
currentChapterUrl,
|
||||
youCanPasteThisInTheForumToEmbedTheChapter,
|
||||
startAtInitialPosition,
|
||||
startAtX,
|
||||
embedThisChapter,
|
||||
readMoreAboutEmbeddingAStudyChapter,
|
||||
open,
|
||||
xBroughtToYouByY,
|
||||
studyNotFound,
|
||||
editChapter,
|
||||
newChapter,
|
||||
orientation,
|
||||
analysisMode,
|
||||
pinnedChapterComment,
|
||||
saveChapter,
|
||||
clearAnnotations,
|
||||
deleteChapter,
|
||||
deleteThisChapter,
|
||||
clearAllCommentsInThisChapter,
|
||||
rightUnderTheBoard,
|
||||
noPinnedComment,
|
||||
normalAnalysis,
|
||||
hideNextMoves,
|
||||
interactiveLesson,
|
||||
chapterX,
|
||||
empty,
|
||||
startFromInitialPosition,
|
||||
editor,
|
||||
startFromCustomPosition,
|
||||
loadAGameByUrl,
|
||||
loadAPositionFromFen,
|
||||
loadAGameFromPgn,
|
||||
automatic,
|
||||
urlOfTheGame,
|
||||
loadAGameFromXOrY,
|
||||
createChapter,
|
||||
configureLiveBroadcast,
|
||||
createStudy,
|
||||
editStudy,
|
||||
visibility,
|
||||
public,
|
||||
`private`,
|
||||
unlisted,
|
||||
inviteOnly,
|
||||
allowCloning,
|
||||
nobody,
|
||||
onlyMe,
|
||||
contributors,
|
||||
members,
|
||||
everyone,
|
||||
enableSync,
|
||||
yesKeepEveryoneOnTheSamePosition,
|
||||
noLetPeopleBrowseFreely,
|
||||
pinnedStudyComment,
|
||||
start,
|
||||
save,
|
||||
clearChat,
|
||||
deleteTheStudyChatHistory,
|
||||
deleteStudy,
|
||||
deleteTheEntireStudy,
|
||||
whereDoYouWantToStudyThat,
|
||||
nbChapters,
|
||||
nbGames,
|
||||
nbMembers,
|
||||
pasteYourPgnTextHereUpToNbGames
|
||||
).map(_.key)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,9 +41,9 @@ object bits {
|
|||
}
|
||||
)
|
||||
|
||||
def jsI18n(implicit ctx: Context) = i18nJsObject(translations)
|
||||
def jsI18n(implicit ctx: Context) = i18nJsObject(i18nKeys)
|
||||
|
||||
private val translations = List(
|
||||
private val i18nKeys = List(
|
||||
trans.standing,
|
||||
trans.starting,
|
||||
trans.tournamentIsStarting,
|
||||
|
@ -70,5 +70,5 @@ object bits {
|
|||
trans.averageOpponent,
|
||||
trans.ratedTournament,
|
||||
trans.casualTournament
|
||||
)
|
||||
).map(_.key)
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ function keyListFrom(name) {
|
|||
const keys = strings.concat(plurals);
|
||||
resolve({
|
||||
name: name,
|
||||
code: keys.map(k => 'val `' + k + '` = new I18nKey("' + k + '", ' + ucfirst(name) + ')').join('\n') + '\n',
|
||||
code: keys.map(k => 'val `' + k + '` = new I18nKey("' + (name == 'site' ? '' : xmlName(name) + ':') + k + '")').join('\n') + '\n',
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
@ -35,8 +35,6 @@ Promise.all(dbs.map(keyListFrom)).then(objs => {
|
|||
const code = `// Generated with bin/trans-dump.js
|
||||
package lila.i18n
|
||||
|
||||
import I18nDb.{ ${dbs.map(ucfirst).sort().join(', ')} }
|
||||
|
||||
// format: OFF
|
||||
object I18nKeys {
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ lazy val i18n = module("i18n",
|
|||
sourceDir = new File("translation/source"),
|
||||
destDir = new File("translation/dest"),
|
||||
dbs = "site arena emails learn activity coordinates study class contact patron coach broadcast streamer tfa settings preferences team perfStat search".split(' ').toList,
|
||||
compileTo = (sourceManaged in Compile).value / "messages"
|
||||
compileTo = (sourceManaged in Compile).value
|
||||
)
|
||||
}.taskValue,
|
||||
scalacOptions += "-P:silencer:pathFilters=modules/i18n/target"
|
||||
|
|
|
@ -33,7 +33,7 @@ final class JsonView(
|
|||
def apply(a: AllChallenges, lang: Lang): JsObject = Json.obj(
|
||||
"in" -> a.in.map(apply(Direction.In.some)),
|
||||
"out" -> a.out.map(apply(Direction.Out.some)),
|
||||
"i18n" -> translations(lang)
|
||||
"i18n" -> lila.i18n.JsDump.keysToObject(i18nKeys, lang)
|
||||
)
|
||||
|
||||
def show(challenge: Challenge, socketVersion: SocketVersion, direction: Option[Direction]) = Json.obj(
|
||||
|
@ -79,17 +79,13 @@ final class JsonView(
|
|||
if (c.variant == chess.variant.FromPosition) '*'
|
||||
else c.perfType.iconChar
|
||||
|
||||
private def translations(lang: Lang) =
|
||||
lila.i18n.JsDump.keysToObject(
|
||||
List(
|
||||
trans.rated,
|
||||
trans.casual,
|
||||
trans.waiting,
|
||||
trans.accept,
|
||||
trans.decline,
|
||||
trans.viewInFullSize,
|
||||
trans.cancel
|
||||
),
|
||||
lang
|
||||
)
|
||||
private val i18nKeys = List(
|
||||
trans.rated,
|
||||
trans.casual,
|
||||
trans.waiting,
|
||||
trans.accept,
|
||||
trans.decline,
|
||||
trans.viewInFullSize,
|
||||
trans.cancel
|
||||
).map(_.key)
|
||||
}
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
package lila.i18n
|
||||
|
||||
import play.api.i18n.Lang
|
||||
|
||||
object I18nDb {
|
||||
|
||||
sealed trait Ref
|
||||
case object Site extends Ref
|
||||
case object Arena extends Ref
|
||||
case object Emails extends Ref
|
||||
case object Learn extends Ref
|
||||
case object Activity extends Ref
|
||||
case object Coordinates extends Ref
|
||||
case object Study extends Ref
|
||||
case object Clas extends Ref
|
||||
case object Contact extends Ref
|
||||
case object Patron extends Ref
|
||||
case object Coach extends Ref
|
||||
case object Broadcast extends Ref
|
||||
case object Streamer extends Ref
|
||||
case object Tfa extends Ref
|
||||
case object Settings extends Ref
|
||||
case object Preferences extends Ref
|
||||
case object Team extends Ref
|
||||
case object PerfStat extends Ref
|
||||
case object Search extends Ref
|
||||
|
||||
val site: Messages = lila.i18n.db.site.Registry.load
|
||||
val arena: Messages = lila.i18n.db.arena.Registry.load
|
||||
val emails: Messages = lila.i18n.db.emails.Registry.load
|
||||
val learn: Messages = lila.i18n.db.learn.Registry.load
|
||||
val activity: Messages = lila.i18n.db.activity.Registry.load
|
||||
val coordinates: Messages = lila.i18n.db.coordinates.Registry.load
|
||||
val study: Messages = lila.i18n.db.study.Registry.load
|
||||
val clas: Messages = lila.i18n.db.clas.Registry.load
|
||||
val contact: Messages = lila.i18n.db.contact.Registry.load
|
||||
val patron: Messages = lila.i18n.db.patron.Registry.load
|
||||
val coach: Messages = lila.i18n.db.coach.Registry.load
|
||||
val broadcast: Messages = lila.i18n.db.broadcast.Registry.load
|
||||
val streamer: Messages = lila.i18n.db.streamer.Registry.load
|
||||
val tfa: Messages = lila.i18n.db.tfa.Registry.load
|
||||
val settings: Messages = lila.i18n.db.settings.Registry.load
|
||||
val preferences: Messages = lila.i18n.db.preferences.Registry.load
|
||||
val team: Messages = lila.i18n.db.team.Registry.load
|
||||
val perfStat: Messages = lila.i18n.db.perfStat.Registry.load
|
||||
val search: Messages = lila.i18n.db.search.Registry.load
|
||||
|
||||
def apply(ref: Ref): Messages = ref match {
|
||||
case Site => site
|
||||
case Arena => arena
|
||||
case Emails => emails
|
||||
case Learn => learn
|
||||
case Activity => activity
|
||||
case Coordinates => coordinates
|
||||
case Study => study
|
||||
case Clas => clas
|
||||
case Contact => contact
|
||||
case Patron => patron
|
||||
case Coach => coach
|
||||
case Broadcast => broadcast
|
||||
case Streamer => streamer
|
||||
case Tfa => tfa
|
||||
case Settings => settings
|
||||
case Preferences => preferences
|
||||
case Team => team
|
||||
case PerfStat => perfStat
|
||||
case Search => search
|
||||
}
|
||||
|
||||
val langs: Set[Lang] = site.keys.toSet
|
||||
}
|
|
@ -3,21 +3,21 @@ package lila.i18n
|
|||
import play.api.i18n.Lang
|
||||
import scalatags.Text.RawFrag
|
||||
|
||||
final class I18nKey(val key: String, val db: I18nDb.Ref) {
|
||||
final class I18nKey(val key: String) {
|
||||
|
||||
def apply(args: Any*)(implicit lang: Lang): RawFrag =
|
||||
Translator.frag.literal(key, db, args, lang)
|
||||
Translator.frag.literal(key, args, lang)
|
||||
|
||||
def plural(count: Count, args: Any*)(implicit lang: Lang): RawFrag =
|
||||
Translator.frag.plural(key, db, count, args, lang)
|
||||
Translator.frag.plural(key, count, args, lang)
|
||||
|
||||
def pluralSame(count: Int)(implicit lang: Lang): RawFrag = plural(count, count)
|
||||
|
||||
def txt(args: Any*)(implicit lang: Lang): String =
|
||||
Translator.txt.literal(key, db, args, lang)
|
||||
Translator.txt.literal(key, args, lang)
|
||||
|
||||
def pluralTxt(count: Count, args: Any*)(implicit lang: Lang): String =
|
||||
Translator.txt.plural(key, db, count, args, lang)
|
||||
Translator.txt.plural(key, count, args, lang)
|
||||
|
||||
def pluralSameTxt(count: Int)(implicit lang: Lang): String = pluralTxt(count, count)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -29,12 +29,12 @@ object I18nLangPicker {
|
|||
Lang get str flatMap findCloser
|
||||
|
||||
private val defaultByLanguage: Map[String, Lang] =
|
||||
I18nDb.langs.foldLeft(Map.empty[String, Lang]) {
|
||||
Registry.langs.foldLeft(Map.empty[String, Lang]) {
|
||||
case (acc, lang) => acc + (lang.language -> lang)
|
||||
}
|
||||
|
||||
def findCloser(to: Lang): Option[Lang] =
|
||||
if (I18nDb.langs contains to) Some(to)
|
||||
if (Registry.langs contains to) Some(to)
|
||||
else
|
||||
defaultByLanguage.get(to.language) orElse
|
||||
lichessCodes.get(to.language)
|
||||
|
|
|
@ -2,7 +2,7 @@ package lila.i18n
|
|||
|
||||
import java.io._
|
||||
import scala.concurrent.Future
|
||||
import scala.jdk.CollectionConverters._
|
||||
// import scala.jdk.CollectionConverters._
|
||||
import play.api.libs.json.{ JsObject, JsString }
|
||||
import play.api.i18n.Lang
|
||||
|
||||
|
@ -20,7 +20,7 @@ final private[i18n] class JsDump(path: String)(implicit ec: scala.concurrent.Exe
|
|||
private def dumpFromKey(keys: Set[String], lang: Lang): String =
|
||||
keys
|
||||
.map { key =>
|
||||
""""%s":"%s"""".format(key, escape(Translator.txt.literal(key, I18nDb.Site, Nil, lang)))
|
||||
""""%s":"%s"""".format(key, escape(Translator.txt.literal(key, Nil, lang)))
|
||||
}
|
||||
.mkString("{", ",", "}")
|
||||
|
||||
|
@ -34,9 +34,10 @@ final private[i18n] class JsDump(path: String)(implicit ec: scala.concurrent.Exe
|
|||
.mkString("[", ",", "]")
|
||||
)
|
||||
|
||||
private def writeFullJson() = I18nDb.langs foreach { lang =>
|
||||
val code = dumpFromKey(I18nDb.site(defaultLang).keySet.asScala.toSet, lang)
|
||||
writeFile(new File("%s/%s.all.json".format(pathFile.getCanonicalPath, lang.code)), code)
|
||||
private def writeFullJson() = Registry.langs foreach { lang =>
|
||||
???
|
||||
// val code = dumpFromKey(I18nDb.site(defaultLang).keySet.asScala.toSet, lang)
|
||||
// writeFile(new File("%s/%s.all.json".format(pathFile.getCanonicalPath, lang.code)), code)
|
||||
}
|
||||
|
||||
private def writeFile(file: File, content: String) = {
|
||||
|
@ -64,7 +65,7 @@ object JsDump {
|
|||
|
||||
private type JsTrans = Iterable[(String, JsString)]
|
||||
|
||||
private def translatedJs(k: String, t: Translation): JsTrans = t match {
|
||||
private def translatedJs(k: MessageKey, t: Translation): JsTrans = t match {
|
||||
case literal: Simple => List(k -> JsString(literal.message))
|
||||
case literal: Escaped => List(k -> JsString(literal.message))
|
||||
case plurals: Plurals =>
|
||||
|
@ -73,21 +74,9 @@ object JsDump {
|
|||
}
|
||||
}
|
||||
|
||||
def keysToObject(keys: Seq[I18nKey], lang: Lang): JsObject = JsObject {
|
||||
def keysToObject(keys: Seq[MessageKey], lang: Lang): JsObject = JsObject {
|
||||
keys.flatMap { k =>
|
||||
Translator.findTranslation(k.key, k.db, lang).fold[JsTrans](Nil) { translatedJs(k.key, _) }
|
||||
Translator.findTranslation(k, lang).fold[JsTrans](Nil) { translatedJs(k, _) }
|
||||
}
|
||||
}
|
||||
|
||||
val emptyMessages: MessageMap = new java.util.HashMap()
|
||||
|
||||
def dbToObject(ref: I18nDb.Ref, lang: Lang): JsObject =
|
||||
I18nDb(ref).get(defaultLang) ?? { defaultMsgs =>
|
||||
JsObject {
|
||||
val msgs = I18nDb(ref).get(lang) | emptyMessages
|
||||
defaultMsgs.asScala.flatMap {
|
||||
case (k, v) => translatedJs(k, msgs.getOrDefault(k, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,20 +8,19 @@ import lila.common.String.html.escapeHtml
|
|||
object Translator {
|
||||
|
||||
object frag {
|
||||
def literal(key: MessageKey, db: I18nDb.Ref, args: Seq[Any], lang: Lang): RawFrag =
|
||||
translate(key, db, lang, I18nQuantity.Other /* grmbl */, args)
|
||||
def literal(key: MessageKey, args: Seq[Any], lang: Lang): RawFrag =
|
||||
translate(key, lang, I18nQuantity.Other /* grmbl */, args)
|
||||
|
||||
def plural(key: MessageKey, db: I18nDb.Ref, count: Count, args: Seq[Any], lang: Lang): RawFrag =
|
||||
translate(key, db, lang, I18nQuantity(lang, count), args)
|
||||
def plural(key: MessageKey, count: Count, args: Seq[Any], lang: Lang): RawFrag =
|
||||
translate(key, lang, I18nQuantity(lang, count), args)
|
||||
|
||||
private def translate(
|
||||
key: MessageKey,
|
||||
db: I18nDb.Ref,
|
||||
lang: Lang,
|
||||
quantity: I18nQuantity,
|
||||
args: Seq[Any]
|
||||
): RawFrag =
|
||||
findTranslation(key, db, lang) flatMap { translation =>
|
||||
findTranslation(key, lang) flatMap { translation =>
|
||||
val htmlArgs = escapeArgs(args)
|
||||
try {
|
||||
translation match {
|
||||
|
@ -31,7 +30,7 @@ object Translator {
|
|||
}
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
logger.warn(s"Failed to format html $db/$lang/$key -> $translation (${args.toList})", e)
|
||||
logger.warn(s"Failed to format html $lang/$key -> $translation (${args.toList})", e)
|
||||
Some(RawFrag(key))
|
||||
}
|
||||
} getOrElse {
|
||||
|
@ -49,20 +48,19 @@ object Translator {
|
|||
|
||||
object txt {
|
||||
|
||||
def literal(key: MessageKey, db: I18nDb.Ref, args: Seq[Any], lang: Lang): String =
|
||||
translate(key, db, lang, I18nQuantity.Other /* grmbl */, args)
|
||||
def literal(key: MessageKey, args: Seq[Any], lang: Lang): String =
|
||||
translate(key, lang, I18nQuantity.Other /* grmbl */, args)
|
||||
|
||||
def plural(key: MessageKey, db: I18nDb.Ref, count: Count, args: Seq[Any], lang: Lang): String =
|
||||
translate(key, db, lang, I18nQuantity(lang, count), args)
|
||||
def plural(key: MessageKey, count: Count, args: Seq[Any], lang: Lang): String =
|
||||
translate(key, lang, I18nQuantity(lang, count), args)
|
||||
|
||||
private def translate(
|
||||
key: MessageKey,
|
||||
db: I18nDb.Ref,
|
||||
lang: Lang,
|
||||
quantity: I18nQuantity,
|
||||
args: Seq[Any]
|
||||
): String =
|
||||
findTranslation(key, db, lang) flatMap { translation =>
|
||||
findTranslation(key, lang) flatMap { translation =>
|
||||
try {
|
||||
translation match {
|
||||
case literal: Simple => Some(literal.formatTxt(args))
|
||||
|
@ -71,16 +69,16 @@ object Translator {
|
|||
}
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
logger.warn(s"Failed to format txt $db/$lang/$key -> $translation (${args.toList})", e)
|
||||
logger.warn(s"Failed to format txt $lang/$key -> $translation (${args.toList})", e)
|
||||
Some(key)
|
||||
}
|
||||
} getOrElse {
|
||||
logger.info(s"No translation found for $quantity $db/$lang/$key in $lang")
|
||||
logger.info(s"No translation found for $quantity $lang/$key in $lang")
|
||||
key
|
||||
}
|
||||
}
|
||||
|
||||
private[i18n] def findTranslation(key: MessageKey, db: I18nDb.Ref, lang: Lang): Option[Translation] =
|
||||
I18nDb(db).get(lang).flatMap(t => Option(t get key)) orElse
|
||||
I18nDb(db).get(defaultLang).flatMap(t => Option(t get key))
|
||||
private[i18n] def findTranslation(key: MessageKey, lang: Lang): Option[Translation] =
|
||||
Registry.all.get(lang).flatMap(t => Option(t get key)) orElse
|
||||
Option(Registry.default.get(key))
|
||||
}
|
||||
|
|
|
@ -5,107 +5,77 @@ import scala.xml.XML
|
|||
|
||||
object MessageCompiler {
|
||||
|
||||
def apply(sourceDir: File, destDir: File, dbs: List[String], compileTo: File): Seq[File] =
|
||||
dbs.flatMap { db =>
|
||||
doFile(
|
||||
db = db,
|
||||
sourceFile = sourceDir / s"$db.xml",
|
||||
destDir = destDir / db,
|
||||
compileTo = compileTo / db
|
||||
)
|
||||
}
|
||||
|
||||
private def doFile(db: String, sourceFile: File, destDir: File, compileTo: File): Seq[File] = {
|
||||
destDir.mkdirs()
|
||||
val registry = ("en-GB" -> sourceFile) :: destDir.list.toList
|
||||
.map { f =>
|
||||
f.takeWhile('.' !=) -> (destDir / f)
|
||||
}
|
||||
.sortBy(_._1)
|
||||
def apply(sourceDir: File, destDir: File, dbs: List[String], compileTo: File): Seq[File] = {
|
||||
compileTo.mkdirs()
|
||||
var translatedLocales = Set.empty[String]
|
||||
val res = for {
|
||||
entry <- registry
|
||||
compilable <- {
|
||||
val (locale, file) = entry
|
||||
val compileToFile = compileTo / s"$locale.scala"
|
||||
if (!isFileEmpty(file)) {
|
||||
translatedLocales = translatedLocales + locale
|
||||
if (file.lastModified > compileToFile.lastModified) {
|
||||
printToFile(compileToFile)(render(db, locale, file))
|
||||
}
|
||||
Some(compileToFile)
|
||||
} else None
|
||||
val locales: List[String] = "en-GB" ::
|
||||
(destDir / "site").list.toList.map { _.takeWhile('.' !=) }.sorted
|
||||
val localeFiles = locales
|
||||
.map { locale =>
|
||||
locale -> writeLocale(locale, sourceDir, destDir, compileTo, dbs)
|
||||
}
|
||||
} yield compilable
|
||||
writeRegistry(db, compileTo, translatedLocales) :: res
|
||||
.filter(_._2.exists)
|
||||
writeRegistry(compileTo, localeFiles.map(_._1)) :: localeFiles.map(_._2)
|
||||
}
|
||||
|
||||
private def isFileEmpty(f: File) = {
|
||||
Source.fromFile(f, "UTF-8").getLines.drop(2).next == "<resources></resources>"
|
||||
}
|
||||
// dbs.flatMap { db =>
|
||||
// doFile(
|
||||
// db = db,
|
||||
// sourceFile = sourceDir / s"$db.xml",
|
||||
// destDir = destDir / db,
|
||||
// compileTo = compileTo / db
|
||||
// )
|
||||
// }
|
||||
|
||||
private def packageName(db: String) = if (db == "class") "clas" else db
|
||||
|
||||
private def writeRegistry(db: String, compileTo: File, locales: Iterable[String]) = {
|
||||
val file = compileTo / "Registry.scala"
|
||||
printToFile(file) {
|
||||
val content = locales.map { locale =>
|
||||
s"""Lang("${locale.replace("-", "\",\"")}")->`$locale`.load"""
|
||||
} mkString ",\n"
|
||||
s"""package lila.i18n
|
||||
package db.${packageName(db)}
|
||||
|
||||
import play.api.i18n.Lang
|
||||
|
||||
// format: OFF
|
||||
private[i18n] object Registry {
|
||||
|
||||
def load = Map[Lang, java.util.HashMap[MessageKey, Translation]]($content)
|
||||
}
|
||||
"""
|
||||
}
|
||||
file
|
||||
}
|
||||
|
||||
private def ucfirst(str: String) = str(0).toUpper + str.drop(1)
|
||||
|
||||
private def toKey(e: scala.xml.Node) = s""""${e.\("@name")}""""
|
||||
|
||||
private def escape(str: String) = {
|
||||
// is someone trying to inject scala code?
|
||||
if (str contains "\"\"\"") sys error s"Skipped translation: $str"
|
||||
// crowdin escapes ' and " with \, and encodes &. We'll do it at runtime instead.
|
||||
else str.replace("\\'", "'").replace("\\\"", "\"")
|
||||
}
|
||||
|
||||
private def render(db: String, locale: String, file: File): String = {
|
||||
val xml =
|
||||
try {
|
||||
XML.loadFile(file)
|
||||
} catch {
|
||||
case e: Exception => println(file); throw e;
|
||||
}
|
||||
def quote(msg: String) = s"""""\"$msg""\""""
|
||||
val content = xml.child.collect {
|
||||
case e if e.label == "string" =>
|
||||
val safe = escape(e.text)
|
||||
val translation = escapeHtmlOption(safe) match {
|
||||
case None => s"""new Simple(\"\"\"$safe\"\"\")"""
|
||||
case Some(escaped) => s"""new Escaped(\"\"\"$safe\"\"\",\"\"\"$escaped\"\"\")"""
|
||||
private def writeLocale(
|
||||
locale: String,
|
||||
sourceDir: File,
|
||||
destDir: File,
|
||||
compileTo: File,
|
||||
dbs: List[String]
|
||||
): File = {
|
||||
val scalaFile = compileTo / s"$locale.scala"
|
||||
val xmlFiles =
|
||||
if (locale == "en-GB") dbs.map { db =>
|
||||
db -> (sourceDir / s"$db.xml")
|
||||
} else
|
||||
dbs.map { db =>
|
||||
db -> (destDir / db / s"$locale.xml")
|
||||
}
|
||||
s"""m.put(${toKey(e)},$translation)"""
|
||||
case e if e.label == "plurals" =>
|
||||
val items: Map[String, String] = e.child
|
||||
.filter(_.label == "item")
|
||||
.map { i =>
|
||||
ucfirst(i.\("@quantity").toString) -> s"""\"\"\"${escape(i.text)}\"\"\""""
|
||||
}
|
||||
.toMap
|
||||
s"""m.put(${toKey(e)},new Plurals(${pluralMap(items)}))"""
|
||||
|
||||
val isNew = xmlFiles.exists {
|
||||
case (_, file) => !isFileEmpty(file) && file.lastModified > scalaFile.lastModified
|
||||
}
|
||||
s"""package lila.i18n
|
||||
package db.${packageName(db)}
|
||||
if (!isNew) scalaFile
|
||||
else
|
||||
printToFile(scalaFile) {
|
||||
val puts = xmlFiles flatMap {
|
||||
case (db, file) =>
|
||||
val xml =
|
||||
try {
|
||||
XML.loadFile(file)
|
||||
} catch {
|
||||
case e: Exception => println(file); throw e;
|
||||
}
|
||||
xml.child.collect {
|
||||
case e if e.label == "string" =>
|
||||
val safe = escape(e.text)
|
||||
val translation = escapeHtmlOption(safe) match {
|
||||
case None => s"""new Simple(\"\"\"$safe\"\"\")"""
|
||||
case Some(escaped) => s"""new Escaped(\"\"\"$safe\"\"\",\"\"\"$escaped\"\"\")"""
|
||||
}
|
||||
s"""m.put(${toKey(e, db)},$translation)"""
|
||||
case e if e.label == "plurals" =>
|
||||
val items: Map[String, String] = e.child
|
||||
.filter(_.label == "item")
|
||||
.map { i =>
|
||||
ucfirst(i.\("@quantity").toString) -> s"""\"\"\"${escape(i.text)}\"\"\""""
|
||||
}
|
||||
.toMap
|
||||
s"""m.put(${toKey(e, db)},new Plurals(${pluralMap(items)}))"""
|
||||
}
|
||||
}
|
||||
|
||||
s"""package lila.i18n
|
||||
|
||||
import I18nQuantity._
|
||||
|
||||
|
@ -113,12 +83,55 @@ import I18nQuantity._
|
|||
private object `$locale` {
|
||||
|
||||
def load: java.util.HashMap[MessageKey, Translation] = {
|
||||
val m = new java.util.HashMap[MessageKey, Translation](${content.size + 1})
|
||||
${content mkString "\n"}
|
||||
val m = new java.util.HashMap[MessageKey, Translation](${puts.size + 1})
|
||||
${puts mkString "\n"}
|
||||
m
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
private def isFileEmpty(file: File) = {
|
||||
!file.exists() || Source.fromFile(file, "UTF-8").getLines.drop(2).next == "<resources></resources>"
|
||||
}
|
||||
|
||||
private def packageName(db: String) = if (db == "class") "clas" else db
|
||||
|
||||
private def writeRegistry(destDir: File, locales: Iterable[String]) =
|
||||
printToFile(destDir / "Registry.scala") {
|
||||
val content = locales.map { locale =>
|
||||
s"""Lang("${locale.replace("-", "\",\"")}")->`$locale`.load"""
|
||||
} mkString ",\n"
|
||||
s"""package lila.i18n
|
||||
|
||||
import play.api.i18n.Lang
|
||||
|
||||
// format: OFF
|
||||
private object Registry {
|
||||
|
||||
val all = Map[Lang, java.util.HashMap[MessageKey, Translation]](\n$content)
|
||||
|
||||
val default: java.util.HashMap[MessageKey, Translation] = all(defaultLang)
|
||||
|
||||
val langs: Set[Lang] = all.keys.toSet
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
private def ucfirst(str: String) = str(0).toUpper + str.drop(1)
|
||||
|
||||
private def toKey(e: scala.xml.Node, db: String) =
|
||||
if (db == "site") s""""${e.\("@name")}""""
|
||||
else s""""$db:${e.\("@name")}""""
|
||||
|
||||
private def quote(msg: String) = s"""""\"$msg""\""""
|
||||
|
||||
private def escape(str: String) = {
|
||||
// is someone trying to inject scala code?
|
||||
if (str contains "\"\"\"") sys error s"Skipped translation: $str"
|
||||
// crowdin escapes ' and " with \, and encodes &. We'll do it at runtime instead.
|
||||
else str.replace("\\'", "'").replace("\\\"", "\"")
|
||||
}
|
||||
|
||||
private def pluralMap(items: Map[String, String]): String =
|
||||
|
@ -146,12 +159,13 @@ ${content mkString "\n"}
|
|||
sb.toString
|
||||
} else None
|
||||
|
||||
private def printToFile(f: File)(content: String): Unit = {
|
||||
val p = new java.io.PrintWriter(f, "UTF-8")
|
||||
private def printToFile(file: File)(content: String): File = {
|
||||
val p = new java.io.PrintWriter(file, "UTF-8")
|
||||
try {
|
||||
content.foreach(p.print)
|
||||
} finally {
|
||||
p.close()
|
||||
}
|
||||
file
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue