Refactor: Introduce AppStrings for soft i18n

This commit is contained in:
Horoli 2025-12-07 18:58:44 +09:00
parent 9a9022356a
commit c570d61563
7 changed files with 155 additions and 58 deletions

View File

@ -0,0 +1,64 @@
class AppStrings {
// Main Menu
static const String gameTitle = "Colosseum's Choice";
static const String startGame = "Start Game";
static const String continueGame = "Continue";
static const String exitGame = "Exit Game";
static const String credits = "Credits";
// Common Actions
static const String confirm = "Confirm";
static const String cancel = "Cancel";
static const String back = "Back";
static const String close = "Close";
static const String equip = "Equip";
static const String unequip = "Unequip";
static const String discard = "Discard";
static const String sell = "Sell";
static const String buy = "Buy";
// Stats
static const String hp = "HP";
static const String atk = "ATK";
static const String def = "DEF";
static const String armor = "Armor";
static const String luck = "Luck";
static const String gold = "Gold";
// Battle
static const String attack = "Attack";
static const String defend = "Defend";
static const String turn = "Turn";
static const String playerTurn = "Player's Turn";
static const String enemyTurn = "Enemy's Turn";
static const String victory = "Victory!";
static const String defeat = "Defeat";
static const String reward = "Reward";
static const String chooseReward = "Choose a Reward";
static const String skip = "Skip";
static const String nextStage = "Next Stage";
static const String returnToMenu = "Return to Menu";
static const String restart = "Restart";
// Inventory
static const String inventory = "Inventory";
static const String equipment = "Equipment";
static const String bag = "Bag";
static const String emptySlot = "Empty";
static const String noItems = "No items in inventory";
// Shop
static const String shopTitle = "Merchant";
static const String shopWelcome = "Welcome, traveler!";
static const String refreshShop = "Restock";
static const String notEnoughGold = "Not enough gold!";
static const String inventoryFull = "Inventory is full!";
// Risk Levels
static const String riskSafe = "Safe";
static const String riskNormal = "Normal";
static const String riskRisky = "Risky";
// Settings
static const String settings = "Settings";
}

View File

