Skip to content

Commit 973867b

Browse files
committed
Add Localization for Identity Module
1 parent c63c47d commit 973867b

19 files changed

Lines changed: 118 additions & 89 deletions

File tree

src/BuildingBlocks/Shared/Localization/SharedResource.resx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@
3131
<value>A valid email address is required.</value>
3232
</data>
3333

34+
<!-- Identity validation messages -->
35+
<data name="UserIdsRequired" xml:space="preserve">
36+
<value>At least one user ID is required.</value>
37+
</data>
38+
<data name="UserIdsInvalid" xml:space="preserve">
39+
<value>User IDs cannot be empty or whitespace.</value>
40+
</data>
41+
<data name="PasswordsMustMatch" xml:space="preserve">
42+
<value>Passwords do not match.</value>
43+
</data>
44+
<data name="NewPasswordMustDiffer" xml:space="preserve">
45+
<value>New password must be different from the current password.</value>
46+
</data>
47+
<data name="PasswordInHistory" xml:space="preserve">
48+
<value>This password has been used recently. Please choose a different password.</value>
49+
</data>
50+
<data name="ImageConflict" xml:space="preserve">
51+
<value>You cannot upload a new image and delete the current one simultaneously.</value>
52+
</data>
53+
3454
<!-- Common error messages -->
3555
<data name="Unauthorized" xml:space="preserve">
3656
<value>Authentication failed.</value>
Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,57 @@
11
namespace FSH.Modules.Identity.Constants;
22

3+
using FSH.Framework.Shared.Localization;
4+
using Microsoft.Extensions.Localization;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using System.Globalization;
7+
38
/// <summary>
4-
/// Validation message string constants for the Identity module.
5-
/// These keys are used directly as messages now, and will become .resx lookup keys
6-
/// when localization is added.
9+
/// Validation message helper for the Identity module.
10+
/// Uses SharedResource.resx for localized validation messages.
11+
/// Methods accept an optional IStringLocalizer for runtime localization, falling back to English defaults.
712
/// </summary>
813
internal static class IdentityValidationMessages
914
{
10-
// ── Required ─────────────────────────────────────────────────────────────
11-
public const string GroupIdRequired = "Group ID is required.";
12-
public const string GroupNameRequired = "Group name is required.";
13-
public const string UserIdRequired = "User ID is required.";
14-
public const string UserIdsRequired = "At least one user ID is required.";
15-
public const string UserIdsInvalid = "User IDs cannot be empty or whitespace.";
16-
public const string RoleIdRequired = "Role ID is required.";
17-
public const string RoleNameRequired = "Role name is required.";
18-
public const string SessionIdRequired = "Session ID is required.";
19-
public const string EmailRequired = "Email is required.";
20-
public const string FirstNameRequired = "First name is required.";
21-
public const string LastNameRequired = "Last name is required.";
22-
public const string PasswordRequired = "Password is required.";
23-
public const string PasswordConfirmationRequired = "Password confirmation is required.";
24-
public const string UsernameRequired = "Username is required.";
25-
public const string ConfirmationCodeRequired = "Confirmation code is required.";
26-
public const string TenantRequired = "Tenant is required.";
27-
public const string CurrentPasswordRequired = "Current password is required.";
28-
public const string NewPasswordRequired = "New password is required.";
29-
public const string UserRolesRequired = "User roles list is required.";
30-
31-
// ── Length ────────────────────────────────────────────────────────────────
32-
public const string GroupNameMaxLength = "Group name must not exceed 256 characters.";
33-
public const string DescriptionMaxLength = "Description must not exceed 1024 characters.";
34-
public const string ReasonMaxLength = "Reason must not exceed 500 characters.";
35-
public const string PhoneNumberMaxLength = "Phone number must not exceed 20 characters.";
36-
public const string UsernameMinLength = "Username must be at least 3 characters.";
37-
public const string UsernameMaxLength = "Username must not exceed 50 characters.";
38-
public const string PasswordMinLength = "Password must be at least 6 characters.";
39-
public const string FirstNameMaxLength = "First name must not exceed 100 characters.";
40-
public const string LastNameMaxLength = "Last name must not exceed 100 characters.";
15+
// ── Required — method uses localized format or falls back to default ─────────
16+
public static string Required(string fieldName, IStringLocalizer<SharedResource>? localizer = null)
17+
{
18+
var format = localizer?[nameof(Required)] ?? "{0} is required.";
19+
return string.Format(CultureInfo.InvariantCulture, format, fieldName);
20+
}
21+
22+
// ── Required — non-standard messages that don't fit the field-name pattern ─
23+
public static string UserIdsRequired(IStringLocalizer<SharedResource>? localizer = null) =>
24+
localizer?[nameof(UserIdsRequired)] ?? "At least one user ID is required.";
25+
26+
public static string UserIdsInvalid(IStringLocalizer<SharedResource>? localizer = null) =>
27+
localizer?[nameof(UserIdsInvalid)] ?? "User IDs cannot be empty or whitespace.";
28+
29+
// ── Length — methods use localized format '{0} must not exceed {1} characters.' when localized ──
30+
public static string MaxLength(string fieldName, int max, IStringLocalizer<SharedResource>? localizer = null)
31+
{
32+
var format = localizer?[nameof(MaxLength)] ?? "{0} must not exceed {1} characters.";
33+
return string.Format(CultureInfo.InvariantCulture, format, fieldName, max);
34+
}
35+
36+
public static string MinLength(string fieldName, int min, IStringLocalizer<SharedResource>? localizer = null)
37+
{
38+
var format = localizer?[nameof(MinLength)] ?? "{0} must be at least {1} characters.";
39+
return string.Format(CultureInfo.InvariantCulture, format, fieldName, min);
40+
}
4141

4242
// ── Format & rules ────────────────────────────────────────────────────────
43-
public const string InvalidEmail = "A valid email address is required.";
44-
public const string PasswordsMustMatch = "Passwords do not match.";
45-
public const string NewPasswordMustDiffer = "New password must be different from the current password.";
46-
public const string PasswordInHistory = "This password has been used recently. Please choose a different password.";
47-
public const string ImageConflict = "You cannot upload a new image and delete the current one simultaneously.";
48-
}
43+
public static string InvalidEmail(IStringLocalizer<SharedResource>? localizer = null) =>
44+
localizer?[nameof(InvalidEmail)] ?? "A valid email address is required.";
45+
46+
public static string PasswordsMustMatch(IStringLocalizer<SharedResource>? localizer = null) =>
47+
localizer?[nameof(PasswordsMustMatch)] ?? "Passwords do not match.";
48+
49+
public static string NewPasswordMustDiffer(IStringLocalizer<SharedResource>? localizer = null) =>
50+
localizer?[nameof(NewPasswordMustDiffer)] ?? "New password must be different from the current password.";
51+
52+
public static string PasswordInHistory(IStringLocalizer<SharedResource>? localizer = null) =>
53+
localizer?[nameof(PasswordInHistory)] ?? "This password has been used recently. Please choose a different password.";
54+
55+
public static string ImageConflict(IStringLocalizer<SharedResource>? localizer = null) =>
56+
localizer?[nameof(ImageConflict)] ?? "You cannot upload a new image and delete the current one simultaneously.";
57+
}

