🌐 More localized stellar program

This commit is contained in:
2025-10-03 21:17:47 +08:00
parent 122a796f8c
commit 6df1d96cc9
2 changed files with 93 additions and 50 deletions

View File

@@ -1092,5 +1092,47 @@
"selectRecipient": "Select Recipient", "selectRecipient": "Select Recipient",
"changeRecipient": "Change Recipient", "changeRecipient": "Change Recipient",
"addMessage": "Add Message", "addMessage": "Add Message",
"skipRecipient": "Skip Recipient" "skipRecipient": "Skip Recipient",
"giftSubscriptions": "Gift Subscriptions",
"purchaseAGift": "Purchase a Gift",
"redeemAGift": "Redeem a Gift",
"giftHistory": "Gift History",
"sentGifts": "Sent Gifts",
"receivedGifts": "Received Gifts",
"noSentGifts": "No sent gifts",
"noReceivedGifts": "No received gifts",
"stellarGift": "Stellar Gift",
"novaGift": "Nova Gift",
"supernovaGift": "Supernova Gift",
"sameAsMembership": "Same as membership",
"enterGiftCodeToRedeem": "Enter gift code to redeem",
"enterGiftCode": "Enter gift code",
"giftPurchased": "Gift Purchased!",
"shareCodeWithRecipient": "Share this code with the recipient to redeem the gift.",
"openGiftAnyoneCanRedeem": "This is an open gift that anyone can redeem.",
"ok": "OK",
"selectedRecipient": "Selected recipient",
"noRecipientSelected": "No recipient selected",
"thisWillBeAnOpenGift": "This will be an open gift",
"personalMessage": "Personal Message",
"addPersonalMessageForRecipient": "Add a personal message for the recipient",
"copy": "Copy",
"cancel": "Cancel",
"giftStatusCreated": "Created",
"giftStatusSent": "Sent",
"giftStatusRedeemed": "Redeemed",
"giftStatusCancelled": "Cancelled",
"giftStatusExpired": "Expired",
"giftStatusUnknown": "Unknown",
"giftCodeCopiedToClipboard": "Gift code copied to clipboard",
"codeLabel": "Code: ",
"subscriptionLabel": "Subscription: ",
"toLabel": "To: ",
"fromLabel": "From: ",
"messageLabel": "Message: ",
"giftRedeemed": "Gift Redeemed!",
"giftRedeemedSuccessfully": "You have successfully redeemed the gift. Your new subscription is now active.",
"cancelGift": "Cancel Gift",
"cancelGiftConfirm": "Are you sure you want to cancel this gift? This action cannot be undone.",
"giftCancelledSuccessfully": "Gift cancelled successfully"
} }

View File

