🐛 Bug fixes within the new localization engine

This commit is contained in:
2026-02-05 15:53:25 +08:00
parent 541956d0e9
commit 769b0e0e9c
14 changed files with 283 additions and 46 deletions

View File

@@ -330,11 +330,11 @@ public class AccountEventService(
.Where(x => x.AccountId == user.Id) .Where(x => x.AccountId == user.Id)
.Select(x => new { x.Birthday, x.TimeZone }) .Select(x => new { x.Birthday, x.TimeZone })
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
var accountBirthday = accountProfile?.Birthday; var accountBirthday = accountProfile?.Birthday;
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();
var userTimeZone = DateTimeZone.Utc; var userTimeZone = DateTimeZone.Utc;
if (!string.IsNullOrEmpty(accountProfile?.TimeZone)) if (!string.IsNullOrEmpty(accountProfile?.TimeZone))
{ {
@@ -343,9 +343,9 @@ public class AccountEventService(
var todayInUserTz = now.InZone(userTimeZone).Date; var todayInUserTz = now.InZone(userTimeZone).Date;
var birthdayDate = accountBirthday?.InZone(userTimeZone).Date; var birthdayDate = accountBirthday?.InZone(userTimeZone).Date;
var isBirthday = birthdayDate.HasValue && var isBirthday = birthdayDate.HasValue &&
birthdayDate.Value.Month == todayInUserTz.Month && birthdayDate.Value.Month == todayInUserTz.Month &&
birthdayDate.Value.Day == todayInUserTz.Day; birthdayDate.Value.Day == todayInUserTz.Day;
List<CheckInFortuneTip> tips; List<CheckInFortuneTip> tips;
@@ -561,7 +561,7 @@ public class AccountEventService(
{ {
var userActivities = activitiesByUser.GetValueOrDefault(userId, new List<SnPresenceActivity>()); var userActivities = activitiesByUser.GetValueOrDefault(userId, new List<SnPresenceActivity>());
results[userId] = userActivities; results[userId] = userActivities;
// Update cache for this user // Update cache for this user
var cacheKey = $"{ActivityCacheKey}{userId}"; var cacheKey = $"{ActivityCacheKey}{userId}";
await cache.SetWithGroupsAsync(cacheKey, userActivities, [$"{AccountService.AccountCachePrefix}{userId}"], await cache.SetWithGroupsAsync(cacheKey, userActivities, [$"{AccountService.AccountCachePrefix}{userId}"],

View File

@@ -432,8 +432,8 @@ public class AccountService(
Notification = new PushNotification Notification = new PushNotification
{ {
Topic = "auth.verification", Topic = "auth.verification",
Title = localizer.Get("authCodeTitle"), Title = localizer.Get("authCodeTitle", account.Language),
Body = localizer.Get("authCodeBody", args: new { code }), Body = localizer.Get("authCodeBody", locale: account.Language, args: new { code }),
IsSavable = false IsSavable = false
} }
} }
@@ -464,8 +464,9 @@ public class AccountService(
.SendRazorTemplateEmailAsync<VerificationEmailModel>( .SendRazorTemplateEmailAsync<VerificationEmailModel>(
account.Nick, account.Nick,
contact.Content, contact.Content,
localizer.Get("codeEmailTitle"), localizer.Get("codeEmailTitle", account.Language),
"FactorCode", "FactorCode",
account.Language,
new VerificationEmailModel new VerificationEmailModel
{ {
Name = account.Name, Name = account.Name,

View File

@@ -97,8 +97,9 @@ public class MagicSpellService(
await email.SendRazorTemplateEmailAsync<LandingEmailModel>( await email.SendRazorTemplateEmailAsync<LandingEmailModel>(
contact.Account.Nick, contact.Account.Nick,
contact.Content, contact.Content,
localizer.Get("regConfirmTitle"), localizer.Get("regConfirmTitle", accountLanguage),
"Welcome", "Welcome",
accountLanguage,
new LandingEmailModel new LandingEmailModel
{ {
Name = contact.Account.Name, Name = contact.Account.Name,
@@ -110,8 +111,9 @@ public class MagicSpellService(
await email.SendRazorTemplateEmailAsync<AccountDeletionEmailModel>( await email.SendRazorTemplateEmailAsync<AccountDeletionEmailModel>(
contact.Account.Nick, contact.Account.Nick,
contact.Content, contact.Content,
localizer.Get("accountDeletionTitle"), localizer.Get("accountDeletionTitle", accountLanguage),
"AccountDeletion", "AccountDeletion",
accountLanguage,
new AccountDeletionEmailModel new AccountDeletionEmailModel
{ {
Name = contact.Account.Name, Name = contact.Account.Name,
@@ -123,8 +125,9 @@ public class MagicSpellService(
await email.SendRazorTemplateEmailAsync<PasswordResetEmailModel>( await email.SendRazorTemplateEmailAsync<PasswordResetEmailModel>(
contact.Account.Nick, contact.Account.Nick,
contact.Content, contact.Content,
localizer.Get("passwordResetTitle"), localizer.Get("passwordResetTitle", accountLanguage),
"PasswordReset", "PasswordReset",
accountLanguage,
new PasswordResetEmailModel new PasswordResetEmailModel
{ {
Name = contact.Account.Name, Name = contact.Account.Name,
@@ -138,8 +141,9 @@ public class MagicSpellService(
await email.SendRazorTemplateEmailAsync<ContactVerificationEmailModel>( await email.SendRazorTemplateEmailAsync<ContactVerificationEmailModel>(
contact.Account.Nick, contact.Account.Nick,
contactMethod!, contactMethod!,
localizer.Get("contractMethodVerificationTitle"), localizer.Get("contractMethodVerificationTitle", accountLanguage),
"ContactVerification", "ContactVerification",
accountLanguage,
new ContactVerificationEmailModel new ContactVerificationEmailModel
{ {
Name = contact.Account.Name, Name = contact.Account.Name,

View File

@@ -117,8 +117,8 @@ public class RelationshipService(
Notification = new PushNotification Notification = new PushNotification
{ {
Topic = "relationships.friends.request", Topic = "relationships.friends.request",
Title = localizer.Get("friendRequestTitle", args: new { sender.Nick }), Title = localizer.Get("friendRequestTitle", locale: sender.Language, args: new { sender.Nick }),
Body = localizer.Get("friendRequestBody"), Body = localizer.Get("friendRequestBody", locale: sender.Language),
ActionUri = "/account/relationships", ActionUri = "/account/relationships",
IsSavable = true IsSavable = true
} }

View File

@@ -233,14 +233,14 @@ public class AuthController(
{ {
AccountService.SetCultureInfo(challenge.Account); AccountService.SetCultureInfo(challenge.Account);
await pusher.SendPushNotificationToUserAsync(new SendPushNotificationToUserRequest await pusher.SendPushNotificationToUserAsync(new SendPushNotificationToUserRequest
{
Notification = new PushNotification
{ {
Topic = "auth.login", Notification = new PushNotification
Title = localizer.Get("newLoginTitle"), {
Body = localizer.Get("newLoginBody", args: new { deviceName = challenge.DeviceName ?? "unknown", ipAddress = challenge.IpAddress ?? "unknown" }), Topic = "auth.login",
IsSavable = true Title = localizer.Get("newLoginTitle", challenge.Account.Language),
}, Body = localizer.Get("newLoginBody", locale: challenge.Account.Language, args: new { deviceName = challenge.DeviceName ?? "unknown", ipAddress = challenge.IpAddress ?? "unknown" }),
IsSavable = true
},
UserId = challenge.AccountId.ToString() UserId = challenge.AccountId.ToString()
}); });
als.CreateActionLogFromRequest(ActionLogType.NewLogin, als.CreateActionLogFromRequest(ActionLogType.NewLogin,

View File

@@ -55,8 +55,8 @@ public class RealmService(
Notification = new PushNotification Notification = new PushNotification
{ {
Topic = "invites.realms", Topic = "invites.realms",
Title = localizer.Get("realmInviteTitle"), Title = localizer.Get("realmInviteTitle", account.Language),
Body = localizer.Get("realmInviteBody", args: new { realmName = member.Realm.Name }), Body = localizer.Get("realmInviteBody", locale: account.Language, args: new { realmName = member.Realm.Name }),
ActionUri = "/realms", ActionUri = "/realms",
IsSavable = true IsSavable = true
} }

View File

@@ -127,8 +127,8 @@ public class RealmServiceGrpc(
Notification = new PushNotification Notification = new PushNotification
{ {
Topic = "invites.realms", Topic = "invites.realms",
Title = localizer.Get("realmInviteTitle"), Title = localizer.Get("realmInviteTitle", account.Language),
Body = localizer.Get("realmInviteBody", args: new { realmName = member.Realm?.Name ?? "Unknown Realm" }), Body = localizer.Get("realmInviteBody", locale: account.Language, args: new { realmName = member?.Name ?? "Unknown Realm" }),
ActionUri = "/realms", ActionUri = "/realms",
IsSavable = true IsSavable = true
} }

View File

@@ -23,5 +23,94 @@
"emailAccountDeletionTitle": "Confirm your account deletion", "emailAccountDeletionTitle": "Confirm your account deletion",
"passwordResetTitle": "Reset your password", "passwordResetTitle": "Reset your password",
"contractMethodVerificationTitle": "Verify Contact Method", "contractMethodVerificationTitle": "Verify Contact Method",
"codeEmailTitle": "Your email one-time-password" "codeEmailTitle": "Your email one-time-password",
"friendRequestTitle": "{sender} requested to be your friend",
"friendRequestBody": "You can go to relationships page and decide accept their request or not.",
"newLoginTitle": "New login detected",
"newLoginBody": "Your account logged on to a device named {deviceName} at {ipAddress}",
"chatInviteTitle": "New Chat Invitation",
"chatInviteBody": "You just got invited to join {channel}",
"chatInviteDirectBody": "{user} sent an direct message invitation to you",
"postSubscriptionTitle": "{publisher}: {title}",
"postReplyTitle": "{user} replied your post",
"postReactTitle": "{user} reacted to your post",
"postReactBody": "{user} reacted with {reaction}",
"postReactContentBody": "{user} reacted with {reaction} to \"{title}\"",
"postAwardedTitle": "{user} awarded your post",
"postAwardedBody": "{user} awarded {amount} points",
"postAwardedContentBody": "{user} awarded {amount} points to \"{title}\"",
"realmInviteTitle": "Realm Invitation",
"realmInviteBody": "You have been invited to join {realm}",
"authCodeTitle": "Disposable Verification Code",
"authCodeBody": "{code} is your disposable code, it will expires in 5 minutes",
"subscriptionAppliedTitle": "Subscription {plan} just activated for your account",
"subscriptionAppliedBody": "Thank for supporting Solar Network! Your {days} days {plan} subscription just begun, feel free to explore the newly unlocked features!",
"orderPaidTitle": "Order {orderId} recipient",
"orderPaidBody": "Paid {amount} {currency} for {remark}",
"orderReceivedTitle": "Order {orderId} recipient",
"orderReceivedBody": "Received {amount} {currency} for {remark}",
"giftClaimedTitle": "Gift Claimed",
"giftClaimedBody": "Your gift {subscription} has been claimed by {user}",
"fortuneTipSpecialTitleBirthday": "Have a Birthday Party",
"fortuneTipSpecialContentBirthday": "Happy Birthday, {user}!",
"fortuneTipPositiveTitle1": "Gacha",
"fortuneTipPositiveContent1": "Comes at your first roll",
"fortuneTipPositiveTitle2": "Gaming",
"fortuneTipPositiveContent2": "Ranking up as god",
"fortuneTipPositiveTitle3": "Lottery",
"fortuneTipPositiveContent3": "Never miss a first",
"fortuneTipPositiveTitle4": "Speech",
"fortuneTipPositiveContent4": "Flowing at precision",
"fortuneTipPositiveTitle5": "Drawing",
"fortuneTipPositiveContent5": "Expectation is envisioned",
"fortuneTipPositiveTitle6": "Coding",
"fortuneTipPositiveContent6": "0 error(s), 0 warning(s)",
"fortuneTipPositiveTitle7": "Shopping",
"fortuneTipPositiveContent7": "No taxes and extra fees",
"fortuneTipPositiveTitle8": "Studying",
"fortuneTipPositiveContent8": "Efficiency X",
"fortuneTipPositiveTitle9": "Music composing",
"fortuneTipPositiveContent9": "No need to re-listen",
"fortuneTipPositiveTitle10": "Imaging",
"fortuneTipPositiveContent10": "Perfect every shot",
"fortuneTipPositiveTitle11": "PCB welding",
"fortuneTipPositiveContent11": "Solders, fluxes, GO!",
"fortuneTipPositiveTitle12": "After Effects",
"fortuneTipPositiveContent12": "@ 60 fps",
"fortuneTipPositiveTitle13": "Drone shot",
"fortuneTipPositiveContent13": "Ready to Go",
"fortuneTipPositiveTitle14": "Color grading",
"fortuneTipPositiveContent14": "In this format, at this color",
"fortuneTipPositiveTitle15": "Waterlogging",
"fortuneTipPositiveContent15": "0 waterlogging",
"fortuneTipNegativeTitle1": "Gacha",
"fortuneTipNegativeContent1": "Won't get at 80, but 200",
"fortuneTipNegativeTitle2": "Gaming",
"fortuneTipNegativeContent2": "Ground's limit",
"fortuneTipNegativeTitle3": "Lottery",
"fortuneTipNegativeContent3": "Zero in multiple ten",
"fortuneTipNegativeTitle4": "Speech",
"fortuneTipNegativeContent4": "Be careful what you say",
"fortuneTipNegativeTitle5": "Drawing",
"fortuneTipNegativeContent5": "Your pen is sticky",
"fortuneTipNegativeTitle6": "Coding",
"fortuneTipNegativeContent6": "114 error(s), 514 warning(s)",
"fortuneTipNegativeTitle7": "Shopping",
"fortuneTipNegativeContent7": "245% tariff",
"fortuneTipNegativeTitle8": "Studying",
"fortuneTipNegativeContent8": "Studying Fatigue III",
"fortuneTipNegativeTitle9": "Music composing",
"fortuneTipNegativeContent9": "FL Studio is not responding",
"fortuneTipNegativeTitle10": "Imaging",
"fortuneTipNegativeContent10": "Card cannot be accessed",
"fortuneTipNegativeTitle11": "PCB welding",
"fortuneTipNegativeContent11": "Hand welding",
"fortuneTipNegativeTitle12": "After Effects",
"fortuneTipNegativeContent12": "Baaah!",
"fortuneTipNegativeTitle13": "Drone shot",
"fortuneTipNegativeContent13": "Low battery level, Auto Landing in 16s",
"fortuneTipNegativeTitle14": "Color grading",
"fortuneTipNegativeContent14": "Crummy screen now",
"fortuneTipNegativeTitle15": "Washing film",
"fortuneTipNegativeContent15": "Why is there still something in the box"
} }

View File

@@ -23,5 +23,94 @@
"emailAccountDeletionTitle": "确认删除您的账户", "emailAccountDeletionTitle": "确认删除您的账户",
"passwordResetTitle": "重置您的密码", "passwordResetTitle": "重置您的密码",
"contractMethodVerificationTitle": "验证联系方式", "contractMethodVerificationTitle": "验证联系方式",
"codeEmailTitle": "您的邮箱一次性密码" "codeEmailTitle": "您的邮箱一次性密码",
"friendRequestTitle": "{sender} 请求成为您的朋友",
"friendRequestBody": "您可以前往关系页面决定接受或拒绝其请求。",
"newLoginTitle": "检测到新登录",
"newLoginBody": "您的账户已在名为 {deviceName} 的设备上登录,位置为 {ipAddress}",
"chatInviteTitle": "新聊天邀请",
"chatInviteBody": "您刚刚被邀请加入 {channel}",
"chatInviteDirectBody": "{user} 向您发送了私信邀请",
"postSubscriptionTitle": "{publisher}: {title}",
"postReplyTitle": "{user} 回复了您的帖子",
"postReactTitle": "{user} 对您的帖子做出了反应",
"postReactBody": "{user} 用 {reaction} 做出了反应",
"postReactContentBody": "{user} 用 {reaction} 对 \"{title}\" 做出了反应",
"postAwardedTitle": "{user} 打赏了您的帖子",
"postAwardedBody": "{user} 打赏了 {amount} 积分",
"postAwardedContentBody": "{user} 打赏了 {amount} 积分给 \"{title}\"",
"realmInviteTitle": "领域邀请",
"realmInviteBody": "您被邀请加入 {realm}",
"authCodeTitle": "一次性验证码",
"authCodeBody": "{code} 是您的一次性验证码将在5分钟后过期",
"subscriptionAppliedTitle": "订阅 {plan} 刚刚为您的账户激活",
"subscriptionAppliedBody": "感谢您支持 Solar Network您的 {days} 天 {plan} 订阅刚刚开始,请尽情探索新解锁的功能!",
"orderPaidTitle": "订单 {orderId} 收件人",
"orderPaidBody": "支付了 {amount} {currency} 用于 {remark}",
"orderReceivedTitle": "订单 {orderId} 收件人",
"orderReceivedBody": "收到了 {amount} {currency} 用于 {remark}",
"giftClaimedTitle": "礼物已领取",
"giftClaimedBody": "您的 {subscription} 礼物已被 {user} 领取",
"fortuneTipSpecialTitleBirthday": "举办生日派对",
"fortuneTipSpecialContentBirthday": "生日快乐,{user}",
"fortuneTipPositiveTitle1": "抽卡",
"fortuneTipPositiveContent1": "一抽就中",
"fortuneTipPositiveTitle2": "游戏",
"fortuneTipPositiveContent2": "封神",
"fortuneTipPositiveTitle3": "抽奖",
"fortuneTipPositiveContent3": "绝不错过首抽",
"fortuneTipPositiveTitle4": "配音",
"fortuneTipPositiveContent4": "行云流水",
"fortuneTipPositiveTitle5": "作画",
"fortuneTipPositiveContent5": "预期被实现",
"fortuneTipPositiveTitle6": "编程",
"fortuneTipPositiveContent6": "0个错误0个警告",
"fortuneTipPositiveTitle7": "购物",
"fortuneTipPositiveContent7": "无税,无额外费用",
"fortuneTipPositiveTitle8": "学习",
"fortuneTipPositiveContent8": "效率X",
"fortuneTipPositiveTitle9": "音乐创作",
"fortuneTipPositiveContent9": "无需重新上线",
"fortuneTipPositiveTitle10": "图像处理",
"fortuneTipPositiveContent10": "每张都很完美",
"fortuneTipPositiveTitle11": "手工焊接",
"fortuneTipPositiveContent11": "焊工、焊料、起飞!",
"fortuneTipPositiveTitle12": "后期",
"fortuneTipPositiveContent12": "@ 60帧",
"fortuneTipPositiveTitle13": "云台",
"fortuneTipPositiveContent13": "准备出发",
"fortuneTipPositiveTitle14": "调色",
"fortuneTipPositiveContent14": "在此格式,进行调色",
"fortuneTipPositiveTitle15": "无水纹",
"fortuneTipPositiveContent15": "0个水印",
"fortuneTipNegativeTitle1": "抽卡",
"fortuneTipNegativeContent1": "保底80却出200",
"fortuneTipNegativeTitle2": "游戏",
"fortuneTipNegativeContent2": "地板",
"fortuneTipNegativeTitle3": "抽奖",
"fortuneTipNegativeContent3": "十连没出",
"fortuneTipNegativeTitle4": "配音",
"fortuneTipNegativeContent4": "慎言",
"fortuneTipNegativeTitle5": "作画",
"fortuneTipNegativeContent5": "笔尖不顺畅",
"fortuneTipNegativeTitle6": "编程",
"fortuneTipNegativeContent6": "114个错误514个警告",
"fortuneTipNegativeTitle7": "购物",
"fortuneTipNegativeContent7": "245%关税",
"fortuneTipNegativeTitle8": "学习",
"fortuneTipNegativeContent8": "学习疲劳III",
"fortuneTipNegativeTitle9": "音乐创作",
"fortuneTipNegativeContent9": "FL Studio没有响应",
"fortuneTipNegativeTitle10": "图像处理",
"fortuneTipNegativeContent10": "无法读取SD卡",
"fortuneTipNegativeTitle11": "手工焊接",
"fortuneTipNegativeContent11": "手工焊接",
"fortuneTipNegativeTitle12": "后期",
"fortuneTipNegativeContent12": "啊啊啊!",
"fortuneTipNegativeTitle13": "云台",
"fortuneTipNegativeContent13": "电量低自动降落16秒",
"fortuneTipNegativeTitle14": "调色",
"fortuneTipNegativeContent14": "屏幕现在很烂",
"fortuneTipNegativeTitle15": "洗胶片",
"fortuneTipNegativeContent15": "为什么盒子里还有东西?"
} }

View File

@@ -118,15 +118,16 @@ public partial class PostService(
public (string title, string content) ChopPostForNotification(SnPost post) public (string title, string content) ChopPostForNotification(SnPost post)
{ {
var locale = System.Globalization.CultureInfo.CurrentUICulture.Name;
var content = !string.IsNullOrEmpty(post.Description) var content = !string.IsNullOrEmpty(post.Description)
? post.Description?.Length >= 40 ? post.Description[..37] + "..." : post.Description ? post.Description?.Length >= 40 ? post.Description[..37] + "..." : post.Description
: post.Content?.Length >= 100 : post.Content?.Length >= 100
? string.Concat(post.Content.AsSpan(0, 97), "...") ? string.Concat(post.Content.AsSpan(0, 97), "...")
: post.Content; : post.Content;
var title = post.Title ?? (post.Content?.Length >= 10 ? post.Content[..10] + "..." : post.Content); var title = post.Title ?? (post.Content?.Length >= 10 ? post.Content[..10] + "..." : post.Content);
title ??= localizer.Get("postOnlyMedia"); title ??= localizer.Get("postOnlyMedia", locale: locale);
if (string.IsNullOrWhiteSpace(content)) if (string.IsNullOrWhiteSpace(content))
content = localizer.Get("postOnlyMedia"); content = localizer.Get("postOnlyMedia", locale: locale);
return (title, content); return (title, content);
} }

View File

@@ -9,5 +9,16 @@
"postAwardedContentBody": "{user} awarded {amount} points to \"{title}\"", "postAwardedContentBody": "{user} awarded {amount} points to \"{title}\"",
"postSubscriptionTitle": "{publisher}: {title}", "postSubscriptionTitle": "{publisher}: {title}",
"realmInviteTitle": "Realm Invitation", "realmInviteTitle": "Realm Invitation",
"realmInviteBody": "You have been invited to join {realm}" "realmInviteBody": "You have been invited to join {realm}",
"chatInviteTitle": "New Chat Invitation",
"chatInviteBody": "You just got invited to join {channel}",
"chatInviteDirectBody": "{user} sent an direct message invitation to you",
"postReplyBody": "{user} replied: {content}",
"postReplyContentBody": "{user} replied post \"{title}\": {content}",
"authCodeTitle": "Disposable Verification Code",
"authCodeBody": "{code} is your disposable code, it will expires in 5 minutes",
"subscriptionAppliedTitle": "Subscription {plan} just activated for your account",
"subscriptionAppliedBody": "Thank for supporting the Solar Network! Your {days} days {plan} subscription just begun, feel free to explore the newly unlocked features!",
"orderPaidTitle": "Order {orderId} recipient",
"orderPaidBody": "{amount} {currency} was removed from your wallet to pay for {item}"
} }

View File

@@ -9,5 +9,16 @@
"postAwardedContentBody": "{user} 打赏了 {amount} 积分给 \"{title}\"", "postAwardedContentBody": "{user} 打赏了 {amount} 积分给 \"{title}\"",
"postSubscriptionTitle": "{publisher}: {title}", "postSubscriptionTitle": "{publisher}: {title}",
"realmInviteTitle": "领域邀请", "realmInviteTitle": "领域邀请",
"realmInviteBody": "您被邀请加入 {realm}" "realmInviteBody": "您被邀请加入 {realm}",
"chatInviteTitle": "新聊天邀请",
"chatInviteBody": "您刚刚被邀请加入 {channel}",
"chatInviteDirectBody": "{user} 向您发送了私信邀请",
"postReplyBody": "{user} 回复:{content}",
"postReplyContentBody": "{user} 回复了帖子 \"{title}\"{content}",
"authCodeTitle": "一次性验证码",
"authCodeBody": "{code} 是您的一次性验证码将在5分钟后过期",
"subscriptionAppliedTitle": "订阅 {plan} 刚刚为您的账户激活",
"subscriptionAppliedBody": "感谢您支持 Solar Network您的 {days} 天 {plan} 订阅刚刚开始,请尽情探索新解锁的功能!",
"orderPaidTitle": "订单 {orderId} 收件人",
"orderPaidBody": "{amount} {currency} 已从您的钱包中扣除以支付 {item}"
} }

View File

@@ -180,8 +180,9 @@ public class PaymentService(
{ {
// Due to ID is uuid, it longer than 8 words for sure // Due to ID is uuid, it longer than 8 words for sure
var readableTransactionId = transaction.Id.ToString().Replace("-", "")[..8]; var readableTransactionId = transaction.Id.ToString().Replace("-", "")[..8];
var locale = System.Globalization.CultureInfo.CurrentUICulture.Name;
var readableTransactionRemark = transaction.Remarks ?? $"#{readableTransactionId}"; var readableTransactionRemark = transaction.Remarks ?? $"#{readableTransactionId}";
await pusher.SendPushNotificationToUserAsync( await pusher.SendPushNotificationToUserAsync(
new SendPushNotificationToUserRequest new SendPushNotificationToUserRequest
{ {
@@ -189,13 +190,13 @@ public class PaymentService(
Notification = new PushNotification Notification = new PushNotification
{ {
Topic = "wallets.transactions", Topic = "wallets.transactions",
Title = localizer.Get("transactionNewTitle", args: new { remark = readableTransactionRemark }), Title = localizer.Get("transactionNewTitle", locale: locale, args: new { remark = readableTransactionRemark }),
Body = transaction.Amount > 0 Body = transaction.Amount > 0
? localizer.Get("transactionNewBodyMinus", args: new { ? localizer.Get("transactionNewBodyMinus", locale: locale, args: new {
amount = transaction.Amount.ToString(CultureInfo.InvariantCulture), amount = transaction.Amount.ToString(CultureInfo.InvariantCulture),
currency = transaction.Currency currency = transaction.Currency
}) })
: localizer.Get("transactionNewBodyPlus", args: new { : localizer.Get("transactionNewBodyPlus", locale: locale, args: new {
amount = transaction.Amount.ToString(CultureInfo.InvariantCulture), amount = transaction.Amount.ToString(CultureInfo.InvariantCulture),
currency = transaction.Currency currency = transaction.Currency
}), }),
@@ -208,9 +209,10 @@ public class PaymentService(
if (payeeWallet is not null) if (payeeWallet is not null)
{ {
// Due to ID is uuid, it longer than 8 words for sure // Due to ID is uuid, it longer than 8 words for sure
var locale = System.Globalization.CultureInfo.CurrentUICulture.Name;
var readableTransactionId = transaction.Id.ToString().Replace("-", "")[..8]; var readableTransactionId = transaction.Id.ToString().Replace("-", "")[..8];
var readableTransactionRemark = transaction.Remarks ?? $"#{readableTransactionId}"; var readableTransactionRemark = transaction.Remarks ?? $"#{readableTransactionId}";
await pusher.SendPushNotificationToUserAsync( await pusher.SendPushNotificationToUserAsync(
new SendPushNotificationToUserRequest new SendPushNotificationToUserRequest
{ {
@@ -218,13 +220,13 @@ public class PaymentService(
Notification = new PushNotification Notification = new PushNotification
{ {
Topic = "wallets.transactions", Topic = "wallets.transactions",
Title = localizer.Get("transactionNewTitle", args: new { remark = readableTransactionRemark }), Title = localizer.Get("transactionNewTitle", locale: locale, args: new { remark = readableTransactionRemark }),
Body = transaction.Amount > 0 Body = transaction.Amount > 0
? localizer.Get("transactionNewBodyPlus", args: new { ? localizer.Get("transactionNewBodyPlus", locale: locale, args: new {
amount = transaction.Amount.ToString(CultureInfo.InvariantCulture), amount = transaction.Amount.ToString(CultureInfo.InvariantCulture),
currency = transaction.Currency currency = transaction.Currency
}) })
: localizer.Get("transactionNewBodyMinus", args: new { : localizer.Get("transactionNewBodyMinus", locale: locale, args: new {
amount = transaction.Amount.ToString(CultureInfo.InvariantCulture), amount = transaction.Amount.ToString(CultureInfo.InvariantCulture),
currency = transaction.Currency currency = transaction.Currency
}), }),
@@ -339,9 +341,36 @@ public class PaymentService(
if (payeeWallet is not null) if (payeeWallet is not null)
{ {
// Due to ID is uuid, it longer than 8 words for sure // Due to ID is uuid, it longer than 8 words for sure
var locale = System.Globalization.CultureInfo.CurrentUICulture.Name;
var readableOrderId = order.Id.ToString().Replace("-", "")[..8]; var readableOrderId = order.Id.ToString().Replace("-", "")[..8];
var readableOrderRemark = order.Remarks ?? $"#{readableOrderId}"; var readableOrderRemark = order.Remarks ?? $"#{readableOrderId}";
await pusher.SendPushNotificationToUserAsync(
new SendPushNotificationToUserRequest
{
UserId = payerWallet.AccountId.ToString(),
Notification = new PushNotification
{
Topic = "wallets.orders.paid",
Title = localizer.Get("orderPaidTitle", locale: locale, args: new { orderId = $"#{readableOrderId}" }),
Body = localizer.Get("orderPaidBody", locale: locale, args: new {
amount = order.Amount.ToString(CultureInfo.InvariantCulture),
currency = order.Currency,
remark = readableOrderRemark
}),
IsSavable = true
}
}
);
}
if (payeeWallet is not null)
{
// Due to ID is uuid, it longer than 8 words for sure
var locale = System.Globalization.CultureInfo.CurrentUICulture.Name;
var readableOrderId = order.Id.ToString().Replace("-", "")[..8];
var readableOrderRemark = order.Remarks ?? $"#{readableOrderId}";
await pusher.SendPushNotificationToUserAsync( await pusher.SendPushNotificationToUserAsync(
new SendPushNotificationToUserRequest new SendPushNotificationToUserRequest
{ {
@@ -349,8 +378,8 @@ public class PaymentService(
Notification = new PushNotification Notification = new PushNotification
{ {
Topic = "wallets.orders.received", Topic = "wallets.orders.received",
Title = localizer.Get("orderReceivedTitle", args: new { orderId = $"#{readableOrderId}" }), Title = localizer.Get("orderReceivedTitle", locale: locale, args: new { orderId = $"#{readableOrderId}" }),
Body = localizer.Get("orderReceivedBody", args: new { Body = localizer.Get("orderReceivedBody", locale: locale, args: new {
amount = order.Amount.ToString(CultureInfo.InvariantCulture), amount = order.Amount.ToString(CultureInfo.InvariantCulture),
currency = order.Currency, currency = order.Currency,
remark = readableOrderRemark remark = readableOrderRemark

View File

@@ -420,11 +420,12 @@ public class SubscriptionService(
? subscription.EndedAt.Value.Minus(subscription.BegunAt).Days.ToString() ? subscription.EndedAt.Value.Minus(subscription.BegunAt).Days.ToString()
: "infinite"; : "infinite";
var locale = System.Globalization.CultureInfo.CurrentUICulture.Name;
var notification = new PushNotification var notification = new PushNotification
{ {
Topic = "subscriptions.begun", Topic = "subscriptions.begun",
Title = localizer.Get("subscriptionAppliedTitle", args: new { subscriptionName = humanReadableName }), Title = localizer.Get("subscriptionAppliedTitle", locale: locale, args: new { subscriptionName = humanReadableName }),
Body = localizer.Get("subscriptionAppliedBody", args: new { duration, subscriptionName = humanReadableName }), Body = localizer.Get("subscriptionAppliedBody", locale: locale, args: new { duration, subscriptionName = humanReadableName }),
Meta = GrpcTypeHelper.ConvertObjectToByteString(new Dictionary<string, object> Meta = GrpcTypeHelper.ConvertObjectToByteString(new Dictionary<string, object>
{ {
["subscription_id"] = subscription.Id.ToString() ["subscription_id"] = subscription.Id.ToString()
@@ -917,11 +918,12 @@ public class SubscriptionService(
? humanReadable ? humanReadable
: subscription.Identifier; : subscription.Identifier;
var locale = System.Globalization.CultureInfo.CurrentUICulture.Name;
var notification = new PushNotification var notification = new PushNotification
{ {
Topic = "gifts.claimed", Topic = "gifts.claimed",
Title = localizer.Get("giftClaimedTitle"), Title = localizer.Get("giftClaimedTitle", locale: locale),
Body = localizer.Get("giftClaimedBody", args: new { subscriptionName = humanReadableName, redeemerName = redeemer.Name }), Body = localizer.Get("giftClaimedBody", locale: locale, args: new { subscriptionName = humanReadableName, redeemerName = redeemer.Name }),
Meta = GrpcTypeHelper.ConvertObjectToByteString(new Dictionary<string, object> Meta = GrpcTypeHelper.ConvertObjectToByteString(new Dictionary<string, object>
{ {
["gift_id"] = gift.Id.ToString(), ["gift_id"] = gift.Id.ToString(),