src/Modules/Identity/Modules.Identity/Features/v1/Groups/AddUsersToGroup/AddUsersToGroupCommandValidator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ public AddUsersToGroupCommandValidator()
1010
{
1111
RuleFor(x => x.GroupId)
1212
.NotEmpty()
13-
.WithMessage(IdentityValidationMessages.GroupIdRequired);
13+
.WithMessage(IdentityValidationMessages.Required("Group ID"));
1414

1515
RuleFor(x => x.UserIds)
1616
.NotEmpty()
17-
.WithMessage(IdentityValidationMessages.UserIdsRequired)
17+
.WithMessage(IdentityValidationMessages.UserIdsRequired())
1818
.Must(ids => ids.All(id => !string.IsNullOrWhiteSpace(id)))
19-
.WithMessage(IdentityValidationMessages.UserIdsInvalid);
19+
.WithMessage(IdentityValidationMessages.UserIdsInvalid());
2020
}
2121
}

src/Modules/Identity/Modules.Identity/Features/v1/Groups/CreateGroup/CreateGroupCommandValidator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ public sealed class CreateGroupCommandValidator : AbstractValidator<CreateGroupC
99
public CreateGroupCommandValidator()
1010
{
1111
RuleFor(x => x.Name)
12-
.NotEmpty().WithMessage(IdentityValidationMessages.GroupNameRequired)
13-
.MaximumLength(256).WithMessage(IdentityValidationMessages.GroupNameMaxLength);
12+
.NotEmpty().WithMessage(IdentityValidationMessages.Required("Group name"))
13+
.MaximumLength(256).WithMessage(IdentityValidationMessages.MaxLength("Group name", 256));
1414

1515
RuleFor(x => x.Description)
16-
.MaximumLength(1024).WithMessage(IdentityValidationMessages.DescriptionMaxLength);
16+
.MaximumLength(1024).WithMessage(IdentityValidationMessages.MaxLength("Description", 1024));
1717
}
1818
}