@@ -138,7 +138,7 @@ class _PurchaseGiftSheetState extends State<PurchaseGiftSheet> {
), ),
), ),
subtitle: Text( subtitle: Text(
'Selected recipient', 'selectedRecipient'.tr(),
style: Theme.of( style: Theme.of(
context, context,
).textTheme.bodySmall?.copyWith( ).textTheme.bodySmall?.copyWith(
@@ -173,7 +173,7 @@ class _PurchaseGiftSheetState extends State<PurchaseGiftSheet> {
), ),
const Gap(8), const Gap(8),
Text( Text(
'No recipient selected', 'noRecipientSelected'.tr(),
style: Theme.of( style: Theme.of(
context, context,
).textTheme.bodyMedium?.copyWith( ).textTheme.bodyMedium?.copyWith(
@@ -185,7 +185,7 @@ class _PurchaseGiftSheetState extends State<PurchaseGiftSheet> {
), ),
const Gap(4), const Gap(4),
Text( Text(
'This will be an open gift', 'thisWillBeAnOpenGift'.tr(),
style: Theme.of( style: Theme.of(
context, context,
).textTheme.bodySmall?.copyWith( ).textTheme.bodySmall?.copyWith(
@@ -237,8 +237,8 @@ class _PurchaseGiftSheetState extends State<PurchaseGiftSheet> {
TextField( TextField(
controller: messageController, controller: messageController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Personal Message', labelText: 'personalMessage'.tr(),
hintText: 'Add a personal message for the recipient', hintText: 'addPersonalMessageForRecipient'.tr(),
alignLabelWithHint: true, alignLabelWithHint: true,
border: OutlineInputBorder(), border: OutlineInputBorder(),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
@@ -759,7 +759,7 @@ class StellarProgramTab extends HookConsumerWidget {
), ),
const Gap(8), const Gap(8),
Text( Text(
'Gift Subscriptions', 'giftSubscriptions'.tr(),
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
], ],
@@ -768,7 +768,7 @@ class StellarProgramTab extends HookConsumerWidget {
// Purchase Gift Section // Purchase Gift Section
Text( Text(
'Purchase a Gift', 'purchaseAGift'.tr(),
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
), ),
const Gap(8), const Gap(8),
@@ -777,7 +777,7 @@ class StellarProgramTab extends HookConsumerWidget {
// Redeem Gift Section // Redeem Gift Section
Text( Text(
'Redeem a Gift', 'redeemAGift'.tr(),
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
), ),
const Gap(8), const Gap(8),
@@ -786,7 +786,7 @@ class StellarProgramTab extends HookConsumerWidget {
// Gift History // Gift History
Text( Text(
'Gift History', 'giftHistory'.tr(),
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
), ),
const Gap(8), const Gap(8),
@@ -800,20 +800,20 @@ class StellarProgramTab extends HookConsumerWidget {
final tiers = [ final tiers = [
{ {
'id': 'solian.stellar.primary', 'id': 'solian.stellar.primary',
'name': 'Stellar Gift', 'name': 'stellarGift'.tr(),
'price': 'Same as membership', 'price': 'sameAsMembership'.tr(),
'color': Colors.blue, 'color': Colors.blue,
}, },
{ {
'id': 'solian.stellar.nova', 'id': 'solian.stellar.nova',
'name': 'Nova Gift', 'name': 'novaGift'.tr(),
'price': 'Same as membership', 'price': 'sameAsMembership'.tr(),
'color': Color.fromRGBO(57, 197, 187, 1), 'color': Color.fromRGBO(57, 197, 187, 1),
}, },
{ {
'id': 'solian.stellar.supernova', 'id': 'solian.stellar.supernova',
'name': 'Supernova Gift', 'name': 'supernovaGift'.tr(),
'price': 'Same as membership', 'price': 'sameAsMembership'.tr(),
'color': Colors.orange, 'color': Colors.orange,
}, },
]; ];
@@ -914,7 +914,7 @@ class StellarProgramTab extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'Enter gift code to redeem', 'enterGiftCodeToRedeem'.tr(),
style: Theme.of(context).textTheme.bodyMedium, style: Theme.of(context).textTheme.bodyMedium,
), ),
const Gap(8), const Gap(8),
@@ -922,7 +922,7 @@ class StellarProgramTab extends HookConsumerWidget {
controller: codeController, controller: codeController,
decoration: InputDecoration( decoration: InputDecoration(
isDense: true, isDense: true,
hintText: 'Enter gift code', hintText: 'enterGiftCode'.tr(),
border: OutlineInputBorder(), border: OutlineInputBorder(),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
@@ -960,7 +960,7 @@ class StellarProgramTab extends HookConsumerWidget {
child: OutlinedButton( child: OutlinedButton(
onPressed: onPressed:
() => _showGiftHistorySheet(context, ref, sentGifts, true), () => _showGiftHistorySheet(context, ref, sentGifts, true),
child: Text('Sent Gifts'), child: Text('sentGifts'.tr()),
), ),
), ),
const Gap(8), const Gap(8),
@@ -968,7 +968,7 @@ class StellarProgramTab extends HookConsumerWidget {
child: OutlinedButton( child: OutlinedButton(
onPressed: onPressed:
() => _showGiftHistorySheet(context, ref, receivedGifts, false), () => _showGiftHistorySheet(context, ref, receivedGifts, false),
child: Text('Received Gifts'), child: Text('receivedGifts'.tr()),
), ),
), ),
], ],
@@ -987,7 +987,7 @@ class StellarProgramTab extends HookConsumerWidget {
context: context, context: context,
builder: builder:
(context) => SheetScaffold( (context) => SheetScaffold(
titleText: isSent ? 'Sent Gifts' : 'Received Gifts', titleText: isSent ? 'sentGifts'.tr() : 'receivedGifts'.tr(),
child: giftsAsync.when( child: giftsAsync.when(
data: data:
(gifts) => (gifts) =>
@@ -995,10 +995,13 @@ class StellarProgramTab extends HookConsumerWidget {
? Padding( ? Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Text( child: Text(
isSent ? 'No sent gifts' : 'No received gifts', isSent
? 'noSentGifts'.tr()
: 'noReceivedGifts'.tr(),
), ),
) )
: ListView.builder( : ListView.builder(
padding: const EdgeInsets.only(top: 16),
itemCount: gifts.length, itemCount: gifts.length,
itemBuilder: itemBuilder:
(context, index) => _buildGiftItem( (context, index) => _buildGiftItem(
@@ -1044,7 +1047,7 @@ class StellarProgramTab extends HookConsumerWidget {
child: Row( child: Row(
children: [ children: [
Text( Text(
'Code: ', 'codeLabel'.tr(),
style: TextStyle(fontWeight: FontWeight.w600), style: TextStyle(fontWeight: FontWeight.w600),
), ),
Expanded( Expanded(
@@ -1091,27 +1094,27 @@ class StellarProgramTab extends HookConsumerWidget {
), ),
const Gap(4), const Gap(4),
Text( Text(
'Subscription: ${_getMembershipTierName(gift.subscriptionIdentifier)}', '${'subscriptionLabel'.tr()} ${_getMembershipTierName(gift.subscriptionIdentifier)}',
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall,
), ),
if (gift.recipient != null && isSent) ...[ if (gift.recipient != null && isSent) ...[
const Gap(4), const Gap(4),
Text( Text(
'To: ${gift.recipient!.name}', '${'toLabel'.tr()} ${gift.recipient!.name}',
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall,
), ),
], ],
if (gift.gifter != null && !isSent) ...[ if (gift.gifter != null && !isSent) ...[
const Gap(4), const Gap(4),
Text( Text(
'From: ${gift.gifter!.name}', '${'fromLabel'.tr()} ${gift.gifter!.name}',
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall,
), ),
], ],
if (gift.message != null && gift.message!.isNotEmpty) ...[ if (gift.message != null && gift.message!.isNotEmpty) ...[
const Gap(4), const Gap(4),
Text( Text(
'Message: ${gift.message}', '${'messageLabel'.tr()} ${gift.message}',
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall,
), ),
], ],
@@ -1124,11 +1127,11 @@ class StellarProgramTab extends HookConsumerWidget {
onPressed: () async { onPressed: () async {
await Clipboard.setData(ClipboardData(text: gift.giftCode)); await Clipboard.setData(ClipboardData(text: gift.giftCode));
if (context.mounted) { if (context.mounted) {
showSnackBar('Gift code copied to clipboard'); showSnackBar('giftCodeCopiedToClipboard'.tr());
} }
}, },
icon: const Icon(Icons.copy, size: 16), icon: const Icon(Icons.copy, size: 16),
label: Text('Copy'), label: Text('copy'.tr()),
style: FilledButton.styleFrom( style: FilledButton.styleFrom(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
), ),
@@ -1137,7 +1140,7 @@ class StellarProgramTab extends HookConsumerWidget {
OutlinedButton.icon( OutlinedButton.icon(
onPressed: () => _cancelGift(context, ref, gift), onPressed: () => _cancelGift(context, ref, gift),
icon: const Icon(Icons.cancel, size: 16), icon: const Icon(Icons.cancel, size: 16),
label: const Text('Cancel'), label: Text('cancel'.tr()),
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
foregroundColor: Theme.of(context).colorScheme.error, foregroundColor: Theme.of(context).colorScheme.error,
@@ -1157,17 +1160,17 @@ class StellarProgramTab extends HookConsumerWidget {
String _getGiftStatusText(int status) { String _getGiftStatusText(int status) {
switch (status) { switch (status) {
case 0: case 0:
return 'Created'; return 'giftStatusCreated'.tr();
case 1: case 1:
return 'Sent'; return 'giftStatusSent'.tr();
case 2: case 2:
return 'Redeemed'; return 'giftStatusRedeemed'.tr();
case 3: case 3:
return 'Cancelled'; return 'giftStatusCancelled'.tr();
case 4: case 4:
return 'Expired'; return 'giftStatusExpired'.tr();
default: default:
return 'Unknown'; return 'giftStatusUnknown'.tr();
} }
} }
@@ -1266,7 +1269,7 @@ class StellarProgramTab extends HookConsumerWidget {
context: context, context: context,
builder: builder:
(context) => SheetScaffold( (context) => SheetScaffold(
titleText: 'Gift Purchased!', titleText: 'giftPurchased'.tr(),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
@@ -1303,25 +1306,25 @@ class StellarProgramTab extends HookConsumerWidget {
); );
if (context.mounted) { if (context.mounted) {
showSnackBar( showSnackBar(
'Gift code copied to clipboard', 'giftCodeCopiedToClipboard'.tr(),
); );
} }
}, },
icon: const Icon(Icons.copy), icon: const Icon(Icons.copy),
tooltip: 'Copy gift code', tooltip: 'copyGiftCode'.tr(),
), ),
], ],
), ),
), ),
const Gap(16), const Gap(16),
Text( Text(
'Share this code with the recipient to redeem the gift.', 'shareCodeWithRecipient'.tr(),
style: Theme.of(context).textTheme.bodyMedium, style: Theme.of(context).textTheme.bodyMedium,
), ),
if (updatedGift.recipientId == null) ...[ if (updatedGift.recipientId == null) ...[
const Gap(8), const Gap(8),
Text( Text(
'This is an open gift that anyone can redeem.', 'openGiftAnyoneCanRedeem'.tr(),
style: Theme.of( style: Theme.of(
context, context,
).textTheme.bodySmall?.copyWith( ).textTheme.bodySmall?.copyWith(
@@ -1335,7 +1338,7 @@ class StellarProgramTab extends HookConsumerWidget {
const Gap(24), const Gap(24),
FilledButton( FilledButton(
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
child: Text('OK'), child: Text('ok'.tr()),
), ),
], ],
), ),
@@ -1386,14 +1389,12 @@ class StellarProgramTab extends HookConsumerWidget {
context: context, context: context,
builder: builder:
(context) => AlertDialog( (context) => AlertDialog(
title: Text('Gift Redeemed!'), title: Text('giftRedeemed'.tr()),
content: Text( content: Text('giftRedeemedSuccessfully'.tr()),
'You have successfully redeemed the gift. Your new subscription is now active.',
),
actions: [ actions: [
FilledButton( FilledButton(
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
child: Text('OK'), child: Text('ok'.tr()),
), ),
], ],
), ),
@@ -1415,8 +1416,8 @@ class StellarProgramTab extends HookConsumerWidget {
SnWalletGift gift, SnWalletGift gift,
) async { ) async {
final confirm = await showConfirmAlert( final confirm = await showConfirmAlert(
'Cancel Gift', 'cancelGift'.tr(),
'Are you sure you want to cancel this gift? This action cannot be undone.', 'cancelGiftConfirm'.tr(),
); );
if (!confirm || !context.mounted) return; if (!confirm || !context.mounted) return;
@@ -1427,7 +1428,7 @@ class StellarProgramTab extends HookConsumerWidget {
ref.invalidate(accountSentGiftsProvider); ref.invalidate(accountSentGiftsProvider);
if (context.mounted) { if (context.mounted) {
hideLoadingModal(context); hideLoadingModal(context);
showSnackBar('Gift cancelled successfully'); showSnackBar('giftCancelledSuccessfully'.tr());
} }
} catch (err) { } catch (err) {
if (context.mounted) hideLoadingModal(context); if (context.mounted) hideLoadingModal(context);