From 3be7fc5884d96136b948d93c61aaee1469dc6c85 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 31 Dec 2025 13:29:45 -0600 Subject: [PATCH] Signed-off-by: Matt Bruce --- Baccarat/Baccarat/Engine/GameState.swift | 1 + .../Baccarat/Resources/Localizable.xcstrings | 1327 +++++++++++++---- .../Baccarat/Views/Game/GameTableView.swift | 22 +- Blackjack/Blackjack/Engine/GameState.swift | 1 + .../Blackjack/Resources/Localizable.xcstrings | 651 +++++++- .../Blackjack/Views/Game/GameTableView.swift | 22 +- .../CasinoKit/Models/OnboardingState.swift | 28 + .../CasinoKit/Views/WelcomeSheet.swift | 36 + 8 files changed, 1743 insertions(+), 345 deletions(-) diff --git a/Baccarat/Baccarat/Engine/GameState.swift b/Baccarat/Baccarat/Engine/GameState.swift index 9dbadcc..f77142d 100644 --- a/Baccarat/Baccarat/Engine/GameState.swift +++ b/Baccarat/Baccarat/Engine/GameState.swift @@ -394,6 +394,7 @@ final class GameState: CasinoGameState { self.engine = BaccaratEngine(deckCount: settings.deckCount.rawValue) self.balance = settings.startingBalance self.onboarding = OnboardingState(gameIdentifier: "baccarat") + self.onboarding.registerHintKeys("bettingZone", "dealButton", "firstResult") self.persistence = CloudSyncManager() // Sync sound settings with SoundManager diff --git a/Baccarat/Baccarat/Resources/Localizable.xcstrings b/Baccarat/Baccarat/Resources/Localizable.xcstrings index 92cfed2..51a7ffd 100644 --- a/Baccarat/Baccarat/Resources/Localizable.xcstrings +++ b/Baccarat/Baccarat/Resources/Localizable.xcstrings @@ -25,35 +25,68 @@ }, "%@ streak of %lld" : { "comment" : "A label describing the type of streak and its count. The first argument is the type of streak (\"Banker\" or \"Player\"). The second argument is the count of the streak.", - "isCommentAutoGenerated" : true, "localizations" : { "en" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "%1$@ streak of %2$lld" } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Racha de %1$@: %2$lld" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Série de %1$@ : %2$lld" + } } } }, "%@ streak: %lld in a row" : { "comment" : "Text displayed as a hint in the game UI, providing information about a streak of wins for either the Player or Banker. The argument is the name of the player/banker involved in the streak, and the second argument is the count of consecutive wins.", - "isCommentAutoGenerated" : true, "localizations" : { "en" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "%1$@ streak: %2$lld in a row" } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Racha de %1$@: %2$lld consecutivos" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Série de %1$@ : %2$lld d'affilée" + } } } }, "%@×%lld" : { "comment" : "A text view displaying the streak count and type (e.g., \"3×Banker\"). The first argument is the streak count. The second argument is the type of streak (\"Banker\" or \"Player\").", - "isCommentAutoGenerated" : true, "localizations" : { "en" : { "stringUnit" : { - "state" : "new", + "state" : "translated", + "value" : "%1$@×%2$lld" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@×%2$lld" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", "value" : "%1$@×%2$lld" } } @@ -493,29 +526,6 @@ } } }, - "Add $%lld more to meet minimum" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Add $%lld more to meet minimum" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Añade $%lld más para alcanzar el mínimo" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ajoutez %lld$ de plus pour atteindre le minimum" - } - } - } - }, "After generating:" : { "comment" : "A heading for the instructions section of the icon generator view.", "localizations" : { @@ -564,7 +574,26 @@ }, "ALL TIME SUMMARY" : { "comment" : "Title of a section in the statistics sheet that provides a summary of the user's performance over all time.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "ALL TIME SUMMARY" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "RESUMEN HISTÓRICO" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "RÉSUMÉ GLOBAL" + } + } + } }, "Alternative: Use an online tool" : { "comment" : "A section header that suggests using an online tool to generate app icon sizes.", @@ -704,31 +733,28 @@ } } }, - "B Pair" : { - "extractionState" : "stale", + "B: %lld%%" : { + "comment" : "Labels indicating the percentage of Banker outcomes in a game's result distribution.", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "B Pair" + "value" : "B: %lld%%" } }, "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "Par B" + "value" : "B: %lld%%" } }, "fr-CA" : { "stringUnit" : { "state" : "translated", - "value" : "Paire B" + "value" : "B: %lld%%" } } } - }, - "B: %lld%%" : { - }, "Baccarat has one of the lowest house edges in the casino." : { "comment" : "Description of the house edge of baccarat.", @@ -754,7 +780,27 @@ } }, "BALANCE" : { - + "comment" : "Title of a section showing the user's balance.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "BALANCE" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "SALDO" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "SOLDE" + } + } + } }, "Banker" : { "localizations" : { @@ -941,7 +987,26 @@ }, "Banker bet has lowest house edge" : { "comment" : "Default hint in the game, reminding players about the house edge of the banker bet.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Banker bet has lowest house edge" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "La apuesta al Banquero tiene la menor ventaja de la casa" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le pari Banquier a le plus faible avantage maison" + } + } + } }, "Banker bet has the lowest house edge (1.06%)." : { "comment" : "Description of the house edge for the Banker bet in the Rules Help view.", @@ -1014,18 +1079,95 @@ }, "Banker has the lowest house edge (1.06%)" : { "comment" : "Hint text for beginners about the house edge of the banker bet in a baccarat round.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Banker has the lowest house edge (1.06%)" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "El Banquero tiene la menor ventaja de la casa (1.06%)" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le Banquier a le plus faible avantage maison (1,06%)" + } + } + } }, "Banker running hot (%lld%%)" : { "comment" : "A hint to place a bet on the Banker based on the calculated house edge. The argument is the percentage of the house edge.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Banker running hot (%lld%%)" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "El Banquero está en racha (%lld%%)" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le Banquier est en forme (%lld%%)" + } + } + } }, "Banker Wins" : { "comment" : "Label for the number of banker win rounds in the statistics display.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Banker Wins" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Victorias del Banquero" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Victoires Banquier" + } + } + } }, "Bet on Player, Banker, or Tie" : { - + "comment" : "Welcome screen feature title for betting options.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bet on Player, Banker, or Tie" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apuesta al Jugador, Banquero o Empate" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misez sur Joueur, Banquier ou Égalité" + } + } + } }, "Bet on which hand will win: Player, Banker, or Tie." : { "comment" : "Text describing the objective of the baccarat game.", @@ -1051,11 +1193,50 @@ } }, "Betting tips and trend analysis" : { - + "comment" : "Description for hints feature.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Betting tips and trend analysis" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consejos de apuestas y análisis de tendencias" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conseils de paris et analyse des tendances" + } + } + } }, "BIG ROAD" : { "comment" : "Title for the section in the statistics sheet that shows the user's performance on the Big Road.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "BIG ROAD" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "CAMINO GRANDE" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "GRANDE ROUTE" + } + } + } }, "Blackjack" : { "comment" : "The name of a blackjack game.", @@ -1242,11 +1423,50 @@ } }, "Change table limits and display options" : { - + "comment" : "Welcome screen feature description for customizing settings.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Change table limits and display options" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambia los límites de la mesa y opciones de visualización" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modifiez les limites de table et les options d'affichage" + } + } + } }, "CHIPS STATS" : { "comment" : "Section that shows statistics related to the user's chip count during a Baccarat session.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "CHIPS STATS" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "ESTADÍSTICAS DE FICHAS" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "STATISTIQUES DES JETONS" + } + } + } }, "Chips, cards, and result sounds" : { "comment" : "Subtitle describing sound effects toggle.", @@ -1273,7 +1493,26 @@ }, "Choppy shoe - results alternating" : { "comment" : "Hint text indicating that the current shoe is \"choppy\", with results alternating between Player and Banker.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choppy shoe - results alternating" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zapato irregular - resultados alternando" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sabot irrégulier - résultats alternés" + } + } + } }, "Clear" : { "comment" : "The label of a button that clears all current bets in the game.", @@ -1365,7 +1604,27 @@ } }, "Customize Settings" : { - + "comment" : "Welcome screen feature title for settings customization.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Customize Settings" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personaliza los Ajustes" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personnalisez les Paramètres" + } + } + } }, "DATA" : { "localizations" : { @@ -1389,54 +1648,6 @@ } } }, - "Deal" : { - "comment" : "The label of a button that deals cards in a game.", - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Deal" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Repartir" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Distribuer" - } - } - } - }, - "Dealing..." : { - "comment" : "A placeholder text shown while a game is being dealt.", - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dealing..." - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Repartiendo..." - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Distribution..." - } - } - } - }, "DECK SETTINGS" : { "comment" : "Section header for deck configuration settings.", "localizations" : { @@ -1462,10 +1673,49 @@ }, "Delete" : { "comment" : "A button to delete a session.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer" + } + } + } }, "Delete Session?" : { - + "comment" : "Confirmation dialog title for deleting a session.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete Session?" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Eliminar Sesión?" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer la Session?" + } + } + } }, "DISPLAY" : { "comment" : "Section header for display settings.", @@ -1606,15 +1856,72 @@ }, "Dragon Bonus: high risk, high reward" : { "comment" : "Warning text for dragon bonus bets, advising players to be cautious.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dragon Bonus: high risk, high reward" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bono Dragón: alto riesgo, alta recompensa" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonus Dragon : risque élevé, récompense élevée" + } + } + } }, "End Session" : { "comment" : "The text for a button that ends the current game session.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "End Session" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminar Sesión" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminer la Session" + } + } + } }, "End Session?" : { "comment" : "A confirmation dialog title.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "End Session?" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Terminar Sesión?" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminer la Session?" + } + } + } }, "Example: 5♥ + 5♣ = Pair (wins!)" : { "comment" : "Example of a pair bet winning.", @@ -1662,57 +1969,28 @@ } } }, - "Game Over" : { - "comment" : "The title of the game over screen.", - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Game Over" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fin del juego" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Partie terminée" - } - } - } - }, - "GAME OVER" : { - "comment" : "The title of the game over screen.", - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "GAME OVER" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "FIN DEL JUEGO" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "FIN DE PARTIE" - } - } - } - }, "GAME STATS" : { "comment" : "Section in the statistics sheet dedicated to displaying statistics specific to baccarat.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "GAME STATS" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "ESTADÍSTICAS DEL JUEGO" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "STATISTIQUES DE JEU" + } + } + } }, "Generate & Save Icons" : { "comment" : "A button label that triggers the generation of app icons.", @@ -1831,7 +2109,26 @@ }, "Hands" : { "comment" : "Label for the number of hands played in a summary stat column.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hands" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manos" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mains" + } + } + } }, "handValueFormat" : { "comment" : "Format for displaying hand value. The argument is the numeric value of the hand.", @@ -2290,7 +2587,26 @@ }, "Lost" : { "comment" : "Labels for the outcome circles in the \"Win/Loss/Push\" section.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lost" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perdido" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perdu" + } + } + } }, "Main Bets" : { "comment" : "Title of a rule page in the \"Rules\" help view, describing the main bets available in baccarat.", @@ -2431,7 +2747,26 @@ }, "Net" : { "comment" : "Label for the net winnings in the statistics sheet.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Net" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neto" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Net" + } + } + } }, "Never" : { "localizations" : { @@ -2684,36 +3019,51 @@ } } }, - "P Pair" : { - "extractionState" : "stale", + "P: %lld%%" : { + "comment" : "Labels indicating the percentage of Player, Banker, and Tie outcomes in a game's result distribution.", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "P Pair" + "value" : "P: %lld%%" } }, "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "Par J" + "value" : "J: %lld%%" } }, "fr-CA" : { "stringUnit" : { "state" : "translated", - "value" : "Paire J" + "value" : "J: %lld%%" } } } }, - "P: %lld%%" : { - "comment" : "Labels indicating the percentage of Player, Banker, and Tie outcomes in a game's result distribution.", - "isCommentAutoGenerated" : true - }, "Pair bets have ~10% house edge" : { "comment" : "Warning message for high house edge pair bets.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pair bets have ~10% house edge" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las apuestas de par tienen ~10% de ventaja de la casa" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les paris de paire ont ~10% d'avantage maison" + } + } + } }, "Pair bets have ~10% house edge." : { "comment" : "Description of the house edge of a pair bet in the Rules Help view.", @@ -2786,7 +3136,26 @@ }, "Pairs" : { "comment" : "Label for the total number of player and banker pair occurrences in the statistics UI.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pairs" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pares" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paires" + } + } + } }, "Pairs occur roughly once every 15 hands." : { "comment" : "Explanation of how often pairs occur in a typical game of baccarat.", @@ -2811,30 +3180,6 @@ } } }, - "Play Again" : { - "comment" : "A button label that says \"Play Again\".", - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Play Again" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Jugar de nuevo" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rejouer" - } - } - } - }, "Player" : { "localizations" : { "en" : { @@ -2882,13 +3227,24 @@ }, "Player %lld%%, Banker %lld%%, Tie %lld%%" : { "comment" : "A label describing the percentage of times the player, banker, or tie result occurred in the last spin of the game.", - "isCommentAutoGenerated" : true, "localizations" : { "en" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "Player %1$lld%%, Banker %2$lld%%, Tie %3$lld%%" } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jugador %1$lld%%, Banquero %2$lld%%, Empate %3$lld%%" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Joueur %1$lld%%, Banquier %2$lld%%, Égalité %3$lld%%" + } } } }, @@ -2940,11 +3296,49 @@ }, "Player running hot (%lld%%)" : { "comment" : "A hint to place a bet on the Player, given a significant house edge in favor of the Player. The percentage is included as a context hint.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player running hot (%lld%%)" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "El Jugador está en racha (%lld%%)" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Le Joueur est en forme (%lld%%)" + } + } + } }, "Player Wins" : { "comment" : "Label for the \"Player Wins\" stat in the statistics UI.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player Wins" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Victorias del Jugador" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Victoires Joueur" + } + } + } }, "Player with 0-5: Draws a third card" : { "comment" : "Description of the action for the Player when their third card is 0-5.", @@ -3037,9 +3431,6 @@ } } } - }, - "Practice Free" : { - }, "Privacy Policy" : { "localizations" : { @@ -3065,7 +3456,26 @@ }, "Push" : { "comment" : "A label for the \"Push\" outcome in the game stats section.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Push" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Empate" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Égalité" + } + } + } }, "Red Circle (B): Banker won the hand" : { "comment" : "Explains the red circle icon in the history.", @@ -3161,13 +3571,72 @@ }, "Result distribution" : { "comment" : "A label describing the view that shows the distribution of betting results.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Result distribution" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Distribución de resultados" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Distribution des résultats" + } + } + } }, "Results appear here, then in the road maps below" : { - + "comment" : "Instructional text for new players.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Results appear here, then in the road maps below" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los resultados aparecen aquí y luego en los mapas de carretera abajo" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les résultats apparaissent ici, puis dans les cartes routières ci-dessous" + } + } + } }, "Road maps show game history and trends" : { - + "comment" : "Welcome screen feature description for pattern tracking.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Road maps show game history and trends" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los mapas de carreteras muestran el historial y las tendencias del juego" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les feuilles de route montrent l'historique et les tendances du jeu" + } + } + } }, "Roulette" : { "comment" : "The name of a roulette game.", @@ -3192,41 +3661,13 @@ } } }, - "Rounds" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rounds" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rondas" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Parties" - } - } - } - }, "Rounds played" : { "comment" : "A label displayed next to the number of rounds played in a session.", - "isCommentAutoGenerated" : true - }, - "Rounds Played" : { - "comment" : "A label displayed next to the number of rounds played in the game over screen.", - "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Rounds Played" + "value" : "Rounds played" } }, "es-MX" : { @@ -3243,16 +3684,97 @@ } } }, + "See detailed stats for each play session, just like at a real casino" : { + "comment" : "Welcome screen feature description for session tracking.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "See detailed stats for each play session, just like at a real casino" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ve estadísticas detalladas de cada sesión de juego, como en un casino real" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consultez les statistiques détaillées de chaque session de jeu, comme dans un vrai casino" + } + } + } + }, "Select a chip and tap a bet zone" : { - + "comment" : "Onboarding hint for placing bets.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select a chip and tap a bet zone" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Selecciona una ficha y toca una zona de apuesta" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionnez un jeton et touchez une zone de mise" + } + } + } }, "SESSION PERFORMANCE" : { "comment" : "Title of a section in the statistics sheet that details session performance metrics.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SESSION PERFORMANCE" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "RENDIMIENTO DE SESIÓN" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "PERFORMANCE DE SESSION" + } + } + } }, "Sessions" : { "comment" : "Label for the number of sessions played in the statistics sheet.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sessions" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sesiones" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sessions" + } + } + } }, "Set a budget and stick to it." : { "comment" : "Tip for players to set a budget and stick to it when playing baccarat.", @@ -3324,7 +3846,27 @@ } }, "Show Hints" : { - + "comment" : "Toggle label for showing hints.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Hints" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar Sugerencias" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher les Conseils" + } + } + } }, "Show History" : { "comment" : "Toggle label for showing game history.", @@ -3350,7 +3892,27 @@ } }, "Show Welcome Again" : { - + "comment" : "Button to show the welcome screen again.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Welcome Again" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar Bienvenida de Nuevo" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher l'Accueil à Nouveau" + } + } + } }, "Side bet on Player or Banker winning by a margin." : { "comment" : "Title for a side bet where the player bets on which hand wins by a margin (e.g., Banker by 9 points).", @@ -3465,9 +4027,6 @@ } } } - }, - "Start with $1,000 and play risk-free" : { - }, "STARTING BALANCE" : { "comment" : "Section header for starting balance settings.", @@ -3539,7 +4098,26 @@ }, "Streak" : { "comment" : "An accessibility label for the streak badge.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Streak" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Racha" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Série" + } + } + } }, "Sync Now" : { "localizations" : { @@ -3587,7 +4165,26 @@ }, "T: %lld%%" : { "comment" : "A label indicating that there are ties in the distribution.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "T: %lld%%" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "E: %lld%%" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "É: %lld%%" + } + } + } }, "TABLE LIMITS" : { "comment" : "Section header for table limits settings.", @@ -3612,35 +4209,51 @@ } } }, - "tableLimitsFormat" : { - "comment" : "Format for displaying table limits. First argument is min bet, second is max bet.", - "extractionState" : "stale", + "Tap Deal to start the round" : { + "comment" : "Instructional text for new players.", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "$%@ - $%@" + "value" : "Tap Deal to start the round" } }, "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "$%@ - $%@" + "value" : "Toca Repartir para comenzar la ronda" } }, "fr-CA" : { "stringUnit" : { "state" : "translated", - "value" : "%@ $ - %@ $" + "value" : "Appuyez sur Distribuer pour commencer le tour" } } } - }, - "Tap Deal to start the round" : { - }, "The hand closest to 9 wins" : { - + "comment" : "Welcome screen feature description for betting explanation.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The hand closest to 9 wins" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "La mano más cercana a 9 gana" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "La main la plus proche de 9 gagne" + } + } + } }, "The hand closest to 9 wins." : { "comment" : "Explanation of how the hand closest to 9 wins in baccarat.", @@ -3803,7 +4416,27 @@ } }, "This will permanently remove this session from your history." : { - + "comment" : "Warning message when deleting a session.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "This will permanently remove this session from your history." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esto eliminará permanentemente esta sesión de tu historial." + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cela supprimera définitivement cette session de votre historique." + } + } + } }, "Tie" : { "localizations" : { @@ -3875,43 +4508,95 @@ }, "Tie has 14% house edge" : { "comment" : "Warning message for a tie bet, explaining the high house edge.", - "isCommentAutoGenerated" : true - }, - "Ties" : { - "comment" : "Description of a baccarat statistics category for tie rounds.", - "isCommentAutoGenerated" : true - }, - "Time" : { - "comment" : "Label for the duration of a session in the statistics sheet.", - "isCommentAutoGenerated" : true - }, - "TOTAL" : { - "comment" : "A label displayed next to the total winnings in the result banner.", - "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "TOTAL" + "value" : "Tie has 14% house edge" } }, "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "TOTAL" + "value" : "El Empate tiene 14% de ventaja de la casa" } }, "fr-CA" : { "stringUnit" : { "state" : "translated", - "value" : "TOTAL" + "value" : "L'Égalité a 14% d'avantage maison" + } + } + } + }, + "Ties" : { + "comment" : "Description of a baccarat statistics category for tie rounds.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ties" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Empates" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Égalités" + } + } + } + }, + "Time" : { + "comment" : "Label for the duration of a session in the statistics sheet.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Time" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temps" } } } }, "Total game time" : { "comment" : "Rows in the \"Game stats\" section of the statistics sheet, showing various statistics about a Baccarat session.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total game time" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo total de juego" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temps de jeu total" + } + } + } }, "Total Winnings" : { "localizations" : { @@ -3936,7 +4621,50 @@ } }, "Track Patterns" : { - + "comment" : "Welcome screen feature title for pattern tracking.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Track Patterns" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seguimiento de Patrones" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suivez les Tendances" + } + } + } + }, + "Track Sessions" : { + "comment" : "Welcome screen feature title for session tracking.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Track Sessions" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seguimiento de Sesiones" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suivez vos Sessions" + } + } + } }, "Two Naturals of the same value result in a Tie." : { "comment" : "Text describing the outcome when two players both have a Natural (a total of 8 or 9 with two cards).", @@ -4193,7 +4921,26 @@ }, "Win Rate" : { "comment" : "Label for the win rate in the statistics sheet.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Win Rate" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tasa de Victoria" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taux de Victoire" + } + } + } }, "Winner" : { "comment" : "A description of the player's hand, including its value and whether they won.", @@ -4220,7 +4967,26 @@ }, "Won" : { "comment" : "Labels for the outcome circles in the \"Win/Loss/Push\" section.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Won" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ganado" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gagné" + } + } + } }, "Yellow Dot (bottom-left): A pair occurred in that hand" : { "comment" : "Explains the yellow dot marker in the history.", @@ -4270,36 +5036,23 @@ }, "You played %lld hands with a net result of %@. This session will be saved to your history." : { "comment" : "A message displayed when a user ends a game session. The first argument is the number of rounds played in the session. The second argument is the net result of the session, formatted as currency.", - "isCommentAutoGenerated" : true, - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "You played %1$lld hands with a net result of %2$@. This session will be saved to your history." - } - } - } - }, - "You've run out of chips!" : { - "comment" : "A message displayed when a player runs out of money in the game over screen.", - "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "You've run out of chips!" + "value" : "You played %1$lld hands with a net result of %2$@. This session will be saved to your history." } }, "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "¡Te quedaste sin fichas!" + "value" : "Jugaste %1$lld manos con un resultado neto de %2$@. Esta sesión se guardará en tu historial." } }, "fr-CA" : { "stringUnit" : { "state" : "translated", - "value" : "Vous n'avez plus de jetons!" + "value" : "Vous avez joué %1$lld mains avec un résultat net de %2$@. Cette session sera sauvegardée dans votre historique." } } } diff --git a/Baccarat/Baccarat/Views/Game/GameTableView.swift b/Baccarat/Baccarat/Views/Game/GameTableView.swift index df87694..aab8ab6 100644 --- a/Baccarat/Baccarat/Views/Game/GameTableView.swift +++ b/Baccarat/Baccarat/Views/Game/GameTableView.swift @@ -159,21 +159,17 @@ struct GameTableView: View { description: String(localized: "Change table limits and display options") ) ], - onStartTutorial: { - showWelcome = false - state.onboarding.completeWelcome() - checkOnboardingHints() - }, - onStartPlaying: { - // Mark all hints as shown FIRST so they don't appear - state.onboarding.markHintShown("bettingZone") - state.onboarding.markHintShown("dealButton") - state.onboarding.markHintShown("firstResult") - state.onboarding.completeWelcome() - showWelcome = false - } + onboarding: state.onboarding, + onDismiss: { showWelcome = false }, + onShowHints: checkOnboardingHints ) } + .onChange(of: showWelcome) { wasShowing, isShowing in + // Handle swipe-down dismissal: treat as "Start Playing" (no tooltips) + if wasShowing && !isShowing && !state.onboarding.hasCompletedWelcome { + state.onboarding.skipOnboarding() + } + } .onChange(of: state.totalBetAmount) { _, newTotal in if newTotal > 0, state.onboarding.shouldShowHint("dealButton") { showDealHintWithDelay() diff --git a/Blackjack/Blackjack/Engine/GameState.swift b/Blackjack/Blackjack/Engine/GameState.swift index d882d80..9df2d86 100644 --- a/Blackjack/Blackjack/Engine/GameState.swift +++ b/Blackjack/Blackjack/Engine/GameState.swift @@ -349,6 +349,7 @@ final class GameState: CasinoGameState { self.balance = settings.startingBalance self.engine = BlackjackEngine(settings: settings) self.onboarding = OnboardingState(gameIdentifier: "blackjack") + self.onboarding.registerHintKeys("bettingZone", "dealButton", "playerActions") self.persistence = CloudSyncManager() syncSoundSettings() loadSavedGame() diff --git a/Blackjack/Blackjack/Resources/Localizable.xcstrings b/Blackjack/Blackjack/Resources/Localizable.xcstrings index 305a396..9629d08 100644 --- a/Blackjack/Blackjack/Resources/Localizable.xcstrings +++ b/Blackjack/Blackjack/Resources/Localizable.xcstrings @@ -876,7 +876,26 @@ }, "ALL TIME SUMMARY" : { "comment" : "Title for a section in the statistics sheet that provides a summary of the user's overall performance over all time.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "ALL TIME SUMMARY" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "RESUMEN HISTÓRICO" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "RÉSUMÉ GLOBAL" + } + } + } }, "Allow doubling on split hands" : { "localizations" : { @@ -1133,7 +1152,26 @@ }, "BALANCE" : { "comment" : "Title of a section in the session detail view that shows the user's starting and ending balances.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "BALANCE" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "SALDO" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "SOLDE" + } + } + } }, "Basic Strategy" : { "localizations" : { @@ -1180,7 +1218,27 @@ } }, "Beat the Dealer" : { - + "comment" : "Welcome screen feature title for game objective.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beat the Dealer" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vence al Crupier" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Battez le Croupier" + } + } + } }, "Beat the dealer by getting a hand value closer to 21 without going over." : { "comment" : "Text for the objective of the game.", @@ -1674,7 +1732,27 @@ } }, "Built-in hints show optimal plays based on basic strategy" : { - + "comment" : "Welcome screen feature description for strategy hints.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Built-in hints show optimal plays based on basic strategy" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las sugerencias integradas muestran las jugadas óptimas basadas en estrategia básica" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les conseils intégrés montrent les jeux optimaux basés sur la stratégie de base" + } + } + } }, "BUST" : { "localizations" : { @@ -1876,7 +1954,26 @@ }, "CHIPS STATS" : { "comment" : "Title of a section in the Statistics Sheet that shows statistics related to the user's chips.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "CHIPS STATS" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "ESTADÍSTICAS DE FICHAS" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "STATISTIQUES DES JETONS" + } + } + } }, "Chips, cards, and results" : { "localizations" : { @@ -2222,7 +2319,27 @@ } }, "Customize Rules" : { - + "comment" : "Welcome screen feature title for customizing game rules.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Customize Rules" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personaliza las Reglas" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personnalisez les Règles" + } + } + } }, "DATA" : { "localizations" : { @@ -2638,7 +2755,26 @@ }, "Delete" : { "comment" : "A button label that deletes a session.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer" + } + } + } }, "Delete Session?" : { @@ -3048,7 +3184,26 @@ }, "Doubles" : { "comment" : "Label for a stat item in the statistics UI that shows the number of times a hand was doubled down.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doubles" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doblados" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doublés" + } + } + } }, "Enable 'Card Count' in Settings to practice." : { "localizations" : { @@ -3118,11 +3273,49 @@ }, "End Session" : { "comment" : "The text for a button that ends the current game session.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "End Session" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminar Sesión" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminer la Session" + } + } + } }, "End Session?" : { "comment" : "A confirmation dialog title that asks if the user wants to end their current session.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "End Session?" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Terminar Sesión?" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminer la Session?" + } + } + } }, "European" : { "localizations" : { @@ -3326,7 +3519,26 @@ }, "GAME STATS" : { "comment" : "Title for a section in the statistics sheet dedicated to blackjack-specific statistics.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "GAME STATS" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "ESTADÍSTICAS DEL JUEGO" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "STATISTIQUES DE JEU" + } + } + } }, "GAME STYLE" : { "localizations" : { @@ -3447,7 +3659,27 @@ } }, "Get closer to 21 than the dealer without going over" : { - + "comment" : "Welcome screen feature description for game objective.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Get closer to 21 than the dealer without going over" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acércate más a 21 que el crupier sin pasarte" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Approchez-vous de 21 plus que le croupier sans dépasser" + } + } + } }, "H17 rule, increases house edge" : { "localizations" : { @@ -3495,11 +3727,49 @@ }, "Hands" : { "comment" : "Label for the number of blackjack hands played in a session.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hands" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manos" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mains" + } + } + } }, "Hands played" : { "comment" : "A label describing the number of hands a player has played in a game.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hands played" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manos jugadas" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mains jouées" + } + } + } }, "Haptic Feedback" : { "localizations" : { @@ -3894,7 +4164,26 @@ }, "IN GAME STATS" : { "comment" : "Title of a section in the Statistics Sheet that shows in-game statistics.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "IN GAME STATS" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "ESTADÍSTICAS EN JUEGO" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "STATISTIQUES EN JEU" + } + } + } }, "Increase bets when the count is positive." : { "localizations" : { @@ -4167,7 +4456,27 @@ } }, "Learn Strategy" : { - + "comment" : "Welcome screen feature title for strategy hints.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Learn Strategy" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aprende Estrategia" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apprenez la Stratégie" + } + } + } }, "LEGAL" : { "localizations" : { @@ -4238,7 +4547,26 @@ }, "Lost" : { "comment" : "Label for a game outcome circle indicating a loss.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lost" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perdido" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perdu" + } + } + } }, "Lower house edge" : { "comment" : "Description of a deck count option when the user selects 2 decks.", @@ -5088,7 +5416,26 @@ }, "PLAYER" : { "comment" : "Title to display for a player hand when the hand number is not available.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "PLAYER" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "JUGADOR" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "JOUEUR" + } + } + } }, "Player hand: %@. Value: %@" : { "comment" : "A user-readable string describing a player's blackjack hand, including the card values and any relevant game results. The argument is a comma-separated list of the card descriptions in the player's hand.", @@ -5206,7 +5553,26 @@ }, "Push" : { "comment" : "Label for the \"Push\" outcome in the game stats section of the statistics sheet.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Push" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Empate" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Égalité" + } + } + } }, "PUSH" : { "localizations" : { @@ -5619,14 +5985,72 @@ }, "See detailed stats for each play session, just like at a real casino" : { "comment" : "Description of a feature in the welcome sheet that allows users to track their gaming sessions.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "See detailed stats for each play session, just like at a real casino" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ve estadísticas detalladas de cada sesión de juego, como en un casino real" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consultez les statistiques détaillées de chaque session de jeu, comme dans un vrai casino" + } + } + } }, "Select a chip and tap the bet area" : { - + "comment" : "Onboarding hint for placing bets.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select a chip and tap the bet area" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Selecciona una ficha y toca el área de apuesta" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionnez un jeton et touchez la zone de mise" + } + } + } }, "SESSION PERFORMANCE" : { "comment" : "Title of a section in the statistics sheet that shows performance metrics for individual sessions.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "SESSION PERFORMANCE" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "RENDIMIENTO DE SESIÓN" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "PERFORMANCE DE SESSION" + } + } + } }, "SESSION SUMMARY" : { "extractionState" : "stale", @@ -5653,7 +6077,26 @@ }, "Sessions" : { "comment" : "Label for the number of blackjack game sessions.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sessions" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sesiones" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sessions" + } + } + } }, "Settings" : { "localizations" : { @@ -5817,7 +6260,26 @@ }, "Show Hint" : { "comment" : "Label for a toolbar button that shows a hint.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Hint" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar Sugerencia" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher le Conseil" + } + } + } }, "Show Hints" : { "localizations" : { @@ -6139,7 +6601,26 @@ }, "Splits" : { "comment" : "Label for the number of split hands in the statistics UI.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Splits" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Divisiones" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Séparations" + } + } + } }, "Stand" : { "localizations" : { @@ -6806,11 +7287,49 @@ }, "Time" : { "comment" : "Label for the duration of a blackjack game.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Time" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temps" + } + } + } }, "Total game time" : { "comment" : "Label for a stat row displaying the total game time.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total game time" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiempo total de juego" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temps de jeu total" + } + } + } }, "Total Winnings" : { "localizations" : { @@ -6836,7 +7355,26 @@ }, "Track Sessions" : { "comment" : "Feature description in the welcome sheet for tracking detailed stats for each play session.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Track Sessions" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seguimiento de Sesiones" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suivez vos Sessions" + } + } + } }, "Traditional European casino style." : { "localizations" : { @@ -6998,7 +7536,26 @@ }, "Vegas Strip, Atlantic City, European, or create your own" : { "comment" : "Feature description in the welcome sheet about customizing the game rules.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vegas Strip, Atlantic City, European, or create your own" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vegas Strip, Atlantic City, Europeo o crea el tuyo" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vegas Strip, Atlantic City, Européen ou créez le vôtre" + } + } + } }, "Vibration on actions" : { "localizations" : { @@ -7115,7 +7672,26 @@ }, "Won" : { "comment" : "Label for a game outcome circle that indicates a win.", - "isCommentAutoGenerated" : true + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Won" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ganado" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gagné" + } + } + } }, "Worst" : { "extractionState" : "stale", @@ -7164,13 +7740,24 @@ }, "You played %lld hands with a net result of %@. This session will be saved to your history." : { "comment" : "A message that appears when a user ends a game session. It includes the number of hands played and the net result of the session.", - "isCommentAutoGenerated" : true, "localizations" : { "en" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "You played %1$lld hands with a net result of %2$@. This session will be saved to your history." } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jugaste %1$lld manos con un resultado neto de %2$@. Esta sesión se guardará en tu historial." + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vous avez joué %1$lld mains avec un résultat net de %2$@. Cette session sera sauvegardée dans votre historique." + } } } }, diff --git a/Blackjack/Blackjack/Views/Game/GameTableView.swift b/Blackjack/Blackjack/Views/Game/GameTableView.swift index 25b6e14..6568cec 100644 --- a/Blackjack/Blackjack/Views/Game/GameTableView.swift +++ b/Blackjack/Blackjack/Views/Game/GameTableView.swift @@ -109,21 +109,17 @@ struct GameTableView: View { description: String(localized: "Vegas Strip, Atlantic City, European, or create your own") ) ], - onStartTutorial: { - showWelcome = false - state.onboarding.completeWelcome() - checkOnboardingHints() - }, - onStartPlaying: { - // Mark all hints as shown FIRST so they don't appear - state.onboarding.markHintShown("bettingZone") - state.onboarding.markHintShown("dealButton") - state.onboarding.markHintShown("playerActions") - state.onboarding.completeWelcome() - showWelcome = false - } + onboarding: state.onboarding, + onDismiss: { showWelcome = false }, + onShowHints: checkOnboardingHints ) } + .onChange(of: showWelcome) { wasShowing, isShowing in + // Handle swipe-down dismissal: treat as "Start Playing" (no tooltips) + if wasShowing && !isShowing && !state.onboarding.hasCompletedWelcome { + state.onboarding.skipOnboarding() + } + } .onChange(of: state.currentBet) { _, newBet in if newBet > 0, state.onboarding.shouldShowHint("dealButton") { showDealHintWithDelay() diff --git a/CasinoKit/Sources/CasinoKit/Models/OnboardingState.swift b/CasinoKit/Sources/CasinoKit/Models/OnboardingState.swift index f2a4df8..8f9d1af 100644 --- a/CasinoKit/Sources/CasinoKit/Models/OnboardingState.swift +++ b/CasinoKit/Sources/CasinoKit/Models/OnboardingState.swift @@ -26,6 +26,10 @@ public final class OnboardingState { /// Set of hint keys that have been shown to the user. public var hintsShown: Set = [] + /// Hint keys registered by the app for automatic skipping. + /// When the user skips onboarding, all registered hints are marked as shown. + private var registeredHintKeys: Set = [] + // MARK: - Initialization private let persistenceKey: String @@ -35,6 +39,18 @@ public final class OnboardingState { load() } + /// Registers hint keys that should be marked as shown when skipping onboarding. + /// Call this once during app setup with all hint keys used by the game. + public func registerHintKeys(_ keys: Set) { + registeredHintKeys = keys + } + + /// Registers hint keys that should be marked as shown when skipping onboarding. + /// Call this once during app setup with all hint keys used by the game. + public func registerHintKeys(_ keys: String...) { + registeredHintKeys = Set(keys) + } + // MARK: - Hint Management /// Marks a hint as shown and persists the state. @@ -55,6 +71,18 @@ public final class OnboardingState { save() } + /// Skips onboarding entirely - marks all registered hints as shown and completes welcome. + /// Use this when the user dismisses the welcome sheet without choosing tutorial mode + /// (e.g., swiping down to dismiss, or tapping "Start Playing"). + public func skipOnboarding() { + for key in registeredHintKeys { + hintsShown.insert(key) + } + hasLaunchedBefore = true + hasCompletedWelcome = true + save() + } + /// Enables tutorial mode (shows all hints again). public func startTutorialMode() { isTutorialMode = true diff --git a/CasinoKit/Sources/CasinoKit/Views/WelcomeSheet.swift b/CasinoKit/Sources/CasinoKit/Views/WelcomeSheet.swift index 453b45a..439737b 100644 --- a/CasinoKit/Sources/CasinoKit/Views/WelcomeSheet.swift +++ b/CasinoKit/Sources/CasinoKit/Views/WelcomeSheet.swift @@ -21,6 +21,42 @@ public struct WelcomeSheet: View { @ScaledMetric(relativeTo: .body) private var iconSize: CGFloat = CasinoDesign.IconSize.large @ScaledMetric(relativeTo: .body) private var buttonPadding: CGFloat = CasinoDesign.Spacing.medium + /// Creates a welcome sheet with automatic onboarding state management. + /// + /// This initializer handles the common pattern of: + /// - "Show Me How" → completes welcome and triggers hint display + /// - "Start Playing" → skips all hints and completes welcome + /// + /// - Parameters: + /// - gameName: The name of the game to display + /// - gameEmoji: An emoji representing the game + /// - features: List of features to highlight + /// - onboarding: The onboarding state to manage (must have hint keys registered) + /// - onDismiss: Called after the sheet is dismissed + /// - onShowHints: Called when user chooses "Show Me How" - use this to trigger tooltip display + public init( + gameName: String, + gameEmoji: String = "🎰", + features: [WelcomeFeature], + onboarding: OnboardingState, + onDismiss: @escaping () -> Void, + onShowHints: @escaping () -> Void + ) { + self.gameName = gameName + self.gameEmoji = gameEmoji + self.features = features + self.onStartTutorial = { + onboarding.completeWelcome() + onDismiss() + onShowHints() + } + self.onStartPlaying = { + onboarding.skipOnboarding() + onDismiss() + } + } + + /// Creates a welcome sheet with custom callbacks for full control. public init( gameName: String, gameEmoji: String = "🎰",