feat(l10n): 국제화(L10n) 시스템 도입 및 하드코딩 텍스트 변환

- flutter_localizations 및 intl 패키지 추가
- l10n.yaml 설정 파일 및 app_ko.arb 메시지 파일 생성
- 모든 화면(app, front, game_play, new_character, save_picker)의 하드코딩 텍스트를 L10n 키로 변환
- 테스트 파일에 localizationsDelegates 추가하여 L10n 지원
This commit is contained in:
JiWoong Sul
2025-12-11 17:50:34 +09:00
parent 2b10deba5d
commit 35e3d92316
20 changed files with 2155 additions and 113 deletions

View File

@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:askiineverdie/l10n/app_localizations.dart';
class FrontScreen extends StatelessWidget {
const FrontScreen({super.key, this.onNewCharacter, this.onLoadSave});
@@ -107,7 +109,7 @@ class _HeroHeader extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Ascii Never Die',
L10n.of(context).appTitle,
style: theme.textTheme.headlineSmall?.copyWith(
color: colorScheme.onPrimary,
fontWeight: FontWeight.w700,
@@ -126,14 +128,19 @@ class _HeroHeader extends StatelessWidget {
],
),
const SizedBox(height: 14),
Wrap(
spacing: 8,
runSpacing: 8,
children: const [
_Tag(icon: Icons.cloud_off_outlined, label: 'No network'),
_Tag(icon: Icons.timer_outlined, label: 'Idle RPG loop'),
_Tag(icon: Icons.storage_rounded, label: 'Local saves'),
],
Builder(
builder: (context) {
final l10n = L10n.of(context);
return Wrap(
spacing: 8,
runSpacing: 8,
children: [
_Tag(icon: Icons.cloud_off_outlined, label: l10n.tagNoNetwork),
_Tag(icon: Icons.timer_outlined, label: l10n.tagIdleRpg),
_Tag(icon: Icons.storage_rounded, label: l10n.tagLocalSaves),
],
);
},
),
],
),
@@ -151,6 +158,7 @@ class _ActionRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final l10n = L10n.of(context);
return Wrap(
spacing: 12,
@@ -159,7 +167,7 @@ class _ActionRow extends StatelessWidget {
FilledButton.icon(
onPressed: onNewCharacter,
icon: const Icon(Icons.casino_outlined),
label: const Text('New character'),
label: Text(l10n.newCharacter),
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
textStyle: theme.textTheme.titleMedium,
@@ -168,7 +176,7 @@ class _ActionRow extends StatelessWidget {
OutlinedButton.icon(
onPressed: onLoadSave,
icon: const Icon(Icons.folder_open),
label: const Text('Load save'),
label: Text(l10n.loadSave),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
textStyle: theme.textTheme.titleMedium,
@@ -177,7 +185,7 @@ class _ActionRow extends StatelessWidget {
TextButton.icon(
onPressed: () => _showPlaceholder(context),
icon: const Icon(Icons.menu_book_outlined),
label: const Text('View build plan'),
label: Text(l10n.viewBuildPlan),
),
],
);
@@ -189,11 +197,12 @@ class _StatusCards extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = L10n.of(context);
return Column(
children: const [
children: [
_InfoCard(
icon: Icons.route_outlined,
title: 'Build roadmap',
title: l10n.buildRoadmap,
points: [
'Port PQ 6.4 data set (Config.dfm) into Dart constants.',
'Recreate quest/task loop with deterministic RNG + saves.',
@@ -203,7 +212,7 @@ class _StatusCards extends StatelessWidget {
SizedBox(height: 16),
_InfoCard(
icon: Icons.auto_fix_high_outlined,
title: 'Tech stack',
title: l10n.techStack,
points: [
'Flutter (Material 3) with multiplatform targets enabled.',
'path_provider + shared_preferences for local storage hooks.',