diff --git a/modules/coreI18n/src/main/key.scala b/modules/coreI18n/src/main/key.scala
index 5bb1465cd444..c9ee61c3065e 100644
--- a/modules/coreI18n/src/main/key.scala
+++ b/modules/coreI18n/src/main/key.scala
@@ -1324,6 +1324,8 @@ object I18nKey:
val `zugzwangDescription`: I18nKey = "puzzleTheme:zugzwangDescription"
val `healthyMix`: I18nKey = "puzzleTheme:healthyMix"
val `healthyMixDescription`: I18nKey = "puzzleTheme:healthyMixDescription"
+ val `mix`: I18nKey = "puzzleTheme:mix"
+ val `mixDescription`: I18nKey = "puzzleTheme:mixDescription"
val `playerGames`: I18nKey = "puzzleTheme:playerGames"
val `playerGamesDescription`: I18nKey = "puzzleTheme:playerGamesDescription"
val `puzzleDownloadInformation`: I18nKey = "puzzleTheme:puzzleDownloadInformation"
diff --git a/modules/puzzle/src/main/PuzzleTheme.scala b/modules/puzzle/src/main/PuzzleTheme.scala
index 89cd6a29ed56..3e4a3bd2ca13 100644
--- a/modules/puzzle/src/main/PuzzleTheme.scala
+++ b/modules/puzzle/src/main/PuzzleTheme.scala
@@ -7,77 +7,80 @@ case class PuzzleTheme(key: PuzzleTheme.Key, name: I18nKey, description: I18nKey
object PuzzleTheme:
+ private def apply(name: I18nKey, desc: I18nKey): PuzzleTheme =
+ PuzzleTheme(Key(name.value.split(":", 2).lift(1).getOrElse(name.value)), name, desc)
+
opaque type Key = String
object Key extends OpaqueString[Key]
case class WithCount(theme: PuzzleTheme, count: Int)
- val mix = PuzzleTheme(Key("mix"), i.healthyMix, i.healthyMixDescription)
- val advancedPawn = PuzzleTheme(Key("advancedPawn"), i.advancedPawn, i.advancedPawnDescription)
- val advantage = PuzzleTheme(Key("advantage"), i.advantage, i.advantageDescription)
- val anastasiaMate = PuzzleTheme(Key("anastasiaMate"), i.anastasiaMate, i.anastasiaMateDescription)
- val arabianMate = PuzzleTheme(Key("arabianMate"), i.arabianMate, i.arabianMateDescription)
- val attackingF2F7 = PuzzleTheme(Key("attackingF2F7"), i.attackingF2F7, i.attackingF2F7Description)
- val attraction = PuzzleTheme(Key("attraction"), i.attraction, i.attractionDescription)
- val backRankMate = PuzzleTheme(Key("backRankMate"), i.backRankMate, i.backRankMateDescription)
- val bishopEndgame = PuzzleTheme(Key("bishopEndgame"), i.bishopEndgame, i.bishopEndgameDescription)
- val bodenMate = PuzzleTheme(Key("bodenMate"), i.bodenMate, i.bodenMateDescription)
+ val mix = PuzzleTheme(i.mix, i.mixDescription)
+ val advancedPawn = PuzzleTheme(i.advancedPawn, i.advancedPawnDescription)
+ val advantage = PuzzleTheme(i.advantage, i.advantageDescription)
+ val anastasiaMate = PuzzleTheme(i.anastasiaMate, i.anastasiaMateDescription)
+ val arabianMate = PuzzleTheme(i.arabianMate, i.arabianMateDescription)
+ val attackingF2F7 = PuzzleTheme(i.attackingF2F7, i.attackingF2F7Description)
+ val attraction = PuzzleTheme(i.attraction, i.attractionDescription)
+ val backRankMate = PuzzleTheme(i.backRankMate, i.backRankMateDescription)
+ val bishopEndgame = PuzzleTheme(i.bishopEndgame, i.bishopEndgameDescription)
+ val bodenMate = PuzzleTheme(i.bodenMate, i.bodenMateDescription)
val capturingDefender =
- PuzzleTheme(Key("capturingDefender"), i.capturingDefender, i.capturingDefenderDescription)
- val castling = PuzzleTheme(Key("castling"), i.castling, i.castlingDescription)
- val clearance = PuzzleTheme(Key("clearance"), i.clearance, i.clearanceDescription)
- val crushing = PuzzleTheme(Key("crushing"), i.crushing, i.crushingDescription)
- val defensiveMove = PuzzleTheme(Key("defensiveMove"), i.defensiveMove, i.defensiveMoveDescription)
- val deflection = PuzzleTheme(Key("deflection"), i.deflection, i.deflectionDescription)
+ PuzzleTheme(i.capturingDefender, i.capturingDefenderDescription)
+ val castling = PuzzleTheme(i.castling, i.castlingDescription)
+ val clearance = PuzzleTheme(i.clearance, i.clearanceDescription)
+ val crushing = PuzzleTheme(i.crushing, i.crushingDescription)
+ val defensiveMove = PuzzleTheme(i.defensiveMove, i.defensiveMoveDescription)
+ val deflection = PuzzleTheme(i.deflection, i.deflectionDescription)
val discoveredAttack =
- PuzzleTheme(Key("discoveredAttack"), i.discoveredAttack, i.discoveredAttackDescription)
+ PuzzleTheme(i.discoveredAttack, i.discoveredAttackDescription)
val doubleBishopMate =
- PuzzleTheme(Key("doubleBishopMate"), i.doubleBishopMate, i.doubleBishopMateDescription)
- val doubleCheck = PuzzleTheme(Key("doubleCheck"), i.doubleCheck, i.doubleCheckDescription)
+ PuzzleTheme(i.doubleBishopMate, i.doubleBishopMateDescription)
+ val doubleCheck = PuzzleTheme(i.doubleCheck, i.doubleCheckDescription)
val dovetailMate =
- PuzzleTheme(Key("dovetailMate"), i.dovetailMate, i.dovetailMateDescription)
- val equality = PuzzleTheme(Key("equality"), i.equality, i.equalityDescription)
- val endgame = PuzzleTheme(Key("endgame"), i.endgame, i.endgameDescription)
- val enPassant = PuzzleTheme(Key("enPassant"), I18nKey.learn.enPassant, i.enPassantDescription)
- val exposedKing = PuzzleTheme(Key("exposedKing"), i.exposedKing, i.exposedKingDescription)
- val fork = PuzzleTheme(Key("fork"), i.fork, i.forkDescription)
- val hangingPiece = PuzzleTheme(Key("hangingPiece"), i.hangingPiece, i.hangingPieceDescription)
- val hookMate = PuzzleTheme(Key("hookMate"), i.hookMate, i.hookMateDescription)
- val interference = PuzzleTheme(Key("interference"), i.interference, i.interferenceDescription)
- val intermezzo = PuzzleTheme(Key("intermezzo"), i.intermezzo, i.intermezzoDescription)
- val kingsideAttack = PuzzleTheme(Key("kingsideAttack"), i.kingsideAttack, i.kingsideAttackDescription)
- val knightEndgame = PuzzleTheme(Key("knightEndgame"), i.knightEndgame, i.knightEndgameDescription)
- val long = PuzzleTheme(Key("long"), i.long, i.longDescription)
- val master = PuzzleTheme(Key("master"), i.master, i.masterDescription)
- val masterVsMaster = PuzzleTheme(Key("masterVsMaster"), i.masterVsMaster, i.masterVsMasterDescription)
- val mate = PuzzleTheme(Key("mate"), i.mate, i.mateDescription)
- val mateIn1 = PuzzleTheme(Key("mateIn1"), i.mateIn1, i.mateIn1Description)
- val mateIn2 = PuzzleTheme(Key("mateIn2"), i.mateIn2, i.mateIn2Description)
- val mateIn3 = PuzzleTheme(Key("mateIn3"), i.mateIn3, i.mateIn3Description)
- val mateIn4 = PuzzleTheme(Key("mateIn4"), i.mateIn4, i.mateIn4Description)
- val mateIn5 = PuzzleTheme(Key("mateIn5"), i.mateIn5, i.mateIn5Description)
- val smotheredMate = PuzzleTheme(Key("smotheredMate"), i.smotheredMate, i.smotheredMateDescription)
- val middlegame = PuzzleTheme(Key("middlegame"), i.middlegame, i.middlegameDescription)
- val oneMove = PuzzleTheme(Key("oneMove"), i.oneMove, i.oneMoveDescription)
- val opening = PuzzleTheme(Key("opening"), i.opening, i.openingDescription)
- val pawnEndgame = PuzzleTheme(Key("pawnEndgame"), i.pawnEndgame, i.pawnEndgameDescription)
- val pin = PuzzleTheme(Key("pin"), i.pin, i.pinDescription)
- val promotion = PuzzleTheme(Key("promotion"), i.promotion, i.promotionDescription)
- val queenEndgame = PuzzleTheme(Key("queenEndgame"), i.queenEndgame, i.queenEndgameDescription)
+ PuzzleTheme(i.dovetailMate, i.dovetailMateDescription)
+ val equality = PuzzleTheme(i.equality, i.equalityDescription)
+ val endgame = PuzzleTheme(i.endgame, i.endgameDescription)
+ val enPassant = PuzzleTheme(I18nKey.learn.enPassant, i.enPassantDescription)
+ val exposedKing = PuzzleTheme(i.exposedKing, i.exposedKingDescription)
+ val fork = PuzzleTheme(i.fork, i.forkDescription)
+ val hangingPiece = PuzzleTheme(i.hangingPiece, i.hangingPieceDescription)
+ val hookMate = PuzzleTheme(i.hookMate, i.hookMateDescription)
+ val interference = PuzzleTheme(i.interference, i.interferenceDescription)
+ val intermezzo = PuzzleTheme(i.intermezzo, i.intermezzoDescription)
+ val kingsideAttack = PuzzleTheme(i.kingsideAttack, i.kingsideAttackDescription)
+ val knightEndgame = PuzzleTheme(i.knightEndgame, i.knightEndgameDescription)
+ val long = PuzzleTheme(i.long, i.longDescription)
+ val master = PuzzleTheme(i.master, i.masterDescription)
+ val masterVsMaster = PuzzleTheme(i.masterVsMaster, i.masterVsMasterDescription)
+ val mate = PuzzleTheme(i.mate, i.mateDescription)
+ val mateIn1 = PuzzleTheme(i.mateIn1, i.mateIn1Description)
+ val mateIn2 = PuzzleTheme(i.mateIn2, i.mateIn2Description)
+ val mateIn3 = PuzzleTheme(i.mateIn3, i.mateIn3Description)
+ val mateIn4 = PuzzleTheme(i.mateIn4, i.mateIn4Description)
+ val mateIn5 = PuzzleTheme(i.mateIn5, i.mateIn5Description)
+ val smotheredMate = PuzzleTheme(i.smotheredMate, i.smotheredMateDescription)
+ val middlegame = PuzzleTheme(i.middlegame, i.middlegameDescription)
+ val oneMove = PuzzleTheme(i.oneMove, i.oneMoveDescription)
+ val opening = PuzzleTheme(i.opening, i.openingDescription)
+ val pawnEndgame = PuzzleTheme(i.pawnEndgame, i.pawnEndgameDescription)
+ val pin = PuzzleTheme(i.pin, i.pinDescription)
+ val promotion = PuzzleTheme(i.promotion, i.promotionDescription)
+ val queenEndgame = PuzzleTheme(i.queenEndgame, i.queenEndgameDescription)
val queenRookEndgame =
- PuzzleTheme(Key("queenRookEndgame"), i.queenRookEndgame, i.queenRookEndgameDescription)
- val queensideAttack = PuzzleTheme(Key("queensideAttack"), i.queensideAttack, i.queensideAttackDescription)
- val quietMove = PuzzleTheme(Key("quietMove"), i.quietMove, i.quietMoveDescription)
- val rookEndgame = PuzzleTheme(Key("rookEndgame"), i.rookEndgame, i.rookEndgameDescription)
- val sacrifice = PuzzleTheme(Key("sacrifice"), i.sacrifice, i.sacrificeDescription)
- val short = PuzzleTheme(Key("short"), i.short, i.shortDescription)
- val skewer = PuzzleTheme(Key("skewer"), i.skewer, i.skewerDescription)
- val superGM = PuzzleTheme(Key("superGM"), i.superGM, i.superGMDescription)
- val trappedPiece = PuzzleTheme(Key("trappedPiece"), i.trappedPiece, i.trappedPieceDescription)
- val underPromotion = PuzzleTheme(Key("underPromotion"), i.underPromotion, i.underPromotionDescription)
- val veryLong = PuzzleTheme(Key("veryLong"), i.veryLong, i.veryLongDescription)
- val xRayAttack = PuzzleTheme(Key("xRayAttack"), i.xRayAttack, i.xRayAttackDescription)
- val zugzwang = PuzzleTheme(Key("zugzwang"), i.zugzwang, i.zugzwangDescription)
+ PuzzleTheme(i.queenRookEndgame, i.queenRookEndgameDescription)
+ val queensideAttack = PuzzleTheme(i.queensideAttack, i.queensideAttackDescription)
+ val quietMove = PuzzleTheme(i.quietMove, i.quietMoveDescription)
+ val rookEndgame = PuzzleTheme(i.rookEndgame, i.rookEndgameDescription)
+ val sacrifice = PuzzleTheme(i.sacrifice, i.sacrificeDescription)
+ val short = PuzzleTheme(i.short, i.shortDescription)
+ val skewer = PuzzleTheme(i.skewer, i.skewerDescription)
+ val superGM = PuzzleTheme(i.superGM, i.superGMDescription)
+ val trappedPiece = PuzzleTheme(i.trappedPiece, i.trappedPieceDescription)
+ val underPromotion = PuzzleTheme(i.underPromotion, i.underPromotionDescription)
+ val veryLong = PuzzleTheme(i.veryLong, i.veryLongDescription)
+ val xRayAttack = PuzzleTheme(i.xRayAttack, i.xRayAttackDescription)
+ val zugzwang = PuzzleTheme(i.zugzwang, i.zugzwangDescription)
val checkFirst = PuzzleTheme(Key("checkFirst"), I18nKey("Check first"), I18nKey("Check first"))
val categorized = List[(I18nKey, List[PuzzleTheme])](
diff --git a/translation/source/puzzleTheme.xml b/translation/source/puzzleTheme.xml
index e9515be54866..7f9a97087474 100644
--- a/translation/source/puzzleTheme.xml
+++ b/translation/source/puzzleTheme.xml
@@ -121,6 +121,8 @@
The opponent is limited in the moves they can make, and all moves worsen their position.
Healthy mix
A bit of everything. You don't know what to expect, so you remain ready for anything! Just like in real games.
+ Healthy mix
+ A bit of everything. You don't know what to expect, so you remain ready for anything! Just like in real games.
Player games
Lookup puzzles generated from your games, or from another player's games.
These puzzles are in the public domain, and can be downloaded from %s.
diff --git a/ui/@types/lichess/i18n.d.ts b/ui/@types/lichess/i18n.d.ts
index 435270530664..cbb7f5177504 100644
--- a/ui/@types/lichess/i18n.d.ts
+++ b/ui/@types/lichess/i18n.d.ts
@@ -2495,6 +2495,10 @@ interface I18n {
middlegame: string;
/** A tactic during the second phase of the game. */
middlegameDescription: string;
+ /** Healthy mix */
+ mix: string;
+ /** A bit of everything. You don't know what to expect, so you remain ready for anything! Just like in real games. */
+ mixDescription: string;
/** One-move puzzle */
oneMove: string;
/** A puzzle that is only one move long. */