i8n누락 사항 추가 적용

This commit is contained in:
JiWoong Sul
2025-11-17 19:26:14 +09:00
parent 2cd46a303e
commit b22df5daf3
5 changed files with 146 additions and 79 deletions

View File

@@ -11,6 +11,9 @@
"save": "Save",
"cancel": "Cancel",
"delete": "Delete",
"deleteSubscriptionTitle": "Delete Subscription",
"deleteSubscriptionMessage": "Are you sure you want to delete @ subscription?",
"deleteIrreversibleWarning": "This action cannot be undone",
"edit": "Edit",
"totalSubscriptions": "Total Subscriptions",
"totalMonthlyExpense": "Total Monthly Expense",
@@ -267,6 +270,9 @@
"save": "저장",
"cancel": "취소",
"delete": "삭제",
"deleteSubscriptionTitle": "구독 삭제",
"deleteSubscriptionMessage": "정말로 @ 구독을 삭제하시겠습니까?",
"deleteIrreversibleWarning": "이 작업은 되돌릴 수 없습니다",
"edit": "수정",
"totalSubscriptions": "총 구독",
"totalMonthlyExpense": "이번 달 총 지출",
@@ -523,6 +529,9 @@
"save": "保存",
"cancel": "キャンセル",
"delete": "削除",
"deleteSubscriptionTitle": "サブスクリプション削除",
"deleteSubscriptionMessage": "本当に@のサブスクリプションを削除しますか?",
"deleteIrreversibleWarning": "この操作は取り消せません",
"edit": "編集",
"totalSubscriptions": "総サブスクリプション",
"totalMonthlyExpense": "今月の総支出",
@@ -768,6 +777,9 @@
"save": "保存",
"cancel": "取消",
"delete": "删除",
"deleteSubscriptionTitle": "删除订阅",
"deleteSubscriptionMessage": "确定要删除@订阅吗?",
"deleteIrreversibleWarning": "此操作无法撤销",
"edit": "编辑",
"totalSubscriptions": "订阅总数",
"totalMonthlyExpense": "本月总支出",

View File

