♻️ Refactored localization service

This commit is contained in:
2026-02-04 23:59:41 +08:00
parent c1669286f4
commit 9b6a62ec66
30 changed files with 530 additions and 369 deletions

View File

@@ -5,7 +5,7 @@ using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Queue;
using DysonNetwork.Shared.Registry;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
using DysonNetwork.Shared.Localization;
using NATS.Client.Core;
using NodaTime;
using NodaTime.Extensions;
@@ -15,7 +15,7 @@ namespace DysonNetwork.Pass.Account;
public class AccountEventService(
AppDatabase db,
ICacheService cache,
IStringLocalizer<Localization.AccountEventResource> localizer,
ILocalizationService localizer,
RingService.RingServiceClient pusher,
Pass.Leveling.ExperienceService experienceService,
RemotePaymentService payment,
@@ -359,8 +359,8 @@ public class AccountEventService(
new CheckInFortuneTip
{
IsPositive = true,
Title = localizer["FortuneTipSpecialTitle_Birthday"].Value,
Content = localizer["FortuneTipSpecialContent_Birthday", user.Nick].Value,
Title = localizer.Get("fortuneTipSpecialTitleBirthday"),
Content = localizer.Get("fortuneTipSpecialContentBirthday", args: new { user.Nick }),
}
];
}
@@ -374,8 +374,8 @@ public class AccountEventService(
tips = positiveIndices.Select(index => new CheckInFortuneTip
{
IsPositive = true,
Title = localizer[$"FortuneTipPositiveTitle_{index}"].Value,
Content = localizer[$"FortuneTipPositiveContent_{index}"].Value
Title = localizer.Get($"fortuneTipPositiveTitle{index}"),
Content = localizer.Get($"fortuneTipPositiveContent{index}")
}).ToList();
// Generate 2 negative tips
@@ -387,8 +387,8 @@ public class AccountEventService(
tips.AddRange(negativeIndices.Select(index => new CheckInFortuneTip
{
IsPositive = false,
Title = localizer[$"FortuneTipNegativeTitle_{index}"].Value,
Content = localizer[$"FortuneTipNegativeContent_{index}"].Value
Title = localizer.Get($"fortuneTipNegativeTitle{index}"),
Content = localizer.Get($"fortuneTipNegativeContent{index}")
}));
// The 5 is specialized, keep it alone.

View File

@@ -6,11 +6,11 @@ using DysonNetwork.Pass.Mailer;
using DysonNetwork.Pass.Resources.Emails;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Localization;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Queue;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
using NATS.Client.Core;
using NATS.Net;
using NodaTime;
@@ -28,8 +28,7 @@ public class AccountService(
AffiliationSpellService ars,
EmailService mailer,
RingService.RingServiceClient pusher,
IStringLocalizer<NotificationResource> localizer,
IStringLocalizer<EmailResource> emailLocalizer,
ILocalizationService localizer,
ICacheService cache,
ILogger<AccountService> logger,
RemoteSubscriptionService remoteSubscription,
@@ -434,8 +433,8 @@ public class AccountService(
Notification = new PushNotification
{
Topic = "auth.verification",
Title = localizer["AuthCodeTitle"],
Body = localizer["AuthCodeBody", code],
Title = localizer.Get("authCodeTitle"),
Body = localizer.Get("authCodeBody", args: new { code }),
IsSavable = false
}
}
@@ -466,7 +465,7 @@ public class AccountService(
.SendTemplatedEmailAsync<FactorCodeEmail, VerificationEmailModel>(
account.Nick,
contact.Content,
emailLocalizer["CodeEmailTitle"],
localizer.Get("codeEmailTitle"),
new VerificationEmailModel
{
Name = account.Name,

View File

@@ -5,7 +5,7 @@ using DysonNetwork.Pass.Resources.Emails;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
using DysonNetwork.Shared.Localization;
using NodaTime;
using EmailResource = DysonNetwork.Pass.Localization.EmailResource;
@@ -15,7 +15,7 @@ public class MagicSpellService(
AppDatabase db,
IConfiguration configuration,
ILogger<MagicSpellService> logger,
IStringLocalizer<EmailResource> localizer,
ILocalizationService localizer,
EmailService email,
ICacheService cache
)
@@ -98,7 +98,7 @@ public class MagicSpellService(
await email.SendTemplatedEmailAsync<RegistrationConfirmEmail, LandingEmailModel>(
contact.Account.Nick,
contact.Content,
localizer["RegConfirmTitle"],
localizer.Get("regConfirmTitle"),
new LandingEmailModel
{
Name = contact.Account.Name,
@@ -110,7 +110,7 @@ public class MagicSpellService(
await email.SendTemplatedEmailAsync<AccountDeletionEmail, AccountDeletionEmailModel>(
contact.Account.Nick,
contact.Content,
localizer["AccountDeletionTitle"],
localizer.Get("accountDeletionTitle"),
new AccountDeletionEmailModel
{
Name = contact.Account.Name,
@@ -122,7 +122,7 @@ public class MagicSpellService(
await email.SendTemplatedEmailAsync<PasswordResetEmail, PasswordResetEmailModel>(
contact.Account.Nick,
contact.Content,
localizer["PasswordResetTitle"],
localizer.Get("passwordResetTitle"),
new PasswordResetEmailModel
{
Name = contact.Account.Name,
@@ -136,7 +136,7 @@ public class MagicSpellService(
await email.SendTemplatedEmailAsync<ContactVerificationEmail, ContactVerificationEmailModel>(
contact.Account.Nick,
contactMethod!,
localizer["ContractVerificationTitle"],
localizer.Get("contractVerificationTitle"),
new ContactVerificationEmailModel
{
Name = contact.Account.Name,

View File

@@ -3,7 +3,7 @@ using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
using DysonNetwork.Shared.Localization;
using NodaTime;
namespace DysonNetwork.Pass.Account;
@@ -12,7 +12,7 @@ public class RelationshipService(
AppDatabase db,
ICacheService cache,
RingService.RingServiceClient pusher,
IStringLocalizer<NotificationResource> localizer
ILocalizationService localizer
)
{
private const string UserFriendsCacheKeyPrefix = "accounts:friends:";
@@ -117,8 +117,8 @@ public class RelationshipService(
Notification = new PushNotification
{
Topic = "relationships.friends.request",
Title = localizer["FriendRequestTitle", sender.Nick],
Body = localizer["FriendRequestBody"],
Title = localizer.Get("friendRequestTitle", args: new { sender.Nick }),
Body = localizer.Get("friendRequestBody"),
ActionUri = "/account/relationships",
IsSavable = true
}

View File

@@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore;
using DysonNetwork.Pass.Localization;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Proto;
using Microsoft.Extensions.Localization;
using DysonNetwork.Shared.Localization;
using AccountService = DysonNetwork.Pass.Account.AccountService;
using ActionLogService = DysonNetwork.Pass.Account.ActionLogService;
using DysonNetwork.Shared.Models;
@@ -22,7 +22,7 @@ public class AuthController(
ActionLogService als,
RingService.RingServiceClient pusher,
IConfiguration configuration,
IStringLocalizer<NotificationResource> localizer,
ILocalizationService localizer,
ILogger<AuthController> logger
) : ControllerBase
{
@@ -237,9 +237,8 @@ public class AuthController(
Notification = new PushNotification
{
Topic = "auth.login",
Title = localizer["NewLoginTitle"],
Body = localizer["NewLoginBody", challenge.DeviceName ?? "unknown",
challenge.IpAddress ?? "unknown"],
Title = localizer.Get("newLoginTitle"),
Body = localizer.Get("newLoginBody", args: new { deviceName = challenge.DeviceName ?? "unknown", ipAddress = challenge.IpAddress ?? "unknown" }),
IsSavable = true
},
UserId = challenge.AccountId.ToString()

View File

@@ -134,4 +134,8 @@
<ItemGroup>
<Folder Include="Migrations\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Locales\*.json" />
</ItemGroup>
</Project>

View File

@@ -5,14 +5,14 @@ using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Registry;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
using DysonNetwork.Shared.Localization;
namespace DysonNetwork.Pass.Realm;
public class RealmService(
AppDatabase db,
RingService.RingServiceClient pusher,
IStringLocalizer<NotificationResource> localizer,
ILocalizationService localizer,
ICacheService cache
)
{
@@ -55,8 +55,8 @@ public class RealmService(
Notification = new PushNotification
{
Topic = "invites.realms",
Title = localizer["RealmInviteTitle"],
Body = localizer["RealmInviteBody", member.Realm.Name],
Title = localizer.Get("realmInviteTitle"),
Body = localizer.Get("realmInviteBody", args: new { realmName = member.Realm.Name }),
ActionUri = "/realms",
IsSavable = true
}

View File

@@ -5,14 +5,14 @@ using Microsoft.EntityFrameworkCore;
using DysonNetwork.Pass.Localization;
using DysonNetwork.Shared;
using DysonNetwork.Shared.Cache;
using Microsoft.Extensions.Localization;
using DysonNetwork.Shared.Localization;
namespace DysonNetwork.Pass.Realm;
public class RealmServiceGrpc(
AppDatabase db,
RingService.RingServiceClient pusher,
IStringLocalizer<NotificationResource> localizer,
ILocalizationService localizer,
ICacheService cache
)
: Shared.Proto.RealmService.RealmServiceBase
@@ -127,8 +127,8 @@ public class RealmServiceGrpc(
Notification = new PushNotification
{
Topic = "invites.realms",
Title = localizer["RealmInviteTitle"],
Body = localizer["RealmInviteBody", member.Realm?.Name ?? "Unknown Realm"],
Title = localizer.Get("realmInviteTitle"),
Body = localizer.Get("realmInviteBody", args: new { realmName = member.Realm?.Name ?? "Unknown Realm" }),
ActionUri = "/realms",
IsSavable = true
}

View File

@@ -0,0 +1,22 @@
{
"welcomeMessage": "Hello, {name}!",
"goodbyeMessage": "Goodbye, {name}!",
"itemCount": {
"one": "{count} item",
"other": "{count} items"
},
"userCount": {
"one": "There is {count} user online",
"other": "There are {count} users online"
},
"notificationMessage": "You have {count} new message(s) from {sender}",
"profileUpdated": "Your profile has been updated successfully",
"errorOccurred": "An error occurred: {error}",
"loginSuccess": "Welcome back, {username}!",
"loginFailed": "Login failed. Please try again.",
"accountCreated": "Your account has been created successfully",
"emailVerification": "Please verify your email address",
"passwordReset": "Password reset link has been sent to your email",
"permissionDenied": "You do not have permission to access this resource",
"lotteryWon": "Congratulations! You won the lottery!"
}

View File

@@ -0,0 +1,22 @@
{
"welcomeMessage": "你好,{name}",
"goodbyeMessage": "再见,{name}",
"itemCount": {
"one": "{count}个项目",
"other": "{count}个项目"
},
"userCount": {
"one": "有 {count} 位用户在线",
"other": "有 {count} 位用户在线"
},
"notificationMessage": "您有 {count} 条来自 {sender} 的新消息",
"profileUpdated": "您的个人资料已成功更新",
"errorOccurred": "发生错误:{error}",
"loginSuccess": "欢迎回来,{username}",
"loginFailed": "登录失败,请重试",
"accountCreated": "您的账户已成功创建",
"emailVerification": "请验证您的电子邮件地址",
"passwordReset": "密码重置链接已发送到您的邮箱",
"permissionDenied": "您没有权限访问此资源",
"lotteryWon": "恭喜!您赢得了抽奖!"
}