diff --git a/.gitignore b/.gitignore
index fab584f09e..59d279542d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,5 @@ lost+found
nohup.out
nohup.out.old
ATTIC
+# IntelliJ auto-generated files
+.idea
diff --git a/.scalafmt.conf b/.scalafmt.conf
index cd36a01e4d..055fff1659 100644
--- a/.scalafmt.conf
+++ b/.scalafmt.conf
@@ -4,3 +4,4 @@ maxColumn = 110
spaces.inImportCurlyBraces = true
rewrite.rules = [SortImports, RedundantParens, SortModifiers]
rewrite.redundantBraces.stringInterpolation = true
+runner.dialect = scala213
diff --git a/app/Env.scala b/app/Env.scala
index 5aef82d4a8..03cfff51e2 100644
--- a/app/Env.scala
+++ b/app/Env.scala
@@ -117,16 +117,21 @@ final class Env(
text = "Team IDs that always get their tournaments visible on /tournament. Separated by commas.".some
)
lazy val prizeTournamentMakers = memo.settingStore[UserIds](
- "prizeTournamentMakers ",
+ "prizeTournamentMakers",
default = UserIds(Nil),
text =
"User IDs who can make prize tournaments (arena & swiss) without a warning. Separated by commas.".some
)
lazy val apiExplorerGamesPerSecond = memo.settingStore[Int](
- "apiExplorerGamesPerSecond ",
- default = 200,
+ "apiExplorerGamesPerSecond",
+ default = 300,
text = "Opening explorer games per second".some
)
+ lazy val pieceImageExternal = memo.settingStore[Boolean](
+ "pieceImageExternal",
+ default = false,
+ text = "Use external piece images".some
+ )
lazy val preloader = wire[mashup.Preload]
lazy val socialInfo = wire[mashup.UserInfo.SocialApi]
diff --git a/app/controllers/Api.scala b/app/controllers/Api.scala
index 49b1e96f7b..50009ca937 100644
--- a/app/controllers/Api.scala
+++ b/app/controllers/Api.scala
@@ -10,6 +10,7 @@ import lila.api.{ Context, GameApiV2 }
import lila.app._
import lila.common.config.{ MaxPerPage, MaxPerSecond }
import lila.common.{ HTTPRequest, IpAddress }
+import lila.common.LightUser
final class Api(
env: Env,
@@ -74,17 +75,20 @@ final class Api(
def usersStatus =
ApiRequest { req =>
val ids = get("ids", req).??(_.split(',').take(100).toList map lila.user.User.normalize)
- env.user.lightUserApi asyncMany ids dmap (_.flatten) map { users =>
+ env.user.lightUserApi asyncMany ids dmap (_.flatten) flatMap { users =>
val streamingIds = env.streamer.liveStreamApi.userIds
- toApiResult {
- users.map { u =>
- lila.common.LightUser.lightUserWrites
- .writes(u)
- .add("online" -> env.socket.isOnline(u.id))
- .add("playing" -> env.round.playing(u.id))
- .add("streaming" -> streamingIds(u.id))
+ def toJson(u: LightUser) =
+ lila.common.LightUser.lightUserWrites
+ .writes(u)
+ .add("online" -> env.socket.isOnline(u.id))
+ .add("playing" -> env.round.playing(u.id))
+ .add("streaming" -> streamingIds(u.id))
+ if (getBool("withGameIds", req)) users.map { u =>
+ (env.round.playing(u.id) ?? env.game.cached.lastPlayedPlayingId(u.id)) map { gameId =>
+ toJson(u).add("playingId", gameId)
}
- }
+ }.sequenceFu map toApiResult
+ else fuccess(toApiResult(users map toJson))
}
}
diff --git a/app/controllers/Dev.scala b/app/controllers/Dev.scala
index ad91309cdb..fff8b67fe9 100644
--- a/app/controllers/Dev.scala
+++ b/app/controllers/Dev.scala
@@ -26,7 +26,9 @@ final class Dev(env: Env) extends LilaController(env) {
env.fishnet.openingBookDepth,
env.noDelaySecretSetting,
env.featuredTeamsSetting,
- env.prizeTournamentMakers
+ env.prizeTournamentMakers,
+ env.pieceImageExternal,
+ env.evalCache.enable
)
def settings =
diff --git a/app/controllers/Irwin.scala b/app/controllers/Irwin.scala
index 1cdf029257..74851ceb76 100644
--- a/app/controllers/Irwin.scala
+++ b/app/controllers/Irwin.scala
@@ -7,7 +7,7 @@ final class Irwin(env: Env) extends LilaController(env) {
import lila.irwin.JSONHandlers.reportReader
def dashboard =
- Secure(_.SeeReport) { implicit ctx => _ =>
+ Secure(_.MarkEngine) { implicit ctx => _ =>
env.irwin.api.dashboard map { d =>
Ok(views.html.irwin.dashboard(d))
}
diff --git a/app/controllers/LilaController.scala b/app/controllers/LilaController.scala
index c2d3f5a409..d0a2f8d1b5 100644
--- a/app/controllers/LilaController.scala
+++ b/app/controllers/LilaController.scala
@@ -65,10 +65,11 @@ abstract private[controllers] class LilaController(val env: Env)
implicit def reqConfig(implicit req: RequestHeader) = ui.EmbedConfig(req)
def reqLang(implicit req: RequestHeader) = I18nLangPicker(req)
- protected def EnableSharedArrayBuffer(res: Result): Result =
+ protected def EnableSharedArrayBuffer(res: Result)(implicit req: RequestHeader): Result =
res.withHeaders(
- "Cross-Origin-Opener-Policy" -> "same-origin",
- "Cross-Origin-Embedder-Policy" -> "require-corp"
+ "Cross-Origin-Opener-Policy" -> "same-origin",
+ "Cross-Origin-Embedder-Policy" -> (if (HTTPRequest isChrome96OrMore req) "credentialless"
+ else "require-corp")
)
protected def NoCache(res: Result): Result =
diff --git a/app/controllers/Mod.scala b/app/controllers/Mod.scala
index 171a7adbe8..ad60b5556d 100644
--- a/app/controllers/Mod.scala
+++ b/app/controllers/Mod.scala
@@ -108,6 +108,7 @@ final class Mod(
suspect <- modApi.setTroll(me, prev, prev.user.marks.troll)
_ <- env.msg.api.systemPost(suspect.user.id, preset.text)
_ <- env.mod.logApi.modMessage(me.id, suspect.user.id, preset.name)
+ _ <- preset.isNameClose ?? env.irc.api.nameClosePreset(username)
} yield (inquiry, suspect).some
}
}
diff --git a/app/controllers/PlayApi.scala b/app/controllers/PlayApi.scala
index 7caf2360ba..d4429af035 100644
--- a/app/controllers/PlayApi.scala
+++ b/app/controllers/PlayApi.scala
@@ -122,15 +122,22 @@ final class PlayApi(
def boardCommandGet(cmd: String) =
ScopedBody(_.Board.Play) { implicit req => me =>
cmd.split('/') match {
- case Array("game", id, "chat") =>
- WithPovAsBoard(id, me) { pov =>
- env.chat.api.userChat.find(lila.chat.Chat.Id(pov.game.id)) map
- lila.chat.JsonView.boardApi map JsonOk
- }
- case _ => notFoundJson("No such command")
+ case Array("game", id, "chat") => WithPovAsBoard(id, me)(getChat)
+ case _ => notFoundJson("No such command")
}
}
+ def botCommandGet(cmd: String) =
+ ScopedBody(_.Bot.Play) { implicit req => me =>
+ cmd.split('/') match {
+ case Array("game", id, "chat") => WithPovAsBot(id, me)(getChat)
+ case _ => notFoundJson("No such command")
+ }
+ }
+
+ private def getChat(pov: Pov) =
+ env.chat.api.userChat.find(lila.chat.Chat.Id(pov.game.id)) map lila.chat.JsonView.boardApi map JsonOk
+
// utils
private def toResult(f: Funit): Fu[Result] = catchClientError(f inject jsonOkResult)
diff --git a/app/controllers/Round.scala b/app/controllers/Round.scala
index 4c660e1085..5102d54314 100644
--- a/app/controllers/Round.scala
+++ b/app/controllers/Round.scala
@@ -131,7 +131,7 @@ final class Round(
Open { implicit ctx =>
proxyPov(gameId, color) flatMap {
case Some(pov) =>
- get("pov") match {
+ get("pov").map(UserModel.normalize) match {
case Some(requestedPov) =>
(pov.player.userId, pov.opponent.userId) match {
case (Some(_), Some(opponent)) if opponent == requestedPov =>
diff --git a/app/controllers/Study.scala b/app/controllers/Study.scala
index 4907fff8d9..4c496a9127 100644
--- a/app/controllers/Study.scala
+++ b/app/controllers/Study.scala
@@ -328,15 +328,17 @@ final class Study(
env.study.api.importPgns(
StudyModel.Id(id),
data.toChapterDatas,
- sticky = data.sticky
+ sticky = data.sticky,
+ ctx.pref.showRatings
)(Who(me.id, lila.socket.Socket.Sri(sri)))
)
}
}
def admin(id: String) =
- Secure(_.StudyAdmin) { _ => me =>
- env.study.api.adminInvite(id, me) inject Redirect(routes.Study.show(id))
+ Secure(_.StudyAdmin) { ctx => me =>
+ env.study.api.adminInvite(id, me) inject (if (HTTPRequest isXhr ctx.req) NoContent
+ else Redirect(routes.Study.show(id)))
}
def embed(id: String, chapterId: String) =
diff --git a/app/templating/AssetHelper.scala b/app/templating/AssetHelper.scala
index ec5b3637dc..1e97f83cb3 100644
--- a/app/templating/AssetHelper.scala
+++ b/app/templating/AssetHelper.scala
@@ -22,12 +22,11 @@ trait AssetHelper { self: I18nHelper with SecurityHelper =>
def assetVersion = AssetVersion.current
- def assetUrl(path: String): String = s"$assetBaseUrl/assets/_$assetVersion/$path"
+ def assetUrl(path: String): String = s"$assetBaseUrl/assets/_$assetVersion/$path"
+ def staticAssetUrl(path: String): String = s"$assetBaseUrl/assets/$path"
def cdnUrl(path: String) = s"$assetBaseUrl$path"
- def dbImageUrl(path: String) = s"$assetBaseUrl/image/$path"
-
def cssTag(name: String)(implicit ctx: Context): Frag =
cssTagWithTheme(name, ctx.currentBg)
diff --git a/app/templating/Environment.scala b/app/templating/Environment.scala
index 1dec94f4ca..1e9fdc4420 100644
--- a/app/templating/Environment.scala
+++ b/app/templating/Environment.scala
@@ -51,6 +51,6 @@ object Environment
)
val spinner: Frag = raw(
- """
"""
+ """"""
)
}
diff --git a/app/ui/scalatags.scala b/app/ui/scalatags.scala
index 73d7c3b85b..db018d3444 100644
--- a/app/ui/scalatags.scala
+++ b/app/ui/scalatags.scala
@@ -99,15 +99,16 @@ trait ScalatagsPrefix {
// what to import in a pure scalatags template
trait ScalatagsTemplate
- extends Styles
- with ScalatagsBundle
+ extends ScalatagsBundle
with ScalatagsAttrs
with ScalatagsExtensions
with ScalatagsSnippets
with ScalatagsPrefix {
- val trans = lila.i18n.I18nKeys
- def main = scalatags.Text.tags2.main
+ val trans = lila.i18n.I18nKeys
+ def main = scalatags.Text.tags2.main
+ def cssWidth = scalatags.Text.styles.width
+ def cssHeight = scalatags.Text.styles.height
/* Convert play URLs to scalatags attributes with toString */
implicit val playCallAttr = genericAttr[play.api.mvc.Call]
diff --git a/app/views/account/bits.scala b/app/views/account/bits.scala
index ce40637e2e..c95f002205 100644
--- a/app/views/account/bits.scala
+++ b/app/views/account/bits.scala
@@ -17,7 +17,7 @@ object bits {
div(cls := "personal-data__header")(
p("Here is all personal information Lichess has about ", userLink(u)),
a(cls := "button", href := s"${routes.Account.data}?user=${u.id}&text=1", downloadAttr)(
- trans.downloadRaw()
+ trans.download()
)
)
)
diff --git a/app/views/base/layout.scala b/app/views/base/layout.scala
index f2f8e04818..5bb2fd710d 100644
--- a/app/views/base/layout.scala
+++ b/app/views/base/layout.scala
@@ -34,23 +34,38 @@ object layout {
def pieceSprite(ps: lila.pref.PieceSet): Frag =
link(
id := "piece-sprite",
- href := assetUrl(s"piece-css/$ps.css"),
+ href := assetUrl(s"piece-css/$ps.${env.pieceImageExternal.get() ?? "external."}css"),
rel := "stylesheet"
)
}
import bits._
private val noTranslate = raw("""""")
- private def fontPreload(implicit ctx: Context) =
- raw {
- s"""""" +
- !ctx.pref.pieceNotationIsLetter ??
- s""""""
+
+ private def preload(href: String, as: String, crossorigin: Boolean, tpe: Option[String] = None) =
+ raw(s"""
+ s"""type="$t" """
+ )}${crossorigin ?? "crossorigin"}>""")
+
+ private def fontPreload(implicit ctx: Context) = frag(
+ preload(assetUrl(s"font/lichess.woff2"), "font", crossorigin = true, "font/woff2".some),
+ !ctx.pref.pieceNotationIsLetter option
+ preload(assetUrl(s"font/lichess.chess.woff2"), "font", crossorigin = true, "font/woff2".some)
+ )
+ private def boardPreload(implicit ctx: Context) = frag(
+ preload(assetUrl(s"images/board/${ctx.currentTheme.file}"), "image", crossorigin = false),
+ ctx.pref.is3d option
+ preload(s"images/staunton/board/${ctx.currentTheme3d.file}", "image", crossorigin = false)
+ )
+ private def piecesPreload(implicit ctx: Context) =
+ env.pieceImageExternal.get() option raw {
+ (for {
+ c <- List('w', 'b')
+ p <- List('K', 'Q', 'R', 'B', 'N', 'P')
+ href = staticAssetUrl(s"piece/${ctx.currentPieceSet.name}/$c$p.svg")
+ } yield s"""""").mkString
}
+
private val manifests = raw(
""""""
)
@@ -149,7 +164,7 @@ object layout {
def lichessJsObject(nonce: Nonce)(implicit lang: Lang) =
embedJsUnsafe(
- s"""lichess={load:new Promise(r=>{window.onload=r}),quantity:${lila.i18n
+ s"""lichess={load:new Promise(r=>{document.addEventListener("DOMContentLoaded",r)}),quantity:${lila.i18n
.JsQuantity(lang)}};$timeagoLocaleScript""",
nonce
)
@@ -229,7 +244,7 @@ object layout {
content := openGraph.fold(trans.siteDescription.txt())(o => o.description),
name := "description"
),
- link(rel := "mask-icon", href := assetUrl("logo/lichess.svg"), color := "black"),
+ link(rel := "mask-icon", href := assetUrl("logo/lichess.svg"), attr("color") := "black"),
favicons,
!robots option raw(""""""),
noTranslate,
@@ -248,6 +263,8 @@ object layout {
)
},
fontPreload,
+ boardPreload,
+ piecesPreload,
manifests,
jsLicense
),
diff --git a/app/views/base/notFound.scala b/app/views/base/notFound.scala
index 459e53ba7c..0725f2900e 100644
--- a/app/views/base/notFound.scala
+++ b/app/views/base/notFound.scala
@@ -32,8 +32,8 @@ object notFound {
iframe(
src := assetUrl(s"vendor/ChessPursuit/bin-release/index.html"),
st.frameborder := 0,
- width := 400,
- height := 500
+ widthA := 400,
+ heightA := 500
),
p(cls := "credits")(
a(href := "https://github.com/Saturnyn/ChessPursuit")("ChessPursuit"),
diff --git a/app/views/coach/picture.scala b/app/views/coach/picture.scala
index 083f2bec66..019b38ab77 100644
--- a/app/views/coach/picture.scala
+++ b/app/views/coach/picture.scala
@@ -46,8 +46,8 @@ object picture {
img(
widthA := Coach.imageSize,
heightA := Coach.imageSize,
- width := cssSize,
- height := cssSize,
+ cssWidth := cssSize,
+ cssHeight := cssSize,
cls := "picture",
src := url(c.coach),
alt := s"${c.user.titleUsername} Lichess coach picture"
diff --git a/app/views/coach/show.scala b/app/views/coach/show.scala
index 5f544a59fd..f1dc4f3b46 100644
--- a/app/views/coach/show.scala
+++ b/app/views/coach/show.scala
@@ -97,8 +97,8 @@ object show {
div(cls := "list")(
profile.youtubeUrls.map { url =>
iframe(
- width := "256",
- height := "192",
+ widthA := "256",
+ heightA := "192",
src := url.value,
attr("frameborder") := "0",
frame.allowfullscreen
diff --git a/app/views/mobile.scala b/app/views/mobile.scala
index 039c6805d6..a340e42239 100644
--- a/app/views/mobile.scala
+++ b/app/views/mobile.scala
@@ -47,16 +47,16 @@ object mobile {
),
div(cls := "right-side")(
img(
- cls := "nexus5-playing",
- width := "268",
- height := "513",
- src := assetUrl("images/mobile/nexus5-playing.png"),
- alt := "Lichess mobile on nexus 5"
+ widthA := "437",
+ heightA := "883",
+ cls := "mobile-playing",
+ src := assetUrl("images/mobile/lichesstv-mobile.png"),
+ alt := "Lichess TV on mobile"
),
img(
cls := "qrcode",
- width := "200",
- height := "200",
+ widthA := "200",
+ heightA := "200",
src := assetUrl("images/mobile/dynamic-qrcode.png"),
alt := "Download QR code"
)
diff --git a/app/views/mod/menu.scala b/app/views/mod/menu.scala
index ba5217e2a8..c1e072a63c 100644
--- a/app/views/mod/menu.scala
+++ b/app/views/mod/menu.scala
@@ -30,7 +30,7 @@ object menu {
a(cls := active.active("tour"), href := routes.TournamentCrud.index(1))("Tournaments"),
isGranted(_.ManageEvent) option
a(cls := active.active("event"), href := routes.Event.manager)("Events"),
- isGranted(_.SeeReport) option
+ isGranted(_.MarkEngine) option
a(cls := active.active("irwin"), href := routes.Irwin.dashboard)("Irwin dashboard"),
isGranted(_.Shadowban) option
a(cls := active.active("panic"), href := routes.Mod.chatPanic)(
diff --git a/app/views/oAuth/authorize.scala b/app/views/oAuth/authorize.scala
index 2608f17e6c..028a04d9d8 100644
--- a/app/views/oAuth/authorize.scala
+++ b/app/views/oAuth/authorize.scala
@@ -17,7 +17,8 @@ object authorize {
moreJs = embedJsUnsafe(
// ensure maximum browser compatibility
"""setTimeout(function(){var el=document.getElementById('oauth-authorize');el.removeAttribute('disabled');el.setAttribute('class','button')}, 2000);"""
- )
+ ),
+ csp = defaultCsp.withLegacyCompatibility.some
) {
main(cls := "oauth box box-pad")(
div(cls := "oauth__top")(
diff --git a/app/views/setup/forms.scala b/app/views/setup/forms.scala
index 47eb144611..eca03be81a 100644
--- a/app/views/setup/forms.scala
+++ b/app/views/setup/forms.scala
@@ -167,14 +167,24 @@ object forms {
div(cls := "ratings")(
form3.hidden("rating", "?"),
lila.rating.PerfType.nonPuzzle.map { perfType =>
- div(cls := perfType.key)(
- trans.perfRatingX(
- raw(s"""${ctx.pref.showRatings ?? me
- .perfs(perfType.key)
- .map(_.intRating.toString)
- .getOrElse("?")} ${perfType.trans}""")
+ {
+ val rating = me
+ .perfs(perfType.key)
+ .map(_.intRating.toString)
+ .getOrElse("?")
+ div(cls := perfType.key)(
+ if (ctx.pref.showRatings)
+ trans.perfRatingX(
+ raw(s"""${rating} ${perfType.trans}""")
+ )
+ else
+ frag(
+ i(dataIcon := perfType.iconChar),
+ strong(cls := "none")(rating), // To calculate rating range in JS
+ perfType.trans
+ )
)
- )
+ }
}
)
}
diff --git a/app/views/site/dailyPuzzleSlackApp.scala b/app/views/site/dailyPuzzleSlackApp.scala
index d0543af4a8..0a1d0406bd 100644
--- a/app/views/site/dailyPuzzleSlackApp.scala
+++ b/app/views/site/dailyPuzzleSlackApp.scala
@@ -25,8 +25,8 @@ object dailyPuzzleSlackApp {
)(
img(
alt := "Add to Slack",
- height := 40,
- width := 139,
+ heightA := 40,
+ widthA := 139,
src := assetUrl("images/add-to-slack.png")
)
),
diff --git a/app/views/swiss/show.scala b/app/views/swiss/show.scala
index 97ad4d06bc..41f7a5a688 100644
--- a/app/views/swiss/show.scala
+++ b/app/views/swiss/show.scala
@@ -83,8 +83,7 @@ object show {
def round(s: Swiss, r: SwissRound.Number, pairings: Paginator[SwissPairing])(implicit ctx: Context) =
views.html.base.layout(
title = s"${fullName(s)} • Round $r/${s.round}",
- moreCss = cssTag("swiss.show"),
- moreJs = infiniteScrollTag
+ moreCss = cssTag("swiss.show")
) {
val pager = views.html.base.bits
.pagination(p => routes.Swiss.round(s.id.value, p).url, r.value, s.round.value, showPost = true)
@@ -95,7 +94,6 @@ object show {
),
pager(cls := "pagination--top"),
table(cls := "slist slist-pad")(
- tbody(cls := "infinite-scroll")(
pairings.currentPageResults map { p =>
tr(cls := "paginated")(
td(a(href := routes.Round.watcher(p.gameId, "white"), cls := "glpt")(s"#${p.gameId}")),
@@ -104,9 +102,7 @@ object show {
td(p strResultOf chess.Black),
td(userIdLink(p.black.some))
)
- },
- pagerNextTable(pairings, p => routes.Swiss.round(s.id.value, r.value).url)
- )
+ }
),
pager(cls := "pagination--bottom")
)
diff --git a/app/views/ublog/post.scala b/app/views/ublog/post.scala
index 416dd7fb6b..5c9a34c362 100644
--- a/app/views/ublog/post.scala
+++ b/app/views/ublog/post.scala
@@ -73,7 +73,7 @@ object post {
post.lived map { live =>
span(cls := "ublog-post__meta__date")(semanticDate(live.at))
},
- likeButton(post, liked)(ctx)(cls := "ublog-post__like--mini button-link"),
+ likeButton(post, liked, showText = false),
span(cls := "ublog-post__views")(
trans.ublog.nbViews.plural(post.views.value, strong(post.views.value.localize))
),
@@ -104,9 +104,7 @@ object post {
div(cls := "ublog-post__footer")(
(ctx.isAuth && !ctx.is(user)) option
div(cls := "ublog-post__actions")(
- likeButton(post, liked)(ctx)(cls := "ublog-post__like--big button button-big button-red")(
- span(cls := "button-label")("Like this post")
- ),
+ likeButton(post, liked, showText = true),
followButton(user, post, followed)
),
h2(a(href := routes.Ublog.index(user.username))(trans.ublog.moreBlogPostsBy(user.username))),
@@ -122,15 +120,27 @@ object post {
dataIcon := ""
)(trans.edit())
- private def likeButton(post: UblogPost, liked: Boolean)(implicit ctx: Context) = button(
- tpe := "button",
- cls := List(
- "ublog-post__like is" -> true,
- "ublog-post__like--liked" -> liked
- ),
- dataRel := post.id.value,
- title := trans.study.like.txt()
- )(span(cls := "ublog-post__like__nb")(post.likes.value.localize))
+ private def likeButton(post: UblogPost, liked: Boolean, showText: Boolean)(implicit ctx: Context) = {
+ val text = if (liked) trans.study.unlike.txt() else trans.study.like.txt()
+ button(
+ tpe := "button",
+ cls := List(
+ "ublog-post__like is" -> true,
+ "ublog-post__like--liked" -> liked,
+ "ublog-post__like--big button button-big button-red" -> showText,
+ "ublog-post__like--mini button-link" -> !showText
+ ),
+ dataRel := post.id.value,
+ title := text
+ )(
+ span(cls := "ublog-post__like__nb")(post.likes.value.localize),
+ showText option span(
+ cls := "button-label",
+ attr("data-i18n-like") := trans.study.like.txt(),
+ attr("data-i18n-unlike") := trans.study.unlike.txt()
+ )(text)
+ )
+ }
private def followButton(user: User, post: UblogPost, followed: Boolean)(implicit ctx: Context) =
div(
diff --git a/app/views/user/bots.scala b/app/views/user/bots.scala
index 69665e0c36..ff54048974 100644
--- a/app/views/user/bots.scala
+++ b/app/views/user/bots.scala
@@ -37,9 +37,7 @@ object bots {
private def botTable(users: List[User])(implicit ctx: Context) = table(cls := "slist slist-pad")(
tbody(
- users.sortBy { u =>
- (if (u.isVerified) -1 else 1, -u.playTime.??(_.total))
- } map { u =>
+ users map { u =>
tr(
td(userLink(u)),
u.profile
diff --git a/app/views/user/perfStat.scala b/app/views/user/perfStat.scala
index 62441a7591..a16eddfb47 100644
--- a/app/views/user/perfStat.scala
+++ b/app/views/user/perfStat.scala
@@ -62,7 +62,7 @@ object perfStat {
counter(stat.count),
highlow(stat),
resultStreak(stat.resultStreak),
- result(stat),
+ result(stat, user),
playStreakNb(stat.playStreak),
playStreakTime(stat.playStreak)
)
@@ -259,7 +259,9 @@ object perfStat {
resultStreakSide(streak.loss, losingStreak(), "red")
)
- private def resultTable(results: lila.perfStat.Results, title: Frag)(implicit lang: Lang): Frag =
+ private def resultTable(results: lila.perfStat.Results, title: Frag, user: User)(implicit
+ lang: Lang
+ ): Frag =
div(
table(
thead(
@@ -271,17 +273,21 @@ object perfStat {
results.results map { r =>
tr(
td(userIdLink(r.opId.value.some, withOnline = false), " (", r.opInt, ")"),
- td(a(cls := "glpt", href := routes.Round.watcher(r.gameId, "white"))(absClientDateTime(r.at)))
+ td(
+ a(cls := "glpt", href := s"${routes.Round.watcher(r.gameId, "white")}?pov=${user.username}")(
+ absClientDateTime(r.at)
+ )
+ )
)
}
)
)
)
- private def result(stat: PerfStat)(implicit lang: Lang): Frag =
+ private def result(stat: PerfStat, user: User)(implicit lang: Lang): Frag =
st.section(cls := "result split")(
- resultTable(stat.bestWins, bestRated()),
- resultTable(stat.worstLosses, worstRated())
+ resultTable(stat.bestWins, bestRated(), user),
+ resultTable(stat.worstLosses, worstRated(), user)
)
private def playStreakNbStreak(s: lila.perfStat.Streak, title: Frag => Frag)(implicit lang: Lang): Frag =
diff --git a/app/views/user/show/otherTrophies.scala b/app/views/user/show/otherTrophies.scala
index 0c1411e6dc..55cce07d48 100644
--- a/app/views/user/show/otherTrophies.scala
+++ b/app/views/user/show/otherTrophies.scala
@@ -46,7 +46,7 @@ object otherTrophies {
ariaTitle(t.kind.name),
style := "width: 65px; margin: 0 3px!important;"
)(
- img(src := assetUrl(s"images/trophy/${t.kind._id}.png"), width := 65, height := 80)
+ img(src := assetUrl(s"images/trophy/${t.kind._id}.png"), cssWidth := 65, cssHeight := 80)
)
},
info.trophies.filter(_.kind.klass.has("icon3d")).sorted.map { trophy =>
diff --git a/conf/base.conf b/conf/base.conf
index 7e4e91e862..29351430b5 100644
--- a/conf/base.conf
+++ b/conf/base.conf
@@ -108,9 +108,7 @@ pagerDuty {
serviceId = ""
apiKey = ""
}
-prismic {
- api_url = "https://lichess.cdn.prismic.io/api"
-}
+prismic.api_url = "https://lichess-clone.cdn.prismic.io/api"
blog {
prismic = ${prismic}
collection = blog
diff --git a/conf/routes b/conf/routes
index 4d5236e407..2fc8fe6e00 100644
--- a/conf/routes
+++ b/conf/routes
@@ -707,9 +707,10 @@ GET /api/user/:name/games controllers.Api.userGames(name: String)
# Bot API
GET /api/bot/game/stream/:id controllers.PlayApi.botGameStream(id: String)
POST /api/bot/game/:id/move/:uci controllers.PlayApi.botMove(id: String, uci: String, offeringDraw: Option[Boolean] ?= None)
-POST /api/bot/*cmd controllers.PlayApi.botCommand(cmd: String)
-GET /player/bots controllers.PlayApi.botOnline
GET /api/bot/online controllers.PlayApi.botOnlineApi
+POST /api/bot/*cmd controllers.PlayApi.botCommand(cmd: String)
+GET /api/bot/*cmd controllers.PlayApi.botCommandGet(cmd: String)
+GET /player/bots controllers.PlayApi.botOnline
# Board API
GET /api/board/game/stream/:id controllers.PlayApi.boardGameStream(id: String)
diff --git a/modules/activity/src/main/JsonView.scala b/modules/activity/src/main/JsonView.scala
index 50595d060f..e8c151cd5f 100644
--- a/modules/activity/src/main/JsonView.scala
+++ b/modules/activity/src/main/JsonView.scala
@@ -6,7 +6,6 @@ import play.api.libs.json._
import lila.common.Iso
import lila.common.Json._
-import lila.game.JsonView.colorWrites
import lila.game.LightPov
import lila.rating.PerfType
import lila.simul.Simul
diff --git a/modules/api/src/main/GameApi.scala b/modules/api/src/main/GameApi.scala
index 3226de6426..c254507791 100644
--- a/modules/api/src/main/GameApi.scala
+++ b/modules/api/src/main/GameApi.scala
@@ -8,7 +8,7 @@ import reactivemongo.api.ReadPreference
import lila.analyse.{ JsonView => analysisJson, Analysis }
import lila.common.config._
-import lila.common.Json.jodaWrites
+import lila.common.Json._
import lila.common.paginator.{ Paginator, PaginatorJson }
import lila.db.dsl._
import lila.db.paginator.Adapter
diff --git a/modules/api/src/main/GameApiV2.scala b/modules/api/src/main/GameApiV2.scala
index 43693d81d8..6f038fd463 100644
--- a/modules/api/src/main/GameApiV2.scala
+++ b/modules/api/src/main/GameApiV2.scala
@@ -9,7 +9,7 @@ import scala.concurrent.duration._
import lila.analyse.{ JsonView => analysisJson, Analysis }
import lila.common.config.MaxPerSecond
-import lila.common.Json.jodaWrites
+import lila.common.Json._
import lila.common.{ HTTPRequest, LightUser }
import lila.db.dsl._
import lila.game.JsonView._
diff --git a/modules/blog/src/main/BlogApi.scala b/modules/blog/src/main/BlogApi.scala
index 6fbade9a40..e53163eb03 100644
--- a/modules/blog/src/main/BlogApi.scala
+++ b/modules/blog/src/main/BlogApi.scala
@@ -6,6 +6,7 @@ import play.api.libs.ws.StandaloneWSClient
import lila.common.config.MaxPerPage
import lila.common.paginator._
+import scala.util.Try
final class BlogApi(
config: BlogConfig
@@ -20,7 +21,7 @@ final class BlogApi(
page: Int,
maxPerPage: MaxPerPage,
ref: Option[String]
- ): Fu[Option[Paginator[Document]]] =
+ ): Fu[Option[Paginator[Document]]] = Try {
api
.forms(collection)
.ref(ref | api.master.ref)
@@ -30,6 +31,9 @@ final class BlogApi(
.submit()
.fold(_ => none, some)
.dmap2 { PrismicPaginator(_, page, maxPerPage) }
+ } recover { case _: NoSuchElementException =>
+ fuccess(none)
+ } get
def recent(
prismic: BlogApi.Context,
@@ -94,9 +98,7 @@ final class BlogApi(
} getOrElse reqRef
}
- private val prismicBuilder = new Prismic
-
- def prismicApi = prismicBuilder.get(config.apiUrl)
+ def prismicApi = (new Prismic).get(config.apiUrl)
}
object BlogApi {
diff --git a/modules/bot/src/main/BotJsonView.scala b/modules/bot/src/main/BotJsonView.scala
index 2f42fbf4ef..bc80dc65ca 100644
--- a/modules/bot/src/main/BotJsonView.scala
+++ b/modules/bot/src/main/BotJsonView.scala
@@ -3,6 +3,7 @@ package lila.bot
import play.api.i18n.Lang
import play.api.libs.json._
+import lila.common.Json._
import lila.common.Json.jodaWrites
import lila.game.JsonView._
import lila.game.{ Game, GameRepo, Pov }
diff --git a/modules/challenge/src/main/JsonView.scala b/modules/challenge/src/main/JsonView.scala
index 3401b188d3..814ccf5ef3 100644
--- a/modules/challenge/src/main/JsonView.scala
+++ b/modules/challenge/src/main/JsonView.scala
@@ -1,8 +1,10 @@
package lila.challenge
-import play.api.libs.json._
import play.api.i18n.Lang
+import play.api.libs.json._
+import lila.common.Json._
+import lila.game.JsonView._
import lila.i18n.{ I18nKeys => trans }
import lila.socket.Socket.SocketVersion
import lila.socket.UserLagCache
@@ -13,7 +15,6 @@ final class JsonView(
isOnline: lila.socket.IsOnline
) {
- import lila.game.JsonView._
import Challenge._
implicit private val RegisteredWrites = OWrites[Challenger.Registered] { r =>
diff --git a/modules/common/src/main/ContentSecurityPolicy.scala b/modules/common/src/main/ContentSecurityPolicy.scala
index 7677142576..666b18aa07 100644
--- a/modules/common/src/main/ContentSecurityPolicy.scala
+++ b/modules/common/src/main/ContentSecurityPolicy.scala
@@ -11,11 +11,9 @@ case class ContentSecurityPolicy(
baseUri: List[String]
) {
- def withNonce(nonce: Nonce) = copy(scriptSrc =
- nonce.scriptSrc ::
- "'unsafe-inline'" :: // ignored by browsers supporting nonce
- scriptSrc
- )
+ def withNonce(nonce: Nonce) = copy(scriptSrc = nonce.scriptSrc :: scriptSrc)
+
+ def withLegacyCompatibility = copy(scriptSrc = "'unsafe-inline'" :: scriptSrc)
def withWebAssembly =
copy(
diff --git a/modules/common/src/main/HTTPRequest.scala b/modules/common/src/main/HTTPRequest.scala
index d5a5361fb6..a270b82423 100644
--- a/modules/common/src/main/HTTPRequest.scala
+++ b/modules/common/src/main/HTTPRequest.scala
@@ -45,6 +45,7 @@ object HTTPRequest {
private def uaContains(req: RequestHeader, str: String) = userAgent(req).exists(_ contains str)
def isChrome(req: RequestHeader) = uaContains(req, "Chrome/")
+ val isChrome96OrMore = UaMatcher("""Chrome/(?:\d{3,}|9[6-9])""")
def origin(req: RequestHeader): Option[String] = req.headers get HeaderNames.ORIGIN
@@ -58,7 +59,7 @@ object HTTPRequest {
def sid(req: RequestHeader): Option[String] = req.session get LilaCookie.sessionId
val isCrawler = UaMatcher {
- """(?i)googlebot|googlebot-mobile|googlebot-image|mediapartners-google|bingbot|slurp|java|wget|curl|commons-httpclient|python-urllib|libwww|httpunit|nutch|phpcrawl|msnbot|adidxbot|blekkobot|teoma|ia_archiver|gingercrawler|webmon|httrack|webcrawler|fast-webcrawler|fastenterprisecrawler|convera|biglotron|grub\.org|usinenouvellecrawler|antibot|netresearchserver|speedy|fluffy|jyxobot|bibnum\.bnf|findlink|exabot|gigabot|msrbot|seekbot|ngbot|panscient|yacybot|aisearchbot|ioi|ips-agent|tagoobot|mj12bot|dotbot|woriobot|yanga|buzzbot|mlbot|purebot|lingueebot|yandex\.com/bots|""" +
+ """(?i)googlebot|googlebot-mobile|googlebot-image|mediapartners-google|bingbot|slurp|java|wget|curl|python-requests|commons-httpclient|python-urllib|libwww|httpunit|nutch|phpcrawl|msnbot|adidxbot|blekkobot|teoma|ia_archiver|gingercrawler|webmon|httrack|webcrawler|fast-webcrawler|fastenterprisecrawler|convera|biglotron|grub\.org|usinenouvellecrawler|antibot|netresearchserver|speedy|fluffy|jyxobot|bibnum\.bnf|findlink|exabot|gigabot|msrbot|seekbot|ngbot|panscient|yacybot|aisearchbot|ioi|ips-agent|tagoobot|mj12bot|dotbot|woriobot|yanga|buzzbot|mlbot|purebot|lingueebot|yandex\.com/bots|""" +
"""voyager|cyberpatrol|voilabot|baiduspider|citeseerxbot|spbot|twengabot|postrank|turnitinbot|scribdbot|page2rss|sitebot|linkdex|ezooms|dotbot|mail\.ru|discobot|zombie\.js|heritrix|findthatfile|europarchive\.org|nerdbynature\.bot|sistrixcrawler|ahrefsbot|aboundex|domaincrawler|wbsearchbot|summify|ccbot|edisterbot|seznambot|ec2linkfinder|gslfbot|aihitbot|intelium_bot|yeti|retrevopageanalyzer|lb-spider|sogou|lssbot|careerbot|wotbox|wocbot|ichiro|duckduckbot|lssrocketcrawler|drupact|webcompanycrawler|acoonbot|openindexspider|gnamgnamspider|web-archive-net\.com\.bot|backlinkcrawler|""" +
"""coccoc|integromedb|contentcrawlerspider|toplistbot|seokicks-robot|it2media-domain-crawler|ip-web-crawler\.com|siteexplorer\.info|elisabot|proximic|changedetection|blexbot|arabot|wesee:search|niki-bot|crystalsemanticsbot|rogerbot|360spider|psbot|interfaxscanbot|lipperheyseoservice|ccmetadatascaper|g00g1e\.net|grapeshotcrawler|urlappendbot|brainobot|fr-crawler|binlar|simplecrawler|simplecrawler|livelapbot|twitterbot|cxensebot|smtbot|facebookexternalhit|daumoa|sputnikimagebot|visionutils|yisouspider|parsijoobot|mediatoolkit\.com|semrushbot"""
}
diff --git a/modules/common/src/main/Json.scala b/modules/common/src/main/Json.scala
index 98167b21ce..41d0027c74 100644
--- a/modules/common/src/main/Json.scala
+++ b/modules/common/src/main/Json.scala
@@ -41,9 +41,16 @@ object Json {
JsNumber(time.getMillis)
}
+ implicit val colorWrites: Writes[chess.Color] = Writes { c =>
+ JsString(c.name)
+ }
+
implicit val fenFormat: Format[FEN] = stringIsoFormat[FEN](Iso.fenIso)
- implicit val uciReader: Reads[Uci] = Reads.of[String] flatMapResult { str =>
+ implicit val uciReads: Reads[Uci] = Reads.of[String] flatMapResult { str =>
JsResult.fromTry(Uci(str) toTry s"Invalid UCI: $str")
}
+ implicit val uciWrites: Writes[Uci] = Writes { u =>
+ JsString(u.uci)
+ }
}
diff --git a/modules/common/src/main/Markdown.scala b/modules/common/src/main/Markdown.scala
index b837917388..6384ca4a5a 100644
--- a/modules/common/src/main/Markdown.scala
+++ b/modules/common/src/main/Markdown.scala
@@ -60,11 +60,14 @@ final class Markdown(
private def mentionsToLinks(markdown: Text): Text =
RawHtml.atUsernameRegex.replaceAllIn(markdown, "[$1](/@/$1)")
+ private val tooManyUnderscoreRegex = """(_{4,})""".r
+ private def preventStackOverflow(text: String) = tooManyUnderscoreRegex.replaceAllIn(text, "_" * 3)
+
def apply(key: Key)(text: Text): Html =
Chronometer
.sync {
try {
- addLinkAttributes(renderer.render(parser.parse(mentionsToLinks(text))))
+ addLinkAttributes(renderer.render(parser.parse(mentionsToLinks(preventStackOverflow(text)))))
} catch {
case e: StackOverflowError =>
logger.branch(key).error("StackOverflowError", e)
diff --git a/modules/common/src/main/Random.scala b/modules/common/src/main/Random.scala
index 3fe0a0e128..e03c899056 100644
--- a/modules/common/src/main/Random.scala
+++ b/modules/common/src/main/Random.scala
@@ -38,10 +38,17 @@ abstract class Random {
vec.nonEmpty ?? {
vec lift nextInt(vec.size)
}
+
+ // odds(1) = 100% true
+ // odds(2) = 50% true
+ // odds(3) = 33% true
+ def odds(n: Int): Boolean = nextInt(n) == 0
}
object ThreadLocalRandom extends Random {
override def current = java.util.concurrent.ThreadLocalRandom.current
+
+ def nextLong(n: Long): Long = current.nextLong(n)
}
object SecureRandom extends Random {
diff --git a/modules/common/src/main/base/PimpedUtils.scala b/modules/common/src/main/base/PimpedUtils.scala
index 7a86a93ad5..d42bb5d286 100644
--- a/modules/common/src/main/base/PimpedUtils.scala
+++ b/modules/common/src/main/base/PimpedUtils.scala
@@ -24,8 +24,6 @@ final class PimpedOption[A](private val self: Option[A]) extends AnyVal {
def err(message: => String): A = self.getOrElse(sys.error(message))
- def ifNone(n: => Unit): Unit = if (self.isEmpty) n
-
def has(a: A) = self contains a
}
diff --git a/modules/common/src/main/mon.scala b/modules/common/src/main/mon.scala
index f5e1cb6a28..407db66086 100644
--- a/modules/common/src/main/mon.scala
+++ b/modules/common/src/main/mon.scala
@@ -646,9 +646,15 @@ object mon {
def request(hit: Boolean) = counter("fishnet.http.acquire").withTag("hit", hit)
}
def move(level: Int) = counter("fishnet.move.time").withTag("level", level)
- def openingBook(level: Int, variant: String, ply: Int, hit: Boolean) =
+ def openingBook(level: Int, variant: String, ply: Int, hit: Boolean, success: Boolean) =
timer("fishnet.opening.hit").withTags(
- Map("level" -> level, "variant" -> variant, "ply" -> ply, "hit" -> hit)
+ Map(
+ "level" -> level.toLong,
+ "variant" -> variant,
+ "ply" -> ply.toLong,
+ "hit" -> hitTag(hit),
+ "success" -> successTag(success)
+ )
)
}
object study {
@@ -727,6 +733,7 @@ object mon {
)
private def successTag(success: Boolean) = if (success) "success" else "failure"
+ private def hitTag(hit: Boolean) = if (hit) "hit" else "miss"
private def apiTag(api: Option[ApiVersion]) = api.fold("-")(_.toString)
diff --git a/modules/db/src/main/dsl.scala b/modules/db/src/main/dsl.scala
index ab3933eabc..210453e6f6 100644
--- a/modules/db/src/main/dsl.scala
+++ b/modules/db/src/main/dsl.scala
@@ -63,6 +63,10 @@ trait dsl {
def $nor(expressions: Bdoc*): Bdoc = {
$doc("$nor" -> expressions)
}
+
+ def $not(expression: Bdoc): Bdoc = {
+ $doc("$not" -> expression)
+ }
// End of Top Level Logical Operators
//**********************************************************************************************//
@@ -291,13 +295,6 @@ trait dsl {
}
- trait LogicalOperators { self: ElementBuilder =>
- def $not(f: String => Expression[Bdoc]): SimpleExpression[Bdoc] = {
- val expression = f(field)
- SimpleExpression(field, $doc("$not" -> expression.value))
- }
- }
-
trait ElementOperators { self: ElementBuilder =>
def $exists(v: Boolean): SimpleExpression[Bdoc] = {
SimpleExpression(field, $doc("$exists" -> v))
@@ -381,7 +378,6 @@ trait dsl {
with ComparisonOperators
with ElementOperators
with EvaluationOperators
- with LogicalOperators
with ArrayOperators
implicit def toBSONDocument[V: BSONWriter](expression: Expression[V]): Bdoc =
diff --git a/modules/evalCache/src/main/Env.scala b/modules/evalCache/src/main/Env.scala
index 6ea164e8f4..97c5df1c7f 100644
--- a/modules/evalCache/src/main/Env.scala
+++ b/modules/evalCache/src/main/Env.scala
@@ -16,6 +16,7 @@ final class Env(
userRepo: lila.user.UserRepo,
yoloDb: lila.db.AsyncDb @@ lila.db.YoloDb,
cacheApi: lila.memo.CacheApi,
+ settingStore: lila.memo.SettingStore.Builder,
scheduler: akka.actor.Scheduler
)(implicit
ec: scala.concurrent.ExecutionContext,
@@ -27,6 +28,12 @@ final class Env(
private lazy val truster = wire[EvalCacheTruster]
+ lazy val enable = settingStore[Boolean](
+ "useCeval",
+ default = true,
+ text = "Enable cloud eval (disable in case of server trouble)".some
+ )
+
private lazy val upgrade = wire[EvalCacheUpgrade]
lazy val api: EvalCacheApi = wire[EvalCacheApi]
diff --git a/modules/evalCache/src/main/EvalCacheApi.scala b/modules/evalCache/src/main/EvalCacheApi.scala
index 0041cd0259..2299c46415 100644
--- a/modules/evalCache/src/main/EvalCacheApi.scala
+++ b/modules/evalCache/src/main/EvalCacheApi.scala
@@ -9,13 +9,16 @@ import scala.concurrent.duration._
import lila.db.AsyncCollFailingSilently
import lila.db.dsl._
import lila.memo.CacheApi._
+import lila.memo.SettingStore
import lila.socket.Socket
+import lila.user.User
final class EvalCacheApi(
coll: AsyncCollFailingSilently,
truster: EvalCacheTruster,
upgrade: EvalCacheUpgrade,
- cacheApi: lila.memo.CacheApi
+ cacheApi: lila.memo.CacheApi,
+ setting: SettingStore[Boolean]
)(implicit ec: scala.concurrent.ExecutionContext) {
import EvalCacheEntry._
@@ -36,7 +39,7 @@ final class EvalCacheApi(
def put(trustedUser: TrustedUser, candidate: Input.Candidate, sri: Socket.Sri): Funit =
candidate.input ?? { put(trustedUser, _, sri) }
- def shouldPut = truster shouldPut _
+ def shouldPut(user: User) = setting.get() && truster.shouldPut(user)
def getSinglePvEval(variant: Variant, fen: FEN): Fu[Option[Eval]] =
getEval(
diff --git a/modules/evalCache/src/main/EvalCacheUpgrade.scala b/modules/evalCache/src/main/EvalCacheUpgrade.scala
index 1b3066d623..d0cd72800b 100644
--- a/modules/evalCache/src/main/EvalCacheUpgrade.scala
+++ b/modules/evalCache/src/main/EvalCacheUpgrade.scala
@@ -9,38 +9,44 @@ import lila.socket.Socket
import lila.memo.ExpireCallbackMemo
import scala.collection.mutable
+import lila.memo.SettingStore
/* Upgrades the user's eval when a better one becomes available,
* by remembering the last evalGet of each socket member,
* and listening to new evals stored.
*/
-final private class EvalCacheUpgrade(scheduler: akka.actor.Scheduler)(implicit
+final private class EvalCacheUpgrade(setting: SettingStore[Boolean], scheduler: akka.actor.Scheduler)(implicit
ec: scala.concurrent.ExecutionContext,
mode: play.api.Mode
) {
import EvalCacheUpgrade._
private val members = mutable.AnyRefMap.empty[SriString, WatchingMember]
- private val evals = mutable.AnyRefMap.empty[SetupId, Set[SriString]]
- private val expirableSris = new ExpireCallbackMemo(20 minutes, sri => unregister(Socket.Sri(sri)))
+ private val evals = mutable.AnyRefMap.empty[SetupId, EvalState]
+ private val expirableSris = new ExpireCallbackMemo(10 minutes, sri => unregister(Socket.Sri(sri)))
private val upgradeMon = lila.mon.evalCache.upgrade
- def register(sri: Socket.Sri, variant: Variant, fen: FEN, multiPv: Int, path: String)(push: Push): Unit = {
- members get sri.value foreach { wm =>
- unregisterEval(wm.setupId, sri)
+ def register(sri: Socket.Sri, variant: Variant, fen: FEN, multiPv: Int, path: String)(push: Push): Unit =
+ if (setting.get()) {
+ members get sri.value foreach { wm =>
+ unregisterEval(wm.setupId, sri)
+ }
+ val setupId = makeSetupId(variant, fen, multiPv)
+ members += (sri.value -> WatchingMember(push, setupId, path))
+ evals += (setupId -> evals.get(setupId).fold(EvalState(Set(sri.value), 0))(_ addSri sri))
+ expirableSris put sri.value
}
- val setupId = makeSetupId(variant, fen, multiPv)
- members += (sri.value -> WatchingMember(push, setupId, path))
- evals += (setupId -> (~evals.get(setupId) + sri.value))
- expirableSris put sri.value
- }
- def onEval(input: EvalCacheEntry.Input, sri: Socket.Sri): Unit = {
+ def onEval(input: EvalCacheEntry.Input, sri: Socket.Sri): Unit = if (setting.get()) {
(1 to input.eval.multiPv) flatMap { multiPv =>
- evals get makeSetupId(input.id.variant, input.fen, multiPv)
- } foreach { sris =>
- val wms = sris.withFilter(sri.value !=) flatMap members.get
+ val setupId = makeSetupId(input.id.variant, input.fen, multiPv)
+ evals get setupId map (setupId -> _)
+ } filter {
+ _._2.depth < input.eval.depth
+ } foreach { case (setupId, eval) =>
+ evals += (setupId -> eval.copy(depth = input.eval.depth))
+ val wms = eval.sris.withFilter(sri.value !=) flatMap members.get
if (wms.nonEmpty) {
val json = JsonHandlers.writeEval(input.eval, input.fen)
wms foreach { wm =>
@@ -59,10 +65,10 @@ final private class EvalCacheUpgrade(scheduler: akka.actor.Scheduler)(implicit
}
private def unregisterEval(setupId: SetupId, sri: Socket.Sri): Unit =
- evals get setupId foreach { sris =>
- val newSris = sris - sri.value
+ evals get setupId foreach { eval =>
+ val newSris = eval.sris - sri.value
if (newSris.isEmpty) evals -= setupId
- else evals += (setupId -> newSris)
+ else evals += (setupId -> eval.copy(sris = newSris))
}
scheduler.scheduleWithFixedDelay(1 minute, 1 minute) { () =>
@@ -78,6 +84,10 @@ private object EvalCacheUpgrade {
private type SetupId = String
private type Push = JsObject => Unit
+ private case class EvalState(sris: Set[SriString], depth: Int) {
+ def addSri(sri: Socket.Sri) = copy(sris = sris + sri.value)
+ }
+
private def makeSetupId(variant: Variant, fen: FEN, multiPv: Int): SetupId =
s"${variant.id}${EvalCacheEntry.SmallFen.make(variant, fen).value}^$multiPv"
diff --git a/modules/explorer/src/main/ExplorerIndexer.scala b/modules/explorer/src/main/ExplorerIndexer.scala
index 722d4951e4..576027ceff 100644
--- a/modules/explorer/src/main/ExplorerIndexer.scala
+++ b/modules/explorer/src/main/ExplorerIndexer.scala
@@ -71,21 +71,22 @@ final private class ExplorerIndexer(
case Correspondence | Classical => 1.00f
case Rapid if rating >= 2200 => 1.00f
- case Rapid if rating >= 2000 => 0.50f
- case Rapid if rating >= 1800 => 0.28f
- case Rapid if rating >= 1600 => 0.24f
+ case Rapid if rating >= 2000 => 0.83f
+ case Rapid if rating >= 1800 => 0.46f
+ case Rapid if rating >= 1600 => 0.39f
case Rapid => 0.02f
case Blitz if rating >= 2500 => 1.00f
- case Blitz if rating >= 2200 => 0.24f
- case Blitz if rating >= 2000 => 0.11f
- case Blitz if rating >= 1600 => 0.08f
+ case Blitz if rating >= 2200 => 0.38f
+ case Blitz if rating >= 2000 => 0.18f
+ case Blitz if rating >= 1600 => 0.13f
case Blitz => 0.02f
case Bullet if rating >= 2500 => 1.00f
- case Bullet if rating >= 2200 => 0.30f
- case Bullet if rating >= 2000 => 0.17f
- case Bullet if rating >= 1600 => 0.11f
+ case Bullet if rating >= 2200 => 0.48f
+ case Bullet if rating >= 2000 => 0.27f
+ case Bullet if rating >= 1800 => 0.19f
+ case Bullet if rating >= 1600 => 0.18f
case Bullet => 0.02f
case UltraBullet => 1.00f
diff --git a/modules/fishnet/src/main/Analyser.scala b/modules/fishnet/src/main/Analyser.scala
index 68d5d40212..c9e0f98cc8 100644
--- a/modules/fishnet/src/main/Analyser.scala
+++ b/modules/fishnet/src/main/Analyser.scala
@@ -18,7 +18,7 @@ final class Analyser(
system: akka.actor.ActorSystem
) {
- val maxPlies = 200
+ val maxPlies = 300
private val workQueue =
new lila.hub.AsyncActorSequencer(maxSize = 256, timeout = 5 seconds, "fishnetAnalyser")
diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala
index e7add7e163..48c15494cd 100644
--- a/modules/fishnet/src/main/FishnetOpeningBook.scala
+++ b/modules/fishnet/src/main/FishnetOpeningBook.scala
@@ -2,17 +2,19 @@ package lila.fishnet
import chess.format.Forsyth
import chess.format.Uci
-import chess.Speed
+import chess.{ Color, Speed }
import com.softwaremill.tagging._
import play.api.libs.json._
import play.api.libs.ws.JsonBodyReadables._
import play.api.libs.ws.StandaloneWSClient
import scala.concurrent.ExecutionContext
+import scala.concurrent.duration._
-import lila.common.Json.uciReader
+import lila.common.Json._
import lila.common.ThreadLocalRandom
import lila.game.Game
import lila.memo.SettingStore
+import scala.util.{ Failure, Success }
final private class FishnetOpeningBook(
ws: StandaloneWSClient,
@@ -21,32 +23,42 @@ final private class FishnetOpeningBook(
import FishnetOpeningBook._
- def apply(game: Game, level: Int): Fu[Option[Uci]] = (game.turns < depth.get()) ?? {
- ws.url(endpoint)
- .withQueryStringParameters(
- "variant" -> game.variant.key,
- "fen" -> Forsyth.>>(game.chess).value,
- "topGames" -> "0",
- "recentGames" -> "0",
- "ratings" -> (~levelRatings.get(level)).mkString(","),
- "speeds" -> (~openingSpeeds.get(game.speed)).map(_.key).mkString(",")
- )
- .get()
- .map {
- case res if res.status != 200 =>
- logger.warn(s"opening book ${game.id} ${level} ${res.status} ${res.body}")
- none
- case res =>
- for {
- data <- res.body[JsValue].validate[Response](responseReader).asOpt
- move <- data.randomPonderedMove
- } yield move.uci
- }
- .monValue(uci =>
- _.fishnet
- .openingBook(level = level, variant = game.variant.key, ply = game.turns, hit = uci.isDefined)
- )
- }
+ private val outOfBook = new lila.memo.ExpireSetMemo(10 minutes)
+
+ def apply(game: Game, level: Int): Fu[Option[Uci]] =
+ (game.turns < depth.get() && !outOfBook.get(game.id)) ?? {
+ ws.url(endpoint)
+ .withQueryStringParameters(
+ "variant" -> game.variant.key,
+ "fen" -> Forsyth.>>(game.chess).value,
+ "topGames" -> "0",
+ "recentGames" -> "0",
+ "ratings" -> (~levelRatings.get(level)).mkString(","),
+ "speeds" -> (~openingSpeeds.get(game.speed)).map(_.key).mkString(",")
+ )
+ .get()
+ .map {
+ case res if res.status != 200 =>
+ logger.warn(s"opening book ${game.id} ${level} ${res.status} ${res.body}")
+ none
+ case res =>
+ for {
+ data <- res.body[JsValue].validate[Response](responseReader).asOpt
+ _ = if (data.moves.isEmpty) outOfBook.put(game.id)
+ move <- data randomPonderedMove (game.turnColor, level)
+ } yield move.uci
+ }
+ .monTry { res =>
+ _.fishnet
+ .openingBook(
+ level = level,
+ variant = game.variant.key,
+ ply = game.turns,
+ hit = res.toOption.exists(_.isDefined),
+ success = res.isSuccess
+ )
+ }
+ }
}
object FishnetOpeningBook {
@@ -54,20 +66,26 @@ object FishnetOpeningBook {
trait Depth
case class Response(moves: List[Move]) {
- def randomPonderedMove: Option[Move] = {
- val sum = moves.map(_.nb).sum
- val rng = ThreadLocalRandom nextInt sum
+
+ def randomPonderedMove(turn: Color, level: Int): Option[Move] = {
+ val sum = moves.map(_.score(turn, level)).sum
+ val novelty = 5L * 14 // score of 5 winning games
+ val rng = ThreadLocalRandom.nextLong(sum + novelty)
moves
- .foldLeft((none[Move], 0)) { case ((found, it), next) =>
- val nextIt = it + next.nb
+ .foldLeft((none[Move], 0L)) { case ((found, it), next) =>
+ val nextIt = it + next.score(turn, level)
(found orElse (nextIt > rng).option(next), nextIt)
}
._1
}
}
- case class Move(uci: Uci, white: Int, draws: Int, black: Int) {
- def nb = white + draws + black
+ case class Move(uci: Uci, white: Long, draws: Long, black: Long) {
+ def score(turn: Color, level: Int): Long =
+ // interpolate: real frequency at lvl 1, expectation value at lvl 8
+ 14L * turn.fold(white, black) +
+ (15L - level) * draws +
+ (16L - 2 * level) * turn.fold(black, white)
}
implicit val moveReader = Json.reads[Move]
diff --git a/modules/fishnet/src/main/FishnetPlayer.scala b/modules/fishnet/src/main/FishnetPlayer.scala
index f37c0fc3fd..7c921ef8f4 100644
--- a/modules/fishnet/src/main/FishnetPlayer.scala
+++ b/modules/fishnet/src/main/FishnetPlayer.scala
@@ -1,15 +1,13 @@
package lila.fishnet
+import chess.format.Uci
+import chess.{ Black, Clock, White }
import scala.concurrent.duration._
-import chess.{ Black, Clock, White }
-
-import lila.common.{ Future, ThreadLocalRandom }
+import lila.common.{ Bus, Future, ThreadLocalRandom }
import lila.game.{ Game, GameRepo, UciMemo }
-import lila.common.Bus
import lila.hub.actorApi.map.Tell
import lila.hub.actorApi.round.FishnetPlay
-import chess.format.Uci
final class FishnetPlayer(
redis: FishnetRedis,
@@ -28,7 +26,7 @@ final class FishnetPlayer(
openingBook(game, level) flatMap {
case Some(move) =>
fuccess {
- Bus.publish(Tell(game.id, FishnetPlay(move, game.turns)), "roundSocket")
+ Bus.publish(Tell(game.id, FishnetPlay(move, game.playedTurns)), "roundSocket")
}
case None => makeWork(game, level) addEffect redis.request void
}
diff --git a/modules/game/src/main/Cached.scala b/modules/game/src/main/Cached.scala
index 5e9a45bf8c..ad2b9888e1 100644
--- a/modules/game/src/main/Cached.scala
+++ b/modules/game/src/main/Cached.scala
@@ -24,7 +24,7 @@ final class Cached(
private val lastPlayedPlayingIdCache: LoadingCache[User.ID, Fu[Option[Game.ID]]] =
CacheApi.scaffeineNoScheduler
- .expireAfterWrite(5 seconds)
+ .expireAfterWrite(11 seconds)
.build(gameRepo.lastPlayedPlayingId)
lila.common.Bus.subscribeFun("startGame") { case lila.game.actorApi.StartGame(game) =>
diff --git a/modules/game/src/main/Captcher.scala b/modules/game/src/main/Captcher.scala
index 9bca056886..c3d56c9e81 100644
--- a/modules/game/src/main/Captcher.scala
+++ b/modules/game/src/main/Captcher.scala
@@ -47,21 +47,16 @@ final private class Captcher(gameRepo: GameRepo)(implicit ec: scala.concurrent.E
private val capacity = 256
private var challenges = NonEmptyList.one(Captcha.default)
- private def add(c: Captcha): Unit = {
- find(c.gameId) ifNone {
+ private def add(c: Captcha): Unit =
+ if (find(c.gameId).isEmpty) {
challenges = NonEmptyList(c, challenges.toList take capacity)
}
- }
private def find(id: String): Option[Captcha] =
challenges.find(_.gameId == id)
private def createFromDb: Fu[Option[Captcha]] =
- findCheckmateInDb(10) flatMap {
- _.fold(findCheckmateInDb(1))(g => fuccess(g.some))
- } flatMap {
- _ ?? fromGame
- }
+ findCheckmateInDb(10) orElse findCheckmateInDb(1) flatMap { _ ?? fromGame }
private def findCheckmateInDb(distribution: Int): Fu[Option[Game]] =
gameRepo findRandomStandardCheckmate distribution
diff --git a/modules/game/src/main/Event.scala b/modules/game/src/main/Event.scala
index 9c97ed0551..365c956ca8 100644
--- a/modules/game/src/main/Event.scala
+++ b/modules/game/src/main/Event.scala
@@ -17,6 +17,7 @@ import chess.{
import JsonView._
import lila.chat.{ PlayerLine, UserLine }
import lila.common.ApiVersion
+import lila.common.Json._
sealed trait Event {
def typ: String
diff --git a/modules/game/src/main/Game.scala b/modules/game/src/main/Game.scala
index 5dbfe52efc..4bf24b8b44 100644
--- a/modules/game/src/main/Game.scala
+++ b/modules/game/src/main/Game.scala
@@ -555,7 +555,7 @@ case class Game(
def abandoned =
(status <= Status.Started) && {
movedAt isBefore {
- if (hasAi && !hasCorrespondenceClock) Game.aiAbandonedDate
+ if (hasAi && hasClock) Game.aiAbandonedDate
else Game.abandonedDate
}
}
diff --git a/modules/game/src/main/GameRepo.scala b/modules/game/src/main/GameRepo.scala
index 292b74eb1c..ddc2f98bca 100644
--- a/modules/game/src/main/GameRepo.scala
+++ b/modules/game/src/main/GameRepo.scala
@@ -1,5 +1,7 @@
package lila.game
+import scala.concurrent.duration._
+
import chess.format.{ FEN, Forsyth }
import chess.{ Color, Status }
import org.joda.time.DateTime
@@ -19,6 +21,8 @@ final class GameRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionCont
import Game.{ ID, BSONFields => F }
import Player.holdAlertBSONHandler
+ val fixedColorLobbyCache = new lila.memo.ExpireSetMemo(2 hours)
+
def game(gameId: ID): Fu[Option[Game]] = coll.byId[Game](gameId)
def gameFromSecondary(gameId: ID): Fu[Option[Game]] = coll.secondaryPreferred.byId[Game](gameId)
diff --git a/modules/game/src/main/JsonView.scala b/modules/game/src/main/JsonView.scala
index ac32290eef..a20f80564c 100644
--- a/modules/game/src/main/JsonView.scala
+++ b/modules/game/src/main/JsonView.scala
@@ -5,7 +5,7 @@ import play.api.libs.json._
import chess.format.{ FEN, Forsyth }
import chess.variant.Crazyhouse
import chess.{ Clock, Color }
-import lila.common.Json.jodaWrites
+import lila.common.Json._
final class JsonView(rematches: Rematches) {
@@ -143,12 +143,4 @@ object JsonView {
implicit val sourceWriter: Writes[Source] = Writes { s =>
JsString(s.name)
}
-
- implicit val colorWrites: Writes[Color] = Writes { c =>
- JsString(c.name)
- }
-
- implicit val fenWrites: Writes[FEN] = Writes { f =>
- JsString(f.value)
- }
}
diff --git a/modules/game/src/main/PgnDump.scala b/modules/game/src/main/PgnDump.scala
index 6c51281333..60218cf8dd 100644
--- a/modules/game/src/main/PgnDump.scala
+++ b/modules/game/src/main/PgnDump.scala
@@ -57,10 +57,12 @@ final class PgnDump(
private def gameLightUsers(game: Game): Fu[(Option[LightUser], Option[LightUser])] =
(game.whitePlayer.userId ?? lightUserApi.async) zip (game.blackPlayer.userId ?? lightUserApi.async)
- private def rating(p: Player) = p.rating.fold("?")(_.toString)
+ private def rating(p: Player) = p.rating.orElse(p.nameSplit.flatMap(_._2)).fold("?")(_.toString)
def player(p: Player, u: Option[LightUser]) =
- p.aiLevel.fold(u.fold(p.name | lila.user.User.anonymous)(_.name))("lichess AI level " + _)
+ p.aiLevel.fold(u.fold(p.nameSplit.map(_._1).orElse(p.name) | lila.user.User.anonymous)(_.name))(
+ "lichess AI level " + _
+ )
private val customStartPosition: Set[chess.variant.Variant] =
Set(chess.variant.Chess960, chess.variant.FromPosition, chess.variant.Horde, chess.variant.RacingKings)
diff --git a/modules/game/src/main/Player.scala b/modules/game/src/main/Player.scala
index cf3eed3302..1d21d22530 100644
--- a/modules/game/src/main/Player.scala
+++ b/modules/game/src/main/Player.scala
@@ -60,8 +60,8 @@ case class Player(
def nameSplit: Option[(String, Option[Int])] =
name map {
- case Player.nameSplitRegex(n, r) => n -> r.toIntOption
- case n => n -> none
+ case Player.nameSplitRegex(n, r) => n.trim -> r.toIntOption
+ case n => n -> none
}
def before(other: Player) =
diff --git a/modules/insight/src/main/Share.scala b/modules/insight/src/main/Share.scala
index 1bbf212fde..dd19566b27 100644
--- a/modules/insight/src/main/Share.scala
+++ b/modules/insight/src/main/Share.scala
@@ -9,20 +9,18 @@ final class Share(
relationApi: lila.relation.RelationApi
)(implicit ec: scala.concurrent.ExecutionContext) {
- def getPrefId(insighted: User) = prefApi.getPrefById(insighted.id) dmap (_.insightShare)
+ def getPrefId(insighted: User) = prefApi.getPref(insighted.id, _.insightShare)
def grant(insighted: User, to: Option[User]): Fu[Boolean] =
if (to ?? Granter(_.SeeInsight)) fuTrue
else
- prefApi.getPrefById(insighted.id) flatMap { pref =>
- pref.insightShare match {
- case _ if to.contains(insighted) => fuTrue
- case Pref.InsightShare.EVERYBODY => fuTrue
- case Pref.InsightShare.FRIENDS =>
- to ?? { t =>
- relationApi.fetchAreFriends(insighted.id, t.id)
- }
- case Pref.InsightShare.NOBODY => fuFalse
- }
+ getPrefId(insighted) flatMap {
+ case _ if to.contains(insighted) => fuTrue
+ case Pref.InsightShare.EVERYBODY => fuTrue
+ case Pref.InsightShare.FRIENDS =>
+ to ?? { t =>
+ relationApi.fetchAreFriends(insighted.id, t.id)
+ }
+ case Pref.InsightShare.NOBODY => fuFalse
}
}
diff --git a/modules/irc/src/main/IrcApi.scala b/modules/irc/src/main/IrcApi.scala
index d12ac2a037..4a3a312ce2 100644
--- a/modules/irc/src/main/IrcApi.scala
+++ b/modules/irc/src/main/IrcApi.scala
@@ -38,7 +38,7 @@ final class IrcApi(
)
case Some(note) =>
zulip.sendAndGetLink(stream, "/" + user.username)(
- s"${markdown.modLink(mod.user.username)} :pepenote: **${markdown
+ s"${markdown.modLink(mod.user)} :pepenote: **${markdown
.userLink(user.username)}** (${markdown.userNotesLink(user.username)}):\n" +
markdown.linkifyUsers(note.text take 2000)
)
@@ -70,7 +70,7 @@ final class IrcApi(
def commlog(mod: Holder, user: User, reportBy: Option[User.ID]): Funit =
zulip(_.mod.adminLog, "private comms checks")({
val finalS = if (user.username endsWith "s") "" else "s"
- s"**${markdown modLink mod.user.username}** checked out **${markdown userLink user.username}**'$finalS communications "
+ s"**${markdown modLink mod.user}** checked out **${markdown userLink user.username}**'$finalS communications "
} + reportBy.filter(mod.id !=).fold("spontaneously") { by =>
s"while investigating a report created by ${markdown.userLink(by)}"
})
@@ -96,10 +96,11 @@ final class IrcApi(
// def printBan(mod: Holder, print: String, userIds: List[User.ID]): Funit =
// logMod(mod.id, "footprints", s"Ban print $print of ${userIds} users: ${userIds map linkifyUsers}")
- def chatPanic(mod: Holder, v: Boolean): Funit =
- zulip(_.mod.log, "chat panic")(
+ def chatPanic(mod: Holder, v: Boolean): Funit = {
+ val msg =
s":stop: ${markdown.modLink(mod.user)} ${if (v) "enabled" else "disabled"} ${markdown.lichessLink("/mod/chat-panic", " Chat Panic")}"
- )
+ zulip(_.mod.log, "chat panic")(msg) >> zulip(_.mod.commsPublic, "main")(msg)
+ }
def garbageCollector(msg: String): Funit =
zulip(_.mod.adminLog, "garbage collector")(markdown linkifyUsers msg)
@@ -132,6 +133,9 @@ final class IrcApi(
}
}
+ def nameClosePreset(username: String): Funit =
+ zulip(_.mod.commsPublic, "/" + username)("@**remind** here in 48h to close this account")
+
def stop(): Funit = zulip(_.general, "lila")("Lichess is restarting.")
def publishEvent(event: Event): Funit = event match {
@@ -208,7 +212,7 @@ object IrcApi {
private object markdown {
def link(url: String, name: String) = s"[$name]($url)"
def lichessLink(path: String, name: String) = s"[$name](https://lichess.org$path)"
- def userLink(name: String): String = lichessLink(s"/@/$name?mod", name)
+ def userLink(name: String): String = lichessLink(s"/@/$name?mod¬es", name)
def userLink(user: User): String = userLink(user.username)
def modLink(name: String): String = lichessLink(s"/@/$name", name)
def modLink(user: User): String = modLink(user.username)
diff --git a/modules/irc/src/main/ZulipClient.scala b/modules/irc/src/main/ZulipClient.scala
index fbef967183..d15d6f1406 100644
--- a/modules/irc/src/main/ZulipClient.scala
+++ b/modules/irc/src/main/ZulipClient.scala
@@ -80,6 +80,7 @@ private object ZulipClient {
val log = "mod-log"
val adminLog = "mod-admin-log"
val adminGeneral = "mod-admin-general"
+ val commsPublic = "mod-comms-public"
val commsPrivate = "mod-comms-private"
val hunterCheat = "mod-hunter-cheat"
val adminAppeal = "mod-admin-appeal"
diff --git a/modules/lobby/src/main/AbortListener.scala b/modules/lobby/src/main/AbortListener.scala
index 54f521d59b..548cbf936b 100644
--- a/modules/lobby/src/main/AbortListener.scala
+++ b/modules/lobby/src/main/AbortListener.scala
@@ -4,6 +4,7 @@ import lila.game.{ Pov, Source }
final private class AbortListener(
userRepo: lila.user.UserRepo,
+ gameRepo: lila.game.GameRepo,
seekApi: SeekApi,
lobbyActor: LobbySyncActor
)(implicit ec: scala.concurrent.ExecutionContext) {
@@ -14,7 +15,10 @@ final private class AbortListener(
lobbyActor.registerAbortedGame(pov.game)
private def cancelColorIncrement(pov: Pov): Unit =
- if (pov.game.source.exists(s => s == Source.Lobby || s == Source.Pool)) pov.game.userIds match {
+ if (
+ pov.game.source
+ .exists(s => s == Source.Lobby || s == Source.Pool) && !gameRepo.fixedColorLobbyCache.get(pov.game.id)
+ ) pov.game.userIds match {
case List(u1, u2) =>
userRepo.incColor(u1, -1)
userRepo.incColor(u2, 1)
diff --git a/modules/lobby/src/main/Biter.scala b/modules/lobby/src/main/Biter.scala
index ce7c7b6038..ed1b755d45 100644
--- a/modules/lobby/src/main/Biter.scala
+++ b/modules/lobby/src/main/Biter.scala
@@ -36,6 +36,7 @@ final private class Biter(
_ <- gameRepo insertDenormalized game
} yield {
lila.mon.lobby.hook.join.increment()
+ rememberIfFixedColor(hook.realColor, game)
JoinHook(sri, hook, game, creatorColor)
}
@@ -50,7 +51,14 @@ final private class Biter(
blackUser = creatorColor.fold(user.some, owner.some)
).withUniqueId
_ <- gameRepo insertDenormalized game
- } yield JoinSeek(user.id, seek, game, creatorColor)
+ } yield {
+ rememberIfFixedColor(seek.realColor, game)
+ JoinSeek(user.id, seek, game, creatorColor)
+ }
+
+ private def rememberIfFixedColor(color: Color, game: Game) =
+ if (color != Color.Random)
+ gameRepo.fixedColorLobbyCache put game.id
private def assignCreatorColor(
creatorUser: Option[User],
diff --git a/modules/mod/src/main/ModQueueStats.scala b/modules/mod/src/main/ModQueueStats.scala
index 4b65053074..78bb497f41 100644
--- a/modules/mod/src/main/ModQueueStats.scala
+++ b/modules/mod/src/main/ModQueueStats.scala
@@ -51,11 +51,10 @@ final class ModQueueStats(
data <- doc.getAsOpt[List[Bdoc]]("data")
} yield date -> {
for {
- entry <- data
- nb <- entry.int("nb")
- roomStr <- entry.string("room")
- room <- Room.byKey get roomStr
- score <- entry.int("score")
+ entry <- data
+ nb <- entry.int("nb")
+ room <- entry.string("room")
+ score <- entry.int("score")
} yield (room, score, nb)
}
}
@@ -66,20 +65,25 @@ final class ModQueueStats(
"common" -> Json.obj(
"xaxis" -> days.map(_._1.getMillis)
),
- "rooms" -> Room.all.map { room =>
- Json.obj(
- "name" -> room.name,
- "series" -> scores.collect {
- case score if score > 20 || room == Room.Boost =>
- Json.obj(
- "name" -> score,
- "data" -> days.map(~_._2.collectFirst {
- case (r, s, nb) if r == room && s == score => nb
- })
- )
- }
- )
- }
+ "rooms" -> Room.all
+ .map { room =>
+ room.key -> room.name
+ }
+ .appended { ("appeal", "Appeal") }
+ .map { case (roomKey, roomName) =>
+ Json.obj(
+ "name" -> roomName,
+ "series" -> scores.collect {
+ case score if score > 20 || roomKey == Room.Boost.key =>
+ Json.obj(
+ "name" -> score,
+ "data" -> days.map(~_._2.collectFirst {
+ case (r, s, nb) if r == roomKey && s == score => nb
+ })
+ )
+ }
+ )
+ }
)
)
}
diff --git a/modules/mod/src/main/ModlogApi.scala b/modules/mod/src/main/ModlogApi.scala
index caa6ed94e8..f37f001fd8 100644
--- a/modules/mod/src/main/ModlogApi.scala
+++ b/modules/mod/src/main/ModlogApi.scala
@@ -321,7 +321,8 @@ final class ModlogApi(repo: ModlogRepo, userRepo: UserRepo, ircApi: IrcApi)(impl
case M.engine | M.unengine | M.booster | M.unbooster | M.reopenAccount | M.unalt =>
Some(IrcApi.ModDomain.Hunt)
case M.troll | M.untroll | M.chatTimeout | M.closeTopic | M.openTopic | M.disableTeam |
- M.enableTeam | M.setKidMode | M.deletePost | M.postAsAnonMod | M.editAsAnonMod =>
+ M.enableTeam | M.setKidMode | M.deletePost | M.postAsAnonMod | M.editAsAnonMod | M.blogTier |
+ M.blogPostEdit =>
Some(IrcApi.ModDomain.Comm)
case _ => Some(IrcApi.ModDomain.Other)
}
diff --git a/modules/mod/src/main/Presets.scala b/modules/mod/src/main/Presets.scala
index caf76ea09c..1fba16ac90 100644
--- a/modules/mod/src/main/Presets.scala
+++ b/modules/mod/src/main/Presets.scala
@@ -50,11 +50,15 @@ case class ModPresets(value: List[ModPreset]) {
value.find(_.text.filter(_.isLetter) == clean)
}
}
-case class ModPreset(name: String, text: String, permissions: Set[Permission])
+case class ModPreset(name: String, text: String, permissions: Set[Permission]) {
+
+ def isNameClose = name contains ModPresets.nameClosePresetName
+}
object ModPresets {
- val groups = List("PM", "appeal")
+ val groups = List("PM", "appeal")
+ val nameClosePresetName = "Account closure for name in 48h"
private[mod] object setting {
diff --git a/modules/perfStat/src/main/JsonView.scala b/modules/perfStat/src/main/JsonView.scala
index a321865bb7..c273eb4bfe 100644
--- a/modules/perfStat/src/main/JsonView.scala
+++ b/modules/perfStat/src/main/JsonView.scala
@@ -5,6 +5,7 @@ import org.joda.time.format.ISODateTimeFormat
import play.api.i18n.Lang
import play.api.libs.json._
+import lila.common.Json.{ jodaWrites => _, _ }
import lila.common.LightUser
import lila.rating.{ Glicko, Perf, PerfType }
import lila.user.User
diff --git a/modules/plan/src/main/PlanApi.scala b/modules/plan/src/main/PlanApi.scala
index 6ea1f74425..512e29ce5b 100644
--- a/modules/plan/src/main/PlanApi.scala
+++ b/modules/plan/src/main/PlanApi.scala
@@ -282,25 +282,24 @@ final class PlanApi(
.void >> setDbUserPlanOnCharge(user, levelUp = false)
def gift(from: User, to: User, money: Money): Funit =
- !to.isPatron ?? {
- for {
- isLifetime <- pricingApi isLifetime money
- _ <- patronColl.update
- .one(
- $id(to.id),
- $set(
- "lastLevelUp" -> DateTime.now,
- "lifetime" -> isLifetime,
- "free" -> Patron.Free(DateTime.now, by = from.id.some),
- "expiresAt" -> (!isLifetime option DateTime.now.plusMonths(1))
- ),
- upsert = true
- )
- newTo = to.mapPlan(_.incMonths)
- _ <- setDbUserPlan(newTo)
- } yield {
- notifier.onGift(from, newTo, isLifetime)
- }
+ for {
+ toPatronOpt <- userPatron(to)
+ isLifetime <- fuccess(toPatronOpt.exists(_.isLifetime)) >>| (pricingApi isLifetime money)
+ _ <- patronColl.update
+ .one(
+ $id(to.id),
+ $set(
+ "lastLevelUp" -> DateTime.now,
+ "lifetime" -> isLifetime,
+ "free" -> Patron.Free(DateTime.now, by = from.id.some),
+ "expiresAt" -> (!isLifetime option DateTime.now.plusMonths(1))
+ ),
+ upsert = true
+ )
+ newTo = to.mapPlan(p => if (toPatronOpt.exists(_.canLevelUp)) p.incMonths else p.enable)
+ _ <- setDbUserPlan(newTo)
+ } yield {
+ notifier.onGift(from, newTo, isLifetime)
}
def recentGiftFrom(from: User): Fu[Option[Patron]] =
diff --git a/modules/playban/src/main/PlaybanApi.scala b/modules/playban/src/main/PlaybanApi.scala
index b19242a9a9..97dcac80d7 100644
--- a/modules/playban/src/main/PlaybanApi.scala
+++ b/modules/playban/src/main/PlaybanApi.scala
@@ -132,7 +132,7 @@ final class PlaybanApi(
Status.Resign.is(status)
}
.map { c =>
- (c.estimateTotalSeconds / 10) atLeast 15 atMost (3 * 60)
+ (c.estimateTotalSeconds / 10) atLeast 30 atMost (3 * 60)
}
.exists(_ < nowSeconds - game.movedAt.getSeconds)
.option {
diff --git a/modules/pref/src/main/Pref.scala b/modules/pref/src/main/Pref.scala
index 907c9f924e..de73719cad 100644
--- a/modules/pref/src/main/Pref.scala
+++ b/modules/pref/src/main/Pref.scala
@@ -1,5 +1,9 @@
package lila.pref
+import org.joda.time.DateTime
+
+import lila.user.User
+
case class Pref(
_id: String, // user id
bg: Int,
@@ -413,7 +417,14 @@ object Pref {
object Zen extends BooleanPref {}
object Ratings extends BooleanPref {}
- def create(id: String) = default.copy(_id = id)
+ val darkByDefaultSince = new DateTime(2021, 11, 7, 8, 0)
+
+ def create(id: User.ID) = default.copy(_id = id)
+
+ def create(user: User) = default.copy(
+ _id = user.id,
+ bg = if (user.createdAt isAfter darkByDefaultSince) Bg.DARK else Bg.LIGHT
+ )
lazy val default = Pref(
_id = "",
diff --git a/modules/pref/src/main/PrefApi.scala b/modules/pref/src/main/PrefApi.scala
index 3aee2d168c..b029cd4311 100644
--- a/modules/pref/src/main/PrefApi.scala
+++ b/modules/pref/src/main/PrefApi.scala
@@ -37,13 +37,16 @@ final class PrefApi(
.void >>- { cache invalidate user.id }
} >>- { cache invalidate user.id }
- def getPrefById(id: User.ID): Fu[Pref] = cache get id dmap (_ getOrElse Pref.create(id))
- val getPref = getPrefById _
- def getPref(user: User): Fu[Pref] = getPref(user.id)
- def getPref(user: Option[User]): Fu[Pref] = user.fold(fuccess(Pref.default))(getPref)
+ def getPrefById(id: User.ID): Fu[Option[Pref]] = cache get id
- def getPref[A](user: User, pref: Pref => A): Fu[A] = getPref(user) dmap pref
- def getPref[A](userId: User.ID, pref: Pref => A): Fu[A] = getPref(userId) dmap pref
+ def getPref(user: User): Fu[Pref] = cache get user.id dmap {
+ _ getOrElse Pref.create(user)
+ }
+
+ def getPref[A](user: User, pref: Pref => A): Fu[A] = getPref(user) dmap pref
+
+ def getPref[A](userId: User.ID, pref: Pref => A): Fu[A] =
+ getPrefById(userId).dmap(p => pref(p | Pref.default))
def getPref(user: User, req: RequestHeader): Fu[Pref] =
getPref(user) dmap RequestPref.queryParamOverride(req)
@@ -81,9 +84,6 @@ final class PrefApi(
def setPref(user: User, change: Pref => Pref): Funit =
getPref(user) map change flatMap setPref
- def setPref(userId: User.ID, change: Pref => Pref): Funit =
- getPref(userId) map change flatMap setPref
-
def setPrefString(user: User, name: String, value: String): Funit =
getPref(user) map { _.set(name, value) } orFail
s"Bad pref ${user.id} $name -> $value" flatMap setPref
diff --git a/modules/pref/src/main/Theme.scala b/modules/pref/src/main/Theme.scala
index a66d06c7fe..c4ebe4b211 100644
--- a/modules/pref/src/main/Theme.scala
+++ b/modules/pref/src/main/Theme.scala
@@ -1,13 +1,10 @@
package lila.pref
-sealed class Theme private[pref] (val name: String, val colors: Theme.HexColors) {
+sealed class Theme private[pref] (val name: String, val file: String) {
override def toString = name
def cssClass = name
-
- def light = colors._1
- def dark = colors._2
}
sealed trait ThemeObject {
@@ -27,49 +24,33 @@ sealed trait ThemeObject {
object Theme extends ThemeObject {
- case class HexColor(value: String) extends AnyVal with StringValue
- type HexColors = (HexColor, HexColor)
-
- private[pref] val defaultHexColors = (HexColor("b0b0b0"), HexColor("909090"))
-
- private val colors: Map[String, HexColors] = Map(
- "blue" -> (HexColor("dee3e6") -> HexColor("8ca2ad")),
- "brown" -> (HexColor("f0d9b5") -> HexColor("b58863")),
- "green" -> (HexColor("ffffdd") -> HexColor("86a666")),
- "purple" -> (HexColor("9f90b0") -> HexColor("7d4a8d")),
- "ic" -> (HexColor("ececec") -> HexColor("c1c18e")),
- "horsey" -> (HexColor("f1d9b6") -> HexColor("8e6547"))
- )
-
val all = List(
- "blue",
- "blue2",
- "blue3",
- "blue-marble",
- "canvas",
- "wood",
- "wood2",
- "wood3",
- "wood4",
- "maple",
- "maple2",
- "brown",
- "leather",
- "green",
- "marble",
- "green-plastic",
- "grey",
- "metal",
- "olive",
- "newspaper",
- "purple",
- "purple-diag",
- "pink",
- "ic",
- "horsey"
- ) map { name =>
- new Theme(name, colors.getOrElse(name, defaultHexColors))
- }
+ new Theme("blue", "svg/blue.svg"),
+ new Theme("blue2", "blue2.jpg"),
+ new Theme("blue3", "blue3.jpg"),
+ new Theme("blue-marble", "blue-marble.jpg"),
+ new Theme("canvas", "canvas2.jpg"),
+ new Theme("wood", "wood.jpg"),
+ new Theme("wood2", "wood2.jpg"),
+ new Theme("wood3", "wood3.jpg"),
+ new Theme("wood4", "wood4.jpg"),
+ new Theme("maple", "maple.jpg"),
+ new Theme("maple2", "maple2.jpg"),
+ new Theme("brown", "svg/brown.svg"),
+ new Theme("leather", "leather.jpg"),
+ new Theme("green", "svg/green.svg"),
+ new Theme("marble", "marble.jpg"),
+ new Theme("green-plastic", "green-plastic.png"),
+ new Theme("grey", "grey.jpg"),
+ new Theme("metal", "metal.jpg"),
+ new Theme("olive", "olive.jpg"),
+ new Theme("newspaper", "newspaper.png"),
+ new Theme("purple", "svg/purple.svg"),
+ new Theme("purple-diag", "purple-diag.png"),
+ new Theme("pink", "pink-pyramid.png"),
+ new Theme("ic", "svg/ic.svg"),
+ new Theme("horsey", "horsey.jpg")
+ )
lazy val default = allByName get "brown" err "Can't find default theme D:"
}
@@ -77,28 +58,26 @@ object Theme extends ThemeObject {
object Theme3d extends ThemeObject {
val all = List(
- "Black-White-Aluminium",
- "Brushed-Aluminium",
- "China-Blue",
- "China-Green",
- "China-Grey",
- "China-Scarlet",
- "China-Yellow",
- "Classic-Blue",
- "Gold-Silver",
- "Green-Glass",
- "Light-Wood",
- "Power-Coated",
- "Purple-Black",
- "Rosewood",
- "Wood-Glass",
- "Marble",
- "Wax",
- "Jade",
- "Woodi"
- ) map { name =>
- new Theme(name, Theme.defaultHexColors)
- }
+ new Theme("Black-White-Aluminium", "Black-White-Aluminium.png"),
+ new Theme("Brushed-Aluminium", "Brushed-Aluminium.png"),
+ new Theme("China-Blue", "China-Blue.png"),
+ new Theme("China-Green", "China-Green.png"),
+ new Theme("China-Grey", "China-Grey.png"),
+ new Theme("China-Scarlet", "China-Scarlet.png"),
+ new Theme("China-Yellow", "China-Yellow.png"),
+ new Theme("Classic-Blue", "Classic-Blue.png"),
+ new Theme("Gold-Silver", "Gold-Silver.png"),
+ new Theme("Green-Glass", "Green-Glass.png"),
+ new Theme("Light-Wood", "Light-Wood.png"),
+ new Theme("Power-Coated", "Power-Coated.png"),
+ new Theme("Purple-Black", "Purple-Black.png"),
+ new Theme("Rosewood", "Rosewood.png"),
+ new Theme("Wood-Glass", "Wood-Glass.png"),
+ new Theme("Marble", "Marble.png"),
+ new Theme("Wax", "Wax.png"),
+ new Theme("Jade", "Jade.png"),
+ new Theme("Woodi", "Woodi.png")
+ )
lazy val default = allByName get "Woodi" err "Can't find default theme D:"
}
diff --git a/modules/puzzle/src/main/DailyPuzzle.scala b/modules/puzzle/src/main/DailyPuzzle.scala
index 8a237a3a3d..5a14cbc5d9 100644
--- a/modules/puzzle/src/main/DailyPuzzle.scala
+++ b/modules/puzzle/src/main/DailyPuzzle.scala
@@ -7,6 +7,7 @@ import scala.concurrent.duration._
import lila.db.dsl._
import lila.memo.CacheApi._
+import lila.common.ThreadLocalRandom
final private[puzzle] class DailyPuzzle(
colls: PuzzleColls,
@@ -26,7 +27,7 @@ final private[puzzle] class DailyPuzzle(
def get: Fu[Option[DailyPuzzle.WithHtml]] = cache.getUnit
private def find: Fu[Option[DailyPuzzle.WithHtml]] =
- (findCurrent orElse findNew) recover { case e: Exception =>
+ (findCurrent orElse findNewBiased()) recover { case e: Exception =>
logger.error("find daily", e)
none
} flatMap { _ ?? makeDaily }
@@ -48,6 +49,18 @@ final private[puzzle] class DailyPuzzle(
.one[Puzzle]
}
+ private def findNewBiased(tries: Int = 0): Fu[Option[Puzzle]] = {
+ def tryAgainMaybe = (tries < 5) ?? findNewBiased(tries + 1)
+ import lila.common.ThreadLocalRandom.odds
+ import PuzzleTheme._
+ findNew flatMap {
+ case None => tryAgainMaybe
+ case Some(p) if p.hasTheme(anastasiaMate) && !odds(3) => tryAgainMaybe dmap (_ orElse p.some)
+ case Some(p) if p.hasTheme(arabianMate) && odds(2) => tryAgainMaybe dmap (_ orElse p.some)
+ case p => fuccess(p)
+ }
+ }
+
private def findNew: Fu[Option[Puzzle]] =
colls
.path {
diff --git a/modules/puzzle/src/main/Puzzle.scala b/modules/puzzle/src/main/Puzzle.scala
index c806e91e1c..4e1555b193 100644
--- a/modules/puzzle/src/main/Puzzle.scala
+++ b/modules/puzzle/src/main/Puzzle.scala
@@ -29,6 +29,8 @@ case class Puzzle(
} err s"Can't apply puzzle $id first move"
def color = fen.color.fold[chess.Color](chess.White)(!_)
+
+ def hasTheme(theme: PuzzleTheme) = themes(theme.key)
}
object Puzzle {
diff --git a/modules/relay/src/main/RelaySync.scala b/modules/relay/src/main/RelaySync.scala
index f6c28bd2ad..0bfceb5e6b 100644
--- a/modules/relay/src/main/RelaySync.scala
+++ b/modules/relay/src/main/RelaySync.scala
@@ -155,7 +155,8 @@ final private class RelaySync(
(tour.official && chapter.root.mainline.sizeIs > 10) ?? studyApi.analysisRequest(
studyId = study.id,
chapterId = chapter.id,
- userId = study.ownerId
+ userId = study.ownerId,
+ unlimited = true
)
} >>- {
multiboard.invalidate(study.id)
diff --git a/modules/relay/src/main/SyncLog.scala b/modules/relay/src/main/SyncLog.scala
index e7b0924a3f..c4057236c0 100644
--- a/modules/relay/src/main/SyncLog.scala
+++ b/modules/relay/src/main/SyncLog.scala
@@ -14,7 +14,10 @@ case class SyncLog(events: Vector[SyncLog.Event]) extends AnyVal {
def add(event: SyncLog.Event) =
copy(
- events = events.take(SyncLog.historySize - 1) :+ event
+ events = {
+ if (events.sizeIs > SyncLog.historySize) events drop 1
+ else events
+ } :+ event
)
}
diff --git a/modules/report/src/main/Reason.scala b/modules/report/src/main/Reason.scala
index 7fbb648e3e..0341b8a786 100644
--- a/modules/report/src/main/Reason.scala
+++ b/modules/report/src/main/Reason.scala
@@ -49,10 +49,10 @@ object Reason {
def isGrantedFor(mod: Holder)(reason: Reason) = {
import lila.security.Granter
reason match {
- case Cheat => Granter.is(_.MarkEngine)(mod)
- case AltPrint | CheatPrint => Granter.is(_.Admin)(mod)
- case Comm => Granter.is(_.Shadowban)(mod)
- case Boost | Playbans | Other => Granter.is(_.MarkBooster)(mod)
+ case Cheat => Granter.is(_.MarkEngine)(mod)
+ case AltPrint | CheatPrint | Playbans | Other => Granter.is(_.Admin)(mod)
+ case Comm => Granter.is(_.Shadowban)(mod)
+ case Boost => Granter.is(_.MarkBooster)(mod)
}
}
}
diff --git a/modules/round/src/main/Drawer.scala b/modules/round/src/main/Drawer.scala
index 5ff6135c7f..16e00900a9 100644
--- a/modules/round/src/main/Drawer.scala
+++ b/modules/round/src/main/Drawer.scala
@@ -22,9 +22,9 @@ final private[round] class Drawer(
import Pref.PrefZero
if (game.playerHasOfferedDrawRecently(pov.color)) fuccess(pov.some)
else
- pov.player.userId ?? prefApi.getPref map { pref =>
- pref.autoThreefold == Pref.AutoThreefold.ALWAYS || {
- pref.autoThreefold == Pref.AutoThreefold.TIME &&
+ pov.player.userId ?? { uid => prefApi.getPref(uid, _.autoThreefold) } map { autoThreefold =>
+ autoThreefold == Pref.AutoThreefold.ALWAYS || {
+ autoThreefold == Pref.AutoThreefold.TIME &&
game.clock ?? { _.remainingTime(pov.color) < Centis.ofSeconds(30) }
} || pov.player.userId.exists(isBotSync)
} map (_ option pov)
@@ -37,7 +37,7 @@ final private[round] class Drawer(
case pov if pov.game.history.threefoldRepetition =>
finisher.other(pov.game, _.Draw, None)
case pov if pov.opponent.isOfferingDraw =>
- finisher.other(pov.game, _.Draw, None, Some(trans.drawOfferAccepted.txt()))
+ finisher.other(pov.game, _.Draw, None, Messenger.Persistent(trans.drawOfferAccepted.txt()).some)
case Pov(g, color) if g playerCanOfferDraw color =>
val progress = Progress(g) map { _ offerDraw color }
messenger.system(g, color.fold(trans.whiteOffersDraw, trans.blackOffersDraw).txt())
diff --git a/modules/round/src/main/Finisher.scala b/modules/round/src/main/Finisher.scala
index af35ddbca0..d9f338797c 100644
--- a/modules/round/src/main/Finisher.scala
+++ b/modules/round/src/main/Finisher.scala
@@ -48,7 +48,7 @@ final private class Finisher(
other(game, _.Aborted, none)
} else if (game.player(!game.player.color).isOfferingDraw) {
- apply(game, _.Draw, None, Some(trans.drawOfferAccepted.txt()))
+ apply(game, _.Draw, None, Messenger.Persistent(trans.drawOfferAccepted.txt()).some)
} else {
val winner = Some(!game.player.color) ifFalse game.situation.opponentHasInsufficientMaterial
apply(game, _.Outoftime, winner) >>-
@@ -62,14 +62,14 @@ final private class Finisher(
lila.mon.round.expiration.count.increment()
playban.noStart(Pov(game, culprit))
if (game.isMandatory) apply(game, _.NoStart, Some(!culprit.color))
- else apply(game, _.Aborted, None, Some("Game aborted by server"))
+ else apply(game, _.Aborted, None, Messenger.Persistent("Game aborted by server").some)
}
def other(
game: Game,
status: Status.type => Status,
winner: Option[Color],
- message: Option[String] = None
+ message: Option[Messenger.SystemMessage] = None
)(implicit proxy: GameProxy): Fu[Events] =
apply(game, status, winner, message) >>- playban.other(game, status, winner).unit
@@ -108,7 +108,7 @@ final private class Finisher(
game: Game,
makeStatus: Status.type => Status,
winnerC: Option[Color],
- message: Option[String] = None
+ message: Option[Messenger.SystemMessage] = None
)(implicit proxy: GameProxy): Fu[Events] = {
val status = makeStatus(Status)
val prog = game.finish(status, winnerC)
@@ -139,7 +139,7 @@ final private class Finisher(
.flatMap { case (whiteO, blackO) =>
val finish = FinishGame(g, whiteO, blackO)
updateCountAndPerfs(finish) map { ratingDiffs =>
- message foreach { messenger.system(g, _) }
+ message foreach { messenger(g, _) }
gameRepo game g.id foreach { newGame =>
newGame foreach proxy.setFinishedGame
val newFinish = finish.copy(game = newGame | g)
diff --git a/modules/round/src/main/JsonView.scala b/modules/round/src/main/JsonView.scala
index 688ab39124..adf47dbd87 100644
--- a/modules/round/src/main/JsonView.scala
+++ b/modules/round/src/main/JsonView.scala
@@ -7,6 +7,7 @@ import play.api.libs.json._
import scala.math
import lila.common.ApiVersion
+import lila.common.Json._
import lila.game.JsonView._
import lila.game.{ Pov, Game, Player => GamePlayer }
import lila.pref.Pref
diff --git a/modules/round/src/main/Messenger.scala b/modules/round/src/main/Messenger.scala
index fd7f587dbb..60e11fa5ce 100644
--- a/modules/round/src/main/Messenger.scala
+++ b/modules/round/src/main/Messenger.scala
@@ -13,6 +13,11 @@ final class Messenger(api: ChatApi) {
def volatile(game: Game, message: String): Unit =
system(persistent = false)(game, message)
+ def apply(game: Game, message: Messenger.SystemMessage): Unit = message match {
+ case Messenger.Persistent(msg) => system(persistent = true)(game, msg)
+ case Messenger.Volatile(msg) => system(persistent = false)(game, msg)
+ }
+
def system(persistent: Boolean)(game: Game, message: String): Unit = if (game.nonAi) {
api.userChat.volatile(watcherId(Chat.Id(game.id)), message, _.Round)
if (persistent) api.userChat.system(Chat.Id(game.id), message, _.Round)
@@ -58,3 +63,10 @@ final class Messenger(api: ChatApi) {
private def watcherId(chatId: Chat.Id) = Chat.Id(s"$chatId/w")
private def watcherId(gameId: Game.Id) = Chat.Id(s"$gameId/w")
}
+
+private object Messenger {
+
+ sealed trait SystemMessage { val msg: String }
+ case class Persistent(msg: String) extends SystemMessage
+ case class Volatile(msg: String) extends SystemMessage
+}
diff --git a/modules/round/src/main/Rematcher.scala b/modules/round/src/main/Rematcher.scala
index 50605f5754..0f36554b99 100644
--- a/modules/round/src/main/Rematcher.scala
+++ b/modules/round/src/main/Rematcher.scala
@@ -56,10 +56,10 @@ final private class Rematcher(
}
def no(pov: Pov): Fu[Events] = {
- if (isOffering(pov)) messenger.system(pov.game, trans.rematchOfferCanceled.txt())
+ if (isOffering(pov)) messenger.volatile(pov.game, trans.rematchOfferCanceled.txt())
else if (isOffering(!pov)) {
declined put pov.fullId
- messenger.system(pov.game, trans.rematchOfferDeclined.txt())
+ messenger.volatile(pov.game, trans.rematchOfferDeclined.txt())
}
offers invalidate pov.game.id
fuccess(List(Event.RematchOffer(by = none)))
@@ -80,7 +80,7 @@ final private class Rematcher(
_ = if (pov.game.variant == Chess960 && !chess960.get(pov.gameId)) chess960.put(nextGame.id)
_ <- gameRepo insertDenormalized nextGame
} yield {
- messenger.system(pov.game, trans.rematchOfferAccepted.txt())
+ messenger.volatile(pov.game, trans.rematchOfferAccepted.txt())
onStart(nextGame.id)
redirectEvents(nextGame)
}
@@ -88,7 +88,7 @@ final private class Rematcher(
}
private def rematchCreate(pov: Pov): Events = {
- messenger.system(pov.game, trans.rematchOfferSent.txt())
+ messenger.volatile(pov.game, trans.rematchOfferSent.txt())
pov.opponent.userId foreach { forId =>
Bus.publish(lila.hub.actorApi.round.RematchOffer(pov.gameId), s"rematchFor:$forId")
}
diff --git a/modules/round/src/main/RoundAsyncActor.scala b/modules/round/src/main/RoundAsyncActor.scala
index 324c2bd166..f2e36f57b9 100644
--- a/modules/round/src/main/RoundAsyncActor.scala
+++ b/modules/round/src/main/RoundAsyncActor.scala
@@ -403,7 +403,7 @@ final private[round] class RoundAsyncActor(
case WsBoot =>
handle { game =>
game.playable ?? {
- messenger.system(game, "Lichess has been updated! Sorry for the inconvenience.")
+ messenger.volatile(game, "Lichess has been updated! Sorry for the inconvenience.")
val progress = moretimer.give(game, Color.all, 20 seconds)
proxy save progress inject progress.events
}
diff --git a/modules/round/src/main/Takebacker.scala b/modules/round/src/main/Takebacker.scala
index ec4441b96b..26cbf444c9 100644
--- a/modules/round/src/main/Takebacker.scala
+++ b/modules/round/src/main/Takebacker.scala
@@ -35,7 +35,7 @@ final private class Takebacker(
double(game) >>- publishTakeback(pov) dmap (_ -> situation)
case Pov(game, color) if (game playerCanProposeTakeback color) && situation.offerable =>
{
- messenger.system(game, trans.takebackPropositionSent.txt())
+ messenger.volatile(game, trans.takebackPropositionSent.txt())
val progress = Progress(game) map { g =>
g.updatePlayer(color, _ proposeTakeback g.turns)
}
@@ -50,7 +50,7 @@ final private class Takebacker(
def no(situation: TakebackSituation)(pov: Pov)(implicit proxy: GameProxy): Fu[(Events, TakebackSituation)] =
pov match {
case Pov(game, color) if pov.player.isProposingTakeback =>
- messenger.system(game, trans.takebackPropositionCanceled.txt())
+ messenger.volatile(game, trans.takebackPropositionCanceled.txt())
val progress = Progress(game) map { g =>
g.updatePlayer(color, _.removeTakebackProposition)
}
@@ -58,7 +58,7 @@ final private class Takebacker(
publishTakebackOffer(progress.game) inject
List(Event.TakebackOffers(white = false, black = false)) -> situation.decline
case Pov(game, color) if pov.opponent.isProposingTakeback =>
- messenger.system(game, trans.takebackPropositionDeclined.txt())
+ messenger.volatile(game, trans.takebackPropositionDeclined.txt())
val progress = Progress(game) map { g =>
g.updatePlayer(!color, _.removeTakebackProposition)
}
diff --git a/modules/security/src/main/GarbageCollector.scala b/modules/security/src/main/GarbageCollector.scala
index 53a5dbf53e..77cb76123a 100644
--- a/modules/security/src/main/GarbageCollector.scala
+++ b/modules/security/src/main/GarbageCollector.scala
@@ -97,25 +97,31 @@ final class GarbageCollector(
private def isBadAccount(user: User) = user.lameOrTrollOrAlt
- private def collect(user: User, email: EmailAddress, msg: => String): Funit =
- justOnce(user.id) ?? {
- val armed = isArmed()
- val wait = (30 + ThreadLocalRandom.nextInt(300)).seconds
- val message =
- s"Will dispose of @${user.username} in $wait. Email: ${email.value}. $msg${!armed ?? " [SIMULATION]"}"
- logger.info(message)
- noteApi.lichessWrite(user, s"Garbage collected because of $msg")
- irc.garbageCollector(message) >>- {
- if (armed) {
- doInitialSb(user)
- system.scheduler
- .scheduleOnce(wait) {
- doCollect(user)
- }
- .unit
+ private def collect(user: User, email: EmailAddress, msg: => String): Funit = justOnce(user.id) ?? {
+ hasBeenCollectedBefore(user) flatMap {
+ case true => funit
+ case _ =>
+ val armed = isArmed()
+ val wait = (30 + ThreadLocalRandom.nextInt(300)).seconds
+ val message =
+ s"Will dispose of @${user.username} in $wait. Email: ${email.value}. $msg${!armed ?? " [SIMULATION]"}"
+ logger.info(message)
+ noteApi.lichessWrite(user, s"Garbage collected because of $msg")
+ irc.garbageCollector(message) >>- {
+ if (armed) {
+ doInitialSb(user)
+ system.scheduler
+ .scheduleOnce(wait) {
+ doCollect(user)
+ }
+ .unit
+ }
}
- }
}
+ }
+
+ private def hasBeenCollectedBefore(user: User): Fu[Boolean] =
+ noteApi.byUserForMod(user.id).map(_.exists(_.text startsWith "Garbage collected"))
private def doInitialSb(user: User): Unit =
Bus.publish(
diff --git a/modules/simul/src/main/JsonView.scala b/modules/simul/src/main/JsonView.scala
index 9c45b4be6e..f2904d0327 100644
--- a/modules/simul/src/main/JsonView.scala
+++ b/modules/simul/src/main/JsonView.scala
@@ -3,6 +3,7 @@ package lila.simul
import play.api.libs.json._
import lila.common.LightUser
+import lila.common.Json._
import lila.game.{ Game, GameRepo }
import lila.user.User
@@ -12,10 +13,6 @@ final class JsonView(
proxyRepo: lila.round.GameProxyRepo
)(implicit ec: scala.concurrent.ExecutionContext) {
- implicit private val colorWriter: Writes[chess.Color] = Writes { c =>
- JsString(c.name)
- }
-
implicit private val simulTeamWriter = Json.writes[SimulTeam]
private def fetchGames(simul: Simul) =
diff --git a/modules/study/src/main/ChapterMaker.scala b/modules/study/src/main/ChapterMaker.scala
index d2ca3e969a..248f214af3 100644
--- a/modules/study/src/main/ChapterMaker.scala
+++ b/modules/study/src/main/ChapterMaker.scala
@@ -19,14 +19,14 @@ final private class ChapterMaker(
import ChapterMaker._
- def apply(study: Study, data: Data, order: Int, userId: User.ID): Fu[Chapter] =
+ def apply(study: Study, data: Data, order: Int, userId: User.ID, withRatings: Boolean): Fu[Chapter] =
data.game.??(parseGame) flatMap {
case None =>
data.game ?? pgnFetch.fromUrl flatMap {
case Some(pgn) => fromFenOrPgnOrBlank(study, data.copy(pgn = pgn.some), order, userId)
case _ => fromFenOrPgnOrBlank(study, data, order, userId)
}
- case Some(game) => fromGame(study, game, data, order, userId)
+ case Some(game) => fromGame(study, game, data, order, userId, withRatings)
} map { (c: Chapter) =>
if (c.name.value.isEmpty) c.copy(name = Chapter defaultName order) else c
}
@@ -125,14 +125,15 @@ final private class ChapterMaker(
data: Data,
order: Int,
userId: User.ID,
+ withRatings: Boolean,
initialFen: Option[FEN] = None
): Fu[Chapter] =
for {
root <- game2root(game, initialFen)
- tags <- pgnDump.tags(game, initialFen, none, withOpening = true, withRating = true)
+ tags <- pgnDump.tags(game, initialFen, none, withOpening = true, withRatings)
name <- {
if (data.isDefaultName)
- Namer.gameVsText(game, withRatings = false)(lightUser.async) dmap Chapter.Name.apply
+ Namer.gameVsText(game, withRatings)(lightUser.async) dmap Chapter.Name.apply
else fuccess(data.name)
}
_ = notifyChat(study, game, userId)
diff --git a/modules/study/src/main/JsonView.scala b/modules/study/src/main/JsonView.scala
index 7494ab5238..d62f78c687 100644
--- a/modules/study/src/main/JsonView.scala
+++ b/modules/study/src/main/JsonView.scala
@@ -127,21 +127,12 @@ object JsonView {
implicit val chapterIdWrites: Writes[Chapter.Id] = stringIsoWriter(Chapter.idIso)
implicit val chapterNameWrites: Writes[Chapter.Name] = stringIsoWriter(Chapter.nameIso)
- implicit private[study] val uciWrites: Writes[Uci] = Writes[Uci] { u =>
- JsString(u.uci)
- }
implicit private val posReader: Reads[Pos] = Reads[Pos] { v =>
(v.asOpt[String] flatMap Pos.fromKey).fold[JsResult[Pos]](JsError(Nil))(JsSuccess(_))
}
implicit private[study] val pathWrites: Writes[Path] = Writes[Path] { p =>
JsString(p.toString)
}
- implicit private[study] val colorWriter: Writes[chess.Color] = Writes[chess.Color] { c =>
- JsString(c.name)
- }
- implicit private[study] val fenWriter: Writes[FEN] = Writes[FEN] { f =>
- JsString(f.value)
- }
implicit private[study] val sriWriter: Writes[Sri] = Writes[Sri] { sri =>
JsString(sri.value)
}
diff --git a/modules/study/src/main/ServerEval.scala b/modules/study/src/main/ServerEval.scala
index f20f30f3ca..885c07ba39 100644
--- a/modules/study/src/main/ServerEval.scala
+++ b/modules/study/src/main/ServerEval.scala
@@ -22,14 +22,15 @@ object ServerEval {
private val onceEvery = lila.memo.OnceEvery(5 minutes)
- def apply(study: Study, chapter: Chapter, userId: User.ID): Funit =
+ def apply(study: Study, chapter: Chapter, userId: User.ID, unlimited: Boolean = false): Funit =
chapter.serverEval.fold(true) { eval =>
!eval.done && onceEvery(chapter.id.value)
} ?? {
val unlimitedFu =
- fuccess(userId == User.lichessId) >>| userRepo
- .byId(userId)
- .map(_.exists(Granter(_.Relay)))
+ fuccess(unlimited) >>|
+ fuccess(userId == User.lichessId) >>| userRepo
+ .byId(userId)
+ .map(_.exists(Granter(_.Relay)))
unlimitedFu flatMap { unlimited =>
chapterRepo.startServerEval(chapter) >>- {
fishnet ! StudyChapterRequest(
diff --git a/modules/study/src/main/StudyApi.scala b/modules/study/src/main/StudyApi.scala
index 9e069100ce..7c5a143722 100644
--- a/modules/study/src/main/StudyApi.scala
+++ b/modules/study/src/main/StudyApi.scala
@@ -132,7 +132,8 @@ final class StudyApi(
addChapter(
studyId = study.id,
data = data.form.toChapterData,
- sticky = study.settings.sticky
+ sticky = study.settings.sticky,
+ withRatings
)(Who(user.id, Sri(""))) >> byIdWithLastChapter(studyId)
case _ => fuccess(none)
} orElse importGame(data.copy(form = data.form.copy(asStr = none)), user, withRatings)
@@ -587,11 +588,13 @@ final class StudyApi(
}
}
- def addChapter(studyId: Study.Id, data: ChapterMaker.Data, sticky: Boolean)(who: Who): Funit =
+ def addChapter(studyId: Study.Id, data: ChapterMaker.Data, sticky: Boolean, withRatings: Boolean)(
+ who: Who
+ ): Funit =
data.manyGames match {
case Some(datas) =>
lila.common.Future.applySequentially(datas) { data =>
- addChapter(studyId, data, sticky)(who)
+ addChapter(studyId, data, sticky, withRatings)(who)
}
case _ =>
sequenceStudy(studyId) { study =>
@@ -605,7 +608,7 @@ final class StudyApi(
}
} >>
chapterRepo.nextOrderByStudy(study.id) flatMap { order =>
- chapterMaker(study, data, order, who.u) flatMap { chapter =>
+ chapterMaker(study, data, order, who.u, withRatings) flatMap { chapter =>
doAddChapter(study, chapter, sticky, who)
} addFailureEffect {
case ChapterMaker.ValidationException(error) =>
@@ -624,9 +627,11 @@ final class StudyApi(
studyRepo.updateSomeFields(study) >>- indexStudy(study)
}
- def importPgns(studyId: Study.Id, datas: List[ChapterMaker.Data], sticky: Boolean)(who: Who) =
+ def importPgns(studyId: Study.Id, datas: List[ChapterMaker.Data], sticky: Boolean, withRatings: Boolean)(
+ who: Who
+ ) =
lila.common.Future.applySequentially(datas) { data =>
- addChapter(studyId, data, sticky)(who)
+ addChapter(studyId, data, sticky, withRatings)(who)
}
def doAddChapter(study: Study, chapter: Chapter, sticky: Boolean, who: Who) =
@@ -731,7 +736,13 @@ final class StudyApi(
chapterRepo.orderedMetadataByStudy(studyId).flatMap { chaps =>
// deleting the only chapter? Automatically create an empty one
if (chaps.sizeIs < 2) {
- chapterMaker(study, ChapterMaker.Data(Chapter.Name("Chapter 1")), 1, who.u) flatMap { c =>
+ chapterMaker(
+ study,
+ ChapterMaker.Data(Chapter.Name("Chapter 1")),
+ 1,
+ who.u,
+ withRatings = true
+ ) flatMap { c =>
doAddChapter(study, c, sticky = true, who) >> doSetChapter(study, c.id, who)
}
} // deleting the current chapter? Automatically move to another one
@@ -841,10 +852,15 @@ final class StudyApi(
}
}
- def analysisRequest(studyId: Study.Id, chapterId: Chapter.Id, userId: User.ID): Funit =
+ def analysisRequest(
+ studyId: Study.Id,
+ chapterId: Chapter.Id,
+ userId: User.ID,
+ unlimited: Boolean = false
+ ): Funit =
sequenceStudyWithChapter(studyId, chapterId) { case Study.WithChapter(study, chapter) =>
Contribute(userId, study) {
- serverEvalRequester(study, chapter, userId)
+ serverEvalRequester(study, chapter, userId, unlimited)
}
}
diff --git a/modules/study/src/main/StudyInvite.scala b/modules/study/src/main/StudyInvite.scala
index 7c2a21e313..e67bfc8e34 100644
--- a/modules/study/src/main/StudyInvite.scala
+++ b/modules/study/src/main/StudyInvite.scala
@@ -40,7 +40,9 @@ final private class StudyInvite(
invited <-
userRepo
.named(invitedUsername)
- .map(_.filterNot(_.id == User.lichessId)) orFail "No such invited"
+ .map(
+ _.filterNot(_.id == User.lichessId && !Granter(_.StudyAdmin)(inviter))
+ ) orFail "No such invited"
_ <- study.members.contains(invited) ?? fufail[Unit]("Already a member")
relation <- relationApi.fetchRelation(invited.id, byUserId)
_ <- relation.has(Block) ?? fufail[Unit]("This user does not want to join")
@@ -62,7 +64,6 @@ final private class StudyInvite(
else if (inviter.roles has "ROLE_COACH") 20
else if (inviter.hasTitle) 20
else if (inviter.perfs.bestRating >= 2000) 50
- else if (invited.hasTitle) 200
else 100
_ <- shouldNotify ?? notifyRateLimit(inviter.id, rateLimitCost) {
val notificationContent = InvitedToStudy(
diff --git a/modules/study/src/main/StudyMultiBoard.scala b/modules/study/src/main/StudyMultiBoard.scala
index 72b1b1539a..8ca57ea3c3 100644
--- a/modules/study/src/main/StudyMultiBoard.scala
+++ b/modules/study/src/main/StudyMultiBoard.scala
@@ -106,6 +106,8 @@ final class StudyMultiBoard(
private object handlers {
+ import lila.common.Json._
+
implicit val previewPlayerWriter: Writes[ChapterPreview.Player] = Writes[ChapterPreview.Player] { p =>
Json
.obj("name" -> p.name)
diff --git a/modules/study/src/main/StudySocket.scala b/modules/study/src/main/StudySocket.scala
index 2fe9263b94..554ebf256e 100644
--- a/modules/study/src/main/StudySocket.scala
+++ b/modules/study/src/main/StudySocket.scala
@@ -124,7 +124,7 @@ final private class StudySocket(
case "addChapter" =>
reading[ChapterMaker.Data](o) { data =>
val sticky = o.obj("d").flatMap(_.boolean("sticky")) | true
- who foreach api.addChapter(studyId, data, sticky = sticky)
+ who foreach api.addChapter(studyId, data, sticky = sticky, withRatings = true)
}
case "setChapter" =>
o.get[Chapter.Id]("d") foreach { chapterId =>
@@ -304,7 +304,8 @@ final private class StudySocket(
"w" -> who
)
)
- def setLiking(liking: Study.Liking, who: Who) = notify("liking", Json.obj("l" -> liking, "w" -> who))
+ def setLiking(liking: Study.Liking, who: Who) =
+ notifySri(who.sri, "liking", Json.obj("l" -> liking, "w" -> who))
def setShapes(pos: Position.Ref, shapes: Shapes, who: Who) =
version(
"shapes",
diff --git a/modules/team/src/main/TeamApi.scala b/modules/team/src/main/TeamApi.scala
index dfdb861434..0b0c7682ea 100644
--- a/modules/team/src/main/TeamApi.scala
+++ b/modules/team/src/main/TeamApi.scala
@@ -288,6 +288,7 @@ final class TeamApi(
lila.security.Granter(_.ManageTeam)(by) || team.createdBy == by.id ||
(team.leaders(by.id) && !team.leaders(team.createdBy))
) {
+ logger.info(s"toggleEnabled ${team.id}: ${!team.enabled} by @${by.id}")
if (team.enabled)
teamRepo.disable(team).void >>
memberRepo.userIdsByTeam(team.id).map { _ foreach cached.invalidateTeamIds } >>
diff --git a/modules/user/src/main/UserRepo.scala b/modules/user/src/main/UserRepo.scala
index 060610ed32..b0156c2415 100644
--- a/modules/user/src/main/UserRepo.scala
+++ b/modules/user/src/main/UserRepo.scala
@@ -173,7 +173,8 @@ final class UserRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionCont
coll
.update(ordered = false, WriteConcern.Unacknowledged)
.one(
- $id(userId) ++ (value < 0).??($doc(F.colorIt $gt -3)),
+ // limit to -3 <= colorIt <= 5 but set when undefined
+ $id(userId) ++ $doc(F.colorIt -> $not(if (value < 0) $lte(-3) else $gte(5))),
$inc(F.colorIt -> value)
)
.unit
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 7905ab1361..0e68d44091 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -13,7 +13,7 @@ object Dependencies {
val maxmind = "com.sanoma.cda" %% "maxmind-geoip2-scala" % "1.3.1-THIB"
val prismic = "io.prismic" %% "scala-kit" % "1.2.19-THIB213"
val scaffeine = "com.github.blemale" %% "scaffeine" % "5.1.1" % "compile"
- val googleOAuth = "com.google.auth" % "google-auth-library-oauth2-http" % "1.2.2"
+ val googleOAuth = "com.google.auth" % "google-auth-library-oauth2-http" % "1.3.0"
val scalaUri = "io.lemonlabs" %% "scala-uri" % "3.6.0"
val scalatags = "com.lihaoyi" %% "scalatags" % "0.10.0"
val lettuce = "io.lettuce" % "lettuce-core" % "6.1.5.RELEASE"
@@ -21,7 +21,7 @@ object Dependencies {
val autoconfig = "io.methvin.play" %% "autoconfig-macros" % "0.3.2" % "provided"
val scalatest = "org.scalatest" %% "scalatest" % "3.1.0" % Test
val uaparser = "org.uaparser" %% "uap-scala" % "0.13.0"
- val specs2 = "org.specs2" %% "specs2-core" % "4.13.0" % Test
+ val specs2 = "org.specs2" %% "specs2-core" % "4.13.1" % Test
val apacheText = "org.apache.commons" % "commons-text" % "1.9"
val bloomFilter = "com.github.alexandrnikitin" %% "bloom-filter" % "0.13.1"
@@ -38,17 +38,17 @@ object Dependencies {
val version = "2.4.2"
val macros = "com.softwaremill.macwire" %% "macros" % version % "provided"
val util = "com.softwaremill.macwire" %% "util" % version % "provided"
- val tagging = "com.softwaremill.common" %% "tagging" % "2.3.1"
+ val tagging = "com.softwaremill.common" %% "tagging" % "2.3.2"
def bundle = Seq(macros, util, tagging)
}
object reactivemongo {
- val version = "1.0.7"
+ val version = "1.0.8"
val driver = "org.reactivemongo" %% "reactivemongo" % version
val stream = "org.reactivemongo" %% "reactivemongo-akkastream" % version
val epoll = "org.reactivemongo" % "reactivemongo-shaded-native" % s"$version-linux-x86-64"
- val kamon = "org.reactivemongo" %% "reactivemongo-kamon" % "1.0.7"
+ val kamon = "org.reactivemongo" %% "reactivemongo-kamon" % "1.0.8"
def bundle = Seq(driver, stream)
}
diff --git a/project/build.properties b/project/build.properties
index 10fd9eee04..bb3a9b7dc6 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=1.5.5
+sbt.version=1.5.6
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 9795a8de6c..7ff9c64724 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -3,5 +3,5 @@ resolvers += Resolver.url(
url("https://raw.githubusercontent.com/ornicar/lila-maven/master")
)(Resolver.ivyStylePatterns)
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.8-lila_1.8")
-addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.3")
+addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.5")
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.11")
diff --git a/public/images/mobile/lichesstv-mobile.png b/public/images/mobile/lichesstv-mobile.png
new file mode 100644
index 0000000000..1a47c68f29
Binary files /dev/null and b/public/images/mobile/lichesstv-mobile.png differ
diff --git a/public/images/trophy/atomicwc21.png b/public/images/trophy/atomicwc21.png
new file mode 100644
index 0000000000..805c4c8184
Binary files /dev/null and b/public/images/trophy/atomicwc21.png differ
diff --git a/translation/dest/activity/hi-IN.xml b/translation/dest/activity/hi-IN.xml
index 0f749db7cc..f931121801 100644
--- a/translation/dest/activity/hi-IN.xml
+++ b/translation/dest/activity/hi-IN.xml
@@ -62,6 +62,7 @@
- %4$s में %3$s खेल के साथ %1$s (टॉप %2$s%%) रैंक किया गया
- %4$s में %3$s खेल के साथ %1$s (टॉप %2$s%%) रैंक किया गया
+ #%1$s स्थान %2$s मे
lichess.org पर साइन किया गया
- %s टीम में शामिल हुए
diff --git a/translation/dest/activity/ne-NP.xml b/translation/dest/activity/ne-NP.xml
index df052afd29..82d4b3ed15 100644
--- a/translation/dest/activity/ne-NP.xml
+++ b/translation/dest/activity/ne-NP.xml
@@ -1,7 +1,7 @@
क्रियाकलाप
- लाइभ स्ट्रोम आयोजना गर्नुभयो
+ लाइभ स्ट्रीम आयोजना गर्नुभयो
- lichess.org लाई %1$s महिनासम्म %2$s भई समर्थन गर्नुभयो
- lichess.org लाई %1$s महिनासम्म %2$s भई समर्थन गर्नुभयो
@@ -62,6 +62,11 @@
- %4$s मा %3$s वटा खेल खेली #%1$s औ स्थान (माथिल्लो %2$s%%)
- %4$s मा %3$s वटा खेलहरु खेली #%1$s औ स्थान (माथिल्लो %2$s%%)
+
+ - %sवटा स्वीस् प्रतियोगितामा सहभागी हुनुभयो।
+ - %sवटा स्वीस् प्रतियोगिताहरूमा सहभागी हुनुभयो
+
+ %2$s मा #%1$s स्थान प्राप्त
lichess.org मा भर्ना हुनुभयो
- %s वटा टोलीमा सम्मिलित हुनुभयो
diff --git a/translation/dest/activity/or-IN.xml b/translation/dest/activity/or-IN.xml
index ab4d2aab72..bf0382980f 100644
--- a/translation/dest/activity/or-IN.xml
+++ b/translation/dest/activity/or-IN.xml
@@ -1,4 +1,8 @@
କାର୍ଯ୍ୟକଳାପ
+
+ - ଜଣେ %2$s ଭାବରେ %1$s ମାସ ପାଇଁ lichess.org କୁ ସମର୍ଥନ କଲେ
+ - ଜଣେ %2$s ଭାବରେ %1$s ମାସ ପାଇଁ lichess.org କୁ ସମର୍ଥନ କଲେ
+
diff --git a/translation/dest/activity/pl-PL.xml b/translation/dest/activity/pl-PL.xml
index 0484ddf9cc..3b555e870f 100644
--- a/translation/dest/activity/pl-PL.xml
+++ b/translation/dest/activity/pl-PL.xml
@@ -3,8 +3,8 @@
Aktywność
Udostępnił stream na żywo
- - Wsparcie lichess.org przez miesiąc jako %2$s
- - Wsparcie lichess.org przez %1$s miesiące jako %2$s
+ - Wspomógł lichess.org przez miesiąc jako %2$s
+ - Wspiera Lichess przez %1$s miesiące jako %2$s
- Wsparcie lichess.org przez %1$s miesięcy jako %2$s
- Wsparcie lichess.org przez %1$s miesięcy jako %2$s
diff --git a/translation/dest/arena/kk-KZ.xml b/translation/dest/arena/kk-KZ.xml
index 574deb3344..ed34ba9db7 100644
--- a/translation/dest/arena/kk-KZ.xml
+++ b/translation/dest/arena/kk-KZ.xml
@@ -41,4 +41,16 @@
Адамдар қосылсын десеңіз, осы URL-мен бөлісіңіз: %s
Тепе-теңдік тізбегі: Ойыншының Алаңда қатарынан тепе-теңдіктері болса, алғашқы тепе-теңдік немесе %s жүрістен кейін орын алған тепе-теңдіктер ұпай береді. Тепе-теңдік тізбегін жеңіліс пен тепе-теңдікпен емес, тек жеңіспен ғана тоқтатуға болады.
Алаң журналы
+
+ - Топты қарау
+ - Барлық %s топты қарау
+
+ Жаңа Топтық Күрес
+ Басталудың ерекше күні
+ Сіздің жергілікті уақыт белдеуіңіз. Бұл \"Жарысқа дейінгі уақыт\" баптауына әсер етеді
+ Берсерк-ті рұқсат ету
+ Қосымша ұпай жинау үшін ойыншыларға уақытты жартылай қыстартуды рұқсат ету
+ Ойыншыларға чат бөлмесінде сөйлесуді рұқсат ету
+ Алаң тізбектері
+ Қатарынан болған 2 жеңіс 2 ұпайдың орнына 4 ұпай тұрады.
diff --git a/translation/dest/arena/or-IN.xml b/translation/dest/arena/or-IN.xml
index 3ea04e700d..ce023a84fd 100644
--- a/translation/dest/arena/or-IN.xml
+++ b/translation/dest/arena/or-IN.xml
@@ -1,2 +1,4 @@
-
+
+ ସ୍କୋରଗୁଡ଼ିକ କିପରି ଗଣନା କରାଯାଏ?
+
diff --git a/translation/dest/broadcast/he-IL.xml b/translation/dest/broadcast/he-IL.xml
index 81318e367a..0fd30c35a8 100644
--- a/translation/dest/broadcast/he-IL.xml
+++ b/translation/dest/broadcast/he-IL.xml
@@ -1,9 +1,14 @@
שידורים
+ משדר משחקים חיים
+ משדר חי חדש
+ הוסף סבב
כרגע
בקרוב
שהושלמו
+ שם סבב
+ מספר סבב
שם הטורניר
תיאור הטורניר בקצרה
תיאור מלא של הטורניר
diff --git a/translation/dest/challenge/af-ZA.xml b/translation/dest/challenge/af-ZA.xml
index 3ea04e700d..2071078edc 100644
--- a/translation/dest/challenge/af-ZA.xml
+++ b/translation/dest/challenge/af-ZA.xml
@@ -1,2 +1,25 @@
-
+
+ Uitdagings
+ Daag uit tot \'n spel
+ Uitdaging afgewys
+ Uitdaging aanvaar!
+ Uitdaging gekanselleer.
+ Registreer om uitdagings te stuur.
+ Jy kan nie %s uitdaag nie.
+ %s aanvaar nie uitdagings nie.
+ Jou %1$s gradering is te ver van %2$s.
+ Kan nie uitdaag nie weens voorlopige %s gradering.
+ %s aanvaar net uitdagings van vriende.
+ Ek aanvaar nie uitdagings op die oomblik nie.
+ Dit is \'n ongeleë tyd, vra asseblief weer later.
+ Die tydskontrole is te vinnig vir my, daag my weer uit met \'n stadiger tydskontrole.
+ Die tydskontrole is te stadig vir my, daag my weer uit met \'n vinniger tydskontrole.
+ Ek aanvaar nie uitdagings met hierdie tydskontrole nie.
+ Stuur eerder vir my \'n gegradeerde uitdaging asseblief.
+ Stuur eerder vir my \'n vriendskaplike uitdaging asseblief.
+ Ek aanvaar nie variant-uitdagings op die oomblik nie.
+ Ek is nie bereid om hierdie variant op die oomblik te speel nie.
+ Ek aanvaar nie uitdagings deur bots nie.
+ Ek aanvaar slegs uitdagings deur bots.
+
diff --git a/translation/dest/challenge/fr-FR.xml b/translation/dest/challenge/fr-FR.xml
index f6149aee69..5089227697 100644
--- a/translation/dest/challenge/fr-FR.xml
+++ b/translation/dest/challenge/fr-FR.xml
@@ -4,7 +4,7 @@
Défier ce joueur
Défi refusé
Défi accepté!
- Défi annulé
+ Défi annulé.
Veuillez vous inscrire pour envoyer des défis.
Vous ne pouvez pas défier %s.
%s n’accepte pas les défis.
diff --git a/translation/dest/challenge/kk-KZ.xml b/translation/dest/challenge/kk-KZ.xml
index ca617282e9..3ab08c2a13 100644
--- a/translation/dest/challenge/kk-KZ.xml
+++ b/translation/dest/challenge/kk-KZ.xml
@@ -17,8 +17,8 @@
Бұл уақыт қалпы мен үшін тым баяу, шапшаңдау ойынға шақыруды жіберіңізші.
Мен бұндай уақыт қалпындағы ойындарға шақыруды қабылдамаймын.
Оның орнына маған бағаланатын шақыруды жіберіңізші.
- Оның орнына маған қалыпты шақыруды жіберіңізші.
- Мен қазір шахмат түріне негізделген шақыруларды қабылдамаймын.
+ Оның орнына маған жай шақыруды жіберіңізші.
+ Мен қазір классикалық емес шақыруларды қабылдамаймын.
Мен қазір осы шахмат түрін ойнағым келмейді.
Мен роботтардан шақыруларды қабылдамаймын.
Мен тек роботтардан келген шақыруларды қабылдаймын.
diff --git a/translation/dest/challenge/ne-NP.xml b/translation/dest/challenge/ne-NP.xml
index 3ea04e700d..1879556dfa 100644
--- a/translation/dest/challenge/ne-NP.xml
+++ b/translation/dest/challenge/ne-NP.xml
@@ -1,2 +1,22 @@
-
+
+ चुनौतीहरू
+ खेलको लागि चुनौति दिनुहोस्
+ चुनौती अस्वीकृत
+ चुनौती स्वीकृत!
+ चुनौती रद्द।
+ चुनौति दिनका लागि दर्ता गर्नुहोस्।
+ तपाईँ %sलाई चुनौति दिन सक्नुहुन्न।
+ %sले चुनौती स्वीकार गर्नुभएन।
+ तपाईँको %1$s रेटिङ्ग %2$s भन्दा धेरै टाढा छ।
+ अस्थायी %s रेटिङका कारण खेलको लागि आमन्त्रण गर्न असम्भव छ।
+ %sले मित्रहरूको आमन्त्रण मात्र स्वीकार गर्नुहुन्छ।
+ म यो समय चुनौति स्वीकार गरिरहेको छैँन।
+ मेरा लागि यो उपयुक्त समय होइन, कृपया केही समय पछि सोध्नुहोला।
+ यो टाइम कन्ट्रोल मेरा लागि धेरै नै छिटो भयो, कृपया ढिलो खेलका लागि चुनौती दिनुहोस्।
+ यो टाइम कन्ट्रोल मेरा लागि धेरै नै ढिलो भयो, कृपया छिटो खेलका लागि चुनौती दिनुहोस्।
+ म यो टाइम कन्ट्रोलको चुनौति स्वीकार गरिरहेको छैँन।
+ कृपया मलाई सरेटिङ चुनौति पठाउनुहोस्।
+ कृपया मलाई अरेटिङ चुनौति पठाउनुहोस्।
+ म बटहरूको आमन्त्रण मात्र स्वीकार गरिरहेको छु।
+
diff --git a/translation/dest/challenge/ru-RU.xml b/translation/dest/challenge/ru-RU.xml
index fc18631961..5428910381 100644
--- a/translation/dest/challenge/ru-RU.xml
+++ b/translation/dest/challenge/ru-RU.xml
@@ -13,7 +13,7 @@
%s принимает вызовы только от друзей.
Я не принимаю вызовы прямо сейчас.
Я не принимаю вызовы прямо сейчас, пожалуйста, вызовите меня позже.
- Такой контроль времени слишком быстрый для меня, пожалуйста, вызовите меня на игру с контролем времени побольше.
+ Такой контроль слишком быстрый для меня, пожалуйста, вызовите меня с контролем времени побольше.
Такой контроль слишком медленный для меня, пожалуйста, вызовите меня с контролем времени поменьше.
Я не принимаю вызовы с таким контролем времени.
Вызовите меня на рейтинговую игру, пожалуйста.
diff --git a/translation/dest/class/he-IL.xml b/translation/dest/class/he-IL.xml
index 5789d8a75b..92adcd4fe8 100644
--- a/translation/dest/class/he-IL.xml
+++ b/translation/dest/class/he-IL.xml
@@ -71,6 +71,8 @@
כתובת דוא\"ל אמיתית וייחודית של התלמיד. אנו נשלח אליו דוא\"ל לאישור, עם קישור לשחרור החשבון.
סגור את החשבון
סגור את חשבון התלמיד לצמיתות.
+ התלמיד לעולם לא יוכל עוד להשתמש בחשבון זה. סגירת החשבון היא סופית. אנא וודא שהתלמיד מבין ומסכים.
+ אולי תרצה במקום לתת לתלמיד שליטה על החשבון, כך שיוכל להמשיך להשתמש בו.
מורים
- מורה
diff --git a/translation/dest/class/or-IN.xml b/translation/dest/class/or-IN.xml
index 6cfd607b6d..1ad571dfcc 100644
--- a/translation/dest/class/or-IN.xml
+++ b/translation/dest/class/or-IN.xml
@@ -1,16 +1,16 @@
- ଛାତ୍ରମାନଙ୍କ ପାଇଁ ଶୀଘ୍ର ସୁରକ୍ଷିତ ଉପଭୋକ୍ତା ନାମ ଏବଂ ପାସ୍ଓ୍ୱାର୍ଡ଼ ସୃଷ୍ଟି କରନ୍ତୁ
- ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କର
- ଏବେ ପାସ୍ଓ୍ୱାର୍ଡ଼ କପି କିମ୍ୱା ଲେଖିବାକୁ ନିଶ୍ଚିତ କରନ୍ତୁ। ଆପଣ ଏହାକୁ ପୁନର୍ବାର ଦେଖିବାକୁ ସମର୍ଥ ହେବେ ନାହିଁ!
- ପାସ୍ଓ୍ୱାର୍ଡ଼: %s
- ଛାତ୍ରଙ୍କ ପାଇଁ ଏକ ନୂତନ ପାସ୍ଓ୍ୱାର୍ଡ଼ ସୃଷ୍ଟି କରନ୍ତୁ
+ ଛାତ୍ରମାନଙ୍କ ପାଇଁ ଶୀଘ୍ର ସୁରକ୍ଷିତ ଉପଭୋକ୍ତା ନାମ ଏବଂ ପାସ୍ୱାର୍ଡ଼ ସୃଷ୍ଟି କରନ୍ତୁ
+ ପାସ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କର
+ ଏବେ ପାସ୍ୱାର୍ଡ଼ କପି କିମ୍ୱା ଲେଖିବାକୁ ନିଶ୍ଚିତ କରନ୍ତୁ। ଆପଣ ଏହାକୁ ପୁନର୍ବାର ଦେଖିବାକୁ ସମର୍ଥ ହେବେ ନାହିଁ!
+ ପାସ୍ୱାର୍ଡ଼: %s
+ ଛାତ୍ରଙ୍କ ପାଇଁ ଏକ ନୂତନ ପାସ୍ୱାର୍ଡ଼ ସୃଷ୍ଟି କରନ୍ତୁ
ଛାତ୍ର: %1$s
ଉପଭୋକ୍ତା ନାମ: %2$s
-ପାସ୍ଓ୍ୱାର୍ଡ଼: %3$s
- କୌଣସି ଇମେଲ୍ ଠିକଣା ଆବଶ୍ୟକ ନାହିଁ। ଏକ ପାସ୍ଓ୍ୱାର୍ଡ଼ ସୃଷ୍ଟି ହେବ, ଏବଂ ଆପଣଙ୍କୁ ଏହାକୁ ଛାତ୍ରଙ୍କ ନିକଟକୁ ପଠାଇବାକୁ ପଡ଼ିବ, ଯାହା ଫଳରେ ସେମାନେ ଲଗ୍ ଇନ୍ କରିପାରିବେ।
+ପାସ୍ୱାର୍ଡ଼: %3$s
+ କୌଣସି ଇମେଲ୍ ଠିକଣା ଆବଶ୍ୟକ ନାହିଁ। ଏକ ପାସ୍ୱାର୍ଡ଼ ସୃଷ୍ଟି ହେବ, ଏବଂ ଆପଣଙ୍କୁ ଏହାକୁ ଛାତ୍ରଙ୍କ ନିକଟକୁ ପଠାଇବାକୁ ପଡ଼ିବ, ଯାହା ଫଳରେ ସେମାନେ ଲଗ୍ ଇନ୍ କରିପାରିବେ।
ପରିଚାଳିତ
ଏହି ଛାତ୍ର ଆକାଉଣ୍ଟ୍ ଟି ପରିଚାଳିତ ଅଟେ
ପରିଚାଳିତରୁ ସ୍ୱୟଂଶାସିତକୁ ଅପଗ୍ରେଡ୍ କର
- ଏକ ସ୍ନାତକ ଆକାଉଣ୍ଟ ପୁନର୍ବାର ପରିଚାଳନା କରାଯାଇପାରିବ ନାହିଁ। ଛାତ୍ର ଶିଶୁ ମୋଡ୍ ଟୋଗଲ୍ କରିବାକୁ ଏବଂ ନିଜେ ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ସମର୍ଥ ହେବେ।
+ ଏକ ସ୍ନାତକ ଆକାଉଣ୍ଟ ପୁନର୍ବାର ପରିଚାଳନା କରାଯାଇପାରିବ ନାହିଁ। ଛାତ୍ର ଶିଶୁ ମୋଡ୍ ଟୋଗଲ୍ କରିବାକୁ ଏବଂ ନିଜେ ପାସ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ସମର୍ଥ ହେବେ।
diff --git a/translation/dest/contact/he-IL.xml b/translation/dest/contact/he-IL.xml
index 44ea6963ab..d17b7182c1 100644
--- a/translation/dest/contact/he-IL.xml
+++ b/translation/dest/contact/he-IL.xml
@@ -27,6 +27,7 @@
אני רוצה לנקות את ההיסטוריה או הדירוג שלי
לא ניתן לנקות את היסטוריית המשחק, היסטוריית הפאזלים, או הדירוגים שלך.
אני רוצה לדווח על שחקן
+ כדי לדווח על שחקן, השתמש בטופס הדיווח
אתה יכול גם להגיע לדף זה על ידי לחיצה על כפתור הדוח %s בדף הפרופיל.
אל תדווחו על שחקנים בפורום.
אל תשלחו לנו אימיילים כדי לדווח.
@@ -51,6 +52,7 @@
אפשרי לבצע מט רק אם פרש או רץ, אם ליריב יש יותר ממלך על הלוח.
נקודות מד כושר לא הוענקו
בדוק אם שיחקת משחק מדורג. משחק לא מדורג לא משפיע על מד הכושר.
+ בנסיבות מסויימות כאשר משחק משוחק אל מול חשבון של בוט, משחק מנוקד עשוי שלא להעניק נקודות אם יקבע שהשחקן מנצל את הבוט לצבירת ניקוד.
שגיאה בדף
אם התמודדת עם שגיאה בדף, אתה יכול לדווח על זה:
ערער על איסור או על הגבלה בIP
diff --git a/translation/dest/contact/kk-KZ.xml b/translation/dest/contact/kk-KZ.xml
index e6b60417ad..7664061d15 100644
--- a/translation/dest/contact/kk-KZ.xml
+++ b/translation/dest/contact/kk-KZ.xml
@@ -51,7 +51,7 @@
FIDE Шахмат Заңының §6.9 сәйкес, егер ережеге сай кез-келген жүрістермен матқа жету мүмкінкіндігі бар болса, онда ойын қорытындысы тепе-тең деп саналмайды
Егер қарсыласта патшадан басқа тағы тас болса, оған матты жалғыз ат немесе пілмен ғана қоюға болады.
Ешбір рейтинг ұпайы қосылмады
- Бағаланатын ойын екенін тексеріңіз. Қалыпты ойындар ойыншының рейтингіне әсер етпейді.
+ Бағаланатын ойын екенін тексеріңіз. Жай ойындар ойыншының рейтингіне әсер етпейді.
Ақаулық парақшасы
Ақаулық парақшасына тап болсаңыз, осында хабарлауыңызға болады:
Шектеуге не IP шектеуге қарсылық шағымы
diff --git a/translation/dest/contact/or-IN.xml b/translation/dest/contact/or-IN.xml
index 0eb3c534d2..de64c50c1a 100644
--- a/translation/dest/contact/or-IN.xml
+++ b/translation/dest/contact/or-IN.xml
@@ -1,7 +1,8 @@
- ମୁଁ ମୋର ପାସ୍ଓ୍ୱାର୍ଡ଼ ଭୁଲିଗଲି
- ଆପଣଙ୍କ ଦ୍ୱିତୀୟ ଫ୍ୟାକ୍ଟର ଅପସାରଣ କରିବାକୁ ଏକ ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ
+ ମୁଁ ମୋର ପାସ୍ୱାର୍ଡ଼ ଭୁଲିଗଲି
+ ଆପଣଙ୍କ ଦ୍ୱିତୀୟ ଫ୍ୟାକ୍ଟର ଅପସାରଣ କରିବାକୁ ଏକ ପାସ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ
ଏକ ଖାତା ବନ୍ଦ କରିବାକୁ ଆମକୁ ଇମେଲ୍ ମାଧ୍ୟମରେ କୁହନ୍ତୁ ନାହିଁ, ଆମେ ତାହା କରିବୁ ନାହିଁ।
ଏକ ଖାତା ପୁନଃଖୋଲିବାକୁ ଆମକୁ ଇମେଲ୍ ମାଧ୍ୟମରେ କୁହନ୍ତୁ ନାହିଁ, ଆମେ ତାହା କରିବୁ ନାହିଁ।
+ ଫୋରମର Lichess Feedback ବିଭାଗରେ
diff --git a/translation/dest/coordinates/be-BY.xml b/translation/dest/coordinates/be-BY.xml
index d6121f2f79..0983f6560b 100644
--- a/translation/dest/coordinates/be-BY.xml
+++ b/translation/dest/coordinates/be-BY.xml
@@ -2,8 +2,8 @@
Каардынаты
Трэніроўка па каардынатах
- Сярэдні лік за белых: %s
- Сярэдні лік за чорных: %s
+ Сярэдні вынік за белых: %s
+ Сярэдні вынік за чорных: %s
Ведаць каардынаты шахматнай дошкі - вельмі важны шахматны навык:
Большасць шахматных задач та заданняў выкарыстоўваць шахматнаю натацыю.
Гэта спрошчвае гутарку з сябрамі-шахматыстамі, бо вы абодва разумееце \"шахматную мову\".
diff --git a/translation/dest/coordinates/ne-NP.xml b/translation/dest/coordinates/ne-NP.xml
index 3fcc435995..ca996ecd45 100644
--- a/translation/dest/coordinates/ne-NP.xml
+++ b/translation/dest/coordinates/ne-NP.xml
@@ -1,12 +1,12 @@
- को-अर्डिनेटहरु
- को-अर्डिनेटको अभ्यास
+ समकक्षहरु
+ समकक्षको अभ्यास
सेतो लिएर औसत अंक: %s
कालो लिएर औसत अंक: %s
- चेस बोर्ड को को-अर्डिनेटहरूको को ज्ञान हुनु धेरै महत्वपूर्ण छ:
- प्राय सबै चेस कोर्स र अभ्यासहरुमा एल्जेब्रिक नोटेशन प्रयोग हुन्छ।
- यसले तपाइलाई आफ्नो चेस खेल्ने मित्रहरु संग कुरा गर्न सहज हुन्छ, किनकि तपाईहरु दुवै \"चेस को भाषा\" बुझ्नुहुन्छ।
+ चेस बोर्डका समकक्षहरूको को ज्ञान हुनु धेरै महत्वपूर्ण छ:
+ प्राय सबै चेस कोर्स र अभ्यासहरूमा विजीय सङ्केत प्रयोग हुन्छ।
+ यसले तपाइलाईँ आफ्ना चेस खेल्ने मित्रहरू संग कुरा गर्न सहज हुन्छ, किनकि तपाईहरू दुवै \"चेस को भाषा\" बुझ्नुहुन्छ।
तपाइले खेलहरु प्रभावकारी रुपमा विश्लेषण गर्न सक्नुहुन्छ किनकि तपाइलाई कोठाको नामहरु खोजिरहनु पर्दैन।
कोठाको नाम बोर्डमा आउछ अनि तपाइले सहि कोठामा क्लिक गर्नुपर्ने हुन्छ| तपाइसंग तपाइले सकेजति कोठा चिन्न ३० सेकेन्ड हुनेछ!
अभ्यास सुरु गर्नुहोस्
diff --git a/translation/dest/coordinates/or-IN.xml b/translation/dest/coordinates/or-IN.xml
index 3ea04e700d..88cdffeb23 100644
--- a/translation/dest/coordinates/or-IN.xml
+++ b/translation/dest/coordinates/or-IN.xml
@@ -1,2 +1,5 @@
-
+
+ ଧଳା ଭାବରେ ହାରାହାରି ସ୍କୋର୍: %s
+ କଳା ଭାବରେ ହାରାହାରି ସ୍କୋର୍: %s
+
diff --git a/translation/dest/emails/aa-ER.xml b/translation/dest/emails/aa-ER.xml
index 3ea04e700d..a79101e711 100644
--- a/translation/dest/emails/aa-ER.xml
+++ b/translation/dest/emails/aa-ER.xml
@@ -1,2 +1,6 @@
-
+
+ Bevestig jou lichess.org-rekening, %s
+ Klik op die skakel om jou Lichess-rekening te aktiveer.
+ As jy nie by Lichess geregistreer het nie, kan jy hierdie boodskap veilig ignoreer.
+
diff --git a/translation/dest/emails/az-AZ.xml b/translation/dest/emails/az-AZ.xml
index fa7366b9d0..e594603ee5 100644
--- a/translation/dest/emails/az-AZ.xml
+++ b/translation/dest/emails/az-AZ.xml
@@ -5,11 +5,11 @@
\"Lichess\"-də qeydiyyatdan keçməmisinizsə, bu mesaja əhəmiyyət verməyə bilərsiniz.
Lichess.org şifrənizi sıfırlayın, %s
Hesabınızın şifrəsini sıfırlamaq üçün bir sorğu aldıq.
- Əgər bu sorğunu siz etmisinizsə, aşağıdakı linkə klikləyin. Əks halda, bu e-poçta əhəmiyyət verməyin.
+ Əgər bu sorğunu siz etmisinizsə, aşağıdakı linkə klikləyin. Siz etməmisinizsə, bu e-poçta əhəmiyyət verməyin.
Yeni e-poçt ünvanınızı təsdiqləyin, %s
E-poçt ünvanınızın dəyişdirilməsi üçün sorğu göndərdiniz.
Bu e-poçt sizin üçün əlçatandırsa, zəhmət olmasa aşağıdakı bağlantıya klikləyin:
- \"Lichess.org\"-a xoş gəldiniz, %s
+ Lichess.org\'a xoş gəldiniz, %s
Hesabınız https://lichess.org üzərindən uğurla yaradıldı.
Profil səhifəniz: %1$s. Profilinizi %2$s üzərindən özəlləşdirə bilərsiniz.
diff --git a/translation/dest/emails/be-BY.xml b/translation/dest/emails/be-BY.xml
index 7a5255109c..b8de0546e2 100644
--- a/translation/dest/emails/be-BY.xml
+++ b/translation/dest/emails/be-BY.xml
@@ -2,21 +2,21 @@
%s, пацвердзіце ваш уліковы запіс на lichess.org
Націсніце на спасылку, каб актываваць уліковы запіс Lichess:
- Калі вы не рэгістраваліся на Lichess, проста праігнаруйце гэты ліст.
- %s, вы скідваеце ваш пароль на lichess.org
- Мы атрымалі запыт на скасаванне пароля да вашага ўліковага запісу.
+ Калі вы не рэгістраваліся на Lichess, проста праігнаруйце гэта паведамленне.
+ %s, вы скідаеце ваш пароль на lichess.org
+ Мы атрымалі запыт на скіданне пароля да вашага ўліковага запісу.
Калі запыт выкананы вамі, націсніце на спасылку ніжэй. Калі не, праігнаруйце гэты ліст.
%s, пацвердзіце новы адрас пошты
Мы атрымалі запыт на змену вашага адраса электроннай пошты.
- Доўжачы пацверджанні наяўнасці доступу да гэтай пошты, калі ласка, націсніце на спасылку ніжэй:
+ Для пацвярджэння таго, што ў вас ёсць доступ да адраса электроннай пошты, калі ласка, націсніце спасылку ніжэй:
Вітаем вас на lichess.org, %s
- Вы паспяхова зарэгістравалі свой акаўнт на https://lichess.org.
+ Вы паспяхова зарэгістравалі свой уліковы запіс на https://lichess.org.
-Вось спасылка на Ваш профіль: %1$s. Вы можаце наладзіць яго тут %2$s.
+Вось спасылка на ваш профіль: %1$s. Вы можаце наладзіць яго тут %2$s.
-Поспехаў, і ды знойдуць Вашы фігуры шлях да караля суперніка!
+Поспехаў, і ды знойдуць Вашы фігуры шлях да караля саперніка!
Уваход на lichess.org, %s
- (Клік па спасылцы не працуе? Паспрабуйце ўставіць у ваш браўзер!)
- Гэта службовы ліст ад %s.
+ (Націск на спасылку не працуе? Паспрабуйце ўставіць у ваш браўзер!)
+ Гэта службовы ліст, які звязаны з вашым выкарыстаннем %s.
Каб звязацца з намі, калі ласка, выкарыстоўвайце %s.
diff --git a/translation/dest/emails/kk-KZ.xml b/translation/dest/emails/kk-KZ.xml
index 6485c264e4..739a87cd5a 100644
--- a/translation/dest/emails/kk-KZ.xml
+++ b/translation/dest/emails/kk-KZ.xml
@@ -6,7 +6,7 @@
%s, lichess.org құпиясөзін қайта орнатыңыз
Бізге тіркелгіңіздің кұпиясөзін қайта орнату туралы тапсырыс келді.
Егер бұл тапсырысты жіберген сіз болсаңыз, төмендегі сілтемеге басыңыз. Кері жағдайда осы хатты назарсыз қалдыра салыңыз.
- %s, жаңа поштаңызды растаңыз
+ Жаңа поштаңызды растаңыз
Сіз поштаңызды ауыстыруға тапсырыс жіберген едіңіз.
Сол поштаға кіруге рұқсатыңыз бар екенін растау үшін төмендегі сілтемеге басыңыз:
%s, Личес-ке қош келдіңіз
diff --git a/translation/dest/emails/ky-KG.xml b/translation/dest/emails/ky-KG.xml
index 3ea04e700d..52e9f564fe 100644
--- a/translation/dest/emails/ky-KG.xml
+++ b/translation/dest/emails/ky-KG.xml
@@ -1,2 +1,4 @@
-
+
+ (Башсаныз иштебей жатыр бы? Анда башка браузер мен кириниз)
+
diff --git a/translation/dest/emails/la-LA.xml b/translation/dest/emails/la-LA.xml
index fe55e1df2e..70d9dc2055 100644
--- a/translation/dest/emails/la-LA.xml
+++ b/translation/dest/emails/la-LA.xml
@@ -1,5 +1,5 @@
Signum vestrum lichess.org repone, %s
- Salve lichess.org, %s
+ Bene advenisti ad lichess.org, %s
diff --git a/translation/dest/emails/ne-NP.xml b/translation/dest/emails/ne-NP.xml
index bbdaf293b2..08609051a8 100644
--- a/translation/dest/emails/ne-NP.xml
+++ b/translation/dest/emails/ne-NP.xml
@@ -1,21 +1,21 @@
- %s, कृपया तपाइको lichess.org खाता पुष्टी गर्नुहोस्।
+ %s, कृपया तपाईँको lichess.org खाता पुष्टी गर्नुहोस्।
तपाईंको Lichess खाता सुचारु गर्न यो लिंकमा क्लिक गर्नुहोस्:
- यदि तपाईले Lichessमा दर्ता गर्नुभएको छैन भने यो सन्देशलाई नडराइकन बेवास्ता गर्नुहोस्।
+ यदि तपाईँले Lichessमा दर्ता गर्नुभएको छैन भने यो सन्देशलाई बेवास्ता गर्नुहोस्।
%s, तपाईंको lichess.org को पासवर्ड परिवर्तन गर्नुहोस्।
हामीले तपाईंको खाताको पासवर्ड फेर्ने अनुरोध प्राप्त गरेका छौं।
- यदि तपाईले यो अनुरोध गर्नुभएको हो भने तलको लिंकमा क्लिक गर्नुहोस्। अन्यथा यो इमेललाइ बेवास्ता गर्नुहोस्।
- %s, नयाँ इमेल पुष्टी गर्नुहोस्।
- तपाईले इमेल ठेगाना परिवर्तन गर्न अनुरोध गर्नुभएको छ।
+ यदि तपाईँले यो अनुरोध गर्नुभएको हो भने तलको लिङ्कमा क्लिक गर्नुहोस्। अन्यथा यो इमेललाई बेवास्ता गर्नुहोस्।
+ %s, नयाँ इमेल पुष्टि गर्नुहोस्।
+ तपाईँले इमेल परिवर्तन गर्न अनुरोध गर्नुभएको छ।
यो इमेलमा तपाईंको पहुँच छ भन्ने यकिन गर्न तलको लिंकमा क्लिक गर्नुहोस्:
%s, lichess.org मा तपाईलाई स्वागत् छ।
- तपाई https://lichess.org मा खाता दर्ता गर्न सफल हुनुभएको छ।
+ तपाईँ https://lichess.org मा खाता दर्ता गर्न सफल हुनुभएको छ।
-तपाईंको प्रोफाइल पृष्ठ यहाँ छ: %1$s। यसलाई %2$s मा तपाईलाई अनुकुल पर्नेगरी परिवर्तन गर्न सक्नुहुनेछ।
+तपाईंको प्रोफाइल पृष्ठ यहाँ छ: %1$s। यसलाई %2$s मा तपाईँलाई अनुकुल पर्नेगरी परिवर्तन गर्न सक्नुहुनेछ।
-रमाइलो गर्नुहोस्। तपाईंको गोटीहरु विपक्षी राजा सम्म पुगुन्।
- %s, lichess.org मा लग इन गरौं
+रमाइलो गर्नुहोस्। तपाईंका गोटीहरू विपक्षी राजा सम्म पुगुन्।
+ %s, lichess.org मा लग इन गर्नुहोस्।
(क्लिक भएन? ब्राउजरमा पेष्ट गरेर हेर्नुहोस्!)
यो तपाईंको %s को प्रयोग संग सम्बन्धित इमेल हो।
कृपया हामीसँग सम्पर्क गर्न %s को प्रयोग गर्नुहोस्।
diff --git a/translation/dest/emails/or-IN.xml b/translation/dest/emails/or-IN.xml
index 7c3d8ec4c9..fcfc30b490 100644
--- a/translation/dest/emails/or-IN.xml
+++ b/translation/dest/emails/or-IN.xml
@@ -1,7 +1,7 @@
ଆପଣଙ୍କର lichess.org ଖାତା,%s ନିଶ୍ଚିତ କରନ୍ତୁ |
- ଆପଣଙ୍କ lichess.org ପାସ୍ଓ୍ୱାର୍ଡ଼ କୁ ପୁନଃସେଟ୍ କରନ୍ତୁ, %s
- ଆପଣଙ୍କ ଆକାଉଣ୍ଟ ପାଇଁ ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ଆମେ ଏକ ଅନୁରୋଧ ପାଇଲୁ।
+ ଆପଣଙ୍କ lichess.org ପାସ୍ୱାର୍ଡ଼ କୁ ପୁନଃସେଟ୍ କରନ୍ତୁ, %s
+ ଆପଣଙ୍କ ଆକାଉଣ୍ଟ ପାଇଁ ପାସ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ଆମେ ଏକ ଅନୁରୋଧ ପାଇଲୁ।
ଯଦି ଆପଣ ଏହି ଅନୁରୋଧ କରିଛନ୍ତି, ନିମ୍ନ ଲିଙ୍କ୍ କ୍ଲିକ୍ କରନ୍ତୁ। ଯଦି ନୁହେଁ, ତେବେ ଆପଣ ଏହି ଇମେଲ୍ ଅଣଦେଖା କରିପାରିବେ।
diff --git a/translation/dest/emails/os-SE.xml b/translation/dest/emails/os-SE.xml
index 7d21d92042..84607506b1 100644
--- a/translation/dest/emails/os-SE.xml
+++ b/translation/dest/emails/os-SE.xml
@@ -1,22 +1,22 @@
Сфидар кæн дæ lichess.org аккаунт, %s
- Дæ Lichess аккаунт цæмæй скусын кæнай, æрæлхъив дæнцæгыл:
+ Дæ Lichess аккаунт цæмæй скусын кæнай, нылхъив дæнцæгыл:
Дæхи Lichess-ы кæд регистраци нæ кодтай, уæд ацы фыстæг ницæмæ дар.
Раппар дæ lichess.org пароль, %s
- Мах райстам бафæрст дæ аккаунты паролы раппæрыныл.
- Ды кæд уыцы бафæрст скодтай, уæд æрæлхъив уыцы дæнцæгыл. Кæд нæ, уæд ницæмæ дар уыцы фыстаг.
- Сфидар кæн дæ ног пост, %s
- Ды бафарстай дæ посты ивынад.
- Цæмæй дæ пост сфидар кæнай, æрæлхъив уыцы дæнцæгыл:
+ Мах райстам бафарст дæ аккаунты пароль раппарын.
+ Ды кæд ахæм бафарст кодтай, уæд нылхъив дæнцæгыл дæлдæр. Кæд нæ, уæд ницæмæ дар ацы фыстаг.
+ Сфидар кæн дæ ног посты адрис, %s
+ Ды бафарстай дæ посты адрис баивын.
+ Цæмæй дæ посты адрис сфидар кæнай, нылхъив дæнцæгыл дæлдæр:
Æгас цу lichess.org-мæ, %s
Æнтыстджынæй дæ lichess.org аккаунт сарæзтай.
-Мæнæ ам ис дæ профилы фарс: %1$s. Сфæлындын æй дæ бон у %2$s.
+Мæнæ ам ис дæ профилы фарс: %1$s. Сфæлындын æй дæ бон у ам: %2$s.
-Хъæлдзаг у, æмæ дæ фигурæтæ ныхмæлæууæджы къаролмæ цæмæй сæ фæндаг æдзухдæр ссарой!
+Бантысæд дын, цæмæй дæ фигурæтæ ныхмæлæууæджы къаролмæ сæ фæндаг æдзухдæр ссарой!
Бацу lichess.org-мæ, %s
(Æлхъивын нæ кусы? Бафæлвар æй дæ браузермæ бавæрын!)
- Уый у сервисы фыстæг, дæ %s спайдакæнынмæ баст кæй у, ахæм.
- Махимæ цæмæй хи сбæттын, %s пайда кæн.
+ Ай у службæйон фыстæг, дæ %s спайдакæнынмæ баст кæй у, ахæм.
+ Махимæ цæмæй бастдзинад скæнын, %s пайда кæн.
diff --git a/translation/dest/emails/sa-IN.xml b/translation/dest/emails/sa-IN.xml
index 3ea04e700d..5a0f646cdf 100644
--- a/translation/dest/emails/sa-IN.xml
+++ b/translation/dest/emails/sa-IN.xml
@@ -1,2 +1,4 @@
-
+
+ क्रीडकस्य नामः %s
+
diff --git a/translation/dest/emails/te-IN.xml b/translation/dest/emails/te-IN.xml
index 1d29f0a6f8..032e47a95e 100644
--- a/translation/dest/emails/te-IN.xml
+++ b/translation/dest/emails/te-IN.xml
@@ -1,7 +1,7 @@
మీ lichess.org ఖాతా %s, ధ్రువపరచండి
- మీ Lichess ఖాతాను నిద్రలేపి, వాడడం మొదలుపెట్టడానికి ఈ లంకెను నొక్కండి:
+ మీ Lichess ఖాతాను వాడడం మొదలుపెట్టడానికి ఈ లంకెను నొక్కండి:
ఒకవేళ మీరు lichess లో చేరియుండక పొతే ఈ సందేశాన్ని పట్టించుకోనవసరం లేదు.
మీ lichess రహస్యపదాన్ని మార్చండి, %s
మీ ఖాతాకు ప్రస్తుతమున్న రహస్యపదాన్ని తుడిచిపెట్టి, కొత్త రహస్యపదాన్ని సృష్టించమని అభ్యర్థన వచ్చింది.
diff --git a/translation/dest/faq/fa-IR.xml b/translation/dest/faq/fa-IR.xml
index 110b0f053a..9e4a89085b 100644
--- a/translation/dest/faq/fa-IR.xml
+++ b/translation/dest/faq/fa-IR.xml
@@ -15,6 +15,7 @@
بازی منصفانه
اگر حریف شما به صورت مداوم بازی را ترک کند، از بازی محروم می شود این به این معنی است که به صورت موقت اجازه بازی کردن را نخواهد داشت. این امر در حساب کاربری فرد نمایش داده نمی شود. اگر این رفتار ادامه پیدا کند، مدت زمان محرومیت از بازی افزایش می یابد و انجام این کار در مدت زمان طولانی ممکن است منجر به مسدود شدن حساب کاربر شود.
چگونه میتوانم یک مدیر باشم؟
+ گیم پلی
معرفی و مقدمه خوب
موقعیتها
پیکربندی نمایید
@@ -26,5 +27,9 @@
نام کاربری من چه میتواند باشد؟
دستورالعملها
آیا میتوانم نام کاربری خود را تغییر دهم؟
+ بازیکنان با قدرت یکسان
+ چگونه وقتی بازی می کنیم ریتینگ ها را پنهان کنیم؟
+ تنظیمات نمایش
+ من یک بازی را بخاطر قطعی اینترنت باختم. آیا می توانم ریتینگ از دست رفته ام را بازگردانی کنم؟
چگونه...
diff --git a/translation/dest/faq/kk-KZ.xml b/translation/dest/faq/kk-KZ.xml
index 89406270b0..3ecfca3adb 100644
--- a/translation/dest/faq/kk-KZ.xml
+++ b/translation/dest/faq/kk-KZ.xml
@@ -33,9 +33,9 @@
(бастапқы уақыт) + 40 × (қосылғыш)
< %1$sс = %2$s
≥ %1$s с = %2$s
- Личесте қандай шахмат түрлерін ойнасам болады?
- Личесте әдеттегі шахмат пен %1$s бар.
- 8 шахмат түрлері
+ Личесте шахматтың қай түрлерін ойнасам болады?
+ Личесте классикалық әрі %1$s бар.
+ шахматтың 8 түрі
Қателіктердің орташа мәні (ACPL – average centipawn loss) деген не?
Қателіктердің орташа мәнінің бірлігі сентипоун (\"centi\" - жүзден бір бөлік, \"pawn\" - сарбаз). Бұл шама ойыншының жетістік дәрежесін көрсетеді. Ойын кезінде бұл шама соншалықты маңызды емес, бірақ компьютер үшін ойын күйін бағалауда орны зор.
@@ -115,7 +115,7 @@
Мықтылар қатары
белгіленген рейтингте кем дегенде 30 бағаланатын ойын ойнау,
кейінгі аптада белгіленген рейтингте бағаланатын ойынға қатысу,
- рейтинг ауытқуы стандарт шахматта %1$s-тен кем, басқа түрлерінде %2$s-тен кем болу керек,
+ рейтинг ауытқуы классикалық шахматта %1$s-тен кем, басқа түрлерінде %2$s-тен кем болу керек,
белгіленген рейтингтің үздік 10 қатарында болу.
Екінші талап – өз тіркелгілерін пайдаланбайтын ойыншылар Мықтылар қатарынан шығады.
Рейтинг басқа FIDE, USCF пен ICC сияқты ұйымдармен салыстырғанда неге жоғарылау болып тұр?
@@ -123,7 +123,7 @@
Рейтингті салыстырмалы шама деп ойлаған дұрыс: ойыншылар арасында рейтинг айырмашылығына қарап, кім жеңетінін, тең ойнайтынын не жеңілетінін бағалауға болады. \"Менің рейтингім осынша\" дегеннің ешбір мәні жоқ, тек басқа ойыншымен салыстырғаннан пайдасы болар.
Рейтингті ойын кезінде қалай жасырсам болады?
- %1$s-ге кіріп Zen қалпын қосыңыз немесе ойын кезінде %2$s басыңыз.
+ %1$s-ге кіріп Оқшау көріністі қосыңыз немесе ойын кезінде %2$s басыңыз.
баптауларды көрсету
Желінің ажырауы/кідірісі кесірінен ойын тоқталды. Рейтинг ұпайларын қайтара аласыз ба?
Өкінішке орай, бұл ажырау не кідіріс кімнің жағынан болғанына қарамастан, біз осы жайтта ұпайды қайтара алмаймыз. Біздің жағымыздан туындаған ажырау мен кідіріс – мүлдем сирек болатын жағдай. Онымен қатар, Личес жүйесінің қайта қосылуынан сіз уақыт жоғалтсаңыз, әділсіз шығын болмағандай біз ойынды үзіп тастаймыз.
diff --git a/translation/dest/faq/or-IN.xml b/translation/dest/faq/or-IN.xml
index acbf89c93d..03505f7fde 100644
--- a/translation/dest/faq/or-IN.xml
+++ b/translation/dest/faq/or-IN.xml
@@ -1,5 +1,5 @@
- ହଁ। Lichess ପ୍ରକୃତରେ ଅନ୍ୟ ଖୋଲା-ଉତ୍ସ ସାଇଟଗୁଡ଼ିକୁ ପ୍ରେରଣା ଦେଇଛି ଯାହା ଆମର %1$s, %2$s, କିମ୍ୱା %3$s ବ୍ୟବହାର କରନ୍ତି।
+ ହଁ। Lichess ପ୍ରକୃତରେ ଅନ୍ୟ ଖୋଲା-ଉତ୍ସ ସାଇଟଗୁଡ଼ିକୁ ପ୍ରେରଣା ଦେଇଛି ଯାହା ଆମର %1$s, %2$s, କିମ୍ୱା %3$s ବ୍ୟବହାର କରନ୍ତି।
ପ୍ରଦର୍ଶନ ଅଗ୍ରାଧିକାର
diff --git a/translation/dest/faq/os-SE.xml b/translation/dest/faq/os-SE.xml
index 1d45be186d..4a8932c291 100644
--- a/translation/dest/faq/os-SE.xml
+++ b/translation/dest/faq/os-SE.xml
@@ -3,27 +3,64 @@
АрФар
Арæх Фарстатæ
Lichess-æн йæ ном цæмæн у \"Lichess\"?
- \"Lichess\" у \"live/light/libre\" æмæ \"chess\" — удæгас, рог æмæ сæрибар шахмæттæ. Дзурын хъæуы афтæ: %1$s.
+ \"Lichess\" у \"live/light/libre\" æмæ \"chess\"-ы комбинаци — удæгас, рог æмæ сæрибар шахмæттæ. Дзурын хъæуы афтæ: %1$s.
личец
- Байхъус специалист æй куыд дзуры.
- Удæгас уый тыххæй, æмæ ды хъазыс æмæ кæсыс хъазтытæ реалон рæстæджы цыфæнды афоны; рог æмæ сæрибар уымæн, æмæ Lichess æргом æмæ быронæй сыгъдæг у, иннæ æргъыл сайтытæм нæ кæсгæйæ.
- Уый дæр хауы Lichess-ы райдайæн кодмæ, %1$s-мæ, кæй нысан кæны li[chess in sca]la, уымæн æмæ Lichess у фыст %2$s-йæ, æнцонæмбарон программон æвзагæй.
+ Байхъус, специалист æй куыд дзуры.
+ Удæгас, уымæн æмæ дæ бон у хъазын æмæ кæсын хъæзтытæ реалон рæстæджы цыфæнды афоны; рог æмæ сæрибар, уымæн æмæ Lichess у æргом æмæ дзы нæй проприетарон быроны, иннæ коммерцион проектты хуызæн.
+ Уый дæр хауы Lichess-ы райдайæн кодмæ, %1$s-мæ, кæй нысан кæны li[chess in sca]la, уымæн æмæ Lichess сæйрагдæр фыст у %2$s-йæ, æнцонæмбарæн программон æвзагæй.
Куыд баххуыс кæнон Lichess-æн?
- Lichess цæры хъазджыты снывондытæй æмæ волонтёрты куыстæй.
- Дæ бон у базонын %1$s, уый (æмæ %2$s дæр). Кæд дæ фæнды баххуысын Lichess-æн дæ рæстæгæй æмæ арæхстытæй, уæд дзы ис %3$s.
+ Lichess цæры хъазджыты нывондытæй æмæ волонтёрты куыстæй.
+ Дæ бон у базонын, %1$s (æмæ %2$s дæр). Кæд дæ фæнды баххуыс кæнын Lichess-æн дæ рæстæгæй æмæ арæхстытæй, уæд дзы ис %3$s.
аххуысгæнæгæй куыд суын
нæ хардзыты хыгъдлæвæрд
æндæр амалтæ баххуыс кæнын
- Ис дзы æндæр Lichess-вебсайтытæ?
- О, Lichess ныфс радта æндæр æргом сайтытæм, кæй пайда кæнынц нæ %1$s, %2$s æмæ %3$s.
+ Ис дзы æндæр сайтытæ Lichess-æй бындурыл?
+ О, æндæр æргом сайтытæ Lichess-æй удцырын райстой æмæ пайда кæнынц нæ %1$s, %2$s æмæ %3$s.
Рæстаг Хъазт
- Сайæгой мæ кæд рамбулдзæн, уæд мын мæ рейтинг фæстæмæ ратдзыстут?
- Хъазты арæзт
- Нæмыг, блиц æмæ æндæр сахаты цæстдардтæ куыд рабæрæг конд ысты?
+ Сайæгой мæ кæд рамбылдта, уæд мын мæ рейтинг фæстæмæ ратдзыстут?
+ Сайæгойы кæд раргом кодтам, уæд, иу минутæйы фæстæ, йæ 40 ахæм рейтингимæ хъæзтытæ, фæстаг 3 боны кæм рамбылдта, уыдзысты равзард. Ды ацы хъæзтыты йæ ныхмæлæууæг кæд уыдтæ, æмæ дæхион рейтинг æууæнккаг кæд у, уæд дæ рейтинг дын фæстæмæ ратдзыстæм. Баххæст уыдзæн æрæнгонд дæ иуылстыр рейтингæй æмæ æнæраст хъæзтыты фæстæ рейтингы ивындзинæдтæй.
+ (Зæгъæм, дæ рейтинг ацы хъæзтыты фæстæ кæд тынг фæфылдæр, уæд, чи зоны, ницы дын баххæст кæндзыстæм, кæннод йæ хай æрмæст ратдзыстæм.) Цыфæндыйæ дæр дæ баххæст 150 гæбæлтæй нæ ахисдзæн.
+ Хъазджытæн, хъазтæй кæй рацæуынц æнæ хи дæттгæйæ, цы вæййы?
+ Дæ ныхмæлæууæг йæ хъазт кæд арæх фæуромы/фæуадзы, уæд йын хъæзтытæм бацæуæн рæстæгмæ æхгæд уыдзæн, æмæ йæ уæлдæй ничи фендзæн уый. Кæд уый фæстæ йæ митæ нæ бауромдзæн, уæд йын ногæй сыхгæндзыстæм хъæстытæм бацæуæн, фæлæ фылдæр æмгъуыдмæ. Æппын фæстаг, йæ аккаунт æххæстæй сыхгæндзыстæм.
+ Ис дзы мæ бон модераторæй суын?
+ Мах модераторы бынатмæ курдиаттæ нæ исæм. Кæд фендзыстæм, исчи, махмæ гæсгæ, уыдзæн хорз модератор, уæд нæхæдæг йемæ бастдзинад саразæм.
+ Ис дзы хицæндзинад фыстæгтæ фысгæйæ хъазтæй хуымæтæг хъазты \'хсæн?
+ Lichess-ы сæйраг хицæндзинад фыстæгтæ фысгæ хъазы уагæвæрдты у райдайæнты чиныг. Компьютеры программæты пайдакæнæн иваргонд у æмæ расайдзæн аккаунты æхгæнæн. ICCF-мæ нæкæсгæйæ, Lichess компьютеры программæты аххуыс нæ уадзы.
+ Хъазты æрфыст
+ Нæмыг, блиц æмæ æндæр хъазы хуызтæ сахаты цæстдардмæ гæсгæ куыд рабæрæг конд ысты?
+ Lichess сахаты цæстдард баст у æввахс хъазы дæргъцы = %1$s
+Зæъæм, æввахс 5+3 хъазы дæргъц у 5 × 60 + 40 × 3 = 420 сикъунды.
+ (райдайæн рæстæг) + 40 × (уæлæмхасæн)
< %1$s с = %2$s
≥ %1$s с = %2$s
- Цавæр варианттæ мæ бон у хъазын Lichess-ы?
+ Цавæр шахмæтты хуызтæ мæ бон у хъазын Lichess-ы?
Lichess-мæ ис классикон шахмæттæ æмæ %1$s.
+ 8 шахмæтты хуызты
+ «Рæстæмбис сантипешкæты сæфт» (ACPL) та ци у?
+ Сантипешкæ у барынады иуæг, кæй пайда у æвдисын уæлидзинад шахмæтты. Сантипешкæ у ¹/₁₀₀ пешкæйы аргъы. 100 сантипешкæты æмбæрц у 1 пешкæмæ. Ацы нысаниуæгтæ ницы давынц хъазты, фæлæ сты пайдаджын компьютерон шахмæтты æмæ позицийы аргъкæнынады компьютерон анализы.
+
+Фыццаг хаххы комьютеры цыд нысан кæны нулон сантипешкæты сафт, æмæ алы æндæр цыд фæкæны позицийы фæфыддæр, кæй бæрæг у сантипешкæты сафтæй. Цас фыддæр цыд у, уыйас фылдæр сафты нысаниуæг.
+
+Ацы нысаниуæг у хъазты бæрцы æвдисæн. Цас каддæр у сантипешкæты сæфт цыды рæстæмбисæй, уыйас хъаруджындæр у хъазт.
+
+Lichess компьютерон анализы пайда кæны Stockfish.
+ Æмбылд рæстæгæй, хæрæгсаст æмæ æрмæджы хъуаг
+ Фылдæр хатт, хъазæджы рæстæг куы фæуы, уæд йын фембылд банымайæм. Фæлæ хъазт хæрæгсастæй фæуы, ныхмæлæууæгмæ кæд нæй фадаты мат нывварын дарддæр хъазгæйæ (%1$s).
+
+Хатгай зын вæййы уынаффæ хæдархайгæ рахæссын (æнæбары цыдты сери, фидар). Æмткæй райсгæйæ мах райсæм фарс хъазæджы, кæмæн ма баззадис рæстæг.
+
+Хъуыдыйы дар, ныхмæлæууæгмæ кæд ис фигурæ кæнæ пешкæ, йæ бон кæмæн у къаролæн блокадæ сывæрын, уæд мат нывварын фадат ис иу бæхæй дæр, пылæй дæр.
+ FIDE-йы уагæвæрдтæ %s
+ Цæмæн пешкæйæн байст ис, цæвгæгонд быдырыл кæд нырид ахизыд, уæд? (ацæуæны байст)
+ Уый у цыд, зындгонд номæй «ацæуæны байст». Уацхъуыд уырыссаг Википедийы дæтты %1$s.
+
+Он описан в разделе 3.7 (d), %2$s:
+
+«Пешка, занимающая поле на той же горизонтали и на соседней вертикали с пешкой соперника, которая только что продвинулась на два поля в один ход от своей первоначальной позиции, может взять эту продвинутую пешку соперника, как если бы последний ее ход был только на одно поле. Это взятие возможно только ходом, следующим за этим продвижением, и называется взятием «на проходе».»
+
+ Посмотрите %3$s и попрактикуйтесь.
+ хорз райхæлд
+ Æртывæр позицийы фæлхатын
Ис дзы гæнæн мын раттын Lichess Мастеры (LM) хуындад?
Нæй.
Уый у каджын хуындад, æнæофициалон, æмæ æрмæст вæййы Lichess-ы.
diff --git a/translation/dest/learn/be-BY.xml b/translation/dest/learn/be-BY.xml
index b7a7719b13..7eb925d277 100644
--- a/translation/dest/learn/be-BY.xml
+++ b/translation/dest/learn/be-BY.xml
@@ -11,7 +11,7 @@
Ладдзя
Рухаецца па простых лініях
Ладдзя — гэта цяжкая фігура. Ці гатовы вы кіраваць ёй?
- Пстрыкніце на ладдзю і давядзіце яе да зоркі!
+ Націсніце на ладдзю і давядзіце яе да зорачкі!
Збярыце ўсе зоркі!
Чым менш хадоў вы зробіце,
тым больш балаў атрымаеце!
@@ -35,7 +35,7 @@
І апошні!
Вы навучыліся хадзіць каралём!
Конь
- Ён ходзіць літарай \"Г\"
+ Xодзіць літарай \"Г\"
Асвоіць каня не так проста, бо ён — хітрая фігура.
Коні маюць мудрагелісты спосаб пераскокваць!
Коні могуць скакаць праз перашкоды!
@@ -124,9 +124,9 @@
Абараніце свайго караля і выведзіце ў бой ладдзю!
Перасуньце свайго караля на дзве клеткі да ладдзі каралеўскага фланга!
Перасуньце свайго караля на дзве клеткі да ладдзі ферзевага фланга!
- Конь у дарозе! Зрабіце ход канём, потым зрабіце ракіроўку.
- Кароткая ракіроўка! Спачатку вам трэба развіць фігуры.
- Доўгая ракіроўка! Спачатку вам трэба развіць фігуры.
+ Конь перашкаджае! Пахадзіце канём, потым зрабіце ракіроўку.
+ Кароткая ракіроўка! Спачатку вам трэба вывесці фігуры.
+ Доўгая ракіроўка! Спачатку вам трэба вывесці фігуры.
Вы не можаце рабіць ракіроўку,
калі кароль ці ладдзя,
якія ўдзельнічаюць у ракіроўцы, рабілі ход.
@@ -151,7 +151,7 @@
Гульня з\'яўляецца нічыйнай
Калі гульцу не пастаўлены шах і няма магчымасці зрабіць ход, гэта пат. Гульня сканчаецца: без пераможцаў і пераможаных.
Пат чорным:
-- Чорныя не могуць схадзіць.
+- Чорныя не могуць пахадзіць.
- Кароль не пад шахам.
Віншуем! Лепш атрымаць пат, чым мат!
Прасунуты этап
@@ -167,12 +167,11 @@
Атакуйце фігуры з больш высокай каштоўнасцю!
Ферзь > Слон
Вазьміце найбольш каштоўную фігуру!
-Не разменьвайце больше каштоўную фігуру на менш каштоўную.
+Не разменьвайце больш каштоўную фігуру на менш каштоўную.
Вазьміце
найбольш каштоўную фігуру!
Пераканайцеся, што ход магчымы!
- Атакуйце фігуры
-з больш высокай каштоўнасцю!
+ Вазьміце найкаштоўнейшую фігуру!
Віншуем! Вы ведаеце каштоўнасць фігур!
Ферзь = 9 пешак
Ладдзя= 5 пешак
diff --git a/translation/dest/learn/fr-FR.xml b/translation/dest/learn/fr-FR.xml
index 6ecc924c2a..044bf257b8 100644
--- a/translation/dest/learn/fr-FR.xml
+++ b/translation/dest/learn/fr-FR.xml
@@ -185,7 +185,7 @@ Fou = 3
Cavalier = 3
Pion = 1
Échec en deux coups
- Donnez un échec en deux coups
+ Mettez échec en deux coups
Trouvez la bonne combinaison en deux coups qui met le Roi de l\'adversaire en échec !
Menacez le Roi adverse
en deux coups !
diff --git a/translation/dest/learn/or-IN.xml b/translation/dest/learn/or-IN.xml
index 503721090f..7f3b6d6b5b 100644
--- a/translation/dest/learn/or-IN.xml
+++ b/translation/dest/learn/or-IN.xml
@@ -5,24 +5,25 @@
ରାଣୀ
ରାଣୀ = ଡଙ୍ଗା + ହାତୀ
ରଜା
- ଘୋଡ଼ା
+ ଘୋଡ଼ା
ସୈନିକ
ବିକଶିତ
- ଉଚ୍ଚ ଗତିଶୀଳତା ଥିବା ଗୋଟିଗୁଡ଼ିକର ଅଧିକ ମୂଲ୍ୟ ଅଛି!
+ ଉଚ୍ଚ ଗତିଶୀଳତା ଥିବା ଗୋଟିଗୁଡ଼ିକର ଅଧିକ ମୂଲ୍ୟ ଅଛି!
ରାଣୀ = 9
ଡଙ୍ଗା = 5
ହାତୀ = 3
-ଘୋଡ଼ା = 3
+ଘୋଡ଼ା = 3
ସୈନିକ = 1
ରାଜା ଅମୂଲ୍ୟ ଅଟେ! ଏହାକୁ ହରାଇବାର ଅର୍ଥ ହେଉଛି ଖେଳ ହାରିବା।
ଅଭିନନ୍ଦନ! ଆପଣ ମୋହରା ର ମୂଲ୍ୟ ଜାଣନ୍ତି!
ରାଣୀ = 9
ଡଙ୍ଗା = 5
ହାତୀ = 3
-ଘୋଡ଼ା = 3
+ଘୋଡ଼ା = 3
ସୈନିକ = 1
ପ୍ରହେଳିକା
ଆପଣଙ୍କ କୌଶଳ ଅଭ୍ୟାସ କରନ୍ତୁ
+ ଆପଣଙ୍କ ସ୍କୋର୍
ପ୍ରହେଳିକା ବିଫଳ ହେଲା!
ପୁନଃଚେଷ୍ଟା କରନ୍ତୁ
diff --git a/translation/dest/patron/be-BY.xml b/translation/dest/patron/be-BY.xml
index 3119ff4d6c..dee03b4a47 100644
--- a/translation/dest/patron/be-BY.xml
+++ b/translation/dest/patron/be-BY.xml
@@ -58,7 +58,7 @@
- Спонсар Lichess на працягу %s месяцаў
Вы ахвяруеце lichess.org %s кожны месяц.
- Дзякуй вілікі за дапамогу! Вы неверагодны!
+ Дзякуй вялікі за дапамогу! Вы неверагодны!
Бягучы статус
Наступны плацёж
%2$s вы заплаціце %1$s.
diff --git a/translation/dest/patron/de-CH.xml b/translation/dest/patron/de-CH.xml
index 9925eb7045..19838f1ad1 100644
--- a/translation/dest/patron/de-CH.xml
+++ b/translation/dest/patron/de-CH.xml
@@ -1,7 +1,7 @@
Schpändä
- Spende als %s
+ Spändä als %s
Lichess Patroon
Kostenloses Konto
Wird Lichess Patroon
diff --git a/translation/dest/patron/or-IN.xml b/translation/dest/patron/or-IN.xml
index dcd51f23e9..aff9b6604c 100644
--- a/translation/dest/patron/or-IN.xml
+++ b/translation/dest/patron/or-IN.xml
@@ -1,7 +1,7 @@
ଦାନ କରନ୍ତୁ
- %s ଭାବରେ ଦାନ କରନ୍ତୁ
+ %s ଭାବରେ ଦାନ କରନ୍ତୁ
Lichess Patron
ମାଗଣା ଖାତା
ଏକ Lichess Patron ହୁଅନ୍ତୁ
@@ -17,9 +17,9 @@
ମାସିକ
ଅନ୍ୟ
ଦୟାକରି %s ରେ ଏକ ରାଶି ପ୍ରବେଶ କରନ୍ତୁ
- କ୍ରେଡ଼ିଟ୍ କାର୍ଡ଼
+ କ୍ରେଡ଼ିଟ୍ କାର୍ଡ଼
PayPal
- ଟଙ୍କା କୁଆଡ଼େ ଯାଏ?
+ ଟଙ୍କା କୁଆଡ଼େ ଯାଏ?
ସର୍ବପ୍ରଥମେ, ଶକ୍ତିଶାଳୀ ସର୍ଭର।
ତା\'ପରେ ଆମେ ଏକ ପୂର୍ଣ୍ଣକାଳୀନ ବିକାଶକାରୀ: %sଙ୍କୁ, Lichess ର ପ୍ରତିଷ୍ଠାତା, ଦେୟ ଦେଇଥାଉ।
Lichess ସମର୍ଥନ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ
@@ -35,7 +35,7 @@
ଆପଣଙ୍କ ସମର୍ଥନ ପ୍ରତ୍ୟାହାର କରନ୍ତୁ
%1$s ବା %2$s
Lichess ଆପଣଙ୍କ ପାଇଁ କେତେ ମୂଲ୍ୟବାନ ତାହା ସ୍ଥିର କରନ୍ତୁ:
- ଆପଣଙ୍କ କ୍ରେଡ଼ିଟ୍ କାର୍ଡ଼ ପ୍ରତ୍ୟାହାର କରନ୍ତୁ ଏବଂ ଦେୟ ବନ୍ଦ କରନ୍ତୁ:
+ ଆପଣଙ୍କ କ୍ରେଡ଼ିଟ୍ କାର୍ଡ଼ ପ୍ରତ୍ୟାହାର କରନ୍ତୁ ଏବଂ ଦେୟ ବନ୍ଦ କରନ୍ତୁ:
ଆଉ Lichess କୁ ସମର୍ଥନ କରନ୍ତି ନାହିଁ
ତାରିଖ
ରାଶି
diff --git a/translation/dest/preferences/ar-SA.xml b/translation/dest/preferences/ar-SA.xml
index 471302a527..3aed58d867 100644
--- a/translation/dest/preferences/ar-SA.xml
+++ b/translation/dest/preferences/ar-SA.xml
@@ -12,6 +12,7 @@
رمز قطعة الشطرنج
حروف (K, Q, R, B, N)
وضع التأمل
+ إظهار تقييمات اللاعب
أظهر زر تعديل حجم الرقعة
خلال الوضع المبدئي فقط
(معصوب العينين (إخفاء القطع
diff --git a/translation/dest/preferences/de-CH.xml b/translation/dest/preferences/de-CH.xml
index effdce4b3c..fa46da7c00 100644
--- a/translation/dest/preferences/de-CH.xml
+++ b/translation/dest/preferences/de-CH.xml
@@ -12,6 +12,7 @@
Schachfiguräsymbool
Buächschtabä (K, Q, R, B, N)
Zän Modus
+ Wertigszahl vo Spieler ahzeigä
Regler zum Ändärä vodä Brättgrössi aazeigä
Nur idä Aafangsschtellig
Blindschach (unsichtbari Figurä)
diff --git a/translation/dest/preferences/id-ID.xml b/translation/dest/preferences/id-ID.xml
index 1832005ca1..8707e938fc 100644
--- a/translation/dest/preferences/id-ID.xml
+++ b/translation/dest/preferences/id-ID.xml
@@ -12,6 +12,7 @@
Simbol bidak catur
Huruf (K, Q, R, B, N)
Moda Zen
+ Munculkan rating pemain
Tampilkan pengubah ukuran papan catur
Hanya di posisi awal
Catur buta (buah catur disembunyikan)
diff --git a/translation/dest/preferences/kk-KZ.xml b/translation/dest/preferences/kk-KZ.xml
index bebcd229fa..85cf551514 100644
--- a/translation/dest/preferences/kk-KZ.xml
+++ b/translation/dest/preferences/kk-KZ.xml
@@ -11,7 +11,8 @@
Жүрісті жазу тәртібі
Тас таңбасы
Әріптер (K, Q, R, B, N)
- Zen қалпы
+ Оқшау көрініс
+ Ойыншылардың рейтингін көрсету
Тақтаны үлкейту тұтқасын көрсету
Тек бастапқы күйде ғана
Бүркемелі шахмат (көрінбейтін тастар)
@@ -28,7 +29,7 @@
Екі әдіспен де
Алдын-ала жүру (қарсыластың жүру кезегі кезінде жүру)
Жүрісті қайтару (қарсыластың келісімі қажет)
- Қалыпты ойындарда ғана
+ Жай ойындарда ғана
Уәзірге автоматты түрде айналдыру
Алдын-ала жүрісте
Үш реттік қайталану болса, автоматты түрде тепе-теңдік жариялау
diff --git a/translation/dest/preferences/ko-KR.xml b/translation/dest/preferences/ko-KR.xml
index 11e92654ff..2b4e72fde5 100644
--- a/translation/dest/preferences/ko-KR.xml
+++ b/translation/dest/preferences/ko-KR.xml
@@ -12,6 +12,7 @@
체스 말 기호
글자 (K, Q, R, B, N)
젠 모드
+ 플레이어 레이팅 보기
보드 크기 재조정 핸들 보이기
초기 상태에서만
눈을 가리고 체스 (기물이 보이지 않음)
diff --git a/translation/dest/preferences/vi-VN.xml b/translation/dest/preferences/vi-VN.xml
index 0db6e5698d..ce506d36e8 100644
--- a/translation/dest/preferences/vi-VN.xml
+++ b/translation/dest/preferences/vi-VN.xml
@@ -12,6 +12,7 @@
Biểu tượng quân cờ
Ký tự (K, Q, R, B, N)
Chế độ Zen
+ Hiện xếp hạng của người chơi
Hiện nút thay đổi kích cỡ
Chỉ ở vị trí khởi tạo
Cờ tưởng (quân cờ vô hình)
diff --git a/translation/dest/puzzle/en-US.xml b/translation/dest/puzzle/en-US.xml
index a0e353ca70..cf50c6b8e9 100644
--- a/translation/dest/puzzle/en-US.xml
+++ b/translation/dest/puzzle/en-US.xml
@@ -79,7 +79,7 @@ Play rapid and classical games to increase your chances of having a puzzle of yo
- %s to replay
- %s to replay
- %s solved
+ %s solved
Nothing to show, go play some puzzles first!
Train these to optimize your progress!
You perform the best in these themes
diff --git a/translation/dest/puzzle/hi-IN.xml b/translation/dest/puzzle/hi-IN.xml
index 882cad68ff..4fe2b415d1 100644
--- a/translation/dest/puzzle/hi-IN.xml
+++ b/translation/dest/puzzle/hi-IN.xml
@@ -13,6 +13,7 @@
विशेष चाल
क्या आपको यह पहेली पसंद आई?
अगले एक को लोड करने के लिए वोट दें!
+ आपकी पज़ल रेटिङ नही बदलेगी।पज़ल एक स्पर्धा नही है रेटिङ आपके अनुकुल पज़ल चुनने मे मदद करती है।
सफेद रंग के लिए सबसे अच्छी चाल का पता लगाएं।
काले रंग के लिए सबसे अच्छी चाल का पता लगाएं।
वैयक्तिकृत पहेलियाँ प्राप्त करने के लिए:
@@ -62,4 +63,7 @@
अपने कॉम्बो को संरक्षित करने के लिए इस कदम को छोड़ दें। सम्पूर्ण पारी मे केवल एक बार काम करता है ।
श्रंखला बनाए रखें ।
नई श्रंखला
+ मेरे खेलों से
+ %s के खेलो की पज़ल|
+ पज़ल खोजे
diff --git a/translation/dest/puzzle/ko-KR.xml b/translation/dest/puzzle/ko-KR.xml
index 1f847f15e8..cc62a951d1 100644
--- a/translation/dest/puzzle/ko-KR.xml
+++ b/translation/dest/puzzle/ko-KR.xml
@@ -13,6 +13,7 @@
특수 행마
이 퍼즐이 괜찮았나요?
다음 퍼즐을 위해 투표해주세요!
+ 당신의 퍼즐 레이팅은 바뀌지 않을 것입니다. 퍼즐은 경쟁이 아니라는 걸 기억하세요. 레이팅은 당신의 현재 스킬에 맞는 퍼즐을 선택하도록 돕습니다.
백의 최고의 수를 찾아보세요.
흑의 최고의 수를 찾아보세요.
개인화된 퍼즐을 위해선:
@@ -73,7 +74,7 @@
- %s 다시 풀기
- %s 해결함
+ %s 해결함
표시할 것이 없습니다. 먼저 퍼즐을 플레이하세요!
실력을 높이기 위해 훈련하세요!
이 테마에서 최고의 성적을 냈습니다
diff --git a/translation/dest/puzzleTheme/fa-IR.xml b/translation/dest/puzzleTheme/fa-IR.xml
index 5c861b6793..df97d18378 100644
--- a/translation/dest/puzzleTheme/fa-IR.xml
+++ b/translation/dest/puzzleTheme/fa-IR.xml
@@ -1,17 +1,28 @@
پیاده پیش رفته
+ یکی از پیاده های شما در زمین حریف پیشروی کرده است. شاید خطر ارتقا پیاده وجود داشته باشد.
برتری
مات آناستازیا
+ یک اسب و یک رخ به همدیگر کمک میکنند تا شاه حریف را بین گوشه های زمین و یک مهره از حریف زندانی کنند.
مات عربی
یک اسب و یک رخ برای به دام انداختن شاه حریف در گوشه صفحه همکاری می کنند.
حمله به خانه f2 یا f7
+ حمله ای که در آن روی پیاده های f2 و f7 تمرکز می شود، مانند دفاع دو اسب.
جلب کردن
مات عرض آخر
به دام انداختن شاه حریف در عرض اولیه خود زمانی که با مهره های خودی به دام افتاده است.
آخر بازی فیل
+ یک آخر بازی تنها با فیل ها و پیاده ها.
+ مات Boden
+ دو فیل به حالت ضربدری که به گوشه زمین حمله می کنند یک شاه که راهش با مهره های خودش سد شده را مات می کنند.
قلعه رفتن
شاه خود را ایمن کنید و رخ خود را برای حمله مستقر کنید.
+ مهره دفاع کننده را بگیرید
+ گرفتن یک مهره که برای دفاع از یک مهره دیگر حیاتی است، اجازه می دهد مهره ای که اکنون بدون دفاع است در حرکت بعدی گرفته شود.
+ مات با دو فیل
+ دو فیل که همزمان به گوشه های مجاور حمله می کنند، شاهی که راهش با مهره های خودش سد شده را مات می کنند.
+ وزیری که شاه مجاور خودش را که تنها دو خانه ای که برای فرارش باقی مانده توسط مهره های خودش سد شده، مات می کند.
برابری
حمله به جناه شاه
حمله به شاه حریف زمانی که در جناه شاه قلعه رفته است.
@@ -19,6 +30,7 @@
حرکت تدافعی
منحرف کردن
حمله برخاستی
+ حرکت دادن یک مهره (مانند اسب)، که قبلا جلوی حمله مهره خودمان به یکی از مهره های دور حریف (مانند رخ)، از جلوی راه مهره حمله کننده.
کیش دوگانه
کیش دادن به حریف با دو مهره به صورت هم زمان در نتیجهء یک حمله برخاستی که در آن هم مهره برخاست کننده و هم مهره پشت سر آن به شاه حریف حمله می کنند.
آخر بازی
@@ -28,9 +40,19 @@
چنگال
حرکتی که در آن مهره ای که حرکت می کند دو مهره حریف را به صورت همزمان مورد حمله قرار می دهد.
مهره بی دفاع
+ تاکتیکی که در آن مهره های حریف برای گرفتن، بدون دفاع یا با دفاع ناکافی است.
+ مات Hook
+ مات با یک رخ، اسب و یک پیاده در برابر یک پیاده حریف برای محدود کردن راه های فرار شاه دشمن.
+ چنگال
+ حرکت دادن یک مهره و حمله کردن به دو مهره هم زمان که بتوان یکی از مهره ها را گرفت، مانند حمله یک اسب به به دو تا رخ بصورت هم زمان.
+ آخر بازی اسب
+ یک آخر بازی تنها با اسب ها و پیاده ها.
معمای طولانی
+ سه حرکت برای پیروزی.
بازیهای اساتید
+ پازل های برگرفته شده از بازی هایی که توسط بازی کنان دارای عنوان انجام شده است.
بازیهای استاد در مقابل استاد
+ پازل های برگرفته شده از بازی های بین دو بازی کن دارای عنوان.
کیش و مات
بازی را با سبک خاصی ببرید.
مات در یک حرکت
diff --git a/translation/dest/puzzleTheme/kk-KZ.xml b/translation/dest/puzzleTheme/kk-KZ.xml
index f0e9c0ec01..b3bcb52bb4 100644
--- a/translation/dest/puzzleTheme/kk-KZ.xml
+++ b/translation/dest/puzzleTheme/kk-KZ.xml
@@ -2,7 +2,7 @@
Әккі сарбаз
Айналуға ұмтылған не айналу қаупін төндіретін сарбаз – тактиканың өзегі.
- Артықшылық
+ Басымдылық
Шешуші артықшылық беретін сәтті жіберіп алмаңыз. (200-600 сп)
Анастасия маты
Ат пен тура не уәзірдің бірлесіп, қарсылас патшасын тақта шеті мен өз тасының арасында ұстап алуы.
diff --git a/translation/dest/settings/hi-IN.xml b/translation/dest/settings/hi-IN.xml
index e007e57345..5ca28787e5 100644
--- a/translation/dest/settings/hi-IN.xml
+++ b/translation/dest/settings/hi-IN.xml
@@ -3,7 +3,7 @@
व्यवस्था (सेटिंग्स)
खातें को बंद करें
समापन निश्चित है। वहां से कोई वापसी नहीं है। क्या आपने सुनिश्चित कर लिया है?
- आपको एक ही नाम से एक नया खाता खोलने की अनुमति नहीं दी जाएगी, भले ही मामला अलग हो।
+ आपको एक ही नाम से एक नया खाता खोलने की अनुमति नहीं दी जाएगी, भले ही अक्षर बड़े-छोटे हो।
मैंने अपना मन बदल लिया, मेरे खाता को बंद न करें
क्या आप वाकई अपना खाता बंद करना चाहते हैं? अपना खाता बंद करना एक स्थायी निर्णय है। आप कभी भी लॉग इन नहीं कर पाएंगे।
यह ख़ाता बंद है|
diff --git a/translation/dest/site/af-ZA.xml b/translation/dest/site/af-ZA.xml
index 2543ffb7eb..e5acce2d21 100644
--- a/translation/dest/site/af-ZA.xml
+++ b/translation/dest/site/af-ZA.xml
@@ -48,13 +48,15 @@
Wit het die spel verlaat
Swart het die spel verlaat
WitHetniebeweegnie
+ Swart het nie geskuif nie
Versoek \'n rekenaar analise
Rekenaar analise
Rekenaar analise beskikbaar
+ Rekenaaranalise gedeaktiveer
Ontledingsbord
Diepte %s
Die gebruik van bediener analise
- Enjin is besig om the laai ...
+ Enjin laai tans ...
Wolkanalise
Gaan dieper
Toon bedreiging
@@ -68,7 +70,7 @@
Variant verloor
Variant wen
Onvoldoende materiaal
- Pion skuif
+ Pionskuif
Buiting
Maak toe
Besig om te wen
@@ -85,12 +87,17 @@
- Mat in %s half-skuif
- Mat in %s half-skuiwe
+ DTZ50\" met afronding, gebaseer op die hoeveelheid half-skuiwe tot die volgende vat of pionskuif
Geen spel gevind
Voeg dalk meer wedstryde van die voorkeure kieslys by?
Opening ontdekker
+ Opening/eindspel ontdekker
%s opening ontdekker
Oorwinning gekelder deur 50-skuifreël
Verloor voorkom deur 50-skuifreël
+ Wen of 50 skuiwe deur vorige fout
+ Verloor of 50 skuiwe deur vorige fout
+ Wen/verloor slegs gewaarborg as aanbevole tafelbasis lyn gevolg is sedert die laaste vat of pion skuif, weens moontlike afronding van DTZ waardes in Syzygy tafelbasisse.
Gereed!
Voer PGN in
Skrap
@@ -110,6 +117,18 @@
Flater
Fout
Onakkuraatheid
+
+ - %s flater
+ - %s flaters
+
+
+ - %s fout
+ - %s foute
+
+
+ - %s onakuraatheid
+ - %s onakuraathede
+
Skuif tye
Keer bord om
Drie-malige herhaling
@@ -120,6 +139,8 @@
- %s speler
- %s spelers
+ Gelykop deur wedersydse ooreenkoms
+ Vyftig skuiwe sonder vordering
Lopende spelle
- Sien al %s spelle
@@ -142,7 +163,7 @@
Spelers
Vriende
Gesprekke
- Vandag
+ Vandag
Gister
Minute per kant
Variant
@@ -273,7 +294,7 @@
Boekmerk die spel
Toernooi
Toernooie
- Toernooi punte
+ Toernooipunte
- %s toernooipunt
- %s toernooipunte
@@ -330,7 +351,7 @@
Plak \'n wedstryd PGN om dit deursoekbaar te herspeel,
rekenaar analise, kletskamer en deelbare URL te kry.
- - %s het spel ingevoer
+ - %s ingevoerde spel
- %s het spelle ingetrek
Hierdie is \'n skaak CAPTCHA.
@@ -349,6 +370,8 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Volg
Besig om te volg
Ontvolg
+ Volg %s
+ Ontvolg %s
Blokeer
Geblok
Ontblok
@@ -369,7 +392,7 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Lys
Grafiek
- - Minder as %s minute
+ - Minder as %s minuut
- Minder as %s minute
Vereiste.
@@ -411,9 +434,11 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Bord redigeerder
Pak die bord
Gewilde openinge
- Begin posisie
+ Eindspel posisies
+ Skaak960 begin posisie: %s
+ Begin posisie
Maak bord skoon
- Laai posisie
+ Laai posisie
Privaat
Raporteer %s aan moderators
Profiel voltooiing: %s
@@ -425,13 +450,14 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Noem naam
Van
Biografie
+ Land of vlag
Dankie!
Sosiale media skakels
Binnelyn notasie
Voorigekeer op Lichess TV
Aanlyn spelers
Aktiewe spelers
- Pasop, dié spel is gegradeer, maar sonder \'n klok!
+ Pasop, dié spel is gegradeer, maar sonder \'n klok!
Sukses
- %s spel aan die gang
@@ -443,6 +469,8 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Toernooi wenners
Naam
Beskrywing
+ Private beskrywing
+ Teks wat slegs spanlede sal sien. Indien gestel vervang dit die publieke beskrywing vir spanlede.
Nee
Ja
Help:
@@ -463,6 +491,7 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Kul
Beledig
Boelie
+ Gradering manipulasie
Ander
Plak skakel na die spel(le) en verduidelik wat skort met die lid se gedrag. Moenie net sê hulle kroek nie, maar verduidelik hoe daardie gevolgtrekking bereik is. Jou verslag sal vinniger geantwoord word as dit in Engels geskryf is.
Verskaf asseblief ten minste een skakel na \'n spel waar hulle gekroek het.
@@ -497,7 +526,7 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Op stadig spelle
Altyd
Nooit
- %1$s kompeteer in %2$s
+ %1$s kompeteer in %2$s
Oorwinning
Nederlaag
%1$s vs %2$s in %3$s
@@ -528,7 +557,16 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Gereedskap
Inkrement
Dié veld word vereis
+ Hierdie epos adres is ongeldig
+ Hierdie e-pos adres is nie aanvaarbaar nie. Maak seker dis reg en probeer weer.
+ E-pos adres is ongeldig of reeds geneem
+ Hierdie is reeds jou e-pos adres
+ Moet ten minste %s karakters lank wees
+ Moet op die meeste %s karakters lank wees
+ Moet minstens %s wees
+ Moet meestens %s wees
Indien gradering ± %s is
+ Slegs bestaande gesprekke
Slegs vriende
Kieslys
Kastelering
@@ -615,6 +653,7 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Jy kan ook blaai oor die bord om te beweeg in die spel.
Druk shift+klik of regs-kliek om sirkels en pyle te trek op die bord.
Laat ander spelers boodskappe aan jou stuur
+ Laat weet wanneer in die forum genoem word
Deel jou data oor Skaak Insigte
Met niemand
Met vriende
@@ -625,6 +664,7 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Skakel kindermodus aan
Skakel kindermodus aan
Sekuriteit
+ Sessies
alle sessies herroep
Speel skaak oral
So gratis soos Lichess
@@ -772,4 +812,57 @@ rekenaar analise, kletskamer en deelbare URL te kry.
U kan nog nie in die forums plaas nie. Speel \'n paar speletjies!
Skryf in
Beëindig inskrywing
+ het jou genoem in \"%1$s\".
+ %1$s het jou genoem in \"%2$s\".
+ het jou genooi na \"%1$s\".
+ %1$s het jou genooi na \"%2$s\".
+ Jy is nou deel van die span.
+ Jy het by \"%1$s\" aangesluit.
+ Iemand wat jy aangemeld het, is verban
+ Baie geluk, jy het gewen!
+ Spel teen %1$s
+ %1$s teen %2$s
+ Iemand het jou afrigtersprofiel resenseer.
+ Nuwe hangende resensie
+ Jy het teen iemand verloor wat nie die Lichess bepalings nagekom het nie
+ Terugbetaling:%1$s %2$s graderingspunte.
+ Tyd is byna op!
+ [Klik om e-posadres te beskou]
+ Laai af
+ Welkom!
+ Lichess is \'n liefdadigheidsorganisasie en heeltemal gratis/libre oopbron sagteware.
+Alle bestuurskostes, ontwikkeling en inhoud word heeltemal gefinansier deur lede bydraes.
+ Afrigter bestuurder
+ Uitsender bestuurder
+ Kanseleer die toernooi
+ Toernooi beskrywing
+ Enige iets spesiaal wat jy aan die deelnemers wil vertel? Probeer dit kort hou. \"Markdown\" skakels is beskikbaar: [name](https://url)
+ Spelle is gegradeer
+en beïnvloed speler graderings
+ Slegs lede van span
+ Geen beperking
+ Minimum gegradeerde spelle
+ Minimum gradering
+ Maksimum weeklikse gradering
+ Slegs getitelde spelers
+ Benodig \'n amptelike titel om by die toernooi aan te sluit
+ Plak \'n geldige FEN om elke spel van \'n sekere posisie te begin.
+Dit werk slegs met standaard spelle, nie met variante nie.
+Jy kan die %s gebruik om \'n FEN posisie te genereer, en dit dan hier plak.
+Laat dit leeg om elke spel vanaf die normale posisie te begin.
+ Kanseleer die simul
+ Gasheer kleur vir elke spel
+ Verwagte begin tyd
+ Vertoon op %s
+ Wys jou simul aan almal op %s. Deaktiveer vir private simuls.
+ Simul beskrywing
+ Enige iets wat jy vir die deelnemers wil vertel?
+ %s is beskikbaar vir meer ingewikkelde sintaks.
+ Plak \'n spel URL of a studie hoofstuk URL om dit in te bed.
+ In you eie plaaslike tydsone
+ Toernooi klets
+ Geen klets
+ Slegs spanleiers
+ Slegs spanlede
+ Speel die beste rekenaarskuif
diff --git a/translation/dest/site/be-BY.xml b/translation/dest/site/be-BY.xml
index 6d35a47d61..1c42e6686f 100644
--- a/translation/dest/site/be-BY.xml
+++ b/translation/dest/site/be-BY.xml
@@ -551,7 +551,7 @@
Махінацыі з рэйтынгам
Іншае
Пакіньце ніжэй спасылку на гульню (ці гульні) і патлумачце, што вас непакоіць у паводзінах гэтага карыстальніка. Не пішыце нешта кшталту «ён чмут!» – патлумачце, як вы прыйшлі да гэтага выніку. Мы хутчэй разбярэмся ў сітуацыі, калі вы напішаце нам па-англійску.
- Калі ласка, дадайце спасылку сама менш на адну гульню, дзе, на вашу думку, былі парушаны правілы.
+ Калі ласка, дадайце спасылку хаця б на адну гульню, дзе былі парушаны правілы.
аўтар: %s
Тэма закрытая
Блог
@@ -757,7 +757,7 @@
Хуткі старт
Лобі
Ананімны гулец
- Ваш вынік: %s
+ Ваш лік: %s
Мова
Фон
Светлы
@@ -856,8 +856,8 @@
Мы просім прабачэння за часовыя нязручнасці,
і жадаем вам вельмі добрых гульняў на lichess.org.
Дзякуй за ўвагу!
- Рахунак за ўсё время
- Рахунак у бягучым матчы
+ Лік за ўвесь час
+ Лік бягучага матчу
Я згаджаюся, што я ніколі не скарыстаюся старонняй дапамогай у сваіх гульнях (ад шахматных праграм, кніг, баз даных або іншых гульцоў).
Я згаджаюся, што я заўсёды адносіцца з павагай да другіх гульцоў.
Я згаджаюся, што я не буду ствараць некалькі ўліковых запісаў.
diff --git a/translation/dest/site/bn-BD.xml b/translation/dest/site/bn-BD.xml
index 624697ab0d..13654c45e2 100644
--- a/translation/dest/site/bn-BD.xml
+++ b/translation/dest/site/bn-BD.xml
@@ -47,9 +47,12 @@
কালো হার মেনে নিলেন
সাদা খেলাটি ছেড়ে চলে গেছেন
কালো খেলাটি ছেড়ে চলে গেছেন
+ সাদা চাল দিল না
+ কালো চাল দিল না
কম্পিউটার বিশ্লেষণের জন্য অবেদন করুন
কম্পিউটারের বিশ্লেষণ
কম্পিউটার বিশ্লেষণ পাওয়া যায়
+ কম্পিউটার বিশ্লেষণ নিষ্ক্রিয় করা আছে
বিশ্লেষণ বোর্ড
গভীরতা %s
সার্ভারের বিশ্লেষণ ব্যবহার করা হচ্ছে
@@ -153,7 +156,7 @@
খেলোয়াড়
বন্ধুরা
বার্তাগুলি
- আজ
+ আজ
গতকাল
প্রতিটি পাশের জন্য বরাদ্দ মিনিট
বিকল্প
@@ -360,6 +363,8 @@
অনুসরণ
অনুসরণ করছেন
আর অনুসরণ নয়
+ %sকে অননুসরণ করুন
+ %sকে অননুসরণ করুন
বাধা দিন
বাধাগ্রস্ত
বাধা উঠিয়ে নিন
@@ -422,9 +427,9 @@
বোর্ড সম্পাদক
বোর্ড সেট করুন
জনপ্রিয় চমক
- শুরুর অবস্থান
+ শুরুর অবস্থান
বোর্ড খালি করুন
- অবস্থান পুনঃস্থাপন
+ অবস্থান পুনঃস্থাপন
ব্যক্তিগত
মডারেটরদের কাছে %s এর নামে রিপোর্ট করুন
প্রোফাইল সম্পন্ন: %s
@@ -443,7 +448,7 @@
এর আগে Lichess TV- তে যা হয়েছে
যে সমস্ত খেলোয়ার অনলাইন আছে
সক্রিয় খেলোয়াড়গণ
- সাবধান, এই খেলাটি মূল্যায়িত তবে কোনো সময়সীমা নেই!
+ সাবধান, এই খেলাটি মূল্যায়িত তবে কোনো সময়সীমা নেই!
সফল্য হয়েছে
- %s খেলা চলছে
@@ -512,7 +517,7 @@
খেলায় ধীর গতি
সব সময়
কখনই না
- %1$s করা আছে মধ্যে %2$s
+ %1$s করা আছে মধ্যে %2$s
বিজয়
হার
%1$s বানাম %2$s মধ্যে %3$s
@@ -543,6 +548,8 @@
সরঞ্জামসমূহ
মুনাফা
এই ঘরটি পূরন করা আবশ্যক
+ ইমেইল ঠিকানাটি বৈধ নয়
+ ইমেইল ঠিকানাটি গ্রহণযোগ্য নয়। দয়া করে পুনরায় চেক করুন, অতঃপর চেষ্টা করুন।
যদি অনুপাত হয় ± %s
বিদ্যমান কথোপকথন
শুধু বন্ধুরা
@@ -790,7 +797,7 @@
আপনি এখন ফোরামে পোষ্ট করতে পারবেন না. আগে কিছু গেম খেলুন!
সাবস্ক্রাইব
আনসাবস্ক্রাইব
- \"%1$s\" এ আপনাকে উল্লেখ করেছেন।
+ \"%1$s\" এ আপনাকে উল্লেখ করেছেন।
%1$s , \"%2$s\" এ আপনার নাম উল্লেখ করেছেন।
\"%1$s\" এ আপনাকে আমন্ত্রণ জানিয়েছেন।
%1$s, আপনাকে \"%2$s\" এ আমন্ত্রণ জানিয়েছেন।
diff --git a/translation/dest/site/br-FR.xml b/translation/dest/site/br-FR.xml
index 862737901b..9968e1cad7 100644
--- a/translation/dest/site/br-FR.xml
+++ b/translation/dest/site/br-FR.xml
@@ -159,7 +159,7 @@
C\'hoarierien
Mignoned
Kaozeadennoù
- Hiziv
+ Hiziv
Dec\'h
Munutennoù evit pep c\'hoarier
Doare
@@ -494,9 +494,9 @@
Kargit ul lec\'hiadur
Digoradurioù brudet
Plegennoù an dibenn-partienn
- Plegenn orin
+ Plegenn orin
Tennañ pep tra
- Kargañ ul lec\'hiadur
+ Kargañ ul lec\'hiadur
Prevez
Diskleriañ %s d\'an habaskaerien
Feur leuniañ ar profil: %s
@@ -514,7 +514,7 @@
N\'eus ket pell zo war Lichess TV
C\'hoarierien enlinenn
C\'hoarierien oberiat
- Diwallit, renket eo ar c\'hrogad met n\'eus horolaj ebet!
+ Diwallit, renket eo ar c\'hrogad met n\'eus horolaj ebet!
Deuet oc\'h a-benn
- %s c\'hrogad o ren bremañ
@@ -582,7 +582,7 @@
E-pad ar c\'hrogadoù difonn
Bepred
Morse
- %1$s a gemer perzh e-barzh %2$s
+ %1$s a gemer perzh e-barzh %2$s
Trec\'h
Lamm
%1$s a-enep %2$s e %3$s
diff --git a/translation/dest/site/ckb-IR.xml b/translation/dest/site/ckb-IR.xml
index 81efc5518a..fbaceff27b 100644
--- a/translation/dest/site/ckb-IR.xml
+++ b/translation/dest/site/ckb-IR.xml
@@ -7,6 +7,7 @@
چاوەڕوانی لایەینی بەرامبەر
چاوەڕوانی ڕازیبوونی لایەنی بەرامبەر
سەرەی تۆیە
+ %1$s ئاست %2$s
پلە
هێز
چەسپاندنی چاتەکە
@@ -23,6 +24,7 @@
یاری بە پارچە سپیەکان دەکەیت
یاری بە پارچە ڕەشەکان دەکەیت
سەرەی تۆیە!
+ دەرکەوت کە فێڵ کراوە
پاشا لە ناوەڕاست
سێ چێککردن (هێرش کردنە سەر پاشای بەرامبەر)
کۆتایی هات
@@ -41,20 +43,28 @@
رەش خۆی بەدەستەوەدا
سپی وازیهێنا لە یاریکردن
ڕەش وازیهێنا لە یاریکردن
+ سپی جووڵەی نەکرد
+ ڕەش جووڵەی نەکرد
داواکردنی شیکاری کۆمپیوتەری
شیکاری کۆمپیوتەری
شیکاری کۆمپیوتەری بەردەستە
+ شیکاری کۆمپیوتەری ناچاڵاک کرا
شیکاری تەختەی شەترەنج
قوڵایی%s
بەکارھێنانی شیکاری ڕاژەکاری
بزوێنەرەکە لە باری هەڵگرتندایه...
+ شیکاری ئۆنڵاین
زیاتر
پیشاندانی مەترسی
له شوێنی گەڕانی ناوخۆیی
+ هەڵسەنگاندنی خۆجێیی بگۆڕە
+ برەوپێدانی جۆراوجۆر
کردن بە رستەی سەرەکی
سڕینەوە
+ هێزی جۆراوجۆر
جوڵان
دۆرانی ناچاری
+ براوەی هەمەجۆری
یەکسانبوون بەهۆی نەمانی جوڵە بۆ کش
جوڵەی سەرباز
گرتن
@@ -68,16 +78,56 @@
پۆلێنکردنی تێکرا: %s
دوایین یاریەکانت
بەرزترین یاریەکان
+ دوو ملیۆن یاری سەر بۆرد لە لایەن %1$s+ FIDE-یاریزانانی هەڵسەنگنراو، کە خەڵکی %2$s بۆ %3$s
+ DTZ50\'\' لەگەڵ نزیکخستنەوەیان، بە پێی ژمارەی نیوەی جووڵەکان تا زاڵبوونێکی دیکە یان جوڵەی سەرباز
هیچ یارییەک نەدۆزرایەوە
ئایا دەتەویت جۆری تر دیاری بکەیت?
+ دۆزەرەوەی کردنەوەکان
+ دۆزەرەوەی کردنەوەکان\کۆتاییەکانی یاری
+ %s دۆزەرەوەی کردنەوەکان
+ براوە دیاری ناکرێت بە یاسای-50 جووڵە
+ دۆڕاو دیاری ناکرێت بە یاسای-50 جووڵە
+ براوە یان 50 جووڵە بەهۆی هەڵەی پێشوو
+ دۆڕاو یان 50 جووڵە بەهۆی هەڵەی پێشوو
+ براوە\دۆڕاو کاتێک دەستەبەر دەکرێت کە ئەوەی لە لایەن خشتەی سەر تەختەوە پێشنیار دەکرێت جێبەجێبکرێت، ئەوەش لە دواین زاڵبوون یان جوڵەی سەرباز دەێت، لەبەر ئەگەری نزیکخستنەوەی نرخی DTZ لە خشتەی سەر تەختای Syzygy.
ئامادەیە!
سکرین شوت PGN
سڕینەوە
دڵنیای کە دەتەوێت بیسڕیەوە?
سەیرکردنەوەی یاریەکە
وەک خۆی
+ بەهۆی CPL
+ خوێندنی کراوە
+ چالاککراوە
+ باشترین جووڵە بە تیر دیار بکە
+ پێوانەی هەڵسەنگاندن
+ زیادکردنی هێڵەکان
+ CPUs
+ Memory
+ شیکاریکردنی بێ کۆتا
+ لابردنی دورترین سنوور، بەمەش کۆمپیوتەرەکەت سەلامەتتر دەبێت
+ هەڵەی گەورە
+ هەڵە
+ ناتەواو
+
+ - %s هەڵەی گەورە
+ - %s هەڵە گەورەکان
+
+
+ - %s هەڵە
+ - %s هەڵەکان
+
+
+ - %s نا تەواو
+ - %s ناتەواوەکان
+
+ ژمارەی جوڵەکان
+ هەڵگێڕانەوەی تەختە
+ سێبارە کردنەوە
+ داوای یەکسانی بکە
پێشکەشکردنی یەکسانبوون
یەکسان
+ بە رەزاماندی هەردولا یەکسان بوون
دەرچوون
چوونە ناوە
diff --git a/translation/dest/site/de-CH.xml b/translation/dest/site/de-CH.xml
index d9a84d03f6..d0a5f6ac7b 100644
--- a/translation/dest/site/de-CH.xml
+++ b/translation/dest/site/de-CH.xml
@@ -87,7 +87,7 @@
- Matt i %s Halbzug
- Matt i %s Halbzüg
- DTZ50\'\' mit Rundung, basierend auf der Anzahl der Halbzüge bis zum nächsten Schlag- oder Bauernzug
+ DTZ50\'\' mit Runde, basierend uf dä Ahzahl Halbzüüg bis zum nächstä Schlag- oder Puurezug
Käs Schpiil gfundä
Villicht söted meh Schpiil vom Iischteligs Menu iibezogä werdä?
Eröffnigsdatäbank
@@ -95,9 +95,9 @@
%s Eröffnigsbuäch
Sieg dur d 50-Züg-Reglä verhinderet
Verluscht dur d 50-Züg-Reglä verhinderät
- Sieg oder 50 Züge durch vorherigen Fehler
+ Siig odär 50 Züüg dur en vorherigä Fählär
Verlust odär 50 Züüg dur en vorherigä Fählär
- Gewinn/Verlust ist nur garantiert, falls die empfohlene Datenbank-Zugfolge seit dem letzten Schlag- oder Bauernzug befolgt wurde, da möglicherweise DTZ-Werte in den Syzygy-Datenbanken gerundet sind.
+ Gwünn/Verlust isch nur garantiert, falls diä empfohlänä Datebank-Zugfolge sit äm letztä Schlag- oder Puurezug befolgt worde sind, da möglicherwiis DTZ-Wärt ih de Syzygy-Datebank grundet sind.
Färtig!
PGN importierä
Löschä
@@ -163,7 +163,7 @@
Schpiller/-innä
Fründä
Konvärsazion
- Hüt
+ Hüt
Geschter
Minutä pro Schpiller/-in
Variantä
@@ -435,9 +435,9 @@
Beliäbti Eröffnigä
Ändschpiilpositionä
Fischer Random Aafangsschtellig: %s
- Aafangsposition
+ Aafangsposition
Brätt löschä
- Schtellig ladä
+ Schtellig ladä
Privaat
Mäld %s dä Moderatorä
Profil-Vollschtändigkeit: %s
@@ -456,7 +456,7 @@
Zletscht uf Lichess TV
Schpiller/-innä online
Aktivi Schpiller/-innä
- Obacht, das Schpiil isch gwärtät, hät aber keis Ziitlimit!
+ Obacht, das Schpiil isch gwärtät, hät aber keis Ziitlimit!
Jawoll
- %s laufändi Partii
@@ -525,7 +525,7 @@
Bi langsamä Partiiä
Schtändig
Niä
- %1$s nimmt bi %2$s teil
+ %1$s nimmt bi %2$s teil
Siig
Niderlag
%1$s gäg %2$s in %3$s
@@ -556,14 +556,14 @@
Wärchzüüg
Inkremänt
Das Fäld isch Pflicht
- Diese E-Mail-Adresse ist ungültig
- Diese E-Mail-Adresse ist nicht akzeptabel. Bitte überprüfe sie und versuche es erneut.
- E-Mail-Adresse ungültig oder bereits vergeben
- Dies ist bereits deine E-Mail Adresse
- Mindestlänge ist %s
- Maximallänge ist %s
- Muss mindestens %s sein
- Darf höchstens %s sein
+ Diä E-Mail-Adrässä isch ungültig
+ Diä E-Mail-Adrässe wird nöd akzeptiärt. Bitte überprüäf si und probiär dänn nomol.
+ Diä E-Mail-Adrässe isch invalid oder scho vergäh
+ Das isch scho dini E-Mail-Adrässä
+ Mues mindestens %s Zeichä lang sii
+ Mues höchstens %s Zeichä lang sii
+ Mues mindestens %s sii
+ Mues höchstens %s sii
Nur, wänn d\'Wärtigszahl ± %s isch
Nur bestehändi Konversazionä
Nur Fründä
@@ -812,7 +812,7 @@
Du chasch nonig idä Forä schriibä. Schpill zersch äs paar Partiiä!
Abonniärä
Deabonniärä
- hät dich in \"%1$s\" ärwähnt.
+ hät dich in \"%1$s\" ärwähnt.
%1$s hät dich in \"%2$s\" ärwähnt.
hät dich zu \"%1$s\" iigladä.
%1$s hät dich zu \"%2$s\" iigladä.
diff --git a/translation/dest/site/de-DE.xml b/translation/dest/site/de-DE.xml
index b107ff47d2..1b9dd78921 100644
--- a/translation/dest/site/de-DE.xml
+++ b/translation/dest/site/de-DE.xml
@@ -59,7 +59,7 @@
Engine wird geladen ...
Cloud-Analyse
Tiefe erhöhen
- Bedrohungen zeigen
+ Drohungen anzeigen
im lokalen Browser
Lokale Auswertung an-/ausschalten
Variante aufwerten
@@ -650,7 +650,7 @@
Importiertes Spiel herunterladen
Matchverlauf
Du kannst auch auf dem Brett scrollen, um die Partie durchzugehen.
- Fahre über Computervarianten, um eine Vorschau auf sie zu erhalten.
+ Bewege den Mauszeiger über Computervarianten, um eine Vorschau zu erhalten.
Drücke Shift+Mausklick oder Rechtsklick, um Kreise und Pfeile auf dem Brett zu zeichnen.
Erlaube anderen Spielern dich zu kontaktieren
Benachrichtigungen erhalten, wenn du im Forum erwähnt wirst
@@ -819,7 +819,7 @@
Du bist nun Teammitglied.
Du bist \"%1$s\" beigetreten.
Jemand, den du gemeldet hast, wurde gebannt
- Herzlichen Glückwunsch, du hast gewonnen!
+ Glückwunsch, du hast gewonnen!
Partie gegen %1$s
%1$s gegen %2$s
Jemand hat dein Trainerprofil bewertet.
diff --git a/translation/dest/site/en-US.xml b/translation/dest/site/en-US.xml
index 4881239dfc..08b4390a76 100644
--- a/translation/dest/site/en-US.xml
+++ b/translation/dest/site/en-US.xml
@@ -163,7 +163,7 @@
Players
Friends
Conversations
- Today
+ Today
Yesterday
Minutes per side
Variant
@@ -436,9 +436,9 @@ computer analysis, game chat and shareable URL.
Popular openings
Endgame positions
Chess960 start position: %s
- Starting position
+ Starting position
Clear board
- Load position
+ Load position
Private
Report %s to moderators
Profile completion: %s
@@ -457,7 +457,7 @@ computer analysis, game chat and shareable URL.
Previously on Lichess TV
Online players
Active players
- Beware, the game is rated but has no clock!
+ Beware, the game is rated but has no clock!
Success
- %s game in play
@@ -526,7 +526,7 @@ computer analysis, game chat and shareable URL.
On slow games
Always
Never
- %1$s competes in %2$s
+ %1$s competes in %2$s
Victory
Defeat
%1$s vs %2$s in %3$s
@@ -561,10 +561,10 @@ computer analysis, game chat and shareable URL.
This email address is not acceptable. Please double-check it, and try again.
Email address invalid or already taken
This is already your email address
- Must be at least %s characters long
+ Must be at least %s characters long
Must be at most %s characters long
Must be greater or equal to %s
- Must be at most %s
+ Must be at most %s
If rating is ± %s
Only existing conversations
Only friends
@@ -813,7 +813,7 @@ computer analysis, game chat and shareable URL.
You can\'t post in the forums yet. Play some games!
Subscribe
Unsubscribe
- mentioned you in \"%1$s\".
+ mentioned you in \"%1$s\".
%1$s mentioned you in \"%2$s\".
invited you to \"%1$s\".
%1$s invited you to \"%2$s\".
diff --git a/translation/dest/site/fi-FI.xml b/translation/dest/site/fi-FI.xml
index aeab410e62..b27448cb0d 100644
--- a/translation/dest/site/fi-FI.xml
+++ b/translation/dest/site/fi-FI.xml
@@ -794,8 +794,8 @@
Klassinen
Järjettömän nopeat pelit: alle 30 sekuntia
Hyvin nopeat pelit: alle 3 minuuttia
- Nopeat pelit: 3-8 minuuttia
- Pikapelit: 8-25 minuuttia
+ Pikapelit: 8-25 minuuttia
+ Nopeat pelit: 8-25 minuuttia
Klassiset pelit: 25 minuuttia tai enemmän
Kirjeshakkipelit: yksi tai useampi päivä per siirto
Taktiikkaharjoittelu
diff --git a/translation/dest/site/gu-IN.xml b/translation/dest/site/gu-IN.xml
index fbd4b8990f..71faf0bd49 100644
--- a/translation/dest/site/gu-IN.xml
+++ b/translation/dest/site/gu-IN.xml
@@ -24,6 +24,9 @@
તમે સફેદ ટુકડાઓ સાથે રમસો
તમે કાળા ટુકડાઓ સાથે રમસો
તમારો વારો!
+ આ રમત સમાપ્તિની સ્થિતિ છે, જો રમત ચાલુ હોય ત્યારે કોઈ ખેલાડી છેતરપિંડી કરે છે. રમત તરત જ સમાપ્ત થાય છે.
+
+ અનુવાદ નિષ્ક્રિય અવાજમાં હોવો જોઈએ કારણ કે ખેલાડીઓ અને દર્શકો બંને તેને જુએ છે. કૃપા કરીને શક્ય તેટલું ટૂંકું રાખો.
મધ્યમાં રાજા
ત્રણ ચેક
રેસ પુરી થઇ ગઇ
@@ -86,12 +89,17 @@
- મેટ %s ચાલ માં
- મેટ %s ચાલ માં
+ DTZ50\'\' રાઉન્ડિંગ સાથે, આગામી કેપ્ચર અથવા પ્યાદાની ચાલ સુધી અર્ધ-ચાલની સંખ્યાના આધારે
ડેટાબેઝમાં આવી કોઈ રમત નથી
પસંદગી મેનૂમાંથી વધુ રમતો શામેલ કરીને જોવો?
રમત પ્રારંભ સંશોધક
+ ઓપનિંગ/એન્ડગેમ એક્સપ્લોરર
%s રમત પ્રારંભ સંશોધક
જીતને 50-ચાલના નિયમ દ્વારા અટકાવવામાં આવી છે
50-ચાલના નિયમ દ્વારા હાર અટકાવવામાં આવી છે
+ અગાઉની ભૂલથી જીત અથવા 50 ચાલ
+ અગાઉની ભૂલથી નુકશાન અથવા 50 ચાલ
+ Syzygy ટેબલબેસેસમાં DTZ મૂલ્યોના સંભવિત રાઉન્ડિંગને કારણે, છેલ્લી કેપ્ચર અથવા પ્યાદાની ચાલથી ભલામણ કરેલ ટેબલબેઝ લાઇનને અનુસરવામાં આવે તો જ જીત/હારની ખાતરી આપવામાં આવે છે.
બધું સેટ છે!
PGN આયાત કરો
કાઢી નાખો
@@ -125,6 +133,8 @@
ડ્રો માગણું
ડ્રો પ્રસ્તાવો
ડ્રો
+ પરસ્પર કરાર દ્વારા દોરો
+ પ્રગતિ વિના પચાસ ચાલ
વર્તમાન રમતો
- %s રમત
@@ -185,6 +195,7 @@
રદ કરો
સફેદની સમય મર્યાદા પૂરી
કાળાની સમય મર્યાદા પૂરી
+ ડ્રો ઓફર મોકલી
સ્વીકારો
નકારો
અત્યારે રમાય છે
diff --git a/translation/dest/site/hi-IN.xml b/translation/dest/site/hi-IN.xml
index 79ba584588..7e2d03a8be 100644
--- a/translation/dest/site/hi-IN.xml
+++ b/translation/dest/site/hi-IN.xml
@@ -13,7 +13,7 @@
चैट को टोग्ल करें
बातचीत
हार मान लें
- शाहमात
+ शह और मात
गतिरोध (स्टेलमेट )
सफेद
काला
@@ -94,6 +94,8 @@
%s ओपनिंग एक्सप्लोरर
जीत 50-चालन नियम से रोका गया
हार 50-चाल नियम द्वारा बचाया
+ जीत अथ्वा 50 चाल नियम पुरानी गलती के कारण।
+ हार अथ्वा 50 चाल नियम पुरानी गलती के कारण।
सब तैयार!
PGN आयात करें
हटाएँ
@@ -470,7 +472,7 @@
हाँ
सहायता
एक नया विषय बनाएँ
- विषयों की संख्या
+ विषय
पोस्ट की संख्या
आख़िरी पोस्ट
देखे जाने की संख्या
@@ -561,6 +563,8 @@
यह पहले से ही आपका ईमेल पता है
कम से कम %s वर्ण का शब्द होना चाहिए।
कम से कम %s वर्ण का शब्द होना चाहिए।
+ कम से कम %s
+ %s से ज़्यादा नही।
अगर स्तर %s ऊपर निचे हो तब
केवल मौजूदा वार्तालाप
केवल दोस्त
@@ -854,6 +858,7 @@
समकालिक खेल का विवरण
क्या आप प्रतिभागियों को कुछ बताना चाहते हैं?
%s अत्याधुनिक स्वरूपण के लिए उपलब्ध है।
+ खेल अथवा अध्ययन का URL paste करे एमबेड करने के लिये|
आपके अपने क्षेत्रीय समयानुसार
प्रतियोगिता वार्ता
वार्ता निषेध
@@ -874,5 +879,9 @@
खाते से संबंधित ईमेल पता
हमने आपको लिंक के साथ एक ईमेल भेजा है।
प्रतियोगिता प्रवेश कोड
+ प्रतिक्षा कीजीए।
+ %s के साथ आपका खेल समाप्त नही हुआ है।
+ खेल रद्द करें
+ हार मान ले
जब तक यह गेम समाप्त नहीं हो जाता तब तक आप एक नया गेम शुरू नहीं कर सकते।
diff --git a/translation/dest/site/hr-HR.xml b/translation/dest/site/hr-HR.xml
index 954008eab4..8aa4292961 100644
--- a/translation/dest/site/hr-HR.xml
+++ b/translation/dest/site/hr-HR.xml
@@ -865,6 +865,8 @@ računalnu analizu, chat partije i URL za dijeljenje.
[Klikni za prikaz e-mail adrese]
Preuzmi
Dobrodošli!
+ Postavke za trenera
+ Postavke za strimera
Otkaži turnir
Opis turnira
Samo za članove tima
diff --git a/translation/dest/site/hy-AM.xml b/translation/dest/site/hy-AM.xml
index fc1ca4ab05..76df9da0b0 100644
--- a/translation/dest/site/hy-AM.xml
+++ b/translation/dest/site/hy-AM.xml
@@ -158,7 +158,7 @@
Խաղացողներ
Ընկերներ
Հաղորդագրություններ
- Այսօր
+ Այսօր
Երեկ
Րոպեներ ամեն կողմի համար
Տարբերակ
@@ -318,6 +318,8 @@
- Դուք պետք է խաղաք ևս %s վարկանիշային խաղ
Ձեր վարկանիշը %s-ում դեռևս վավեր չէ
+ Ձեր ամենշաբաթյա վարկանիշը %1$sում (%2$s) չափազանց բարձր է
+ Ձեր վարկանիշը %1$sին (%2$s) մասնակցելու համար բավարար չէ
Վարկանիշը ≥ %1$s %2$s-ում
Վարկանիշը ≤ %1$s %2$s-ում
Դուք պետք է լինեք %s ակումբի անդամ
@@ -426,9 +428,9 @@
Հանրաճանաչ սկզբնախաղեր
Վերջնախաղային դիրքեր
Շախմատ 960-ի սկզբնական դիրք՝ %s
- Սկզբնական դիրք
+ Սկզբնական դիրք
Մաքրել տախտակը
- Բեռնել դիրք
+ Բեռնել դիրք
Անձնական
Տեղեկացնել %s մոդերատորներին
Մասնակցային էջը լրացված է %s-ով
@@ -447,7 +449,7 @@
Նախորդները Lichess TV-ով
Առցանց խաղացողներ
Ակտիվ խաղացողներ
- Զգուշացում. խաղը վարկանիշային է, սակայն առանց ժամանակի
+ Զգուշացում. խաղը վարկանիշային է, սակայն առանց ժամանակի
Հաջողված է
- Ընթանում է %s խաղ
@@ -512,7 +514,7 @@
Դանդաղ խաղերում
Միշտ
Երբեք
- %1$s որոշել է մասնակցել %2$s-ին
+ %1$s որոշել է մասնակցել %2$s-ին
Հաղթանակ
Պարտություն
%1$s-ն ընդդեմ %2$s-ի %3$s-ում
diff --git a/translation/dest/site/id-ID.xml b/translation/dest/site/id-ID.xml
index 467521531a..6bbf41a333 100644
--- a/translation/dest/site/id-ID.xml
+++ b/translation/dest/site/id-ID.xml
@@ -152,7 +152,7 @@
Pemain
Teman
Diskusi
- Hari ini
+ Hari ini
Kemarin
Menit untuk tiap pihak
Variasi permainan
@@ -402,9 +402,9 @@
Pembuka populer
Posisi akhir permainan
Posisi awal Catur960: %s
- Posisi Awal
+ Posisi Awal
Bersihkan papan
- Panggil posisi
+ Panggil posisi
Pribadi
Lapor %s ke moderator
Kelengkapan profil %s
@@ -423,7 +423,7 @@
Sebelumnya di Lichess TV
Pemain online
Pemain aktif
- Awas, permainan ini pake rating namun tanpa pake waktu!
+ Awas, permainan ini pake rating namun tanpa pake waktu!
Berhasil
- %s pertandingan sedang berlangsung
@@ -491,7 +491,7 @@
Pada catur klasik
Selalu
Tidak pernah
- %1$s berkompetisi dalam %2$s
+ %1$s berkompetisi dalam %2$s
Berhasil
Kalah
%1$s melawan %2$s dalam permainan %3$s
@@ -524,10 +524,10 @@
Alamat e-mail ini tidak dapat diterma. Pastikan kembali e-mail Anda, lalu coba lagi.
Alamat e-mail tidak valid atau sudah dipakai
Ini sudah menjadi alamat e-mail Anda
- Panjang minimum adalah %s
- Panjang maksimum adalah %s
- Harus lebih besar atau sama dengan %s
- Harus kurang atau sama dengan %s
+ Panjang minimum adalah %s
+ Panjang maksimum adalah %s
+ Harus lebih besar atau sama dengan %s
+ Harus kurang atau sama dengan %s
Jika ratingnya ± %s
Khusus percakapan yang ada
Hanya teman
@@ -611,7 +611,7 @@
Unduh mentah
Unduh permainan yang telah dimasukkan
Napak tilas
- Anda juga bisa menggunakan tombol gulung untuk bergerak dalam permainan
+ Anda juga bisa menggunakan scroll untuk bergerak dalam permainan.
Letakkan mouse diatas variasi komputer untuk meninjau hal tersebut.
Tekan shift+klik atau klik-kanan untuk menggambar lingkaran dan panah di papan.
Memperbolehkan pemain lain mengirim pesan untuk Anda
@@ -647,7 +647,7 @@
%1$s tuan rumah %2$s
%1$s bergabung %2$s
%1$s suka %2$s
- Pencarian lawan dengan cepat
+ Pencarian cepat
Ruang Lobi
Tanpa Nama
Skor Anda: %s
@@ -770,7 +770,7 @@
Anda belum bisa mengirim pos di forum ini. Mainkan beberapa permainan!
Berlangganan
Berhenti berlangganan
- membahas anda di \"%1$s\".
+ membahas anda di \"%1$s\".
%1$s membahas anda di \"%2$s\".
mengundang anda ke \"%1$s\".
%1$s mengundang ke \"%2$s\".
diff --git a/translation/dest/site/kk-KZ.xml b/translation/dest/site/kk-KZ.xml
index 002a749a27..063f69bec9 100644
--- a/translation/dest/site/kk-KZ.xml
+++ b/translation/dest/site/kk-KZ.xml
@@ -231,9 +231,9 @@
Классикалық
Шектеусіз
Түрі
- Қалыпты
+ Жай
Бағаланатын
- Қалыпты
+ Кәдімгі
Бағаланатын
Бұл ойын бағаланады
Қайта ойнау
@@ -368,6 +368,8 @@
Серік болу
Серіксіз
Серік болмау
+ %s серік болу
+ %s серік болмау
Бұғаттау
Бұғатталған
Бұғаттан шығару
@@ -515,7 +517,7 @@
Дыбыстар
Жоқ
Тез
- Қалыпты
+ Орташа
Баяу
Тақта бетінде
Тақта сыртында
@@ -553,6 +555,14 @@
Құралдар
Қосылғыш
Бұл жолды толтырыңыз
+ Бұл пошта мекенжайы қате
+ Бұл пошта мекенжайы қабылданбайды. Оны тексеріп, қайта көріңіз.
+ Пошта мекенжайы қате немесе бос емес
+ Бұл сіздің бұрыңғы поштаңыз
+ Кемінде %s таңба болу керек
+ Ең көбі %s таңба болу керек
+ Кемінде %s болу керек
+ Ең көбі %s болу керек
Егер рейтингі ± %s болса
Тек бар әңгімелесуден
Достар ғана
@@ -591,10 +601,10 @@
Фишер 50 қарсыластың 47-сін жеңіп, 2-мен тең ойнап, 1-нен жеңілді.
Бұл түсінік шынайы өмірден алынған: ойында бас ойыншы болады, ол тақталарды аралап, бір-бір жүрістен жасап отырады.
Көпшілікке қарсы ойын басталысымен, әр ойыншы ақ тастар иесі – бас ойыншымен жарысады. Барлық ішкі ойындар аяқталғанда, жалпы ойын да аяқталады.
- Көпшілікке қарсы ойын – әрқашанда қалыпты. Қайта ойнау, жүрісті қайтару, уақыт қосу дегендер болмайды.
+ Көпшілікке қарсы ойын – әрқашанда жай ойын. Қайта ойнау, жүрісті қайтару, уақыт қосу дегендер болмайды.
Құру
Сіз көпшілікке қарсы ойынды құрғанда, бір мезгілде бірнеше қарсыласпен ойнайсыз.
- Егер сіз бірнеше шахмат түрін таңдасаңыз, әр ойыншы соның арасынан өзіне лайық біреуін таңдайды.
+ Егер сіз бірнеше шахмат түрін таңдасаңыз, әр ойыншы соның арасынан біреуін таңдайды.
Фишер Сағатын баптау. Қарсыластарыңыз көп болса, уақыт та көп қажет.
Көпшілікке қарсы ойынды жеңілдету үшін өзіңізге қосымша уақыт қоса аласыз.
Бас ойыншыға қосымша уақыт
@@ -700,7 +710,7 @@
Бапкерлер
Қате PGN
Қате FEN
- Қалыпты
+ Басқа
Хабарлама
Рейтинг: %s
@@ -740,7 +750,7 @@
Қайта жасау
Ақтардың қателерін қарау
Қаралардың қателерін қарау
- Артықшылық
+ Басымдылық
- %s секунд
- %s секунд
@@ -782,10 +792,10 @@
Рапид
Классикалық
Лезде ойындар: 30 секундтан кем
- Аса жылдам ойындар: 3 минуттан кем
+ Аса жылдам ойындар: 3 минуттан аз
Жылдам ойындар: 3-тен 8 минутке дейін
Рапид ойындары: 8-ден 25 минутке дейін
- Классикалық ойындар: 25 минут және одан артық
+ Классикалық ойындар: 25 минут әрі одан көп
Хат алмасып ойнау: бір жүріске бір не бірнеше күн
Шахмат тактикасын үйретуші
Ескерту
@@ -836,8 +846,8 @@
Тек атаққа ие ойыншылар
Жарысқа қосу үшін ресми атағын талап ету
Әр ойынды нақты бір күйден бастау үшін FEN еңгізіңіз.
-Бұл тек қалыпты ойындарда ғана жұмыс істейді, басқа шахмат түрлерінде емес.
-Күйдің FEN-ін құру үшін %s қолданыңыз, кейін осында қойыңыз.
+Бұл тек классикалық ойындарда ғана жұмыс істейді, басқа шахмат түрлерінде емес.
+Күйдің FEN-ін құру үшін %s қолданыңыз, кейін осында еңгізіңіз.
Ойынды кәдімгі бастапқы күйден бастау үшін бос қалдыра салыңыз.
Көпшілікке қарсы ойынды болдырмау
Әрқашан бас ойыншы түсінен тәуелді
diff --git a/translation/dest/site/lb-LU.xml b/translation/dest/site/lb-LU.xml
index 490d405d06..5cd52f8746 100644
--- a/translation/dest/site/lb-LU.xml
+++ b/translation/dest/site/lb-LU.xml
@@ -163,7 +163,7 @@
Spiller
Frënn
Konversatiounen
- Haut
+ Haut
Gëschter
Minutten pro Säit
Variant
@@ -435,9 +435,9 @@
Beléift Eröffnungen
Endspill Positiounen
Chess960 Start Positioun: %s
- Start Positioun
+ Start Positioun
Briet opraumen
- Positioun lueden
+ Positioun lueden
Privat
%s den Moderatoren mellen
Profil vollstänneg zu: %s
@@ -456,7 +456,7 @@
Virdrun op Lichess TV
Online Spiller
Aktiv Spiller
- Pass op, d\'Partie ass gewäert mee huet keng Auer!
+ Pass op, d\'Partie ass gewäert mee huet keng Auer!
Succès
- %s Partie amgaang
@@ -525,7 +525,7 @@
An luesen Partien
Ëmmer
Nie
- %1$s hëlt deel bei %2$s
+ %1$s hëlt deel bei %2$s
Victoire
Defaite
%1$s vs %2$s an %3$s
@@ -550,7 +550,7 @@
Auer
Géigner
Léieren
- Léieren
+ Etüden
Üben
Gemeinschaft
Tools
@@ -560,10 +560,10 @@
Dës Email Adress ass net akzeptabel. Iwwerpréif se wannechgelift an probéier nach eng Kéier.
Email Adress ongëlteg oder schon benotzt
Dat ass schon deng Email Adress
- Mindestens %s Zeechen
- Maximal %s Zeechen
- Muss mindestens %s sinn
- Däerf maximal %s sinn
+ Mindestens %s Zeechen
+ Maximal %s Zeechen
+ Muss mindestens %s sinn
+ Däerf maximal %s sinn
Falls Wäertung ± %s
Just lafend Konversatiounen
Just Frënn
@@ -812,7 +812,7 @@
Du kanns nach net am Forum schreiwen. Spill puer Partien!
Abonéieren
Desabonéiren
- huet dech an \"%1$s\" erwähnt.
+ huet dech an \"%1$s\" erwähnt.
%1$s huet dech an \"%2$s\" erwähnt.
huet dech zu \"%1$s\" invitéiert.
%1$s huet dech zu \"%2$s\" invitéiert.
diff --git a/translation/dest/site/ml-IN.xml b/translation/dest/site/ml-IN.xml
index bc290c49bc..c614944ce5 100644
--- a/translation/dest/site/ml-IN.xml
+++ b/translation/dest/site/ml-IN.xml
@@ -24,6 +24,7 @@
നിങ്ങൾ വെള്ള കരുകൾ വെച്ച് കളിക്കുന്നു
നിങ്ങൾ കറുപ്പ് കരുകൾ വെച്ച് കളിക്കുന്നു
നിങ്ങളുടെ കളി!
+ കള്ളത്തരം കണ്ടെത്തി
കിംഗ് ഇൻ ദി സെന്റർ ഗെയിം
മൂനു ചെക്ക് ഗെയിം
ഓട്ടം അവസാനിച്ചിരിക്കുന്നു
@@ -89,6 +90,7 @@
ഒരു ഗെയിമും കണ്ടെത്താൻ സാധിച്ചില്ല
പരിഗണന മെനുവിൽ നിന്ന് കൂടുതൽ ഗെയിമുകൾ ഉൾപ്പെടുത്താണോ?
എക്സ്പ്ലോററും ടേബിൾ ബേസും തുറക്കാം
+ ഓപ്പണിങ്ങ്/എൻഡ്ഗെയിം എക്സ്പ്ലോറർ
%s ഓപ്പണിങ് എക്സ്പ്ലോറർ
50 നീക്കങ്ങളുടെ നിയമം പ്രകാരം ജയം തടയപ്പെട്ടു
50 നീക്കങ്ങളുടെ നിയമം പ്രകാരം തോൽവി തടയപ്പെട്ടു
@@ -428,6 +430,7 @@
കളിക്കളം സജ്ജമാക്കുക
പ്രചാരത്തിലുള്ള ഓപ്പണിങ്ങുകൾ
ഏൻഡ് ഗെയിം പൊസിഷനുകൾ
+ ചതുരംഗം960 ആരംഭ സ്ഥാനം: %s
പ്രാരംഭ സ്ഥാനം
പലക വ്യക്തമാക്കുക
സ്ഥാനത്തു നിന്ന്
@@ -550,6 +553,7 @@
കയറ്റം
ഈ ഭാഗം ആവശ്യമാണ്
ഈ ഇമെയിൽ അഡ്രസ് സാധുവല്ല
+ ഈ ഇമെയിൽ വിലാസം സ്വീകാര്യമല്ല. പരിശോധിച്ചുറപ്പാക്കി വീണ്ടും ശ്രമിക്കുക.
± %s ക്രമത്തിലാണോ
നിലവിലുള്ള സംഭാഷങ്ങൾ മാത്രം
സുഹൃത്തുക്കൾക്കു മാത്രം
diff --git a/translation/dest/site/ms-MY.xml b/translation/dest/site/ms-MY.xml
index b05258a63d..1f669455ea 100644
--- a/translation/dest/site/ms-MY.xml
+++ b/translation/dest/site/ms-MY.xml
@@ -51,6 +51,7 @@
Minta analisis komputer
Analisis komputer
Analisis komputer ada
+ Analisis komputer ditutup
Analisis papan
Depth %s
Menggunakan server analisis
@@ -84,6 +85,7 @@
- Mate dalam %s separuh-pergerakan
+ DTZ50\" yang dibundar, mengikut jumlah pegerakkan separuh sehingga sebuah bidak dimakan atau pegerakkan pion seterusnya
Tiada perlawanan dijumpai
Mungki masukkan lebih banyak perlawanan daripada pilihan menu?
Membuka explorer
@@ -138,7 +140,7 @@
Senarai pemain
Senarai rakan
Senarai perbualan
- Hari ini
+ Hari ini
Semalam
Minit bagi setiap bahagian
Variasi
@@ -350,7 +352,7 @@
Buah putih menang
Menang Hitam
Seri
- Posisi permulaan
+ Posisi permulaan
Pautan media sosial
Pemain aktif
Berjaya
diff --git a/translation/dest/site/ne-NP.xml b/translation/dest/site/ne-NP.xml
index 0441e58ca8..dbaa0d6f7e 100644
--- a/translation/dest/site/ne-NP.xml
+++ b/translation/dest/site/ne-NP.xml
@@ -24,6 +24,7 @@
तपाईं सेतो खेल्नुहुनेछ
तपाईं कालो खेल्नुहुनेछ
तपाईंको पालो!
+ बेइमानी पत्ता पर्यो
राजा बिचमा
तिनवटा चेकहरु
दौड सकियो
@@ -49,6 +50,7 @@
कम्प्युटर विश्लेषण आग्रह गर्नुहोस्
कम्प्युटर विश्लेषण
कम्प्युटर विश्लेषण उपलब्ध छ
+ कम्प्युटर विश्लेषण बन्द छ
विश्लेषण बोर्ड
गहिराइ %s
सर्भरको विश्लेषण प्रयोग गरिदैं छ
@@ -140,7 +142,7 @@
खेलाडीहरू
साथीहरू
वार्तालाप
- आज
+ आज
हिजो
एक पक्षको मिनट
परिमार्जन
@@ -257,6 +259,7 @@
- %s सेकेण्ड दिनु
- %s सेकेण्ड दिनु
+ यस खाताले Lichess Terms of Service को उल्लङ्घन गरेको छ
शुरुका चाल अन्वेषक & टेबलबेस
चाल फिर्ता
चाल फिर्ता लिने प्रस्ताव गरौं
@@ -405,9 +408,9 @@
बोर्ड सम्पादक
बोर्ड सेट गरौं
लोकप्रिय सुरुवाती चालहरु
- शुरुवाती अवस्था
+ शुरुवाती अवस्था
बोर्ड खालि गरौं
- पोजिसन लोड गरौं
+ पोजिसन लोड गरौं
नीजि
%s माथी उजुरी गरौं
प्रोफाइल समापन: %s
@@ -419,13 +422,14 @@
शुभनाम
थर
जीवनी
+ देश अथवा झण्डा
धन्यवाद!
सामाजिक सञ्जाल लिङ्क
पङ्तिमा नोटेशन
लीचेस टिभीमा यसअघि
अनलाइन खेलाडीहरु
कृयाशील खेलाडीहरु
- सावधान, खेल रेटिङवाला हो तर कुनै घडी छैन!
+ सावधान, खेल रेटिङवाला हो तर कुनै घडी छैन!
सफलता
- %s खेल जारी
@@ -475,6 +479,7 @@
घडीको शुरु समय
हरेक चालमा थप हुने समय
गोपनियता
+ गोप्यता नीति
अरु खेलाडीलाई पछ्याउन दिने
अरु खेलाडी लाई खेलको निम्तो दिन दिने
अरु खेलाडीलाई पाठ मा आमन्त्रण गर्न दिने
@@ -488,7 +493,7 @@
ढिलो खेलहरुमा
सधैँ
नाइँ
- %1$s %2$s मा भाग लिनुहुन्छ
+ %1$s %2$s मा भाग लिनुहुन्छ
विजय
हार
%1$s बनाम %2$s: %3$s मा
@@ -754,5 +759,9 @@
प्रायः सोधिने प्रश्नहरूमा
चोरी वा खराब ब्यहोराको उजुरी गर्नुपरेमा %1$s
रिपोर्ट फारम प्रयोग गर्नुहोस्
+ मद्दतका लागि अनुरोध गर्न, %1$s
+ सम्पर्क पेजबाट प्रयास गर्नुहोस्
+ यो विषय सङ्गृहीत छ र यसलाई जवाफ दिन सकिँदैन।
+ %1$s टोली
समय सक्किन लाग्यो!
diff --git a/translation/dest/site/nl-NL.xml b/translation/dest/site/nl-NL.xml
index 5fa6a74dfa..7fed709167 100644
--- a/translation/dest/site/nl-NL.xml
+++ b/translation/dest/site/nl-NL.xml
@@ -163,7 +163,7 @@
Spelers
Vrienden
Discussies
- Vandaag
+ Vandaag
Gisteren
Minuten per speler
Variant
@@ -435,9 +435,9 @@
Populaire openingen
Eindspel posities
Beginstelling Chess960: %s
- Beginstelling
+ Beginstelling
Maak het bord leeg
- Laad een stelling
+ Laad een stelling
Privé
Rapporteer %s aan de toezichthouders
Voltooiing profiel: %s
@@ -456,7 +456,7 @@
Voorheen op Lichess TV
Online spelers
Actieve spelers
- Let op, de partij is met rating maar heeft geen klok!
+ Let op, de partij is met rating maar heeft geen klok!
Succes
- %s partij bezig
@@ -525,7 +525,7 @@
Alleen bij langzame partijen
Altijd
Nooit
- %1$s neemt deel aan %2$s
+ %1$s neemt deel aan %2$s
Overwinning
Nederlaag
%1$s vs. %2$s in %3$s
@@ -560,10 +560,10 @@
Dit e-mailadres is niet acceptabel. Controleer het nogmaals en probeer het opnieuw.
E-mailadres ongeldig of al in gebruik
Dit is al uw e-mailadres
- Minimumlengte is %s tekens
- Maximumlengte is %s tekens
- Moet groter dan of gelijk zijn aan %s
- Moet kleiner dan of gelijk zijn aan %s
+ Minimumlengte is %s tekens
+ Maximumlengte is %s tekens
+ Moet groter dan of gelijk zijn aan %s
+ Moet kleiner dan of gelijk zijn aan %s
Indien de rating ± %s is
Alleen bestaande gesprekken
Alleen vrienden
@@ -812,7 +812,7 @@
Je kunt nog geen post in de forums plaatsen. Speel eerst een paar partijen!
Abonneren
Abonnement opzeggen
- heeft je genoemd in \"%1$s\".
+ heeft je genoemd in \"%1$s\".
%1$s heeft je genoemd in \"%2$s\".
heeft je uitgenodigd voor \"%1$s\".
%1$s heeft je uitgenodigd voor \"%2$s\".
diff --git a/translation/dest/site/or-IN.xml b/translation/dest/site/or-IN.xml
index f06c354397..6d78d9f01a 100644
--- a/translation/dest/site/or-IN.xml
+++ b/translation/dest/site/or-IN.xml
@@ -21,8 +21,8 @@
ଏକ ଖେଳ ସୃଷ୍ଟି କରନ୍ତୁ
ଧଳା ବିଜୟୀ ଅଟେ
କଳା ବିଜୟୀ ଅଟେ
- ଆପଣ ଧଳା ଖଣ୍ଡଗୁଡ଼ିକ ଖେଳୁଛନ୍ତି
- ଆପଣ କଳା ଖଣ୍ଡଗୁଡ଼ିକ ଖେଳୁଛନ୍ତି
+ ଆପଣ ଧଳା ଗୋଟିଗୁଡ଼ିକ ଖେଳୁଛନ୍ତି
+ ଆପଣ କଳା ଗୋଟିଗୁଡ଼ିକ ଖେଳୁଛନ୍ତି
ଏହା ଆପଣଙ୍କର ପାଳି!
କେନ୍ଦ୍ରରେ ରାଜା |
ତିନି ଚେକ୍
@@ -33,7 +33,11 @@
ଖେଳରେ ଯୋଗ ଦିଅନ୍ତୁ
ଧଳାର ପାଳି
କଳାର ପାଳି
- ତୁମର ପ୍ରତିଦ୍ୱନ୍ଦୀ ଖେଳ ଛାଡିଥିଲେ | ଆପଣ ବିଜୟ ଦାବି କରିପାରିବେ, ଖେଳକୁ ଡ୍ର ବୋଲି କହିପାରିବେ କିମ୍ବା ଅପେକ୍ଷା କରିପାରିବେ |
+
+ - ଆପଣଙ୍କ ପ୍ରତିଦ୍ୱନ୍ଦ୍ୱୀ ଖେଳ ଛାଡ଼ିଛନ୍ତି। ଆପଣ %s ସେକେଣ୍ଡରେ ବିଜୟ ଦାବି କରିପାରିବେ।
+ - ଆପଣଙ୍କ ପ୍ରତିଦ୍ୱନ୍ଦ୍ୱୀ ଖେଳ ଛାଡ଼ିଛନ୍ତି। ଆପଣ %s ସେକେଣ୍ଡରେ ବିଜୟ ଦାବି କରିପାରିବେ।
+
+ ଆପଣଙ୍କ ପ୍ରତିଦ୍ୱନ୍ଦ୍ୱୀ ଖେଳ ଛାଡ଼ିଛନ୍ତି। ଆପଣ ବିଜୟ ଦାବି କରିପାରିବେ, ଖେଳକୁ ଡ୍ର ବୋଲି କହିପାରିବେ କିମ୍ବା ଅପେକ୍ଷା କରିପାରିବେ।
ବିଜୟ ଦାବି କରନ୍ତୁ
କଲ୍ ଡ୍ର
ଦୟାକରି ଚାଟରେ ଭଲ ହୁଅନ୍ତୁ!
@@ -142,17 +146,17 @@
ଉପଭୋକ୍ତାଙ୍କ ନାମ
ଉପଭୋକ୍ତା ନାମ କିମ୍ୱା ଇମେଲ୍
ଉପଭୋକ୍ତା ନାମ ପରିବର୍ତ୍ତନ କରନ୍ତୁ
- ପାସ୍ଓ୍ୱାର୍ଡ଼
- ପାସ୍ଓ୍ୱାର୍ଡ଼ ପରିବର୍ତ୍ତନ କରନ୍ତୁ
+ ପାସ୍ୱାର୍ଡ଼
+ ପାସ୍ୱାର୍ଡ଼ ବଦଳାନ୍ତୁ
ଇମେଲ୍ ପରିବର୍ତ୍ତନ କରନ୍ତୁ
ଇମେଲ୍
- ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କର
- ପାସ୍ଓ୍ୱାର୍ଡ଼ ଭୁଲିଯାଇଛନ୍ତି କି?
+ ପାସ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କର
+ ପାସ୍ୱାର୍ଡ଼ ଭୁଲିଯାଇଛନ୍ତି କି?
- %sଟିଏ ପ୍ରହେଳିକା
- %sଟି ପ୍ରହେଳିକା
- ଖେଳାଯାଇଥିବା ଖେଳଗୁଡ଼ିକ
+ ଖେଳାଯାଇଥିବା ଖେଳଗୁଡ଼ିକ
ବର୍ତ୍ତମାନ ଖେଳୁଛନ୍ତି
ଏହି ଆକାଉଣ୍ଟ Lichess ସେବା ସର୍ତ୍ତାବଳୀର ଉଲ୍ଲଂଘନ କରିଛି
@@ -196,16 +200,17 @@
ଉପଭୋକ୍ତା
କାରଣ
ମାମଲା କ\'ଣ?
- ଅବୈଧ ଉପଭୋକ୍ତାଙ୍କ ନାମ କିମ୍ୱା ପାସ୍ଓ୍ୱାର୍ଡ଼
- ଭୁଲ୍ ପାସ୍ଓ୍ୱାର୍ଡ଼
- ଏବେକାର ପାସ୍ଓ୍ୱାର୍ଡ଼
- ନୁତନ ପାସ୍ଓ୍ୱାର୍ଡ଼
- ନୂତନ ପାସ୍ଓ୍ୱାର୍ଡ଼ (ପୁନଃ)
- ନୂତନ ପାସ୍ଓ୍ୱାର୍ଡ଼ଗୁଡ଼ିକ ମେଳ ଖାଉ ନାହିଁ
+ ଅବୈଧ ଉପଭୋକ୍ତାଙ୍କ ନାମ କିମ୍ୱା ପାସ୍ୱାର୍ଡ଼
+ ଭୁଲ୍ ପାସ୍ୱାର୍ଡ଼
+ ଏବେକାର ପାସ୍ୱାର୍ଡ଼
+ ନୂଆ ପାସ୍ୱାର୍ଡ଼
+ ନୂଆ ପାସ୍ୱାର୍ଡ଼ (ପୁନଃ)
+ ନୂତନ ପାସ୍ୱାର୍ଡ଼ଗୁଡ଼ିକ ମେଳ ଖାଉ ନାହିଁ
ଗୋପନୀୟତା
ଗୋପନୀୟତା ନୀତି
ଅନ୍ୟ ଖେଳାଳିମାନଙ୍କୁ ଆପଣଙ୍କୁ ଅଧ୍ୟୟନ ପାଇଁ ଆମନ୍ତ୍ରଣ କରିବାକୁ ଦିଅନ୍ତୁ
ଧ୍ଵନି
+ %1$s %2$sରେ ପ୍ରତିଦ୍ୱନ୍ଦ୍ୱିତା କରନ୍ତି
ବିଜୟ
ସମୟରେଖା
ଆରମ୍ଭ:
@@ -223,7 +228,7 @@
କେବଳ ବନ୍ଧୁଗଣ
ସେବାର ସର୍ତ୍ତାବଳୀ
ସୃଷ୍ଟି କରନ୍ତୁ
- ଆମେ %s କୁ ଏକ ଇମେଲ୍ ପଠାଇଛୁ। ଆପଣଙ୍କ ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ଇମେଲରେ ଥିବା ଲିଙ୍କକୁ କ୍ଲିକ୍ କରନ୍ତୁ।
+ ଆମେ %s କୁ ଏକ ଇମେଲ୍ ପଠାଇଛୁ। ଆପଣଙ୍କ ପାସ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ଇମେଲରେ ଥିବା ଲିଙ୍କକୁ କ୍ଲିକ୍ କରନ୍ତୁ।
ଆମର %s ବିଷୟରେ ପଢ଼ନ୍ତୁ।
ସୁରକ୍ଷା
ସମାଧାନ ଦେଖନ୍ତୁ
@@ -232,6 +237,7 @@
- %sଟି ଭାଷାରେ ଉପଲବ୍ଧ!
ବେନାମୀ
+ ଆପଣଙ୍କ ସ୍କୋର୍: %s
ଭାଷା
ହାଲୁକା
ଗାଢ଼
@@ -242,14 +248,16 @@
କ୍ଷମା କରନ୍ତୁ :(
କାହିଁକି?
ଆମେ ସମସ୍ତଙ୍କ ପାଇଁ ଏକ ସୁଖଦ ଚେସ୍ ଅଭିଜ୍ଞତା ପ୍ରଦାନ କରିବାକୁ ଲକ୍ଷ୍ୟ ରଖିଛୁ।
- ଏହାକୁ କିପରି ଏଡ଼ାଇ ହେବ?
+ ଏହାକୁ କିପରି ଏଡ଼ାଇ ହେବ?
ପଢ଼ିବା ପାଇଁ ଧନ୍ୟବାଦ!
+ ଆଜୀବନ ସ୍କୋର୍
ମୁଁ ସହମତ ଯେ ମୁଁ ସମସ୍ତ Lichess ନୀତି ଅନୁସରଣ କରିବି।
ସମ୍ପାଦନା
ଶାସ୍ତ୍ରୀୟ
ଚେସ୍ କୌଶଳ ପ୍ରଶିକ୍ଷକ
ସଦସ୍ୟତା ନିଅନ୍ତୁ
ସଦସ୍ୟତା ରଦ୍ଦ କରନ୍ତୁ
+ ଆପଣଙ୍କୁ \"%1$s\"ରେ ଉଲ୍ଲେଖ କରିଛନ୍ତି।
ନୂଆ ବିଚାରାଧୀନ ସମୀକ୍ଷା
ଏହାକୁ ଗ୍ରନ୍ଥନ କରିବା ପାଇଁ ଏକ ଖେଳ URL କିମ୍ବା ଏକ ଅଧ୍ୟାୟ URL ପେଷ୍ଟ କରନ୍ତୁ।
କେବଳ ଦଳର ସଦସ୍ୟଗଣ
diff --git a/translation/dest/site/os-SE.xml b/translation/dest/site/os-SE.xml
index fe14c46d09..464ca20e1c 100644
--- a/translation/dest/site/os-SE.xml
+++ b/translation/dest/site/os-SE.xml
@@ -122,7 +122,7 @@
Рæстæг цыдæн
Фæйнæг разилын
- Æртæхатты позицийы фæлхатын
+ Æртывæр позицийы фæлхатын
Æрдомын хæрæгсаст
Бахъарын хæрæгсаст
Хæрæгсаст
@@ -152,7 +152,7 @@
Хъазджытæ
Æмбалтæ
Ныхæстæ
- Абон
+ Абон
Знон
Минутты партийæн
Вариант
@@ -420,9 +420,9 @@
Фæйнæджы редактор
Æрæвæрын позици
Адæмы уарзон райдайæнтæ
- Райдайæн позици
+ Райдайæн позици
Фæйнæг асыгъдæг кæнын
- Позици бавгæнын
+ Позици бавгæнын
Æхгæд
Фехъусын кæнын %s-ы тыххæй модератортæн
Профилы дзагдзинад: %s
@@ -440,7 +440,7 @@
Раздæр Lichess TV-йы
Онлайн хъазджытæ
Разæнгард хъазджытæ
- Хъус бадар, ацы хъазт у рейтингимæ, фæлæ нæй йын рæстæджы цæстдарды!
+ Хъус бадар, ацы хъазт у рейтингимæ, фæлæ нæй йын рæстæджы цæстдарды!
Рауади
- %s парти
@@ -509,7 +509,7 @@
Сындæг хъæзтыты
Æдзух
Никуы
- %1$s сфæнд кодта %2$s-ы хайад исын
+ %1$s сфæнд кодта %2$s-ы хайад исын
Уæлахиз
Дæрæн
%1$s %2$s-йы ныхмæ %3$s-ы
diff --git a/translation/dest/site/sc-IT.xml b/translation/dest/site/sc-IT.xml
index 3ea04e700d..812c6eb280 100644
--- a/translation/dest/site/sc-IT.xml
+++ b/translation/dest/site/sc-IT.xml
@@ -1,2 +1,16 @@
-
+
+ Gioga con unu amigu
+ Gioga chin su elaboradore
+ Para inbidai calicunu dona issu custu URL
+ As perdiu
+ Ispettende unu enemigu
+ Ispettende
+ A tue
+ %1$s difficultade %2$s
+ Difficultade
+ Fortza
+ Chat
+ Arrendidi
+ Scaccu maccu
+
diff --git a/translation/dest/site/si-LK.xml b/translation/dest/site/si-LK.xml
index 5d06a806d3..ef712e568f 100644
--- a/translation/dest/site/si-LK.xml
+++ b/translation/dest/site/si-LK.xml
@@ -87,6 +87,7 @@
- ඇදීම් %sකින් චෙක්මේට්
- ඇදීම් වාර %sකින් චෙක්මේට්
+ DTZ50\'\' with rounding, based on number of half-moves until next capture or pawn move
සෙල්ලමක් හමුවුන්නෑ
මනාප ලැයිස්තුවේ තවත් සෙල්ලම් තිබිය හැක?
පටන්ගැනීම් ගවේෂකය
@@ -151,7 +152,7 @@
ක්රීඩකයන්
යහළුවන්
සංවාද
- අද
+ අද
ඊයේ
එක් පැත්තකට මිනිත්තු
වර්ගය
diff --git a/translation/dest/site/te-IN.xml b/translation/dest/site/te-IN.xml
index 4b879d8a8f..30535ec188 100644
--- a/translation/dest/site/te-IN.xml
+++ b/translation/dest/site/te-IN.xml
@@ -147,7 +147,7 @@
ఆటగాళ్లు
స్నేహితులు
సంభాషణలు
- ఈరోజు
+ ఈరోజు
నిన్న
గడువు
రకం
@@ -323,6 +323,8 @@ Translation results
సగటు రేటింగ్
స్థానం
ఫిల్టర్ గేమ్లు
+ సేవ్
+ లీడర్బోర్డు
గ్రాఫ్
- %s నిమిషం కన్నా తక్కువ
@@ -367,10 +369,107 @@ Translation results
బోర్డు ఎడిటర్
బోర్డు సెట్ చేయండి
జనాదరణ పొందిన ఓపెనింగ్స్
- ప్రారంభ స్థానం
+ ప్రారంభ స్థానం
క్లియర్ బోర్డు
- స్థానం లోడ్
+ స్థానం లోడ్
ప్రైవేట్
మోడరేటర్లకు %s ని నివేదించండి
ప్రొఫైల్ పూర్తయింది:%s
+ ప్రొఫైల్
+ ప్రొఫైల్ మార్చాలి
+ మీ దేశం లేదా జెండా
+ ధన్యవాదాలు!
+ సోషల్ మీడియా లింక్లు
+ Inline notation
+ గతంలో Lichess TVలో
+ ఆన్లైన్ ప్లేయర్లు
+ Active ఆటగాళ్ళు
+ జాగ్రత్త, గేమ్ రేట్ చేయబడింది కానీ గడియారం లేదు!
+ విజయం
+
+ - %s ఆటలు ఆడుతున్నారు
+ - %s ఆటలు ఆడుతున్నారు
+
+ కదిలించిన తర్వాత దానంతట అదే తదుపరి గేమ్కు వెళ్లాలి
+ ఆటో స్విచ్
+ పజిల్స్
+ టోర్నమెంట్ విజేతలు
+ పేరు
+ అతడు/ఆమె చేసిన తప్పేంటి
+ ప్రైవేట్ సమాచారం
+ టీమ్ మెంబర్స్ మాత్రమే చూడగలిగే సమాచారం. సెట్ చేస్తే, బృంద సభ్యుల కోసం పబ్లిక్ సమాచారాన్ని భర్తీ చేస్తుంది.
+ లేదు
+ అవును
+ సహాయం:
+ కొత్త అంశాన్ని సృష్టించండి
+ అంశాలు
+ పోస్ట్లు
+ చివరి పోస్ట్
+ వీక్షకులు
+ సమాధానాలు
+ ఈ అంశానికి సమాధానం ఇవ్వండి
+ సమాధానం
+ సందేశం
+ కొత్త అంశాన్ని సృష్టించండి
+ రిపోర్ట్ యూజర్
+ వినియోగదారుడు
+ కారణం
+ మీరు ఎందుకు రిపోర్టు చేస్తున్నారు?
+ మోసం
+ అవమానం
+ ఎగతాళి చేయండి
+ రేటింగ్ మానిప్యులేషన్
+ ఇతర
+ గేమ్/గేమ్ల లింక్ను పంపండి మరియు ఈ వినియోగదారు ప్రవర్తనలో తప్పు ఏమిటో వివరించండి. \"వారు మోసం చేసారు\" అని చెప్పకండి, కానీ మీరు ఈ నిర్ణయానికి ఎలా వచ్చారో మాకు చెప్పండి. మీ నివేదికను ఆంగ్లంలో వ్రాస్తే వేగంగా ప్రాసెస్ చేయబడుతుంది.
+ దయచేసి కనీసం మోసం చేసిన ఆట ఒక లింక్నైనౌ అందించండి.
+ %s చే
+ ఈ అంశం ఇప్పుడు మూసివేయబడింది.
+ బ్లాగ్
+ గమనిక
+ మీ ప్రైవేట్ గమనికలను ఇక్కడ టైప్ చేయండి
+ వినియోగదారు పేరు లేదా పాస్వర్డ్ చెల్లదు
+ తప్పు పాస్వర్డ్
+ చెల్లని ప్రమాణీకరణ కోడ్
+ నాకు ఒక లింక్ ఇమెయిల్ చేయండి
+ ప్రస్తుత పాస్వర్డ్
+ కొత్త పాస్వర్డ్
+ కొత్త పాస్వర్డ్ (మళ్లీ టైప్ చేయండి)
+ కొత్త పాస్వర్డ్లు ఒకేలా లేవు
+ పాస్వర్డ్ బలం
+ గడియారం ప్రారంభ సమయం
+ గడియారం పెంపు
+ పరికరాలు
+ ఈ సమాచారం అవసరం
+ కాస్లింగ్
+ తెలుపు O-O
+ నలుపు O-O
+
+ - %s ఫోరమ్ పోస్ట్
+ - %s ఫోరమ్ పోస్ట్లు
+
+ ఆడుతూ గడిపిన సమయం: %s
+ ఆటలు చూడండి
+ టీవీలో ప్రదర్శించబడిన సమయం: %s
+ వీక్షించండి
+ వీడియో లైబ్రరీ
+ స్ట్రీమర్లు
+ మొబైల్ యాప్
+ Webmasters
+ మా గురించి
+ %s గురించి
+ %1$s అనేది ఉచితమైన (%2$s), స్వేచ్ఛమైన, ప్రకటనలు లేని, ఓపెన్ సోర్స్ చెస్ సర్వర్.
+ నిజంగా
+ సహకరించండి
+ సేవా నిబంధనలు
+ సోర్స్ కోడ్
+ Simultaneous exhibitions
+ హోస్ట్
+ హోస్ట్ రంగు: %s
+ కొత్తగా సృష్టించబడిన simuls
+ కొత్త simul చేయండి
+ Simul కనుబడలేదు
+ ఈ simultaneous exhibition లేదు.
+ Simul హోమ్పేజీకి తిరిగి వెళ్ళు
+ Simuls ఒకే ఆటగాడు ఒకేసారి అనేక మంది ఆటగాళ్లను ఎదుర్కొంటాడు.
+ 50 మంది ప్రత్యర్థులలో, ఫిషర్ 47 గేమ్లు గెలిచాడు, 2 డ్రా చేసుకున్నాడు మరియు 1 ఓడిపోయాడు.
diff --git a/translation/dest/site/vi-VN.xml b/translation/dest/site/vi-VN.xml
index 2eb94c3df7..6b2655ce9a 100644
--- a/translation/dest/site/vi-VN.xml
+++ b/translation/dest/site/vi-VN.xml
@@ -46,6 +46,8 @@
Đen chịu thua
Trắng đã thoát khỏi ván cờ
Đen đã thoát khỏi ván cờ
+ Bên trắng không đi quân
+ Bên đen không đi quân
Yêu cầu máy tính phân tích
Máy tính phân tích
Máy tính phân tích có sẵn
@@ -83,6 +85,7 @@
- Chiếu hết trong %s nước
+ DTZ50\"\" được làm tròn, dựa vào số nước đi quân cho tới nước bắt quân hoặc đi tốt tiếp theo
Không có ván đấu nào trùng khớp
Bạn có muốn thêm nhiều ván đấu hơn từ mục lục?
Sách khai cuộc
diff --git a/translation/dest/site/zh-CN.xml b/translation/dest/site/zh-CN.xml
index 9bf07f6add..d41bac5564 100644
--- a/translation/dest/site/zh-CN.xml
+++ b/translation/dest/site/zh-CN.xml
@@ -92,6 +92,7 @@
%s开局浏览器
因50回合规则未赢棋
因50回合规则未输棋
+ 由于先前的错误而损失或打平局(rule50)
好了!
导入PGN文件
删除
@@ -151,7 +152,7 @@
棋手
棋友
讨论组
- 今天
+ 今天
昨天
各方限时(分钟)
变体
@@ -401,9 +402,9 @@
常见开局
残局局面
Chess960 起始局面: %s
- 起始局面
+ 起始局面
清空棋盘
- 载入局面
+ 载入局面
私人锦标赛
举报%s
资料完成度:%s
@@ -422,7 +423,7 @@
过去的 Lichess 直播
在线棋手
活跃棋手
- 注意,此棋局是排位赛,但是不计时!
+ 注意,此棋局是排位赛,但是不计时!
解题成功
- %s 场对局进行中
@@ -490,7 +491,7 @@
在慢棋时
总是
从不
- %1$s参加%2$s
+ %1$s参加%2$s
获胜
负败
%1$s,与 %2$s 进行的 %3$s
@@ -769,7 +770,7 @@
您还不能在论坛发表意见。再下几局棋吧!
订阅
取消订阅
- 在 “%1$s” 中提到了你。
+ 在 “%1$s” 中提到了你。
%1$s 在 \"%2$s\" 中提到了你。
邀请你加入\"%1$s\"。
%1$s 邀请你加入 \"%2$s\"。
diff --git a/translation/dest/storm/be-BY.xml b/translation/dest/storm/be-BY.xml
index 07647d99e7..34f917c268 100644
--- a/translation/dest/storm/be-BY.xml
+++ b/translation/dest/storm/be-BY.xml
@@ -4,11 +4,11 @@
Вы гуляеце белымі фігурамі ва ўсіх задачах
Вы гуляеце чорнымі фігурамі ва ўсіх задачах
задач вырашана
- Новы дзенны рэкорд!
+ Новы дзённы рэкорд!
Новы тыднёвы рэкорд!
Новы месячны рэкорд!
- Новы рэкорд за ўсе часы!
- Папярэні рэкорд быў %s
+ Новы рэкорд за ўвесь час!
+ Папярэдні рэкорд %s
Гуляць яшчэ раз
- 1 спроба
diff --git a/translation/dest/storm/de-CH.xml b/translation/dest/storm/de-CH.xml
index b351a947f8..dc8a95dc2a 100644
--- a/translation/dest/storm/de-CH.xml
+++ b/translation/dest/storm/de-CH.xml
@@ -52,4 +52,10 @@
Überschpring dä Zug, zum dini Kombination byzbhaltä! Du chasch das nur eimol pro Ränä machä.
Fählgschlagi Ufgoobä
Langsami Ufgoobä
+ Die Wuche
+ De Monät
+ Insgesamt
+ Klick, zum neu lade
+ De Durchgang isch abgeloffen!
+ De Durchgang isch imene andere Fenster göffnet worde!
diff --git a/translation/dest/storm/it-IT.xml b/translation/dest/storm/it-IT.xml
index 66e236db85..2888fbf1b1 100644
--- a/translation/dest/storm/it-IT.xml
+++ b/translation/dest/storm/it-IT.xml
@@ -56,4 +56,6 @@
Questo mese
Sempre
Clicca per ricaricare
+ Questa serie è scaduta!
+ Questa serie è stata aperta in un\'altra scheda!
diff --git a/translation/dest/storm/or-IN.xml b/translation/dest/storm/or-IN.xml
index 27379a1835..71c684aad3 100644
--- a/translation/dest/storm/or-IN.xml
+++ b/translation/dest/storm/or-IN.xml
@@ -4,6 +4,7 @@
- 1 ରନ୍
- %s ରନ୍
+ ସ୍କୋର୍
ସମୟ
ଏକ ନୂତନ ଖେଳ ସୃଷ୍ଟି କରନ୍ତୁ
ବାଦ୍ ଦିଅ
diff --git a/translation/dest/storm/zh-CN.xml b/translation/dest/storm/zh-CN.xml
index f9e12874f0..746eb67784 100644
--- a/translation/dest/storm/zh-CN.xml
+++ b/translation/dest/storm/zh-CN.xml
@@ -54,4 +54,6 @@
本月
全部时间
点击重新加载
+ 本次冲刺已超时!
+ 本次冲刺已经在另一个标签页中打开!
diff --git a/translation/dest/streamer/de-CH.xml b/translation/dest/streamer/de-CH.xml
index 88cf0e4fb4..2c1ac39724 100644
--- a/translation/dest/streamer/de-CH.xml
+++ b/translation/dest/streamer/de-CH.xml
@@ -18,7 +18,7 @@
Füäg s\'Stichwort \"lichess.org\" i dim Schtriimtitäl ii und verwänd d\'Kategorie \"Chess\", wänn du uf Lichess schtriimsch.
Entfärn s\'Stichwort, wänn du grad nöd Lichess-bezogä schtriimsch.
Lichess wird automatisch din Schtriim ärkänä und folgändi Zuäsatzdiänscht aktiviärä:
- Lies unsere %s, um Fairplay für alle während deines Streams zu gewährleisten.
+ Liis eusi %s, um Fairplay für alli während dim Schtriim z\'gwährleistä.
Schtriimer Fairplay FAQ
Vorteil fürs Schtriimä mitämä Schlüssälwort
Bechum äs flammänds Schtriimer-Symbol uf dim Lichess-Profil.
diff --git a/translation/dest/streamer/lb-LU.xml b/translation/dest/streamer/lb-LU.xml
index b656902365..6169aa9a20 100644
--- a/translation/dest/streamer/lb-LU.xml
+++ b/translation/dest/streamer/lb-LU.xml
@@ -21,8 +21,10 @@
Lies eis %s fir Fairplay fir jiddereen wärend denge Streams ze garantéieren.
Streaming Fairplay FAQ
Virdeeler fir Streaming mat engem Stéchwuert
+ Twitch Benotzernumm oder URL
Iwwerschrëft
Laang Beschreiwung
+ %s Streamer Bild
Däin Bild änneren/läschen
E Bild héichlueden
Maximal Gréisst: %s
diff --git a/translation/dest/study/be-BY.xml b/translation/dest/study/be-BY.xml
index 1824412e56..f6de5fa8b2 100644
--- a/translation/dest/study/be-BY.xml
+++ b/translation/dest/study/be-BY.xml
@@ -46,7 +46,8 @@
Вы цяпер рэдактар
Вы цяпер глядач
Тэгі PGN
- Падабайка
+ Упадабаць
+ Разпадабаць
Новы тэг
Каментаваць пазіцыю
Каментаваць гэты ход
diff --git a/translation/dest/study/de-CH.xml b/translation/dest/study/de-CH.xml
index a0240603dc..5e05585522 100644
--- a/translation/dest/study/de-CH.xml
+++ b/translation/dest/study/de-CH.xml
@@ -41,6 +41,7 @@
Du bist jetzt Zuschauer
PGN Tags
Gefällt mir
+ Gfallt mer nüme
Neuer Tag
Kommentiere diese Stellung
Kommentiere diesen Zug
diff --git a/translation/dest/study/he-IL.xml b/translation/dest/study/he-IL.xml
index 6ab068ebb2..c80f978003 100644
--- a/translation/dest/study/he-IL.xml
+++ b/translation/dest/study/he-IL.xml
@@ -134,6 +134,7 @@
נקה צ\'אט
למחוק את ההיסטוריה של הצ\'אט של המחקר? אין דרך חזרה!
מחק מחקר
+ האם למחוק את כל הלימוד? אין דרך חזרה! הקלד את שם הלימוד לאישור: %s
איפה אתה מעוניין לחקור זאת?
מסע טוב
טעות
@@ -143,6 +144,8 @@
מסע מפוקפק
המסע היחיד
כפאי
+ עמדה מאוזנת
+ עמדה לא ברורה
יתרון קל ללבן
יתרון קל לשחור
לבן יותר טוב
@@ -151,5 +154,13 @@
שחור מנצח
חידוש
פיתוח
+ יוזמה
התקפה
+ משחק נגד
+ מצוקת זמן
+ עם פיצוי
+ עם הרעיון
+ פרק הבא
+ הפרק הקודם
+ פעולות לימוד
diff --git a/translation/dest/study/hi-IN.xml b/translation/dest/study/hi-IN.xml
index 6247aba8c1..fe86d896f8 100644
--- a/translation/dest/study/hi-IN.xml
+++ b/translation/dest/study/hi-IN.xml
@@ -15,6 +15,7 @@
जोड़ा गया (सबसे पुराना)
हाल ही में अद्यतित
सबसे लोकप्रिय
+ वर्णक्रमानुसार
एक नया अध्याय जोड़ें
- %s अध्याय
@@ -40,6 +41,7 @@
अब आप एक दर्शक हैं
PGN टैग
लाइक
+ नापसन्द करे
नया टैग
इस स्थिति पर टिप्पणी करें
इस चाल पर टिप्पणी करें
@@ -126,4 +128,9 @@
क्या इस पड़ाई से सम्बन्धित बातों को मिटा देना चाहिए? इससे पीछे जाने का कोई रास्ता शेष नहीं है!
अध्याय को मिटा दे
आप इसको खा से पड़ना चाहते है
+ अच्छी चाल!
+ गलती
+ अद्भुत चाल।
+ भयंकर गलती
+ दिलचस्प चाल |
diff --git a/translation/dest/study/hr-HR.xml b/translation/dest/study/hr-HR.xml
index 9541dc2742..614e75d015 100644
--- a/translation/dest/study/hr-HR.xml
+++ b/translation/dest/study/hr-HR.xml
@@ -43,7 +43,7 @@
Postao si gledatelj
PGN oznake
Sviđa mi se
- Ne sviđa mi se
+ Ne sviđa mi se
Nova oznaka
Komentiraj ovu poziciju
Komentiraj ovaj potez
@@ -131,4 +131,16 @@
Dali želite obrisati povijest razgovora? Nakon ovoga nema povratka!
Izbriši studiju
Gdje želiš to studirati?
+ Dobar potez
+ Greška
+ Briljantan potez
+ Gruba greška
+ Zanimljiv potez
+ Sumnjiv potez
+ Jedini potez
+ Nov potez
+ Razvoj
+ Inicijativa
+ Napad
+ Protunapad
diff --git a/translation/dest/study/id-ID.xml b/translation/dest/study/id-ID.xml
index 60a28050dc..780f2759b1 100644
--- a/translation/dest/study/id-ID.xml
+++ b/translation/dest/study/id-ID.xml
@@ -38,6 +38,7 @@
Sekarang Anda adalah penonton
Tagar PGN
Suka
+ Batal Suka
Tagar baru
Komentar di posisi ini
Komentari langkah ini
@@ -122,6 +123,7 @@
Bersihkan obrolan
Hapus riwayat obrolan studi? Ini tidak akan dapat mengulangkan kembali!
Hapus studi
+ Hapus seluruh studi? Tidak dapat kembal lagi! Tuliskan nama studi untuk konfirmasi: %s
Dimana Anda ingin mempelajarinya?
Langkah bagus
Kesalahan
diff --git a/translation/dest/study/kk-KZ.xml b/translation/dest/study/kk-KZ.xml
index 5f3ddfafb9..31d2c0ea5b 100644
--- a/translation/dest/study/kk-KZ.xml
+++ b/translation/dest/study/kk-KZ.xml
@@ -15,6 +15,7 @@
Құрылған күні (ескіден)
Жақында құрылған
Ең танымалдары
+ Әліппе ретімен
Жаңа бөлім құру
- %s бөлім
@@ -40,6 +41,7 @@
Сіз енді көрерменсіз
PGN тэгтері
Ұнату
+ Ұнатпаймын
Жаңа тэг
Осы тақта күйі туралы пікір қалдыру
Осы жүріс туралы пікір қалдыру
@@ -125,5 +127,33 @@
Чатты өшіру
Зерттеудің чат тарихын өшіресіз бе? Кері жол жоқ!
Зерттеуді жою
+ Бүкіл зерттеуді жоясыз ба? Қайтар жол жоқ. Растау үшін зерттеу атауын жазыңыз: %s
Бұл күйдің зерттеуін қай жерде бастайсыз?
+ Жақсы жүріс
+ Қате
+ Әдемі жүріс
+ Өрескел қателік
+ Қызық жүріс
+ Күмәнді жүріс
+ Жалғыз жүріс
+ Цугцванг
+ Күйлері шамалас
+ Күйі анық емес
+ Ақ сәл күштірек
+ Қара сәл күштірек
+ Ақтың жағдайы жақсы
+ Қараның жағдайы жақсы
+ Ақ жеңеді
+ Қара жеңеді
+ Жаңашылдық
+ Дамыту
+ Белсенді
+ Шабуыл
+ Қарсы шабуыл
+ Уақыт қаупі
+ Өтеумен
+ Бір оймен
+ Келесі бөлім
+ Алдыңғы бөлім
+ Зерттеу әрекеттері
diff --git a/translation/dest/study/lb-LU.xml b/translation/dest/study/lb-LU.xml
index 864d99f137..fb24d23a3c 100644
--- a/translation/dest/study/lb-LU.xml
+++ b/translation/dest/study/lb-LU.xml
@@ -41,7 +41,7 @@
Du bass elo en Zuschauer
PGN Tags
Gefällt mir
- Gefällt mer net méi
+ Gefällt mer net méi
Néien Tag
Kommentéier des Positioun
Kommentéier dësen Zuch
@@ -59,11 +59,11 @@
Lescht Säit
Deelen & exportéieren
Klonen
- PGN studéieren
+ Etüden PGN
All Partien eroflueden
Kapitel PGN
Partie eroflueden
- Etüd URL
+ Etüden URL
Aktuellt Kapitel URL
Zum Anbetten an een Forum oder Blog afügen
Mat Startpositioun ufänken
diff --git a/translation/dest/study/or-IN.xml b/translation/dest/study/or-IN.xml
index 76e15360d6..0ea8093111 100644
--- a/translation/dest/study/or-IN.xml
+++ b/translation/dest/study/or-IN.xml
@@ -9,17 +9,17 @@
- %sଟି ଅଧ୍ୟାୟ
ଅଧ୍ୟୟନକୁ ଆମନ୍ତ୍ରଣ କରନ୍ତୁ
- ଅଧ୍ୟୟନ ଛାଡ଼ି ଦିଅନ୍ତୁ
+ ଅଧ୍ୟୟନ ଛାଡ଼ି ଦିଅନ୍ତୁ
PGN ଟ୍ୟାଗ୍
ପସନ୍ଦ କରନ୍ତୁ
- ପସନ୍ଦ ହଟାନ୍ତୁ
+ ପସନ୍ଦ ହଟାନ୍ତୁ
ଅଂଶୀଦାର ଓ ରପ୍ତାନି
ଅଧ୍ୟୟନର PGN
ଅଧ୍ୟାୟର PGN
ଅଧ୍ୟୟନ URL
ଆପଣଙ୍କ ୱେବସାଇଟ୍ ରେ ଗ୍ରନ୍ଥନ କରନ୍ତୁ
ଗ୍ରନ୍ଥନ ବିଷୟରେ ଅଧିକ ପଢ଼ନ୍ତୁ
- କେବଳ ସାର୍ବଜନୀନ ଅଧ୍ୟୟନଗୁଡ଼ିକ ଗ୍ରଥିତ ହୋଇପାରିବ!
+ କେବଳ ସାର୍ବଜନୀନ ଅଧ୍ୟୟନଗୁଡ଼ିକ ଗ୍ରଥିତ ହୋଇପାରିବ!
ଖୋଲନ୍ତୁ
ଅଧ୍ୟୟନ ମିଳିଲା ନାହିଁ
ଅଧ୍ୟାୟ ସମ୍ପାଦନା
diff --git a/translation/dest/swiss/de-CH.xml b/translation/dest/swiss/de-CH.xml
index 676e31d477..1960411642 100644
--- a/translation/dest/swiss/de-CH.xml
+++ b/translation/dest/swiss/de-CH.xml
@@ -38,7 +38,7 @@
Wänn sölläd Turniär noch Schwiizer Syschtem anschtatt Arena-Turniär verwändät wärdä?
Imänä Turniär mit Schwiiizer System, spiiläd alli glich viil partie und jede spielt nur einisch gege jede andär. Ds chöönt ä guäti Waal si für Cklubs und offiziäli Turniär.
Wiä wärdäd Pünkt berächnät?
- Ein Sieg ist einen Punkt wert, ein Remis einen halben Punkt, und eine Niederlage null Punkte. Wenn ein Spieler während einer Runde keinem anderen Spieler zugeteilt werden kann, erhält er einen Punkt.
+ En Sieg isch ein Punkt wärt, es Remi zwei, und wenn verlürsch bechumsch keine. Wenn du kein Gänger hesch, bechumsch eifoch en Punkt.
Wie wärdäd Tie-Breaks berächnät?
Sonneborn-Berger-Wärtig
Mit dem %s.
@@ -46,7 +46,7 @@ Addiere die Punkte aller Gegner, die der Spieler besiegt, und die Hälfte der Pu
Wiä wärdäd Paarigä entschidä?
Niderländischs Syschtem
FIDE-Handbuäch
- Mit dem %1$s, implementiert von %2$s, in Übereinstimmung mit den %3$s.
+ Mit dem %1$s, implementiert von %2$s, in Übereinstimmung mit dem %3$s.
Was passiert, wenn das Turnier mehr Runden als Spieler hat?
Wenn alle möglichen Paarungen gespielt wurden, wird das Turnier beendet und ein Sieger erklärt.
Wieso isch es uf Teams beschränkt?
@@ -75,15 +75,17 @@ Am nähesten an ein online Rundenturnier kommt, ein Turnier nach Schweizer Syste
Schtartät grad
Vergliich
Längi vom Turniär
- Vordefinierte Dauer in Minuten
+ Vordefinierti Duuer ih Minutä
Vordefinierte maximale Rundenanzahl, aber unbekannte Dauer
Aazahl Partiiä
So viele wie in der zugewiesenen Dauer gespielt werden können
Im Voraus entschieden, für alle Spieler gleich
Paarigssüschtem
- Jeder verfügbare Gegner mit ähnlichem Rang
- Beste Paarung, basierend auf Punkten und Feinwertungen
+ Jede verfügbari Gägner mit ähnlichem Rang
+ Besti Paarig, basierend uf Pünkt und Feinwertungen
Warteziit für d Paarig
+ Schnäll: wartet nöd uf all Spieler
+ Langsam: wartet uuf all Spieler
Idäntischi Paarig
Möglich, abär nöd hindäränand
Verbottä
diff --git a/translation/dest/swiss/fr-FR.xml b/translation/dest/swiss/fr-FR.xml
index a815c3a06d..141d924bb6 100644
--- a/translation/dest/swiss/fr-FR.xml
+++ b/translation/dest/swiss/fr-FR.xml
@@ -19,7 +19,7 @@
- %s secondes entre les rondes
- - %s minute entre les rondes
+ - %s minutes entre les rondes
- %s minutes entre les rondes
Commence dans
@@ -87,8 +87,8 @@ Pourquoi? Parce qu\'il n\'existe pas de façon équitable de résoudre le probl
N\'importe quel adversaire disponible avec un classement similaire
Meilleur appariement selon le nombre de points et le départage
Temps d\'attente pour l\'appariement
- Rapide : n\'attend pas tous les joueurs
- Lent : attend tous les joueurs
+ Rapide : n\'attend pas tous les joueurs
+ Lent : attend tous les joueurs
Appariement identique
Possible, mais pas de façon consécutive
Interdit
diff --git a/translation/dest/swiss/he-IL.xml b/translation/dest/swiss/he-IL.xml
index b6ae168d59..0c19e83700 100644
--- a/translation/dest/swiss/he-IL.xml
+++ b/translation/dest/swiss/he-IL.xml
@@ -11,6 +11,8 @@
בטורניר שוויצרי, כל השחקנים משחקים אותו מספר של משחקים, ויכולים לשחק אחד נגד השני פעם אחת בלבד. זאת יכולה להיות אפשרות טובה למועדונים וטורנירים רשמיים.
האם טורנירים שוויצריים יחליפו את טורנירי הזירות?
לא. הם מאפיינים משלימים.
+ משך הטורניר
+ מספר משחקים
כל יריב זמין עם דירוג דומה
מהיר: אין צורך לחכות לשאר השחקנים
איטי: צריך לחכות לסיום המשחקים של כל השחקנים
diff --git a/translation/dest/swiss/id-ID.xml b/translation/dest/swiss/id-ID.xml
index f163b3563f..ba011035d5 100644
--- a/translation/dest/swiss/id-ID.xml
+++ b/translation/dest/swiss/id-ID.xml
@@ -22,10 +22,15 @@
- Permainan yang sedang berlangsung
+ Tanggal mulai turnamen
Jumlah babak
Sebuah babak yang ganjil mengizinkan keseimbangan warna yang optimal.
+ Jarak antar ronde
Turnamen Swiss baru
penilaian Sonneborn–Berger
+ Dapatkah pemain telat bergabung?
+ Ya, sampai lebih dari setengah putaran dimulai; misalnya dalam 11 ronde swiss, pemain dapat bergabung sebelum ronde 6 dimulai dan dalam 12 ronde, sebelum ronde 7 dimulai.
+Peserta yang terlambat mendapatkan satu \"bye\", bahkan jika mereka melewatkan beberapa ronde.
Sedang berlangsung
Segera dimulai
Perbandingan
diff --git a/translation/dest/team/id-ID.xml b/translation/dest/team/id-ID.xml
index 8b736bdcd8..38f6915a2c 100644
--- a/translation/dest/team/id-ID.xml
+++ b/translation/dest/team/id-ID.xml
@@ -6,6 +6,7 @@
- %s anggota
Semua tim
+ Tim yang dipimpin
Tim baru
Tim saya
Tidak ada tim yang ditemukan
@@ -24,6 +25,7 @@
Permintaan bergabung Anda akan ditinjau oleh ketua tim.
Permintaan bergabung Anda telah ditinjau oleh ketua tim.
+ Permintaan bergabung Anda telah ditolak oleh ketua tim.
Berlangganan pesan tim
Pertarungan Tim
Pertarungan antara bebrapa tim, setiap pemain mencetak poin untuk timnya
@@ -47,4 +49,5 @@ Pemain yang tidak suka menerima pesan anda dapat meninggalkan tim.
Tim ini sudah ada.
Turnamen selanjutnya
Turnamen yang telah selesai
+ Permintaan yang Ditolak
diff --git a/translation/dest/team/or-IN.xml b/translation/dest/team/or-IN.xml
index ffed1d7fc6..3a1526b68e 100644
--- a/translation/dest/team/or-IN.xml
+++ b/translation/dest/team/or-IN.xml
@@ -1,7 +1,7 @@
Team
- ଦଳଗୁଡ଼ିକ
+ ଦଳଗୁଡ଼ିକ
ସମସ୍ତ ଦଳ
ନୂତନ ଦଳ
ମୋର ଦଳଗୁଡିକ
@@ -17,7 +17,7 @@
ଆପଣ କାହାକୁ ଦଳରୁ ବାହାର କରିବାକୁ ଚାହୁଁଛନ୍ତି?
ଆପଣଙ୍କର ଯୋଗଦାନ ଅନୁରୋଧ, ଦଳର ନେତାଙ୍କ ଦ୍ୱାରା ସମୀକ୍ଷା କରାଯିବ
ଆପଣଙ୍କର ଯୋଗଦାନ ଅନୁରୋଧ, ଦଳର ନେତାଙ୍କ ଦ୍ୱାରା ସମୀକ୍ଷା କରାଯାଉଛି
- ଦଳ ବାର୍ତ୍ତାଗୁଡ଼ିକୁ ସଦସ୍ୟ ହୁଅନ୍ତୁ
+ ଦଳ ବାର୍ତ୍ତାର ସଦସ୍ୟ ହୁଅନ୍ତୁ
ଦଳ ଯୁଦ୍ଧ
ଏକାଧିକ ଦଳର ଯୁଦ୍ଧରେ, ପ୍ରତ୍ୟେକ ଖେଳାଳି ନିଜ ଦଳ ପାଇଁ ପଏଣ୍ଟ ସ୍କୋର କରନ୍ତି
ଦଳ ଟୁର୍ନାମେଣ୍ଟ
diff --git a/translation/dest/ublog/ca-ES.xml b/translation/dest/ublog/ca-ES.xml
index 8425623a99..326eba9676 100644
--- a/translation/dest/ublog/ca-ES.xml
+++ b/translation/dest/ublog/ca-ES.xml
@@ -11,10 +11,26 @@
Publicat
Publica al teu blog
Si es marca l\'entrada es publicarà al teu blog. Sinó, serà privada i només es mostrarà en les publicacions en esborrany
+
+ - Ha publicat una entrada nova
+ - Ha publicat %s entrades noves
+
+
+ - Una visualització
+ - %s visualitzacions
+
+ %1$s ha publicat %2$s
Aquesta entrada està publicada
Això és un esborrany
+ Més publicacions de %s
+ Encara no hi ha publicacions en aquest blog.
Cap esborrany a mostrar.
Les últimes publicacions en el blog
+
+ - Veure una publicació
+ - Veure les %s publicacions
+
Puja una imatge de per la teva entrada
+ Text alternatiu de la imatge
Crèdits de la imatge
diff --git a/translation/dest/ublog/cs-CZ.xml b/translation/dest/ublog/cs-CZ.xml
index b8088f6cee..6f354d3216 100644
--- a/translation/dest/ublog/cs-CZ.xml
+++ b/translation/dest/ublog/cs-CZ.xml
@@ -1,5 +1,6 @@
+ Blog hráče %s
Nový příspěvek
Upravit svůj příspěvek na blogu
Uložit koncept
diff --git a/translation/dest/ublog/de-CH.xml b/translation/dest/ublog/de-CH.xml
index 56befb4257..fe56ae8e06 100644
--- a/translation/dest/ublog/de-CH.xml
+++ b/translation/dest/ublog/de-CH.xml
@@ -10,17 +10,17 @@
Entwürf
Veröffentlicht
Veröffentlich i dim Blog
- Wenn aktiviert, wird der Beitrag in deinem Blog aufgelistet. Wenn nicht, wird er privat sein, in deinen Entwürfen
+ Wänn aktiviert wird de Biitrag ih diim Blog uufgelistet. Wenn nöd wird er ih diine Entwürf privat sii
- - Hat einen Blog-Beitrag veröffentlicht
- - Hat %s Blog-Beiträge veröffentlicht
+ - Hät en Blog-Biitrag veröffentlicht
+ - Hät %s Blog-Biiträg veröffentlicht
- Eimal aagluägt
- %s-mal aagluägt
- %1$s hat %2$s veröffentlicht
- Dieser Beitrag wurde veröffentlicht
+ %1$s hät %2$s veröffentlicht
+ De Biitrag isch veröffentlicht worde
Das isch än Entwurf
Meh Blog-Posts vo %s
No kei Posts i dem Blog.
diff --git a/translation/dest/ublog/en-US.xml b/translation/dest/ublog/en-US.xml
index 45e7301b86..74034e2810 100644
--- a/translation/dest/ublog/en-US.xml
+++ b/translation/dest/ublog/en-US.xml
@@ -31,6 +31,6 @@
- View all %s posts
Upload an image for your post
- Image alternative text
- Image credit
+ Image alternative text
+ Image credit
diff --git a/translation/dest/ublog/ko-KR.xml b/translation/dest/ublog/ko-KR.xml
index 3ea04e700d..bcb373c436 100644
--- a/translation/dest/ublog/ko-KR.xml
+++ b/translation/dest/ublog/ko-KR.xml
@@ -1,2 +1,12 @@
-
+
+ %s의 블로그
+ 새 글 쓰기
+ 제목
+ 본문
+ 임시 보관 글
+ 게시된 글
+ 더 많은 %s의 글
+ 이 블로그에는 아직 글이 없습니다.
+ 임시저장된 글이 없습니다.
+
diff --git a/translation/dest/ublog/or-IN.xml b/translation/dest/ublog/or-IN.xml
index 0878279911..dedf91670c 100644
--- a/translation/dest/ublog/or-IN.xml
+++ b/translation/dest/ublog/or-IN.xml
@@ -2,13 +2,13 @@
ପ୍ରକାଶିତ ହୋଇଛି
ଆପଣଙ୍କ ବ୍ଲଗ୍ ରେ ପ୍ରକାଶନ କରନ୍ତୁ
- ଯଦି ଯାଞ୍ଚ କରାଯାଏ, ତେବେ ପୋଷ୍ଟଟି ଆପଣଙ୍କ ବ୍ଲଗରେ ତାଲିକାଭୁକ୍ତ ହେବ। ଯଦି ନୁହେଁ, ତେବେ ଏହା ଆପଣଙ୍କ ଡ୍ରାଫ୍ଟ ପୋଷ୍ଟଗୁଡ଼ିକରେ ବ୍ୟକ୍ତିଗତ ରହିବ
+ ଯଦି ଯାଞ୍ଚ କରାଯାଏ, ତେବେ ପୋଷ୍ଟଟି ଆପଣଙ୍କ ବ୍ଲଗରେ ତାଲିକାଭୁକ୍ତ ହେବ। ଯଦି ନୁହେଁ, ତେବେ ଏହା ଆପଣଙ୍କ ଡ୍ରାଫ୍ଟ ପୋଷ୍ଟଗୁଡ଼ିକରେ ବ୍ୟକ୍ତିଗତ ରହିବ
- ଏକ ବ୍ଲଗ୍ ପୋଷ୍ଟ ପ୍ରକାଶିତ ହୋଇଛି
- %sଟି ବ୍ଲଗ୍ ପୋଷ୍ଟ ପ୍ରକାଶିତ ହୋଇଛି
%1$s %2$s ପ୍ରକାଶ କରିଛନ୍ତି
ଏହି ପୋଷ୍ଟ ପ୍ରକାଶିତ ହୋଇଛି
- ପ୍ରତିଛବି ବିକଳ୍ପ ପାଠ୍ୟ
- ପ୍ରତିଛବି କ୍ରେଡିଟ୍
+ ପ୍ରତିଛବି ବିକଳ୍ପ ପାଠ୍ୟ
+ ପ୍ରତିଛବି କ୍ରେଡିଟ୍
diff --git a/translation/source/challenge.xml b/translation/source/challenge.xml
index dedbdae844..097a4fce7f 100644
--- a/translation/source/challenge.xml
+++ b/translation/source/challenge.xml
@@ -4,7 +4,7 @@
Challenge to a game
Challenge declined
Challenge accepted!
- Challenge canceled.
+ Challenge cancelled.
Please register to send challenges.
You cannot challenge %s.
%s does not accept challenges.
diff --git a/ui/@types/lichess/index.d.ts b/ui/@types/lichess/index.d.ts
index 1520451db0..382eeca0e2 100644
--- a/ui/@types/lichess/index.d.ts
+++ b/ui/@types/lichess/index.d.ts
@@ -1,5 +1,5 @@
interface Lichess {
- load: Promise; // window.onload promise
+ load: Promise; // DOMContentLoaded promise
info: any;
requestIdleCallback(f: () => void, timeout?: number): void;
sri: string;
@@ -102,7 +102,7 @@ interface UserCompleteOpts {
}
interface SoundI {
- loadOggOrMp3(name: string, path: string): void;
+ loadOggOrMp3(name: string, path: string, noSoundSet?: boolean): void;
loadStandard(name: string, soundSet?: string): void;
play(name: string, volume?: number): void;
playOnce(name: string): void;
diff --git a/ui/analyse/css/_action-menu.scss b/ui/analyse/css/_action-menu.scss
index 6ceb904b4f..c1945231b3 100644
--- a/ui/analyse/css/_action-menu.scss
+++ b/ui/analyse/css/_action-menu.scss
@@ -82,11 +82,6 @@
text-align: right;
margin-top: 6px;
- .button {
- display: inline-block;
- padding: 0 8px;
- }
-
.button::before {
font-size: 1.2em;
}
diff --git a/ui/analyse/css/_player-clock.scss b/ui/analyse/css/_player-clock.scss
index bcef13b290..b095074075 100644
--- a/ui/analyse/css/_player-clock.scss
+++ b/ui/analyse/css/_player-clock.scss
@@ -14,6 +14,10 @@ $clock-height: 20px;
&.bottom {
top: var(--cg-height, 100%);
z-index: 1; // over the board coords
+
+ .is3d & {
+ top: calc(var(--cg-height, 100%) + 15px);
+ }
}
/* Where to put them in col1 layout? It moves the entire board and controls down for little benefit */
diff --git a/ui/analyse/src/ctrl.ts b/ui/analyse/src/ctrl.ts
index 5026dfb458..89e0d76c72 100644
--- a/ui/analyse/src/ctrl.ts
+++ b/ui/analyse/src/ctrl.ts
@@ -389,7 +389,7 @@ export default class AnalyseCtrl {
userJump = (path: Tree.Path): void => {
this.autoplay.stop();
- this.withCg(cg => cg.selectSquare(null));
+ if (!this.gamebookPlay()) this.withCg(cg => cg.selectSquare(null));
if (this.practice) {
const prev = this.path;
this.practice.preUserJump(prev, path);
@@ -794,7 +794,8 @@ export default class AnalyseCtrl {
this.tree.merge(data.tree);
if (!this.showComputer()) this.tree.removeComputerVariations();
this.data.analysis = data.analysis;
- if (data.analysis) data.analysis.partial = !!treeOps.findInMainline(data.tree, n => !n.eval && !!n.children.length);
+ if (data.analysis)
+ data.analysis.partial = !!treeOps.findInMainline(data.tree, n => !n.eval && !!n.children.length && n.ply <= 300);
if (data.division) this.data.game.division = data.division;
if (this.retro) this.retro.onMergeAnalysisData();
if (this.study) this.study.serverEval.onMergeAnalysisData();
diff --git a/ui/analyse/src/evalCache.ts b/ui/analyse/src/evalCache.ts
index e38e9a3ef6..29e5ceb2e3 100644
--- a/ui/analyse/src/evalCache.ts
+++ b/ui/analyse/src/evalCache.ts
@@ -73,12 +73,20 @@ function toCeval(e: Tree.ServerEval): Tree.ClientEval {
export function make(opts: EvalCacheOpts): EvalCache {
let fetchedByFen: Dictionary = {};
const upgradable = prop(false);
- lichess.pubsub.on('socket.in.crowd', d => upgradable(d.nb > 2));
+ lichess.pubsub.on('socket.in.crowd', d => upgradable(d.nb > 2 && d.nb < 99999));
return {
- onCeval: throttle(500, function () {
+ onCeval: throttle(500, () => {
const node = opts.getNode(),
ev = node.ceval;
- if (ev && !ev.cloud && node.fen in fetchedByFen && qualityCheck(ev) && opts.canPut()) {
+ const fetched = fetchedByFen[node.fen];
+ if (
+ ev &&
+ !ev.cloud &&
+ node.fen in fetchedByFen &&
+ (!fetched || fetched.depth < ev.depth) &&
+ qualityCheck(ev) &&
+ opts.canPut()
+ ) {
opts.send('evalPut', toPutData(opts.variant, ev));
}
}),
diff --git a/ui/analyse/src/explorer/explorerConfig.ts b/ui/analyse/src/explorer/explorerConfig.ts
index c3f947e2fb..833443eb19 100644
--- a/ui/analyse/src/explorer/explorerConfig.ts
+++ b/ui/analyse/src/explorer/explorerConfig.ts
@@ -18,6 +18,13 @@ const allRatings = [1600, 1800, 2000, 2200, 2500];
const minYear = 1952;
type Month = string;
+type ByDbSetting = {
+ since: StoredProp;
+ until: StoredProp;
+};
+type ByDbSettings = {
+ [key in ExplorerDb]: ByDbSetting;
+};
export interface ExplorerConfigData {
open: Prop;
@@ -25,14 +32,14 @@ export interface ExplorerConfigData {
rating: StoredJsonProp;
speed: StoredJsonProp;
mode: StoredJsonProp;
- since: StoredProp;
- until: StoredProp;
+ byDbData: ByDbSettings;
playerName: {
open: Prop;
value: StoredProp;
previous: StoredJsonProp;
};
color: Prop;
+ byDb(): ByDbSetting;
}
export class ExplorerConfigCtrl {
@@ -43,20 +50,29 @@ export class ExplorerConfigCtrl {
constructor(readonly root: AnalyseCtrl, readonly variant: VariantKey, readonly onClose: () => void) {
this.myName = document.body.dataset['user'];
if (variant === 'standard') this.allDbs.unshift('masters');
+ const byDbData = {} as ByDbSettings;
+ for (const db of this.allDbs) {
+ byDbData[db] = {
+ since: storedProp('explorer.since-2.' + db, ''),
+ until: storedProp('explorer.until-2.' + db, ''),
+ };
+ }
this.data = {
open: prop(false),
db: storedProp('explorer.db.' + variant, this.allDbs[0]),
rating: storedJsonProp('explorer.rating', () => allRatings),
speed: storedJsonProp('explorer.speed', () => allSpeeds),
mode: storedJsonProp('explorer.mode', () => allModes),
- since: storedProp('explorer.since-2', ''),
- until: storedProp('explorer.until-2', ''),
+ byDbData,
playerName: {
open: prop(false),
value: storedProp('explorer.player.name', document.body.dataset['user'] || ''),
previous: storedJsonProp('explorer.player.name.previous', () => []),
},
color: prop('white'),
+ byDb() {
+ return this.byDbData[this.db() as ExplorerDb] || this.byDbData.lichess;
+ },
};
}
@@ -91,8 +107,8 @@ export class ExplorerConfigCtrl {
};
fullHouse = () =>
- this.data.since() <= `${minYear}-01` &&
- (!this.data.until() || new Date().toISOString().slice(0, 7) <= this.data.until()) &&
+ this.data.byDb().since() <= `${minYear}-01` &&
+ (!this.data.byDb().until() || new Date().toISOString().slice(0, 7) <= this.data.byDb().until()) &&
(this.data.db() === 'masters' || this.data.speed().length == allSpeeds.length) &&
(this.data.db() !== 'lichess' || this.data.rating().length == allRatings.length) &&
(this.data.db() !== 'player' || this.data.mode().length == allModes.length);
@@ -155,8 +171,8 @@ const playerDb = (ctrl: ExplorerConfigCtrl) => {
const masterDb = (ctrl: ExplorerConfigCtrl) =>
h('div', [
h('section.date', [
- h('label', ['Since', yearInput(ctrl.data.since, () => '', ctrl.root.redraw)]),
- h('label', ['Until', yearInput(ctrl.data.until, ctrl.data.since, ctrl.root.redraw)]),
+ h('label', ['Since', yearInput(ctrl.data.byDb().since, () => '', ctrl.root.redraw)]),
+ h('label', ['Until', yearInput(ctrl.data.byDb().until, ctrl.data.byDb().since, ctrl.root.redraw)]),
]),
]);
@@ -258,8 +274,8 @@ const yearInput = (prop: StoredProp, after: () => Month, redraw: Redraw)
const monthSection = (ctrl: ExplorerConfigCtrl) =>
h('section.date', [
- h('label', ['Since', monthInput(ctrl.data.since, () => '', ctrl.root.redraw)]),
- h('label', ['Until', monthInput(ctrl.data.until, ctrl.data.since, ctrl.root.redraw)]),
+ h('label', ['Since', monthInput(ctrl.data.byDb().since, () => '', ctrl.root.redraw)]),
+ h('label', ['Until', monthInput(ctrl.data.byDb().until, ctrl.data.byDb().since, ctrl.root.redraw)]),
]);
const playerModal = (ctrl: ExplorerConfigCtrl) => {
diff --git a/ui/analyse/src/explorer/explorerView.ts b/ui/analyse/src/explorer/explorerView.ts
index b95ee6ab04..47ba17d236 100644
--- a/ui/analyse/src/explorer/explorerView.ts
+++ b/ui/analyse/src/explorer/explorerView.ts
@@ -358,7 +358,7 @@ const explorerTitle = (explorer: ExplorerCtrl) => {
nodes
);
const playerName = explorer.config.data.playerName.value();
- const masterDbExplanation = explorer.root.trans('masterDbExplanation', 2200, '1952', '2020'),
+ const masterDbExplanation = explorer.root.trans('masterDbExplanation', 2200, '1952', '2021'),
lichessDbExplanation = 'Rated games sampled from all Lichess players';
return h('div.explorer-title', [
db == 'masters'
diff --git a/ui/analyse/src/explorer/explorerXhr.ts b/ui/analyse/src/explorer/explorerXhr.ts
index fb6c821c66..8054ce80e1 100644
--- a/ui/analyse/src/explorer/explorerXhr.ts
+++ b/ui/analyse/src/explorer/explorerXhr.ts
@@ -20,17 +20,18 @@ export async function opening(
processData: (data: ExplorerData) => void
): Promise {
const conf = opts.config;
+ const confByDb = conf.byDb();
const url = new URL(`/${opts.db}`, opts.endpoint);
const params = url.searchParams;
params.set('variant', opts.variant || 'standard');
params.set('fen', opts.rootFen);
params.set('play', opts.play.join(','));
if (opts.db === 'masters') {
- if (conf.since()) params.set('since', conf.since().split('-')[0]);
- if (conf.until()) params.set('until', conf.until().split('-')[0]);
+ if (confByDb.since()) params.set('since', confByDb.since().split('-')[0]);
+ if (confByDb.until()) params.set('until', confByDb.until().split('-')[0]);
} else {
- if (conf.since()) params.set('since', conf.since());
- if (conf.until()) params.set('until', conf.until());
+ if (confByDb.since()) params.set('since', confByDb.since());
+ if (confByDb.until()) params.set('until', confByDb.until());
params.set('speeds', conf.speed().join(','));
}
if (opts.db === 'lichess') {
diff --git a/ui/analyse/src/study/commentForm.ts b/ui/analyse/src/study/commentForm.ts
index 6bd6eb8412..40323cda9a 100644
--- a/ui/analyse/src/study/commentForm.ts
+++ b/ui/analyse/src/study/commentForm.ts
@@ -107,7 +107,7 @@ export function view(root: AnalyseCtrl): VNode {
return h(
'div.study__comments',
{
- // hook: onInsert(() => root.enableWiki(true)),
+ hook: onInsert(() => root.enableWiki(true)),
},
[
currentComments(root, !study.members.canContribute()),
@@ -126,7 +126,7 @@ export function view(root: AnalyseCtrl): VNode {
},
}),
]),
- h('div.analyse__wiki.study__wiki.empty'),
+ h('div.analyse__wiki.study__wiki'),
]
);
}
diff --git a/ui/analyse/src/study/practice/studyPracticeCtrl.ts b/ui/analyse/src/study/practice/studyPracticeCtrl.ts
index 5e1b20c0c1..669170312e 100644
--- a/ui/analyse/src/study/practice/studyPracticeCtrl.ts
+++ b/ui/analyse/src/study/practice/studyPracticeCtrl.ts
@@ -15,8 +15,8 @@ export default function (root: AnalyseCtrl, studyData: StudyData, data: StudyPra
analysisUrl = prop(''),
autoNext = storedProp('practice-auto-next', true);
- lichess.sound.loadOggOrMp3('practiceSuccess', `${lichess.sound.baseUrl}/other/energy3`);
- lichess.sound.loadOggOrMp3('practiceFailure', `${lichess.sound.baseUrl}/other/failure2`);
+ lichess.sound.loadOggOrMp3('practiceSuccess', `${lichess.sound.baseUrl}/other/energy3`, true);
+ lichess.sound.loadOggOrMp3('practiceFailure', `${lichess.sound.baseUrl}/other/failure2`, true);
function onLoad() {
root.showAutoShapes = readOnlyProp(true);
diff --git a/ui/analyse/src/study/studyMembers.ts b/ui/analyse/src/study/studyMembers.ts
index b53d8a9fb3..6f23bb6751 100644
--- a/ui/analyse/src/study/studyMembers.ts
+++ b/ui/analyse/src/study/studyMembers.ts
@@ -1,5 +1,6 @@
import { prop, Prop } from 'common';
-import { bind, onInsert, dataIcon } from 'common/snabbdom';
+import { textRaw as xhrTextRaw } from 'common/xhr';
+import { bind, onInsert, dataIcon, bindNonPassive } from 'common/snabbdom';
import { h, VNode } from 'snabbdom';
import { AnalyseSocketSend } from '../socket';
import { iconTag, scrollTo, titleNameToId } from '../util';
@@ -310,20 +311,12 @@ export function view(ctrl: StudyCtrl): VNode {
'form.admin',
{
key: ':admin',
- attrs: {
- method: 'post',
- action: `/study/${ctrl.data.id}/admin`,
- },
+ hook: bindNonPassive('submit', () => {
+ xhrTextRaw(`/study/${ctrl.data.id}/admin`, { method: 'post' }).then(() => location.reload());
+ return false;
+ }),
},
- [
- h(
- 'button.button.button-red.button-thin',
- {
- attrs: { type: 'submit' },
- },
- 'Enter as admin'
- ),
- ]
+ [h('button.button.button-red.button-thin', 'Enter as admin')]
)
: null,
]
diff --git a/ui/analyse/src/wiki.ts b/ui/analyse/src/wiki.ts
index 822a2d279a..e39c660cb5 100644
--- a/ui/analyse/src/wiki.ts
+++ b/ui/analyse/src/wiki.ts
@@ -31,8 +31,8 @@ export default function wikiTheory(): WikiTheory {
return debounce(
async (nodes: Tree.Node[]) => {
const pathParts = nodes.slice(1).map(n => `${plyPrefix(n)}${n.san}`);
- const path = pathParts.join('/');
- if (!path) show('');
+ const path = pathParts.join('/').replace(/[+!#?]/g, '') ?? '';
+ if (pathParts.length > 30 || !path || path.length > 255 - 21) show('');
else if (cache.has(path)) show(cache.get(path)!);
else if (
Array.from({ length: pathParts.length }, (_, i) => -i - 1)
@@ -50,8 +50,11 @@ export default function wikiTheory(): WikiTheory {
};
if (res.ok) {
const json = await res.json();
- if (json.query.pages[0].missing) saveAndShow('');
- else saveAndShow(transform(json.query.pages[0].extract, title));
+ const page = json.query.pages[0];
+ if (page.missing) saveAndShow('');
+ else if (page.invalid) show('invalid request: ' + page.invalidreason);
+ else if (!page.extract) show('error: unexpected API response:
' + JSON.stringify(page) + '
');
+ else saveAndShow(transform(page.extract, title));
} else saveAndShow('');
} catch (err) {
show('error: ' + err);
diff --git a/ui/ceval/src/ctrl.ts b/ui/ceval/src/ctrl.ts
index b33d94ecdc..925b505a87 100644
--- a/ui/ceval/src/ctrl.ts
+++ b/ui/ceval/src/ctrl.ts
@@ -117,7 +117,11 @@ export default function (opts: CevalOpts): CevalCtrl {
Math.min(Math.ceil((navigator.hardwareConcurrency || 1) / 4), maxThreads)
);
- const maxHashSize = Math.min(((navigator.deviceMemory || 0.25) * 1024) / 8, growableSharedMem ? 1024 : 16);
+ const estimatedMinMemory = technology == 'hce' || technology == 'nnue' ? 2.0 : 0.5;
+ const maxHashSize = Math.min(
+ ((navigator.deviceMemory || estimatedMinMemory) * 1024) / 8,
+ growableSharedMem ? 1024 : 16
+ );
const hashSize = storedProp(storageKey('ceval.hash-size'), 16);
const multiPv = storedProp(storageKey('ceval.multipv'), opts.multiPvDefault || 1);
diff --git a/ui/ceval/src/view.ts b/ui/ceval/src/view.ts
index 9abcde3f77..f2b54d6d72 100644
--- a/ui/ceval/src/view.ts
+++ b/ui/ceval/src/view.ts
@@ -232,7 +232,7 @@ export function renderCeval(ctrl: ParentCtrl): VNode | undefined {
: h(
'div.switch',
{
- attrs: { title: trans.noarg('toggleLocalEvaluation') + ' (l)' },
+ attrs: { title: trans.noarg('toggleLocalEvaluation') + ' (L)' },
},
[
h('input#analyse-toggle-ceval.cmn-toggle.cmn-toggle--subtle', {
diff --git a/ui/common/css/component/_loader.scss b/ui/common/css/component/_loader.scss
index 76139639ee..d1dd3b51e0 100644
--- a/ui/common/css/component/_loader.scss
+++ b/ui/common/css/component/_loader.scss
@@ -1,34 +1,49 @@
/* circular loader */
+$len1: 33.96;
+$len2: 42.6;
+$len3: 104.31;
+$total_len: $len1 + $len2 + $len3;
+
@keyframes mask1 {
0% {
- stroke-dashoffset: 1;
+ stroke-dashoffset: $total_len;
}
- 17.86% {
- stroke-dashoffset: 0;
+ 100% {
+ stroke-dashoffset: -$total_len;
}
}
@keyframes mask2 {
0% {
- stroke-dashoffset: 1;
+ stroke-dashoffset: $total_len + $len1;
}
- 17.86% {
- stroke-dashoffset: 1;
- }
- 31.43% {
- stroke-dashoffset: 0;
+ 100% {
+ stroke-dashoffset: -$total_len + $len1;
}
}
@keyframes mask3 {
0% {
- stroke-dashoffset: 1;
+ stroke-dashoffset: $total_len + $len1 + $len2;
}
- 31.43% {
- stroke-dashoffset: 1;
+ 100% {
+ stroke-dashoffset: -$total_len + $len1 + $len2;
}
- 85% {
- stroke-dashoffset: 0;
+}
+
+@keyframes spinner-color {
+ 0%,
+ 100% {
+ stroke: $c-primary;
+ }
+ 25% {
+ stroke: $c-error;
+ }
+ 50% {
+ stroke: $c-secondary;
+ }
+ 75% {
+ stroke: $c-brag;
}
}
@@ -53,18 +68,25 @@
height: 70px;
margin: auto;
- path:nth-child(1) {
- animation: mask1 2s cubic-bezier(0.417, 0.086, 0.741, 0.452) infinite;
+ path {
+ animation: mask1 2.75s cubic-bezier(0.49, 0.67, 0.45, 0.29) infinite;
}
path:nth-child(2) {
- animation: mask2 2s cubic-bezier(0.333, 0.317, 0.621, 0.661) infinite;
+ animation-name: mask2;
}
path:nth-child(3) {
- animation: mask3 2s cubic-bezier(0, 0, 0.431, 1) infinite;
+ animation-name: mask3;
+ }
+
+ @media (prefers-reduced-motion: reduce) {
+ path {
+ animation: none !important;
+ }
}
g {
- animation: spinner-color 8s steps(1) infinite;
+ animation: spinner-color 11s steps(1) infinite;
+ stroke-dasharray: $total_len $total_len;
}
.white & path {
diff --git a/ui/common/css/vendor/chessground/_coords.scss b/ui/common/css/vendor/chessground/_coords.scss
index 0c6916eaa6..8d3a5a676f 100644
--- a/ui/common/css/vendor/chessground/_coords.scss
+++ b/ui/common/css/vendor/chessground/_coords.scss
@@ -89,8 +89,11 @@ coords {
text-align: center;
}
- coord {
- color: $c-font-page !important;
+ &.ranks,
+ .is2d &.files {
+ coord {
+ color: $c-font-page !important;
+ }
}
}
}
diff --git a/ui/common/src/spinner.ts b/ui/common/src/spinner.ts
index 90c7b71eef..413b7802b6 100644
--- a/ui/common/src/spinner.ts
+++ b/ui/common/src/spinner.ts
@@ -29,18 +29,9 @@ export default function (): VNode {
attrs: {
mask: 'url(#mask)',
fill: 'none',
- stroke: '#888',
- 'stroke-dasharray': 1,
},
},
- pathAttrs.map(attrs => {
- return h('path', {
- attrs: {
- pathLength: 1, // cannot be inherited from parent group
- ...attrs,
- },
- });
- })
+ pathAttrs.map(attrs => h('path', { attrs }))
),
]),
]
diff --git a/ui/dasher/src/piece.ts b/ui/dasher/src/piece.ts
index dc8c3452fe..a5cd0ded92 100644
--- a/ui/dasher/src/piece.ts
+++ b/ui/dasher/src/piece.ts
@@ -93,6 +93,6 @@ function applyPiece(t: Piece, list: Piece[], is3d: boolean) {
$('body').removeClass(list.join(' ')).addClass(t);
} else {
const sprite = document.getElementById('piece-sprite') as HTMLLinkElement;
- sprite.href = sprite.href.replace(/\w+\.css/, t + '.css');
+ sprite.href = sprite.href.replace(/\w+(\.external|)\.css/, t + '$1.css');
}
}
diff --git a/ui/learn/src/sound.ts b/ui/learn/src/sound.ts
index 5c5f63ad90..d15dd1dbe2 100644
--- a/ui/learn/src/sound.ts
+++ b/ui/learn/src/sound.ts
@@ -1,5 +1,5 @@
const make = (file: string, volume?: number) => {
- lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`);
+ lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`, true);
return () => lichess.sound.play(file, volume);
};
diff --git a/ui/lobby/src/view/correspondence.ts b/ui/lobby/src/view/correspondence.ts
index 8f8ba3dadf..5c93845cb8 100644
--- a/ui/lobby/src/view/correspondence.ts
+++ b/ui/lobby/src/view/correspondence.ts
@@ -28,7 +28,7 @@ function renderSeek(ctrl: LobbyController, seek: Seek): VNode {
seek.username
)
: 'Anonymous',
- seek.rating + (seek.provisional ? '?' : ''),
+ seek.rating && ctrl.opts.showRatings ? seek.rating + (seek.provisional ? '?' : '') : '',
seek.days ? ctrl.trans.plural('nbDays', seek.days) : '∞',
h('span', [
h('span.varicon', {
diff --git a/ui/lobby/src/view/pools.ts b/ui/lobby/src/view/pools.ts
index a2235e41d9..99d55f63fc 100644
--- a/ui/lobby/src/view/pools.ts
+++ b/ui/lobby/src/view/pools.ts
@@ -38,7 +38,7 @@ export function render(ctrl: LobbyController) {
},
[
h('div.clock', pool.lim + '+' + pool.inc),
- active && member!.range ? renderRange(member!.range!) : h('div.perf', pool.perf),
+ active && member!.range && ctrl.opts.showRatings ? renderRange(member!.range!) : h('div.perf', pool.perf),
active ? spinner() : null,
]
);
diff --git a/ui/puz/src/util.ts b/ui/puz/src/util.ts
index 5cb0770194..8db9dac4eb 100644
--- a/ui/puz/src/util.ts
+++ b/ui/puz/src/util.ts
@@ -10,7 +10,7 @@ export const uciToLastMove = (uci: string | undefined): [Key, Key] | undefined =
export const puzzlePov = (puzzle: Puzzle) => opposite(parseFen(puzzle.fen).unwrap().turn);
export const loadSound = (file: string, volume?: number, delay?: number) => {
- setTimeout(() => lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`), delay || 1000);
+ setTimeout(() => lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`, true), delay || 1000);
return () => lichess.sound.play(file, volume);
};
diff --git a/ui/puzzle/src/ctrl.ts b/ui/puzzle/src/ctrl.ts
index abb9b53948..cae4fc41c7 100644
--- a/ui/puzzle/src/ctrl.ts
+++ b/ui/puzzle/src/ctrl.ts
@@ -50,7 +50,7 @@ export default function (opts: PuzzleOpts, redraw: Redraw): Controller {
const throttleSound = (name: string) => throttle(100, () => lichess.sound.play(name));
const loadSound = (file: string, volume?: number, delay?: number) => {
- setTimeout(() => lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`), delay || 1000);
+ setTimeout(() => lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`, true), delay || 1000);
return () => lichess.sound.play(file, volume);
};
const sound = {
diff --git a/ui/simul/css/_show.scss b/ui/simul/css/_show.scss
index d904128896..c376ab5d69 100644
--- a/ui/simul/css/_show.scss
+++ b/ui/simul/css/_show.scss
@@ -10,11 +10,16 @@ $box-padding: var(--box-padding);
@import 'started';
.simul {
+ @include breakpoint($mq-xx-small) {
+ .box__top {
+ flex-wrap: nowrap;
+ }
+ }
+
h1 {
font-size: 2.1em;
.author {
- margin-left: 0.7em;
font-size: 0.7em;
}
}
diff --git a/ui/simul/src/view/created.ts b/ui/simul/src/view/created.ts
index 7d5976ad3e..5439976e50 100644
--- a/ui/simul/src/view/created.ts
+++ b/ui/simul/src/view/created.ts
@@ -1,12 +1,12 @@
-import { h, VNode } from 'snabbdom';
-import { bind } from 'common/snabbdom';
+import { h } from 'snabbdom';
+import { bind, MaybeVNode } from 'common/snabbdom';
import SimulCtrl from '../ctrl';
import { Applicant } from '../interfaces';
import xhr from '../xhr';
import * as util from './util';
import modal from 'common/modal';
-export default function (showText: (ctrl: SimulCtrl) => VNode) {
+export default function (showText: (ctrl: SimulCtrl) => MaybeVNode) {
return (ctrl: SimulCtrl) => {
const candidates = ctrl.candidates().sort(byName),
accepted = ctrl.accepted().sort(byName),
diff --git a/ui/simul/src/view/main.ts b/ui/simul/src/view/main.ts
index b26227f2fd..eb390ca55a 100644
--- a/ui/simul/src/view/main.ts
+++ b/ui/simul/src/view/main.ts
@@ -46,11 +46,13 @@ export default function (ctrl: SimulCtrl) {
}
const showText = (ctrl: SimulCtrl) =>
- h('div.simul-text', [
- h('p', {
- hook: richHTML(ctrl.data.text),
- }),
- ]);
+ ctrl.data.text.length > 0
+ ? h('div.simul-text', [
+ h('p', {
+ hook: richHTML(ctrl.data.text),
+ }),
+ ])
+ : undefined;
const started = (ctrl: SimulCtrl) => [util.title(ctrl), showText(ctrl), results(ctrl), pairings(ctrl)];
diff --git a/ui/simul/src/view/util.ts b/ui/simul/src/view/util.ts
index 5dff403a04..442da300c5 100644
--- a/ui/simul/src/view/util.ts
+++ b/ui/simul/src/view/util.ts
@@ -24,4 +24,4 @@ export function player(p: Player, ctrl: SimulCtrl) {
const userName = (u: LightUser) => (u.title ? [h('span.utitle', u.title), ' ' + u.name] : [u.name]);
export const title = (ctrl: SimulCtrl) =>
- h('h1', [ctrl.data.fullName, h('span.author', ctrl.trans.vdom('by', player(ctrl.data.host, ctrl)))]);
+ h('h1', [ctrl.data.fullName, h('br'), h('span.author', ctrl.trans.vdom('by', player(ctrl.data.host, ctrl)))]);
diff --git a/ui/site/css/_mobile.scss b/ui/site/css/_mobile.scss
index 4dec9a9275..68bedd58fc 100644
--- a/ui/site/css/_mobile.scss
+++ b/ui/site/css/_mobile.scss
@@ -40,9 +40,14 @@
}
.right-side {
- flex: 0 0 264px;
+ flex: 0 0 33%;
text-align: center;
+ .mobile-playing {
+ width: 100%;
+ height: auto;
+ }
+
.qrcode {
margin-top: 20px;
}
diff --git a/ui/site/css/user/_activity.scss b/ui/site/css/user/_activity.scss
index 2a109e37b2..3687142925 100644
--- a/ui/site/css/user/_activity.scss
+++ b/ui/site/css/user/_activity.scss
@@ -5,6 +5,10 @@ $c-contours: mix($c-brag, $c-shade, 80%);
font-size: 1.2em;
margin-left: 2rem;
padding-top: 1rem;
+
+ @include breakpoint($mq-not-small) {
+ margin-right: 2em;
+ }
}
section h2 {
diff --git a/ui/site/src/component/sound.ts b/ui/site/src/component/sound.ts
index 7ed9d7cdf0..07793ebef1 100644
--- a/ui/site/src/component/sound.ts
+++ b/ui/site/src/component/sound.ts
@@ -18,7 +18,8 @@ type Name = string;
type Path = string;
const sound: SoundI = new (class {
- sounds = new Map(); // The loaded sounds and their instances
+ soundSetSounds = new Map(); // The loaded sounds and their instances
+ standaloneSounds = new Map(); // Sounds that are independent of the sound set
soundSet = $('body').data('sound-set');
speechStorage = storage.makeBoolean('speech.enabled');
volumeStorage = storage.make('sound-volume');
@@ -30,8 +31,8 @@ const sound: SoundI = new (class {
if (this.soundSet == 'music') setTimeout(this.publish, 500);
}
- loadOggOrMp3 = (name: Name, path: Path) =>
- this.sounds.set(
+ loadOggOrMp3 = (name: Name, path: Path, noSoundSet = false) =>
+ (noSoundSet ? this.standaloneSounds : this.soundSetSounds).set(
name,
new Howl({
src: ['ogg', 'mp3'].map(ext => `${path}.${ext}`),
@@ -49,10 +50,10 @@ const sound: SoundI = new (class {
}
private getOrLoadSound = (name: string, set: string): Howl => {
- let s = this.sounds.get(name);
+ let s = this.soundSetSounds.get(name) ?? this.standaloneSounds.get(name);
if (!s) {
this.loadStandard(name, set);
- s = this.sounds.get(name)!;
+ s = this.soundSetSounds.get(name)!;
}
return s;
};
@@ -115,7 +116,7 @@ const sound: SoundI = new (class {
changeSet = (s: string) => {
this.soundSet = s;
- this.sounds.clear();
+ this.soundSetSounds.clear();
this.publish();
};
diff --git a/ui/site/src/component/spinner.ts b/ui/site/src/component/spinner.ts
index 83da995a81..708f40f581 100644
--- a/ui/site/src/component/spinner.ts
+++ b/ui/site/src/component/spinner.ts
@@ -1,4 +1,4 @@
const spinner =
- '';
+ '';
export default spinner;
diff --git a/ui/site/src/teamAdmin.ts b/ui/site/src/teamAdmin.ts
index c6e83d22c9..0e4970023b 100644
--- a/ui/site/src/teamAdmin.ts
+++ b/ui/site/src/teamAdmin.ts
@@ -12,12 +12,17 @@ lichess.load.then(() => {
});
function initTagify(input: HTMLInputElement, maxTags: number) {
- const team = input.dataset['rel'];
+ const team = input.dataset.rel;
const tagify = new Tagify(input, {
pattern: /.{3,}/,
maxTags,
- enforceWhitelist: true,
- whitelist: input.value.trim().split(/\s*,\s*/),
+ whitelist: [],
+ hooks: {
+ beforePaste: (_, data) => {
+ data.tagify.settings.enforceWhitelist = false;
+ return Promise.resolve(undefined);
+ },
+ },
});
const doFetch: (term: string) => Promise = debounce(
(term: string) => xhr.json(xhr.url('/player/autocomplete', { term, names: 1, team })),
@@ -26,11 +31,12 @@ function initTagify(input: HTMLInputElement, maxTags: number) {
tagify.on('input', e => {
const term = e.detail.value.trim();
if (term.length < 3) return;
- tagify.settings.whitelist!.length = 0; // reset the whitelist
+ tagify.whitelist = [];
+ tagify.settings.enforceWhitelist = true;
// show loading animation and hide the suggestions dropdown
tagify.loading(true).dropdown.hide.call(tagify);
doFetch(term).then((list: string[]) => {
- tagify.settings.whitelist!.splice(0, list.length, ...list); // update whitelist Array in-place
+ tagify.whitelist = list;
tagify.loading(false).dropdown.show.call(tagify, term); // render the suggestions dropdown
});
});
diff --git a/ui/site/src/ublog.ts b/ui/site/src/ublog.ts
index 6107c680be..13bcabcad4 100644
--- a/ui/site/src/ublog.ts
+++ b/ui/site/src/ublog.ts
@@ -14,8 +14,11 @@ lichess.load.then(() => {
method: 'post',
})
.then(likes => {
+ const label = $('.ublog-post__like .button-label');
+ const newText = label.data(`i18n-${liked ? 'unlike' : 'like'}`);
+ label.text(newText);
+ $('.ublog-post__like').toggleClass(likeClass, liked).attr('title', newText);
$('.ublog-post__like__nb').text(likes);
- $('.ublog-post__like').toggleClass(likeClass, liked);
});
})
);
diff --git a/ui/site/src/userGamesDownload.ts b/ui/site/src/userGamesDownload.ts
index cdc4d04efd..81b93b16a6 100644
--- a/ui/site/src/userGamesDownload.ts
+++ b/ui/site/src/userGamesDownload.ts
@@ -9,8 +9,8 @@ function generateSearchParams(): string {
});
['since', 'until'].forEach(name => {
- const minTimestamp = 1356998400070;
- const date = $(`#dl-date-${name}`).val() as string;
+ const minTimestamp = 1356998400070; // 01/01/2013, 01:00:00
+ const date = ($(`#dl-date-${name}`).val() as string).replace(/-/g, '/'); // for Safari https://stackoverflow.com/a/4310986/11955835
const time = $(`#dl-time-${name}`).val() as string;
if (date.length == 10) {
// the 00:00:00 is necessary for the time to be interpreted in the local timezone