@@ -36,6 +36,16 @@ class AppLocalizations {
String get save => _localizedStrings['save'] ?? 'Save';
String get cancel => _localizedStrings['cancel'] ?? 'Cancel';
String get delete => _localizedStrings['delete'] ?? 'Delete';
String get deleteSubscriptionTitle =>
_localizedStrings['deleteSubscriptionTitle'] ?? 'Delete Subscription';
String get deleteSubscriptionMessageTemplate =>
_localizedStrings['deleteSubscriptionMessage'] ??
'Are you sure you want to delete @ subscription?';
String deleteSubscriptionMessage(String serviceName) =>
deleteSubscriptionMessageTemplate.replaceAll('@', serviceName);
String get deleteIrreversibleWarning =>
_localizedStrings['deleteIrreversibleWarning'] ??
'This action cannot be undone';
String get edit => _localizedStrings['edit'] ?? 'Edit';
String get totalSubscriptions =>
_localizedStrings['totalSubscriptions'] ?? 'Total Subscriptions';

View File

@@ -233,24 +233,32 @@ class _SubscriptionPieChartCardState extends State<SubscriptionPieChartCard> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ThemedText.headline(
Expanded(
child: ThemedText.headline(
text: AppLocalizations.of(context)
.subscriptionServiceRatio,
style: const TextStyle(
fontSize: 18,
),
),
FutureBuilder<String>(
),
Flexible(
child: FutureBuilder<String>(
future: CurrencyUtil.getExchangeRateInfoForLocale(
context
.watch<LocaleProvider>()
.locale
.languageCode),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
return Container(
if (snapshot.hasData &&
snapshot.data!.isNotEmpty) {
return Padding(
padding: const EdgeInsets.only(left: 12),
child: Align(
alignment: Alignment.topRight,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
@@ -269,14 +277,15 @@ class _SubscriptionPieChartCardState extends State<SubscriptionPieChartCard> {
width: 1,
),
),
child: Text(
child: ThemedText(
AppLocalizations.of(context)
.exchangeRateFormat(snapshot.data!),
style: TextStyle(
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color:
Theme.of(context).colorScheme.primary,
),
textAlign: TextAlign.right,
),
),
),
);
@@ -284,6 +293,7 @@ class _SubscriptionPieChartCardState extends State<SubscriptionPieChartCard> {
return const SizedBox.shrink();
},
),
),
],
),
const SizedBox(height: 8),

View File

@@ -265,23 +265,39 @@ class DetailHeaderSection extends StatelessWidget {
final loc = AppLocalizations.of(context);
switch (cycle.toLowerCase()) {
case '매월':
case '월간':
case 'monthly':
case '毎月':
case '月付':
case '月間':
case '每月':
return loc.billingCycleMonthly;
case '분기별':
case '분기':
case 'quarterly':
case 'quarter':
case '季付':
case '季度付':
case '四半期':
case '每季度':
return loc.billingCycleQuarterly;
case '반기별':
case 'half-yearly':
case 'half yearly':
case 'semiannual':
case 'semi-annual':
case '半年付':
case '半年払い':
case '半年ごと':
case '每半年':
return loc.billingCycleHalfYearly;
case '매년':
case '연간':
case 'yearly':
case 'annual':
case 'annually':
case '年間':
case '年付':
case '每年':
return loc.billingCycleYearly;
default:

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
// Material 3 기반 다이얼로그
import '../common/buttons/primary_button.dart';
import '../common/buttons/secondary_button.dart';
import '../../l10n/app_localizations.dart';
/// 삭제 확인 다이얼로그
/// 글래스모피즘 스타일의 삭제 확인 다이얼로그입니다.
@@ -15,8 +16,20 @@ class DeleteConfirmationDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
final textThemeColor = Theme.of(context).colorScheme;
final baseMessageStyle = TextStyle(
fontSize: 16,
color: textThemeColor.onSurfaceVariant,
height: 1.5,
);
final highlightStyle = baseMessageStyle.copyWith(
fontWeight: FontWeight.w600,
color: textThemeColor.onSurface,
);
return Dialog(
backgroundColor: Theme.of(context).colorScheme.surface,
backgroundColor: textThemeColor.surface,
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
child: Container(
@@ -44,11 +57,11 @@ class DeleteConfirmationDialog extends StatelessWidget {
// 타이틀
Text(
'구독 삭제',
loc.deleteSubscriptionTitle,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w700,
color: Theme.of(context).colorScheme.onSurface,
color: textThemeColor.onSurface,
),
),
const SizedBox(height: 12),
@@ -57,22 +70,12 @@ class DeleteConfirmationDialog extends StatelessWidget {
RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: TextStyle(
fontSize: 16,
color: Theme.of(context).colorScheme.onSurfaceVariant,
height: 1.5,
style: baseMessageStyle,
children: _buildLocalizedMessageSpans(
loc.deleteSubscriptionMessageTemplate,
serviceName,
highlightStyle,
),
children: [
const TextSpan(text: '정말로 '),
TextSpan(
text: serviceName,
style: TextStyle(
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.onSurface,
),
),
const TextSpan(text: ' 구독을\n삭제하시겠습니까?'),
],
),
),
const SizedBox(height: 8),
@@ -84,14 +87,10 @@ class DeleteConfirmationDialog extends StatelessWidget {
vertical: 12,
),
decoration: BoxDecoration(
color:
Theme.of(context).colorScheme.error.withValues(alpha: 0.05),
color: textThemeColor.error.withValues(alpha: 0.05),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Theme.of(context)
.colorScheme
.error
.withValues(alpha: 0.2),
color: textThemeColor.error.withValues(alpha: 0.2),
width: 1,
),
),
@@ -100,18 +99,15 @@ class DeleteConfirmationDialog extends StatelessWidget {
children: [
Icon(
Icons.warning_amber_rounded,
color: Theme.of(context)
.colorScheme
.error
.withValues(alpha: 0.8),
color: textThemeColor.error.withValues(alpha: 0.8),
size: 20,
),
const SizedBox(width: 8),
Text(
'이 작업은 되돌릴 수 없습니다',
loc.deleteIrreversibleWarning,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.error,
color: textThemeColor.error,
fontWeight: FontWeight.w500,
),
),
@@ -125,7 +121,7 @@ class DeleteConfirmationDialog extends StatelessWidget {
children: [
Expanded(
child: SecondaryButton(
text: '취소',
text: loc.cancel,
onPressed: () {
Navigator.of(context).pop(false);
},
@@ -134,12 +130,12 @@ class DeleteConfirmationDialog extends StatelessWidget {
const SizedBox(width: 12),
Expanded(
child: PrimaryButton(
text: '삭제',
text: loc.delete,
icon: Icons.delete_rounded,
onPressed: () {
Navigator.of(context).pop(true);
},
backgroundColor: Theme.of(context).colorScheme.error,
backgroundColor: textThemeColor.error,
),
),
],
@@ -166,4 +162,27 @@ class DeleteConfirmationDialog extends StatelessWidget {
return result ?? false;
}
List<TextSpan> _buildLocalizedMessageSpans(
String template,
String serviceName,
TextStyle highlightStyle,
) {
final parts = template.split('@');
if (parts.length == 1) {
return [TextSpan(text: template)];
}
final spans = <TextSpan>[];
for (int i = 0; i < parts.length; i++) {
final segment = parts[i];
if (segment.isNotEmpty) {
spans.add(TextSpan(text: segment));
}
if (i < parts.length - 1) {
spans.add(TextSpan(text: serviceName, style: highlightStyle));
}
}
return spans;
}
}