@ -21,6 +21,7 @@ import '../widgets/battle/explosion_widget.dart';
import 'main_menu_screen.dart'; import 'main_menu_screen.dart';
import '../game/config/battle_config.dart'; import '../game/config/battle_config.dart';
import '../game/config/theme_config.dart'; import '../game/config/theme_config.dart';
import '../game/config/app_strings.dart';
class BattleScreen extends StatefulWidget { class BattleScreen extends StatefulWidget {
const BattleScreen({super.key}); const BattleScreen({super.key});
@ -43,7 +44,7 @@ class _BattleScreenState extends State<BattleScreen> {
GlobalKey<BattleAnimationWidgetState>(); GlobalKey<BattleAnimationWidgetState>();
final GlobalKey<ExplosionWidgetState> _explosionKey = final GlobalKey<ExplosionWidgetState> _explosionKey =
GlobalKey<ExplosionWidgetState>(); GlobalKey<ExplosionWidgetState>();
bool _showLogs = true; bool _showLogs = false;
bool _isPlayerAttacking = false; // Player Attack Animation State bool _isPlayerAttacking = false; // Player Attack Animation State
@override @override
@ -421,7 +422,7 @@ class _BattleScreenState extends State<BattleScreen> {
child: FittedBox( child: FittedBox(
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
child: Text( child: Text(
"Turn ${battleProvider.turnCount}", "${AppStrings.turn} ${battleProvider.turnCount}",
style: const TextStyle( style: const TextStyle(
color: ThemeConfig.textColorWhite, color: ThemeConfig.textColorWhite,
fontSize: ThemeConfig.fontSizeHeader, fontSize: ThemeConfig.fontSizeHeader,
@ -540,12 +541,16 @@ class _BattleScreenState extends State<BattleScreen> {
child: SimpleDialog( child: SimpleDialog(
title: Row( title: Row(
children: [ children: [
const Text("Victory! Choose a Reward"), const Text("${AppStrings.victory} ${AppStrings.chooseReward}"),
const Spacer(), const Spacer(),
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon(Icons.monetization_on, color: ThemeConfig.statGoldColor, size: 18), Icon(
Icons.monetization_on,
color: ThemeConfig.statGoldColor,
size: 18,
),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
"${battleProvider.lastGoldReward} G", "${battleProvider.lastGoldReward} G",
@ -568,7 +573,7 @@ class _BattleScreenState extends State<BattleScreen> {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text( content: Text(
"Inventory is full! Cannot take item.", "${AppStrings.inventoryFull} Cannot take item.",
), ),
backgroundColor: Colors.red, backgroundColor: Colors.red,
), ),
@ -586,10 +591,11 @@ class _BattleScreenState extends State<BattleScreen> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.blueGrey[700], color: Colors.blueGrey[700],
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
4), 4,
),
border: Border.all( border: Border.all(
color: item.rarity != color:
ItemRarity.magic item.rarity != ItemRarity.magic
? ItemUtils.getRarityColor( ? ItemUtils.getRarityColor(
item.rarity, item.rarity,
) )
@ -613,7 +619,8 @@ class _BattleScreenState extends State<BattleScreen> {
color: isSkip color: isSkip
? ThemeConfig.textColorGrey ? ThemeConfig.textColorGrey
: ItemUtils.getRarityColor( : ItemUtils.getRarityColor(
item.rarity), item.rarity,
),
), ),
), ),
], ],
@ -651,7 +658,7 @@ class _BattleScreenState extends State<BattleScreen> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const Text( const Text(
"DEFEAT", AppStrings.defeat,
style: TextStyle( style: TextStyle(
color: ThemeConfig.statHpColor, color: ThemeConfig.statHpColor,
fontSize: ThemeConfig.fontSizeHuge, fontSize: ThemeConfig.fontSizeHuge,
@ -677,7 +684,7 @@ class _BattleScreenState extends State<BattleScreen> {
); );
}, },
child: const Text( child: const Text(
"Return to Main Menu", AppStrings.returnToMenu,
style: TextStyle( style: TextStyle(
color: ThemeConfig.textColorWhite, color: ThemeConfig.textColorWhite,
fontSize: ThemeConfig.fontSizeHeader, fontSize: ThemeConfig.fontSizeHeader,
@ -698,10 +705,10 @@ class _BattleScreenState extends State<BattleScreen> {
Widget _buildItemStatText(Item item) { Widget _buildItemStatText(Item item) {
List<String> stats = []; List<String> stats = [];
if (item.atkBonus > 0) stats.add("+${item.atkBonus} ATK"); if (item.atkBonus > 0) stats.add("+${item.atkBonus} ${AppStrings.atk}");
if (item.hpBonus > 0) stats.add("+${item.hpBonus} HP"); if (item.hpBonus > 0) stats.add("+${item.hpBonus} ${AppStrings.hp}");
if (item.armorBonus > 0) stats.add("+${item.armorBonus} DEF"); if (item.armorBonus > 0) stats.add("+${item.armorBonus} ${AppStrings.def}");
if (item.luck > 0) stats.add("+${item.luck} Luck"); if (item.luck > 0) stats.add("+${item.luck} ${AppStrings.luck}");
List<String> effectTexts = item.effects.map((e) => e.description).toList(); List<String> effectTexts = item.effects.map((e) => e.description).toList();
@ -715,7 +722,10 @@ class _BattleScreenState extends State<BattleScreen> {
padding: const EdgeInsets.only(top: 4.0, bottom: 4.0), padding: const EdgeInsets.only(top: 4.0, bottom: 4.0),
child: Text( child: Text(
stats.join(", "), stats.join(", "),
style: const TextStyle(fontSize: ThemeConfig.fontSizeMedium, color: ThemeConfig.statAtkColor), style: const TextStyle(
fontSize: ThemeConfig.fontSizeMedium,
color: ThemeConfig.statAtkColor,
),
), ),
), ),
if (effectTexts.isNotEmpty) if (effectTexts.isNotEmpty)
@ -723,7 +733,10 @@ class _BattleScreenState extends State<BattleScreen> {
padding: const EdgeInsets.only(bottom: 4.0), padding: const EdgeInsets.only(bottom: 4.0),
child: Text( child: Text(
effectTexts.join(", "), effectTexts.join(", "),
style: const TextStyle(fontSize: 11, color: ThemeConfig.rarityLegendary), // 11 is custom, keep or change? Let's use Small style: const TextStyle(
fontSize: 11,
color: ThemeConfig.rarityLegendary,
), // 11 is custom, keep or change? Let's use Small
), ),
), ),
], ],

View File

@ -5,6 +5,7 @@ import '../game/data/player_table.dart';
import 'main_wrapper.dart'; import 'main_wrapper.dart';
import '../widgets/responsive_container.dart'; import '../widgets/responsive_container.dart';
import '../game/config/theme_config.dart'; import '../game/config/theme_config.dart';
import '../game/config/app_strings.dart';
class CharacterSelectionScreen extends StatelessWidget { class CharacterSelectionScreen extends StatelessWidget {
const CharacterSelectionScreen({super.key}); const CharacterSelectionScreen({super.key});
@ -83,19 +84,19 @@ class CharacterSelectionScreen extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
Text( Text(
"HP: ${warrior.baseHp}", "${AppStrings.hp}: ${warrior.baseHp}",
style: const TextStyle( style: const TextStyle(
fontWeight: ThemeConfig.fontWeightBold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
Text( Text(
"ATK: ${warrior.baseAtk}", "${AppStrings.atk}: ${warrior.baseAtk}",
style: const TextStyle( style: const TextStyle(
fontWeight: ThemeConfig.fontWeightBold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
Text( Text(
"DEF: ${warrior.baseDefense}", "${AppStrings.def}: ${warrior.baseDefense}",
style: const TextStyle( style: const TextStyle(
fontWeight: ThemeConfig.fontWeightBold, fontWeight: ThemeConfig.fontWeightBold,
), ),

View File

@ -5,6 +5,7 @@ import '../game/model/item.dart';
import '../game/enums.dart'; import '../game/enums.dart';
import '../utils/item_utils.dart'; import '../utils/item_utils.dart';
import '../game/config/theme_config.dart'; import '../game/config/theme_config.dart';
import '../game/config/app_strings.dart';
class InventoryScreen extends StatelessWidget { class InventoryScreen extends StatelessWidget {
const InventoryScreen({super.key}); const InventoryScreen({super.key});
@ -37,28 +38,28 @@ class InventoryScreen extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
_buildStatItem( _buildStatItem(
"HP", AppStrings.hp,
"${player.hp}/${player.totalMaxHp}", "${player.hp}/${player.totalMaxHp}",
color: ThemeConfig.statHpColor, color: ThemeConfig.statHpColor,
), ),
_buildStatItem( _buildStatItem(
"ATK", AppStrings.atk,
"${player.totalAtk}", "${player.totalAtk}",
color: ThemeConfig.statAtkColor, color: ThemeConfig.statAtkColor,
), ),
_buildStatItem( _buildStatItem(
"DEF", AppStrings.def,
"${player.totalDefense}", "${player.totalDefense}",
color: ThemeConfig.statDefColor, color: ThemeConfig.statDefColor,
), ),
_buildStatItem("Shield", "${player.armor}"), _buildStatItem(AppStrings.armor, "${player.armor}"),
_buildStatItem( _buildStatItem(
"Luck", AppStrings.luck,
"${player.totalLuck}", "${player.totalLuck}",
color: ThemeConfig.statLuckColor, color: ThemeConfig.statLuckColor,
), ),
_buildStatItem( _buildStatItem(
"Gold", AppStrings.gold,
"${player.gold} G", "${player.gold} G",
color: ThemeConfig.statGoldColor, color: ThemeConfig.statGoldColor,
), ),
@ -162,7 +163,7 @@ class InventoryScreen extends StatelessWidget {
FittedBox( FittedBox(
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
child: Text( child: Text(
item?.name ?? "Empty", item?.name ?? AppStrings.emptySlot,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: fontSize:
@ -208,7 +209,7 @@ class InventoryScreen extends StatelessWidget {
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
"Bag (${player.inventory.length}/${player.maxInventorySize})", "${AppStrings.bag} (${player.inventory.length}/${player.maxInventorySize})",
style: const TextStyle( style: const TextStyle(
fontSize: ThemeConfig.fontSizeHeader, fontSize: ThemeConfig.fontSizeHeader,
fontWeight: ThemeConfig.fontWeightBold, fontWeight: ThemeConfig.fontWeightBold,
@ -371,7 +372,7 @@ class InventoryScreen extends StatelessWidget {
children: [ children: [
Icon(Icons.shield, color: ThemeConfig.btnDefendActive), Icon(Icons.shield, color: ThemeConfig.btnDefendActive),
SizedBox(width: 10), SizedBox(width: 10),
Text("Equip"), Text(AppStrings.equip),
], ],
), ),
), ),
@ -391,7 +392,7 @@ class InventoryScreen extends StatelessWidget {
color: ThemeConfig.statGoldColor, color: ThemeConfig.statGoldColor,
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Text("Sell (${item.price} G)"), Text("${AppStrings.sell} (${item.price} G)"),
], ],
), ),
), ),
@ -407,7 +408,7 @@ class InventoryScreen extends StatelessWidget {
children: [ children: [
Icon(Icons.delete, color: ThemeConfig.btnActionActive), Icon(Icons.delete, color: ThemeConfig.btnActionActive),
SizedBox(width: 10), SizedBox(width: 10),
Text("Discard"), Text(AppStrings.discard),
], ],
), ),
), ),
@ -430,7 +431,7 @@ class InventoryScreen extends StatelessWidget {
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(ctx), onPressed: () => Navigator.pop(ctx),
child: const Text("Cancel"), child: const Text(AppStrings.cancel),
), ),
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
@ -440,7 +441,7 @@ class InventoryScreen extends StatelessWidget {
provider.sellItem(item); provider.sellItem(item);
Navigator.pop(ctx); Navigator.pop(ctx);
}, },
child: const Text("Sell", style: TextStyle(color: Colors.black)), child: const Text(AppStrings.sell, style: TextStyle(color: Colors.black)),
), ),
], ],
), ),
@ -460,7 +461,7 @@ class InventoryScreen extends StatelessWidget {
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(ctx), onPressed: () => Navigator.pop(ctx),
child: const Text("Cancel"), child: const Text(AppStrings.cancel),
), ),
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
@ -470,7 +471,7 @@ class InventoryScreen extends StatelessWidget {
provider.discardItem(item); provider.discardItem(item);
Navigator.pop(ctx); Navigator.pop(ctx);
}, },
child: const Text("Discard"), child: const Text(AppStrings.discard),
), ),
], ],
), ),
@ -510,7 +511,7 @@ class InventoryScreen extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
"Equip ${newItem.name}?", "${AppStrings.equip} ${newItem.name}?",
style: const TextStyle(fontWeight: ThemeConfig.fontWeightBold), style: const TextStyle(fontWeight: ThemeConfig.fontWeightBold),
), ),
if (oldItem != null) if (oldItem != null)
@ -524,8 +525,8 @@ class InventoryScreen extends StatelessWidget {
const SizedBox(height: 16), const SizedBox(height: 16),
_buildStatChangeRow("Max HP", currentMaxHp, newMaxHp), _buildStatChangeRow("Max HP", currentMaxHp, newMaxHp),
_buildStatChangeRow("Current HP", currentHp, newHp), _buildStatChangeRow("Current HP", currentHp, newHp),
_buildStatChangeRow("ATK", currentAtk, newAtk), _buildStatChangeRow(AppStrings.atk, currentAtk, newAtk),
_buildStatChangeRow("DEF", currentDef, newDef), _buildStatChangeRow(AppStrings.def, currentDef, newDef),
_buildStatChangeRow( _buildStatChangeRow(
"LUCK", "LUCK",
player.totalLuck, player.totalLuck,
@ -536,14 +537,14 @@ class InventoryScreen extends StatelessWidget {
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(ctx), onPressed: () => Navigator.pop(ctx),
child: const Text("Cancel"), child: const Text(AppStrings.cancel),
), ),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
provider.equipItem(newItem); provider.equipItem(newItem);
Navigator.pop(ctx); Navigator.pop(ctx);
}, },
child: const Text("Confirm"), child: const Text(AppStrings.confirm),
), ),
], ],
), ),
@ -582,27 +583,27 @@ class InventoryScreen extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
"Unequip ${itemToUnequip.name}?", "${AppStrings.unequip} ${itemToUnequip.name}?",
style: const TextStyle(fontWeight: ThemeConfig.fontWeightBold), style: const TextStyle(fontWeight: ThemeConfig.fontWeightBold),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
_buildStatChangeRow("Max HP", currentMaxHp, newMaxHp), _buildStatChangeRow("Max HP", currentMaxHp, newMaxHp),
_buildStatChangeRow("Current HP", currentHp, newHp), _buildStatChangeRow("Current HP", currentHp, newHp),
_buildStatChangeRow("ATK", currentAtk, newAtk), _buildStatChangeRow(AppStrings.atk, currentAtk, newAtk),
_buildStatChangeRow("DEF", currentDef, newDef), _buildStatChangeRow(AppStrings.def, currentDef, newDef),
], ],
), ),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(ctx), onPressed: () => Navigator.pop(ctx),
child: const Text("Cancel"), child: const Text(AppStrings.cancel),
), ),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
provider.unequipItem(itemToUnequip); provider.unequipItem(itemToUnequip);
Navigator.pop(ctx); Navigator.pop(ctx);
}, },
child: const Text("Confirm"), child: const Text(AppStrings.confirm),
), ),
], ],
), ),
@ -657,10 +658,10 @@ class InventoryScreen extends StatelessWidget {
Widget _buildItemStatText(Item item) { Widget _buildItemStatText(Item item) {
List<String> stats = []; List<String> stats = [];
if (item.atkBonus > 0) stats.add("+${item.atkBonus} ATK"); if (item.atkBonus > 0) stats.add("+${item.atkBonus} ${AppStrings.atk}");
if (item.hpBonus > 0) stats.add("+${item.hpBonus} HP"); if (item.hpBonus > 0) stats.add("+${item.hpBonus} ${AppStrings.hp}");
if (item.armorBonus > 0) stats.add("+${item.armorBonus} DEF"); if (item.armorBonus > 0) stats.add("+${item.armorBonus} ${AppStrings.def}");
if (item.luck > 0) stats.add("+${item.luck} Luck"); if (item.luck > 0) stats.add("+${item.luck} ${AppStrings.luck}");
// Include effects // Include effects
List<String> effectTexts = item.effects.map((e) => e.description).toList(); List<String> effectTexts = item.effects.map((e) => e.description).toList();

View File

@ -6,6 +6,7 @@ import '../widgets/responsive_container.dart';
import '../game/save_manager.dart'; import '../game/save_manager.dart';
import '../providers/battle_provider.dart'; import '../providers/battle_provider.dart';
import '../game/config/theme_config.dart'; import '../game/config/theme_config.dart';
import '../game/config/app_strings.dart';
class MainMenuScreen extends StatefulWidget { class MainMenuScreen extends StatefulWidget {
const MainMenuScreen({super.key}); const MainMenuScreen({super.key});
@ -79,7 +80,7 @@ class _MainMenuScreenState extends State<MainMenuScreen> {
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
const Text( const Text(
"COLOSSEUM'S CHOICE", AppStrings.gameTitle,
style: TextStyle( style: TextStyle(
fontSize: ThemeConfig.fontSizeHero, fontSize: ThemeConfig.fontSizeHero,
fontWeight: ThemeConfig.fontWeightBold, fontWeight: ThemeConfig.fontWeightBold,
@ -112,7 +113,7 @@ class _MainMenuScreenState extends State<MainMenuScreen> {
fontWeight: ThemeConfig.fontWeightBold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
child: const Text("CONTINUE"), child: const Text(AppStrings.continueGame),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
], ],
@ -139,7 +140,7 @@ class _MainMenuScreenState extends State<MainMenuScreen> {
fontWeight: ThemeConfig.fontWeightBold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
child: const Text("NEW GAME"), child: const Text(AppStrings.startGame),
), ),
], ],
), ),

View File

@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
import '../providers/battle_provider.dart'; import '../providers/battle_provider.dart';
import 'main_menu_screen.dart'; import 'main_menu_screen.dart';
import '../game/config/theme_config.dart'; import '../game/config/theme_config.dart';
import '../game/config/app_strings.dart';
class SettingsScreen extends StatelessWidget { class SettingsScreen extends StatelessWidget {
const SettingsScreen({super.key}); const SettingsScreen({super.key});
@ -14,7 +15,7 @@ class SettingsScreen extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Text( const Text(
'Settings', AppStrings.settings,
style: TextStyle( style: TextStyle(
fontSize: ThemeConfig.fontSizeTitle, fontSize: ThemeConfig.fontSizeTitle,
fontWeight: ThemeConfig.fontWeightBold, fontWeight: ThemeConfig.fontWeightBold,
@ -43,7 +44,7 @@ class SettingsScreen extends StatelessWidget {
onPressed: () { onPressed: () {
_showConfirmationDialog( _showConfirmationDialog(
context, context,
title: 'Restart Game?', title: '${AppStrings.restart} Game?',
content: 'All progress will be lost. Are you sure?', content: 'All progress will be lost. Are you sure?',
onConfirm: () { onConfirm: () {
context.read<BattleProvider>().initializeBattle(); context.read<BattleProvider>().initializeBattle();
@ -70,7 +71,7 @@ class SettingsScreen extends StatelessWidget {
onPressed: () { onPressed: () {
_showConfirmationDialog( _showConfirmationDialog(
context, context,
title: 'Return to Main Menu?', title: '${AppStrings.returnToMenu}?',
content: 'Unsaved progress may be lost. (Progress is saved automatically after each stage)', content: 'Unsaved progress may be lost. (Progress is saved automatically after each stage)',
onConfirm: () { onConfirm: () {
Navigator.of(context).pushAndRemoveUntil( Navigator.of(context).pushAndRemoveUntil(
@ -80,7 +81,7 @@ class SettingsScreen extends StatelessWidget {
}, },
); );
}, },
child: const Text('Return to Main Menu'), child: const Text(AppStrings.returnToMenu),
), ),
], ],
), ),
@ -96,14 +97,14 @@ class SettingsScreen extends StatelessWidget {
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
child: const Text('Cancel'), child: const Text(AppStrings.cancel),
), ),
TextButton( TextButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
onConfirm(); onConfirm();
}, },
child: const Text('Confirm', style: TextStyle(color: Colors.red)), child: const Text(AppStrings.confirm, style: TextStyle(color: Colors.red)),
), ),
], ],
), ),

View File

@ -0,0 +1,16 @@
# 60. Introduce AppStrings for Soft i18n
## 1. 목표 (Goal)
- UI 텍스트 하드코딩을 방지하고 추후 본격적인 i18n 적용을 대비하기 위해 `AppStrings` 상수 클래스를 도입합니다.
- 자주 사용되는 UI 텍스트(메뉴, 스탯, 행동 등)를 한 곳에서 관리합니다.
## 2. 구현 계획 (Implementation Plan)
1. **`lib/game/config/app_strings.dart` 생성:**
- `actionAttack`, `actionDefend`, `statHp`, `statAtk` 등 카테고리별로 정적 상수를 정의합니다.
2. **UI 코드 수정:**
- `BattleScreen`, `InventoryScreen`, `MainMenuScreen` 등에서 하드코딩된 문자열을 `AppStrings.xxx`로 교체합니다.
- *Note:* 전투 로그와 같이 동적으로 생성되는 복잡한 문장은 이번 단계에서 제외합니다.
## 3. 기대 효과 (Expected Outcome)
- 텍스트 변경 시 `AppStrings`만 수정하면 되므로 유지보수성 향상.
- 추후 다국어 지원 라이브러리 도입 시 마이그레이션이 매우 쉬워짐.