diff --git a/lib/src/core/notification/notification_service.dart b/lib/src/core/notification/notification_service.dart index a456c15..ecaa19c 100644 --- a/lib/src/core/notification/notification_service.dart +++ b/lib/src/core/notification/notification_service.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'package:asciineverdie/data/game_text_l10n.dart' as game_l10n; + /// 알림 타입 (Notification Type) enum NotificationType { levelUp, // 레벨업 @@ -62,8 +64,8 @@ class NotificationService { show( GameNotification( type: NotificationType.levelUp, - title: 'LEVEL UP!', - subtitle: 'Level $newLevel', + title: game_l10n.notifyLevelUp, + subtitle: game_l10n.notifyLevel(newLevel), data: {'level': newLevel}, duration: const Duration(seconds: 2), ), @@ -75,7 +77,7 @@ class NotificationService { show( GameNotification( type: NotificationType.questComplete, - title: 'QUEST COMPLETE!', + title: game_l10n.notifyQuestComplete, subtitle: questName, data: {'quest': questName}, duration: const Duration(seconds: 2), @@ -87,8 +89,8 @@ class NotificationService { /// actNumber: 0=프롤로그, 1=Act I, 2=Act II, ... void showActComplete(int actNumber) { final title = actNumber == 0 - ? 'PROLOGUE COMPLETE!' - : 'ACT $actNumber COMPLETE!'; + ? game_l10n.notifyPrologueComplete + : game_l10n.notifyActComplete(actNumber); show( GameNotification( type: NotificationType.actComplete, @@ -103,7 +105,7 @@ class NotificationService { show( GameNotification( type: NotificationType.newSpell, - title: 'NEW SPELL!', + title: game_l10n.notifyNewSpell, subtitle: spellName, data: {'spell': spellName}, duration: const Duration(seconds: 2), @@ -116,7 +118,7 @@ class NotificationService { show( GameNotification( type: NotificationType.newEquipment, - title: 'NEW EQUIPMENT!', + title: game_l10n.notifyNewEquipment, subtitle: equipmentName, data: {'equipment': equipmentName, 'slot': slot}, duration: const Duration(seconds: 2), @@ -129,7 +131,7 @@ class NotificationService { show( GameNotification( type: NotificationType.bossDefeat, - title: 'BOSS DEFEATED!', + title: game_l10n.notifyBossDefeated, subtitle: bossName, data: {'boss': bossName}, duration: const Duration(seconds: 3), diff --git a/lib/src/features/front/front_screen.dart b/lib/src/features/front/front_screen.dart index 76848d4..05af3d3 100644 --- a/lib/src/features/front/front_screen.dart +++ b/lib/src/features/front/front_screen.dart @@ -217,9 +217,9 @@ class _AnimationPanel extends StatelessWidget { children: [ const Icon(Icons.auto_awesome, color: RetroColors.gold, size: 18), const SizedBox(width: 10), - const Text( - 'ASCII NEVER DIE', - style: TextStyle( + Text( + L10n.of(context).appTitle.toUpperCase(), + style: const TextStyle( fontFamily: 'PressStart2P', fontSize: 14, color: RetroColors.gold, @@ -272,7 +272,7 @@ class _ActionButtons extends StatelessWidget { final l10n = L10n.of(context); return RetroPanel( - title: 'MENU', + title: L10n.of(context).menuTitle, padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, diff --git a/lib/src/features/game/game_play_screen.dart b/lib/src/features/game/game_play_screen.dart index acae2e6..b852271 100644 --- a/lib/src/features/game/game_play_screen.dart +++ b/lib/src/features/game/game_play_screen.dart @@ -551,150 +551,135 @@ class _GamePlayScreenState extends State return const Scaffold(body: Center(child: CircularProgressIndicator())); } - // 로케일 변경 시 전체 위젯 트리 강제 리빌드를 위한 Key final localeKey = ValueKey(game_l10n.currentGameLocale); - // 캐로셀 레이아웃 사용 여부 확인 if (_shouldUseCarouselLayout(context)) { - return NotificationOverlay( - key: localeKey, - notificationService: _notificationService, - child: PopScope( - canPop: false, - onPopInvokedWithResult: (didPop, result) async { - if (didPop) return; - final shouldPop = await _onPopInvoked(); - if (shouldPop && context.mounted) { - await widget.controller.pause(saveOnStop: false); - if (context.mounted) { - Navigator.of(context).pop(); - } - } - }, - child: Stack( - children: [ - MobileCarouselLayout( - state: state, - combatLogEntries: _combatLogController.entries, - speedMultiplier: widget.controller.loop?.speedMultiplier ?? 1, - onSpeedCycle: () { - widget.controller.loop?.cycleSpeed(); - setState(() {}); - }, - onSetSpeed: (speed) { - widget.controller.loop?.setSpeed(speed); - setState(() {}); - }, - // 특수 애니메이션 중에는 일시정지 상태로 표시하지 않음 - isPaused: - !widget.controller.isRunning && _specialAnimation == null, - onPauseToggle: () async { - await widget.controller.togglePause(); - setState(() {}); - }, - onSave: _saveGameState, - onExit: () async { - final shouldExit = await _onPopInvoked(); - if (shouldExit && context.mounted) { - await widget.controller.pause(saveOnStop: false); - if (context.mounted) { - Navigator.of(context).pop(); - } - } - }, - notificationService: _notificationService, - specialAnimation: _specialAnimation, - onLanguageChange: (locale) async { - // navigator 참조를 async gap 전에 저장 - final navigator = Navigator.of(context); - // 1. 현재 상태 저장 - await widget.controller.pause(saveOnStop: true); - // 2. 로케일 변경 - game_l10n.setGameLocale(locale); - // 3. 화면 재생성 (전체 UI 재구성) - if (context.mounted) { - await widget.controller.resume(); - navigator.pushReplacement( - MaterialPageRoute( - builder: (_) => GamePlayScreen( - controller: widget.controller, - audioService: widget.audioService, - ), - ), - ); - } - }, - onDeleteSaveAndNewGame: () async { - // 게임 루프 중지 - await widget.controller.pause(saveOnStop: false); - // 세이브 파일 삭제 - await widget.controller.saveManager.deleteSave(); - // 캐릭터 생성 화면으로 돌아가기 - if (context.mounted) { - Navigator.of(context).pop(); - } - }, - // 사운드 설정 - bgmVolume: _audioController.bgmVolume, - sfxVolume: _audioController.sfxVolume, - onBgmVolumeChange: (volume) { - _audioController.setBgmVolume(volume); - setState(() {}); - }, - onSfxVolumeChange: (volume) { - _audioController.setSfxVolume(volume); - setState(() {}); - }, - // 통계 및 도움말 - onShowStatistics: () => _showStatisticsDialog(context), - onShowHelp: () => HelpDialog.show(context), - // 치트 (디버그 모드) - cheatsEnabled: widget.controller.cheatsEnabled, - onCheatTask: () => widget.controller.loop?.cheatCompleteTask(), - onCheatQuest: () => - widget.controller.loop?.cheatCompleteQuest(), - onCheatPlot: () => widget.controller.loop?.cheatCompletePlot(), - onCreateTestCharacter: () async { - final navigator = Navigator.of(context); - final success = await widget.controller.createTestCharacter(); - if (success && mounted) { - navigator.popUntil((route) => route.isFirst); - } - }, - // 수익화 버프 (자동부활, 광고배속) - autoReviveEndMs: widget.controller.monetization.autoReviveEndMs, - speedBoostEndMs: widget.controller.monetization.speedBoostEndMs, - isPaidUser: widget.controller.monetization.isPaidUser, - onSpeedBoostActivate: _handleSpeedBoost, - isSpeedBoostActive: widget.controller.isSpeedBoostActive, - adSpeedMultiplier: widget.controller.adSpeedMultiplier, - has2xUnlocked: widget.controller.has2xUnlocked, - ), - // 사망 오버레이 - if (state.isDead && state.deathInfo != null) - DeathOverlay( - deathInfo: state.deathInfo!, - traits: state.traits, - onResurrect: _handleResurrect, - onAdRevive: _handleAdRevive, - isPaidUser: IAPService.instance.isAdRemovalPurchased, - ), - // 승리 오버레이 (게임 클리어) - if (widget.controller.isComplete) - VictoryOverlay( - traits: state.traits, - stats: state.stats, - progress: state.progress, - elapsedMs: state.skillSystem.elapsedMs, - onComplete: _handleVictoryComplete, - ), - ], - ), - ), - ); + return _buildMobileLayout(context, state, localeKey); } - // 기존 데스크톱 레이아웃 (레트로 스타일) + return _buildDesktopLayout(context, state, localeKey); + } + + /// 모바일 캐로셀 레이아웃 + Widget _buildMobileLayout( + BuildContext context, + GameState state, + ValueKey localeKey, + ) { + return NotificationOverlay( + key: localeKey, + notificationService: _notificationService, + child: PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, result) async { + if (didPop) return; + final shouldPop = await _onPopInvoked(); + if (shouldPop && context.mounted) { + await widget.controller.pause(saveOnStop: false); + if (context.mounted) { + Navigator.of(context).pop(); + } + } + }, + child: Stack( + children: [ + MobileCarouselLayout( + state: state, + combatLogEntries: _combatLogController.entries, + speedMultiplier: widget.controller.loop?.speedMultiplier ?? 1, + onSpeedCycle: () { + widget.controller.loop?.cycleSpeed(); + setState(() {}); + }, + onSetSpeed: (speed) { + widget.controller.loop?.setSpeed(speed); + setState(() {}); + }, + isPaused: + !widget.controller.isRunning && _specialAnimation == null, + onPauseToggle: () async { + await widget.controller.togglePause(); + setState(() {}); + }, + onSave: _saveGameState, + onExit: () async { + final shouldExit = await _onPopInvoked(); + if (shouldExit && context.mounted) { + await widget.controller.pause(saveOnStop: false); + if (context.mounted) { + Navigator.of(context).pop(); + } + } + }, + notificationService: _notificationService, + specialAnimation: _specialAnimation, + onLanguageChange: (locale) async { + final navigator = Navigator.of(context); + await widget.controller.pause(saveOnStop: true); + game_l10n.setGameLocale(locale); + if (context.mounted) { + await widget.controller.resume(); + navigator.pushReplacement( + MaterialPageRoute( + builder: (_) => GamePlayScreen( + controller: widget.controller, + audioService: widget.audioService, + ), + ), + ); + } + }, + onDeleteSaveAndNewGame: () async { + await widget.controller.pause(saveOnStop: false); + await widget.controller.saveManager.deleteSave(); + if (context.mounted) { + Navigator.of(context).pop(); + } + }, + bgmVolume: _audioController.bgmVolume, + sfxVolume: _audioController.sfxVolume, + onBgmVolumeChange: (volume) { + _audioController.setBgmVolume(volume); + setState(() {}); + }, + onSfxVolumeChange: (volume) { + _audioController.setSfxVolume(volume); + setState(() {}); + }, + onShowStatistics: () => _showStatisticsDialog(context), + onShowHelp: () => HelpDialog.show(context), + cheatsEnabled: widget.controller.cheatsEnabled, + onCheatTask: () => widget.controller.loop?.cheatCompleteTask(), + onCheatQuest: () => widget.controller.loop?.cheatCompleteQuest(), + onCheatPlot: () => widget.controller.loop?.cheatCompletePlot(), + onCreateTestCharacter: () async { + final navigator = Navigator.of(context); + final success = await widget.controller.createTestCharacter(); + if (success && mounted) { + navigator.popUntil((route) => route.isFirst); + } + }, + autoReviveEndMs: widget.controller.monetization.autoReviveEndMs, + speedBoostEndMs: widget.controller.monetization.speedBoostEndMs, + isPaidUser: widget.controller.monetization.isPaidUser, + onSpeedBoostActivate: _handleSpeedBoost, + isSpeedBoostActive: widget.controller.isSpeedBoostActive, + adSpeedMultiplier: widget.controller.adSpeedMultiplier, + has2xUnlocked: widget.controller.has2xUnlocked, + ), + ..._buildOverlays(state), + ], + ), + ), + ); + } + + /// 데스크톱 3패널 레이아웃 + Widget _buildDesktopLayout( + BuildContext context, + GameState state, + ValueKey localeKey, + ) { return NotificationOverlay( key: localeKey, notificationService: _notificationService, @@ -710,135 +695,16 @@ class _GamePlayScreenState extends State } } }, - // 웹/데스크톱 키보드 단축키 지원 child: Focus( autofocus: true, onKeyEvent: (node, event) => _handleKeyboardShortcut(event, context), child: Scaffold( backgroundColor: RetroColors.deepBrown, - appBar: AppBar( - backgroundColor: RetroColors.darkBrown, - title: Text( - L10n.of(context).progressQuestTitle(state.traits.name), - style: const TextStyle( - fontFamily: 'PressStart2P', - fontSize: 15, - color: RetroColors.gold, - ), - ), - actions: [ - // 치트 버튼 (디버그용) - if (widget.controller.cheatsEnabled) ...[ - IconButton( - icon: const Text('L+1'), - tooltip: L10n.of(context).levelUp, - onPressed: () => - widget.controller.loop?.cheatCompleteTask(), - ), - IconButton( - icon: const Text('Q!'), - tooltip: L10n.of(context).completeQuest, - onPressed: () => - widget.controller.loop?.cheatCompleteQuest(), - ), - IconButton( - icon: const Text('P!'), - tooltip: L10n.of(context).completePlot, - onPressed: () => - widget.controller.loop?.cheatCompletePlot(), - ), - ], - // 통계 버튼 - IconButton( - icon: const Icon(Icons.bar_chart), - tooltip: game_l10n.uiStatistics, - onPressed: () => _showStatisticsDialog(context), - ), - // 도움말 버튼 - IconButton( - icon: const Icon(Icons.help_outline), - tooltip: game_l10n.uiHelp, - onPressed: () => HelpDialog.show(context), - ), - // 설정 버튼 - IconButton( - icon: const Icon(Icons.settings), - tooltip: game_l10n.uiSettings, - onPressed: () => _showSettingsScreen(context), - ), - ], - ), + appBar: _buildDesktopAppBar(context, state), body: Stack( children: [ - // 메인 게임 UI - Column( - children: [ - // 상단: ASCII 애니메이션 + Task Progress (Phase 7: 고정 4색 팔레트) - TaskProgressPanel( - progress: state.progress, - speedMultiplier: - widget.controller.loop?.speedMultiplier ?? 1, - onSpeedCycle: () { - widget.controller.loop?.cycleSpeed(); - setState(() {}); - }, - // 특수 애니메이션 중에는 일시정지 상태로 표시하지 않음 - isPaused: - !widget.controller.isRunning && - _specialAnimation == null, - onPauseToggle: () async { - await widget.controller.togglePause(); - setState(() {}); - }, - specialAnimation: _specialAnimation, - weaponName: state.equipment.weapon, - shieldName: state.equipment.shield, - characterLevel: state.traits.level, - monsterLevel: state.progress.currentTask.monsterLevel, - monsterGrade: state.progress.currentTask.monsterGrade, - monsterSize: state.progress.currentTask.monsterSize, - latestCombatEvent: - state.progress.currentCombat?.recentEvents.lastOrNull, - raceId: state.traits.raceId, - ), - - // 메인 3패널 영역 - Expanded( - child: Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // 좌측 패널: Character Sheet - Expanded(flex: 2, child: _buildCharacterPanel(state)), - - // 중앙 패널: Equipment/Inventory - Expanded(flex: 3, child: _buildEquipmentPanel(state)), - - // 우측 패널: Plot/Quest - Expanded(flex: 2, child: _buildQuestPanel(state)), - ], - ), - ), - ], - ), - - // 사망 오버레이 - if (state.isDead && state.deathInfo != null) - DeathOverlay( - deathInfo: state.deathInfo!, - traits: state.traits, - onResurrect: _handleResurrect, - onAdRevive: _handleAdRevive, - isPaidUser: IAPService.instance.isAdRemovalPurchased, - ), - // 승리 오버레이 (게임 클리어) - if (widget.controller.isComplete) - VictoryOverlay( - traits: state.traits, - stats: state.stats, - progress: state.progress, - elapsedMs: state.skillSystem.elapsedMs, - onComplete: _handleVictoryComplete, - ), + _buildDesktopMainContent(state), + ..._buildOverlays(state), ], ), ), @@ -847,6 +713,118 @@ class _GamePlayScreenState extends State ); } + /// 데스크톱 앱바 + PreferredSizeWidget _buildDesktopAppBar(BuildContext context, GameState state) { + return AppBar( + backgroundColor: RetroColors.darkBrown, + title: Text( + L10n.of(context).progressQuestTitle(state.traits.name), + style: const TextStyle( + fontFamily: 'PressStart2P', + fontSize: 15, + color: RetroColors.gold, + ), + ), + actions: [ + if (widget.controller.cheatsEnabled) ...[ + IconButton( + icon: const Text('L+1'), + tooltip: L10n.of(context).levelUp, + onPressed: () => widget.controller.loop?.cheatCompleteTask(), + ), + IconButton( + icon: const Text('Q!'), + tooltip: L10n.of(context).completeQuest, + onPressed: () => widget.controller.loop?.cheatCompleteQuest(), + ), + IconButton( + icon: const Text('P!'), + tooltip: L10n.of(context).completePlot, + onPressed: () => widget.controller.loop?.cheatCompletePlot(), + ), + ], + IconButton( + icon: const Icon(Icons.bar_chart), + tooltip: game_l10n.uiStatistics, + onPressed: () => _showStatisticsDialog(context), + ), + IconButton( + icon: const Icon(Icons.help_outline), + tooltip: game_l10n.uiHelp, + onPressed: () => HelpDialog.show(context), + ), + IconButton( + icon: const Icon(Icons.settings), + tooltip: game_l10n.uiSettings, + onPressed: () => _showSettingsScreen(context), + ), + ], + ); + } + + /// 데스크톱 메인 컨텐츠 (3패널) + Widget _buildDesktopMainContent(GameState state) { + return Column( + children: [ + TaskProgressPanel( + progress: state.progress, + speedMultiplier: widget.controller.loop?.speedMultiplier ?? 1, + onSpeedCycle: () { + widget.controller.loop?.cycleSpeed(); + setState(() {}); + }, + isPaused: !widget.controller.isRunning && _specialAnimation == null, + onPauseToggle: () async { + await widget.controller.togglePause(); + setState(() {}); + }, + specialAnimation: _specialAnimation, + weaponName: state.equipment.weapon, + shieldName: state.equipment.shield, + characterLevel: state.traits.level, + monsterLevel: state.progress.currentTask.monsterLevel, + monsterGrade: state.progress.currentTask.monsterGrade, + monsterSize: state.progress.currentTask.monsterSize, + latestCombatEvent: + state.progress.currentCombat?.recentEvents.lastOrNull, + raceId: state.traits.raceId, + ), + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded(flex: 2, child: _buildCharacterPanel(state)), + Expanded(flex: 3, child: _buildEquipmentPanel(state)), + Expanded(flex: 2, child: _buildQuestPanel(state)), + ], + ), + ), + ], + ); + } + + /// 공통 오버레이 (사망, 승리) + List _buildOverlays(GameState state) { + return [ + if (state.isDead && state.deathInfo != null) + DeathOverlay( + deathInfo: state.deathInfo!, + traits: state.traits, + onResurrect: _handleResurrect, + onAdRevive: _handleAdRevive, + isPaidUser: IAPService.instance.isAdRemovalPurchased, + ), + if (widget.controller.isComplete) + VictoryOverlay( + traits: state.traits, + stats: state.stats, + progress: state.progress, + elapsedMs: state.skillSystem.elapsedMs, + onComplete: _handleVictoryComplete, + ), + ]; + } + /// 키보드 단축키 핸들러 (웹/데스크톱) /// Space: 일시정지/재개, S: 속도 변경, H: 도움말, Esc: 설정 KeyEventResult _handleKeyboardShortcut(KeyEvent event, BuildContext context) { diff --git a/lib/src/features/game/widgets/ascii_animation_card.dart b/lib/src/features/game/widgets/ascii_animation_card.dart index 4be14c5..46f114c 100644 --- a/lib/src/features/game/widgets/ascii_animation_card.dart +++ b/lib/src/features/game/widgets/ascii_animation_card.dart @@ -785,7 +785,9 @@ class _AsciiAnimationCardState extends State { child: AsciiDisintegrateWidget( characterLines: _deathAnimationMonsterLines!, duration: const Duration(milliseconds: 800), - textColor: widget.monsterGrade?.displayColor, + textColor: widget.monsterGrade?.displayColorCode != null + ? Color(widget.monsterGrade!.displayColorCode!) + : null, onComplete: _onDeathAnimationComplete, ), ), diff --git a/lib/src/features/game/widgets/enhanced_animation_panel.dart b/lib/src/features/game/widgets/enhanced_animation_panel.dart index 3412c51..0e907b7 100644 --- a/lib/src/features/game/widgets/enhanced_animation_panel.dart +++ b/lib/src/features/game/widgets/enhanced_animation_panel.dart @@ -756,9 +756,10 @@ class _EnhancedAnimationPanelState extends State final gradePrefix = (isKillTask && grade != null) ? grade.displayPrefix : ''; - final gradeColor = (isKillTask && grade != null) - ? grade.displayColor + final gradeColorCode = (isKillTask && grade != null) + ? grade.displayColorCode : null; + final gradeColor = gradeColorCode != null ? Color(gradeColorCode) : null; return Row( crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/src/features/game/widgets/task_progress_panel.dart b/lib/src/features/game/widgets/task_progress_panel.dart index 47184a6..0cc56ab 100644 --- a/lib/src/features/game/widgets/task_progress_panel.dart +++ b/lib/src/features/game/widgets/task_progress_panel.dart @@ -169,9 +169,10 @@ class TaskProgressPanel extends StatelessWidget { final gradePrefix = (isKillTask && grade != null) ? grade.displayPrefix : ''; - final gradeColor = (isKillTask && grade != null) - ? grade.displayColor + final gradeColorCode = (isKillTask && grade != null) + ? grade.displayColorCode : null; + final gradeColor = gradeColorCode != null ? Color(gradeColorCode) : null; return Text.rich( TextSpan( diff --git a/lib/src/features/settings/settings_screen.dart b/lib/src/features/settings/settings_screen.dart index 2f033d4..3bfe1e9 100644 --- a/lib/src/features/settings/settings_screen.dart +++ b/lib/src/features/settings/settings_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:asciineverdie/data/game_text_l10n.dart' as game_l10n; +import 'package:asciineverdie/l10n/app_localizations.dart'; import 'package:asciineverdie/src/core/engine/debug_settings_service.dart'; import 'package:asciineverdie/src/core/storage/settings_repository.dart'; import 'package:asciineverdie/src/shared/retro_colors.dart'; @@ -145,7 +146,7 @@ class _SettingsScreenState extends State { // 디버그 섹션 (디버그 모드에서만 표시) if (kDebugMode) ...[ const SizedBox(height: 16), - const _RetroSectionTitle(title: 'DEBUG'), + _RetroSectionTitle(title: L10n.of(context).debugTitle), const SizedBox(height: 8), _buildDebugSection(context), ], @@ -308,7 +309,7 @@ class _SettingsScreenState extends State { ), const SizedBox(width: 8), Text( - 'DEVELOPER TOOLS', + L10n.of(context).debugDeveloperTools, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 10, @@ -322,8 +323,8 @@ class _SettingsScreenState extends State { // IAP 시뮬레이션 토글 _RetroDebugToggle( icon: Icons.shopping_cart, - label: 'IAP PURCHASED', - description: 'ON: 유료 유저로 동작 (광고 제거)', + label: L10n.of(context).debugIapPurchased, + description: L10n.of(context).debugIapPurchasedDesc, value: _debugIapSimulated, onChanged: (value) async { await DebugSettingsService.instance.setIapSimulated(value); @@ -346,7 +347,7 @@ class _SettingsScreenState extends State { // 테스트 캐릭터 생성 if (widget.onCreateTestCharacter != null) ...[ Text( - '현재 캐릭터를 레벨 100으로 수정하여\n명예의 전당에 등록합니다.', + L10n.of(context).debugTestCharacterDesc, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 7, @@ -358,7 +359,7 @@ class _SettingsScreenState extends State { SizedBox( width: double.infinity, child: RetroTextButton( - text: 'CREATE TEST CHARACTER', + text: L10n.of(context).debugCreateTestCharacter, icon: Icons.science, onPressed: _handleCreateTestCharacter, ), @@ -388,7 +389,7 @@ class _SettingsScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'OFFLINE HOURS', + L10n.of(context).debugOfflineHours, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 8, @@ -397,7 +398,7 @@ class _SettingsScreenState extends State { ), const SizedBox(height: 2), Text( - '복귀 보상 테스트 (재시작 시 적용)', + L10n.of(context).debugOfflineHoursDesc, style: TextStyle( fontFamily: 'PressStart2P', fontSize: 6, @@ -435,14 +436,10 @@ class _SettingsScreenState extends State { final confirmed = await showDialog( context: context, builder: (context) => _RetroConfirmDialog( - title: 'CREATE TEST CHARACTER?', - message: - '현재 캐릭터가 레벨 100으로 변환되어\n' - '명예의 전당에 등록됩니다.\n\n' - '⚠️ 현재 세이브 파일이 삭제됩니다.\n' - '이 작업은 되돌릴 수 없습니다.', - confirmText: 'CREATE', - cancelText: 'CANCEL', + title: L10n.of(context).debugCreateTestCharacterTitle, + message: L10n.of(context).debugCreateTestCharacterMessage, + confirmText: L10n.of(context).createButton, + cancelText: L10n.of(context).cancel.toUpperCase(), ), );