src/Modules/Identity/Modules.Identity/Features/v1/Groups/DeleteGroup/DeleteGroupCommandValidator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ public sealed class DeleteGroupCommandValidator : AbstractValidator<DeleteGroupC
99
public DeleteGroupCommandValidator()
1010
{
1111
RuleFor(x => x.Id)
12-
.NotEmpty().WithMessage(IdentityValidationMessages.GroupIdRequired);
12+
.NotEmpty().WithMessage(IdentityValidationMessages.Required("Group ID"));
1313
}
1414
}

src/Modules/Identity/Modules.Identity/Features/v1/Groups/RemoveUserFromGroup/RemoveUserFromGroupCommandValidator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ public sealed class RemoveUserFromGroupCommandValidator : AbstractValidator<Remo
99
public RemoveUserFromGroupCommandValidator()
1010
{
1111
RuleFor(x => x.GroupId)
12-
.NotEmpty().WithMessage(IdentityValidationMessages.GroupIdRequired);
12+
.NotEmpty().WithMessage(IdentityValidationMessages.Required("Group ID"));
1313

1414
RuleFor(x => x.UserId)
15-
.NotEmpty().WithMessage(IdentityValidationMessages.UserIdRequired);
15+
.NotEmpty().WithMessage(IdentityValidationMessages.Required("User ID"));
1616
}
1717
}

src/Modules/Identity/Modules.Identity/Features/v1/Groups/UpdateGroup/UpdateGroupCommandValidator.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ public sealed class UpdateGroupCommandValidator : AbstractValidator<UpdateGroupC
99
public UpdateGroupCommandValidator()
1010
{
1111
RuleFor(x => x.Id)
12-
.NotEmpty().WithMessage(IdentityValidationMessages.GroupIdRequired);
12+
.NotEmpty().WithMessage(IdentityValidationMessages.Required("Group ID"));
1313

1414
RuleFor(x => x.Name)
15-
.NotEmpty().WithMessage(IdentityValidationMessages.GroupNameRequired)
16-
.MaximumLength(256).WithMessage(IdentityValidationMessages.GroupNameMaxLength);
15+
.NotEmpty().WithMessage(IdentityValidationMessages.Required("Group name"))
16+
.MaximumLength(256).WithMessage(IdentityValidationMessages.MaxLength("Group name", 256));
1717

1818
RuleFor(x => x.Description)
19-
.MaximumLength(1024).WithMessage(IdentityValidationMessages.DescriptionMaxLength);
19+
.MaximumLength(1024).WithMessage(IdentityValidationMessages.MaxLength("Description", 1024));
2020
}
2121
}

src/Modules/Identity/Modules.Identity/Features/v1/Roles/DeleteRole/DeleteRoleCommandValidator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ public sealed class DeleteRoleCommandValidator : AbstractValidator<DeleteRoleCom
99
public DeleteRoleCommandValidator()
1010
{
1111
RuleFor(x => x.Id)
12-
.NotEmpty().WithMessage(IdentityValidationMessages.RoleIdRequired);
12+
.NotEmpty().WithMessage(IdentityValidationMessages.Required("Role ID"));
1313
}
1414
}

src/Modules/Identity/Modules.Identity/Features/v1/Roles/UpsertRole/UpsertRoleCommandValidator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ public sealed class UpsertRoleCommandValidator : AbstractValidator<UpsertRoleCom
88
{
99
public UpsertRoleCommandValidator()
1010
{
11-
RuleFor(x => x.Name).NotEmpty().WithMessage(IdentityValidationMessages.RoleNameRequired);
11+
RuleFor(x => x.Name).NotEmpty().WithMessage(IdentityValidationMessages.Required("Role name"));
1212
}
1313
}

src/Modules/Identity/Modules.Identity/Features/v1/Sessions/AdminRevokeAllSessions/AdminRevokeAllSessionsCommandValidator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ public sealed class AdminRevokeAllSessionsCommandValidator : AbstractValidator<A
99
public AdminRevokeAllSessionsCommandValidator()
1010
{
1111
RuleFor(x => x.UserId)
12-
.NotEmpty().WithMessage(IdentityValidationMessages.UserIdRequired);
12+
.NotEmpty().WithMessage(IdentityValidationMessages.Required("User ID"));
1313

1414
RuleFor(x => x.Reason)
15-
.MaximumLength(500).WithMessage(IdentityValidationMessages.ReasonMaxLength)
15+
.MaximumLength(500).WithMessage(IdentityValidationMessages.MaxLength("Reason", 500))
1616
.When(x => x.Reason is not null);
1717
}
1818
}

0 commit comments

Comments
 (0)