From fdd21fdf7a0ed218db21f9bb9d74aa5c2cd5b27d Mon Sep 17 00:00:00 2001 From: hello Date: Tue, 9 Apr 2024 07:13:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=86=E9=A1=B5=E5=92=8C=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/helloshop/localization.md | 157 ++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 notes/helloshop/localization.md diff --git a/notes/helloshop/localization.md b/notes/helloshop/localization.md new file mode 100644 index 0000000..2605d75 --- /dev/null +++ b/notes/helloshop/localization.md @@ -0,0 +1,157 @@ +# 全球化与本地化 + +## 创建资源文件 + +```text +Welcome.en-US.resx +Welcome.zh-CN.resx +``` + +## 添加本地化服务和资源定位 + +```csharp +public static IServiceCollection AddCustomLocalization(this IServiceCollection services) +{ + services.AddLocalization(options => options.ResourcesPath = "Resources"); + + return services; +} +``` + +## 使用本地化服务 + +```csharp +public class HelloWorldController(IStringLocalizerFactory stringLocalizerFactory) : ControllerBase +{ + [HttpGet] + public IActionResult Get() + { + var location = Assembly.GetExecutingAssembly().FullName; + + ArgumentException.ThrowIfNullOrWhiteSpace(location); + + var localizer = stringLocalizerFactory.Create("Welcome", location); + + return Ok(localizer["HelloWorld"].Value); + } +} +``` + +## 使用 HTTP 请求设置语言 + +UseRequestLocalization 中间件从请求中获取语言设置,然后设置当前线程的语言,以便在后续的请求中使用,这样就可以实现全局的本地化,而不需要在每个控制器中设置本地化。 + +```csharp +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); + + return app; +} +``` + +使用下面的方式从 Header 、 QueryString 或者 Cookie 中获取语言设置: + +```text +Accept-Language: en-US +``` + +```text +http://localhost:5000/?culture=en-Us +``` + +```text +.AspNetCore.Culture=en-US +``` + +## OpenApi 设置 Accept-Language + +```csharp +services.AddSwaggerGen(c => +{ + c.OperationFilter(); +}); +``` + +```csharp +public class AcceptLanguageHeaderOperationFilter : IOperationFilter +{ + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + if (operation.Parameters == null) + { + operation.Parameters = new List(); + } + + operation.Parameters.Add(new OpenApiParameter + { + Name = "Accept-Language", + In = ParameterLocation.Header, + Required = false, + Schema = new OpenApiSchema + { + Type = "string" + } + }); + } +} +``` + +## 实现模型和属性的本地化 + +```csharp + 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]; + }; +``` + +## 本地化验证错误消息 + +```csharp +public UserCreateRequestValidator(IdentityServiceDbContext dbContext, IStringLocalizer localizer) +{ + RuleFor(m => m.PhoneNumber).NotNull().NotEmpty().Length(11).Matches(@"^1\d{10}$").Must((model, phoneNumber) => + { + return !dbContext.Users.Any(e => e.PhoneNumber == phoneNumber); + }).WithMessage(localizer["PhoneNumberExists"]); +} +``` + +## 微软 Resx 编辑工具 + +必应搜索 `Resx Editor` 关键字可以找到很多工具,可以编辑 Resx 文件。 + +https://learn.microsoft.com/zh-cn/dotnet/framework/tools + +## 使用必应翻译资源文件 + +必应搜索 `Resx Translation` 关键字可以找到很多翻译工具,可以将英文资源文件翻译成其他语言。 + +https://github.com/salarcode/AutoResxTranslator + +https://github.com/stevencohn/ResxTranslator + +https://github.com/HakanL/resxtranslator + +https://apps.microsoft.com/detail/9mtpd7jzxnnn