diff --git a/src/HelloShop.IdentityService/Controllers/HelloWorldController.cs b/src/HelloShop.IdentityService/Controllers/HelloWorldController.cs new file mode 100644 index 0000000..5ecf733 --- /dev/null +++ b/src/HelloShop.IdentityService/Controllers/HelloWorldController.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Localization; +using System.Globalization; +using System.Reflection; + +namespace HelloShop.IdentityService.Controllers; + +[Route("api/[controller]")] +[ApiController] +public class HelloWorldController() : ControllerBase +{ + [HttpGet(nameof(Localizer1))] + public IActionResult Localizer1([FromServices] IStringLocalizerFactory factory) + { + var location = Assembly.GetExecutingAssembly().GetName().Name; + + ArgumentException.ThrowIfNullOrWhiteSpace(location); + + var localizer = factory.Create(typeof(Welcome)); + + string message = localizer["HelloWorld"]; + + return Ok(message); + } + + [HttpGet(nameof(Localizer2))] + public IActionResult Localizer2([FromServices] IStringLocalizer localizer) + { + string message = localizer["HelloWorld"]; + + return Ok(message); + } + + [HttpGet(nameof(Localizer3))] + public IActionResult Localizer3() + { + object result = new + { + Time = TimeProvider.System.GetUtcNow().ToString(), + Amount = Random.Shared.Next(1, 1000).ToString("C") + }; + + return Ok(result); + } +} diff --git a/src/HelloShop.IdentityService/HelloShop.IdentityService.csproj b/src/HelloShop.IdentityService/HelloShop.IdentityService.csproj index 210141a..92492e8 100644 --- a/src/HelloShop.IdentityService/HelloShop.IdentityService.csproj +++ b/src/HelloShop.IdentityService/HelloShop.IdentityService.csproj @@ -3,7 +3,6 @@ net9.0 enable enable - true @@ -17,4 +16,8 @@ + + + + \ No newline at end of file diff --git a/src/HelloShop.IdentityService/Models/Users/UserCreateRequest.cs b/src/HelloShop.IdentityService/Models/Users/UserCreateRequest.cs index 1f58b60..e303fee 100644 --- a/src/HelloShop.IdentityService/Models/Users/UserCreateRequest.cs +++ b/src/HelloShop.IdentityService/Models/Users/UserCreateRequest.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; namespace HelloShop.IdentityService.Models.Users; @@ -6,7 +7,6 @@ public class UserCreateRequest { public required string UserName { get; init; } - [Length(8, 16)] public string? PhoneNumber { get; set; } public string? Email { get; set; } diff --git a/src/HelloShop.IdentityService/Program.cs b/src/HelloShop.IdentityService/Program.cs index 51194f1..5bfbf7e 100644 --- a/src/HelloShop.IdentityService/Program.cs +++ b/src/HelloShop.IdentityService/Program.cs @@ -58,6 +58,7 @@ builder.Services.AddOpenApi(); builder.Services.AddPermissionDefinitions(); builder.Services.AddAuthorization().AddDistributedMemoryCache().AddHttpClient().AddHttpContextAccessor().AddTransient().AddCustomAuthorization(); builder.Services.AddModelMapper().AddModelValidator(); +builder.Services.AddCustomLocalization(); var app = builder.Build(); @@ -72,5 +73,6 @@ app.MapControllers(); app.UseDataSeedingProviders(); app.UseOpenApi(); app.MapGroup("api/Permissions").MapPermissionDefinitions("Permissions"); +app.UseCustomLocalization(); app.Run(); diff --git a/src/HelloShop.IdentityService/Resources/Models/Users/UserCreateRequest.en-US.resx b/src/HelloShop.IdentityService/Resources/Models/Users/UserCreateRequest.en-US.resx new file mode 100644 index 0000000..e164147 --- /dev/null +++ b/src/HelloShop.IdentityService/Resources/Models/Users/UserCreateRequest.en-US.resx @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UserName + + + + The phone number already exists + + + \ No newline at end of file diff --git a/src/HelloShop.IdentityService/Resources/Models/Users/UserCreateRequest.zh-CN.resx b/src/HelloShop.IdentityService/Resources/Models/Users/UserCreateRequest.zh-CN.resx new file mode 100644 index 0000000..6253b0e --- /dev/null +++ b/src/HelloShop.IdentityService/Resources/Models/Users/UserCreateRequest.zh-CN.resx @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 用户名 + + + + 手机号已存在 + + + \ No newline at end of file diff --git a/src/HelloShop.IdentityService/Resources/Welcome.en-US.resx b/src/HelloShop.IdentityService/Resources/Welcome.en-US.resx new file mode 100644 index 0000000..43304d6 --- /dev/null +++ b/src/HelloShop.IdentityService/Resources/Welcome.en-US.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Hello,World. + + + \ No newline at end of file diff --git a/src/HelloShop.IdentityService/Resources/Welcome.zh-CN.resx b/src/HelloShop.IdentityService/Resources/Welcome.zh-CN.resx new file mode 100644 index 0000000..fa249bf --- /dev/null +++ b/src/HelloShop.IdentityService/Resources/Welcome.zh-CN.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 世界,你好。 + + + \ No newline at end of file diff --git a/src/HelloShop.IdentityService/Validations/Users/UserCreateRequestValidator.cs b/src/HelloShop.IdentityService/Validations/Users/UserCreateRequestValidator.cs index 625dfd7..bb0f35c 100644 --- a/src/HelloShop.IdentityService/Validations/Users/UserCreateRequestValidator.cs +++ b/src/HelloShop.IdentityService/Validations/Users/UserCreateRequestValidator.cs @@ -3,19 +3,20 @@ using HelloShop.IdentityService.Entities; using HelloShop.IdentityService.EntityFrameworks; using HelloShop.IdentityService.Models.Users; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Localization; namespace HelloShop.IdentityService.Validations.Users; public class UserCreateRequestValidator : AbstractValidator { - public UserCreateRequestValidator(IdentityServiceDbContext dbContext) + public UserCreateRequestValidator(IdentityServiceDbContext dbContext,IStringLocalizer localizer ) { RuleFor(m => m.UserName).NotNull().NotEmpty().Length(8, 16).Matches("^[a-zA-Z]+$"); RuleFor(m => m.PhoneNumber).NotNull().NotEmpty().Length(11).Matches(@"^1\d{10}$").Must((phoneNumber) => { return !dbContext.Set().Any(e => e.PhoneNumber == phoneNumber); - }); + }).WithMessage(localizer["PhoneNumberAlreadyExists"]); RuleFor(m => m.Password).NotNull().NotEmpty().Length(8, 16); diff --git a/src/HelloShop.IdentityService/Welcome.cs b/src/HelloShop.IdentityService/Welcome.cs new file mode 100644 index 0000000..37177ad --- /dev/null +++ b/src/HelloShop.IdentityService/Welcome.cs @@ -0,0 +1,6 @@ +namespace HelloShop.IdentityService; + +public class Welcome +{ + +} diff --git a/src/HelloShop.OrderingService/HelloShop.OrderingService.csproj b/src/HelloShop.OrderingService/HelloShop.OrderingService.csproj index a97a79f..eeb933d 100644 --- a/src/HelloShop.OrderingService/HelloShop.OrderingService.csproj +++ b/src/HelloShop.OrderingService/HelloShop.OrderingService.csproj @@ -3,7 +3,6 @@ net9.0 enable enable - true diff --git a/src/HelloShop.ProductService/HelloShop.ProductService.csproj b/src/HelloShop.ProductService/HelloShop.ProductService.csproj index a97a79f..eeb933d 100644 --- a/src/HelloShop.ProductService/HelloShop.ProductService.csproj +++ b/src/HelloShop.ProductService/HelloShop.ProductService.csproj @@ -3,7 +3,6 @@ net9.0 enable enable - true diff --git a/src/HelloShop.ServiceDefaults/Extensions/LocalizationExtensions.cs b/src/HelloShop.ServiceDefaults/Extensions/LocalizationExtensions.cs new file mode 100644 index 0000000..ab37891 --- /dev/null +++ b/src/HelloShop.ServiceDefaults/Extensions/LocalizationExtensions.cs @@ -0,0 +1,53 @@ +using FluentValidation; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Reflection; + +namespace HelloShop.ServiceDefaults.Extensions; + +public static class LocalizationExtensions +{ + public static IServiceCollection AddCustomLocalization(this IServiceCollection services) + { + services.AddLocalization(options => options.ResourcesPath = "Resources"); + + return services; + } + + public static IApplicationBuilder UseCustomLocalization(this IApplicationBuilder app) + { + var supportedCultures = new[] { "zh-CN", "en-US" }; + + var localizationOptions = new RequestLocalizationOptions().SetDefaultCulture(supportedCultures.First()) + .AddSupportedCultures(supportedCultures) + .AddSupportedUICultures(supportedCultures); + + app.UseRequestLocalization(localizationOptions); + + IStringLocalizerFactory localizerFactory = app.ApplicationServices.GetRequiredService(); + + ValidatorOptions.Global.DisplayNameResolver = (type, memberInfo, lambdaExpression) => + { + string displayName = memberInfo.Name; + + DisplayAttribute? displayAttribute = memberInfo.GetCustomAttribute(true); + + displayName = displayAttribute?.Name ?? displayName; + + DisplayNameAttribute? displayNameAttribute = memberInfo.GetCustomAttribute(true); + + displayName = displayNameAttribute?.DisplayName ?? displayName; + + var localizer = localizerFactory.Create(type); + + return localizer[displayName]; + }; + + + return app; + } + +} diff --git a/src/HelloShop.ServiceDefaults/Extensions/OpenApiExtensions.cs b/src/HelloShop.ServiceDefaults/Extensions/OpenApiExtensions.cs index 9a54dde..b1bdcea 100644 --- a/src/HelloShop.ServiceDefaults/Extensions/OpenApiExtensions.cs +++ b/src/HelloShop.ServiceDefaults/Extensions/OpenApiExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) HelloShop Corporation. All rights reserved. // See the license file in the project root for more information. +using HelloShop.ServiceDefaults.Infrastructure; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; @@ -25,6 +26,8 @@ namespace HelloShop.ServiceDefaults.Extensions services.Configure(options => { + options.OperationFilter(); + options.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, diff --git a/src/HelloShop.ServiceDefaults/Infrastructure/AcceptLanguageHeaderOperationFilter.cs b/src/HelloShop.ServiceDefaults/Infrastructure/AcceptLanguageHeaderOperationFilter.cs new file mode 100644 index 0000000..7138292 --- /dev/null +++ b/src/HelloShop.ServiceDefaults/Infrastructure/AcceptLanguageHeaderOperationFilter.cs @@ -0,0 +1,27 @@ +using Microsoft.Net.Http.Headers; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace HelloShop.ServiceDefaults.Infrastructure; + +public class AcceptLanguageHeaderOperationFilter : IOperationFilter +{ + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + var parameter = new OpenApiParameter + { + Name = HeaderNames.AcceptLanguage, + In = ParameterLocation.Header, + Required = false, + Schema = new OpenApiSchema + { + Default = new OpenApiString("zh-CN"), + Type = "string", + Enum = [new OpenApiString("zh-CN"), new OpenApiString("en-US")] + } + }; + + operation.Parameters.Add(parameter); + } +}