From d91954e3318ee98e840d49e1f03316cb6ceb6527 Mon Sep 17 00:00:00 2001 From: Seany <17074267@qq.com> Date: Sun, 7 Dec 2025 12:41:04 +0800 Subject: [PATCH] fix culture --- Atomx.Admin/Atomx.Admin.Client/Program.cs | 2 +- .../Services/JsonStringLocalizer.cs | 2 +- .../Services/JsonStringLocalizerFactory.cs | 3 +- .../Services/LanguageProvider.cs | 20 ++---- Atomx.Admin/Atomx.Admin/Atomx.Admin.csproj | 4 +- Atomx.Admin/Atomx.Admin/Components/App.razor | 4 +- .../Middlewares/RequestCultureMiddleware.cs | 72 +++++++++++++++++++ Atomx.Admin/Atomx.Admin/Program.cs | 6 +- .../localization}/en-US.json | 0 .../localization}/zh-Hans.json | 0 10 files changed, 89 insertions(+), 24 deletions(-) create mode 100644 Atomx.Admin/Atomx.Admin/Middlewares/RequestCultureMiddleware.cs rename Atomx.Admin/Atomx.Admin/{Localization => wwwroot/localization}/en-US.json (100%) rename Atomx.Admin/Atomx.Admin/{Localization => wwwroot/localization}/zh-Hans.json (100%) diff --git a/Atomx.Admin/Atomx.Admin.Client/Program.cs b/Atomx.Admin/Atomx.Admin.Client/Program.cs index 953f923..3bf8f73 100644 --- a/Atomx.Admin/Atomx.Admin.Client/Program.cs +++ b/Atomx.Admin/Atomx.Admin.Client/Program.cs @@ -27,7 +27,7 @@ builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder. // 注册本地化工厂(使用相对于站点根的目录名 "Localization" 并注入用于加载静态文件的 HttpClient) builder.Services.AddScoped(sp => - new JsonStringLocalizerFactory("Localization", sp.GetRequiredService())); + new JsonStringLocalizerFactory("localization", sp.GetRequiredService())); // 注册IStringLocalizer builder.Services.AddTransient(typeof(IStringLocalizer<>), typeof(StringLocalizer<>)); diff --git a/Atomx.Admin/Atomx.Admin.Client/Services/JsonStringLocalizer.cs b/Atomx.Admin/Atomx.Admin.Client/Services/JsonStringLocalizer.cs index 7abb333..c1ea153 100644 --- a/Atomx.Admin/Atomx.Admin.Client/Services/JsonStringLocalizer.cs +++ b/Atomx.Admin/Atomx.Admin.Client/Services/JsonStringLocalizer.cs @@ -16,7 +16,7 @@ namespace Atomx.Admin.Client.Services // 鍦 Blazor WebAssembly 鍦烘櫙涓嬶紝浼氫娇鐢ㄦ敞鍏ョ殑 HttpClient 浠 wwwroot/Localization/{culture}.json 鑾峰彇璧勬簮 public JsonStringLocalizer(string resourcesPath, HttpClient? httpClient = null) { - _resourcesPath = (resourcesPath ?? "Localization").Trim('/'); // 瑙勮寖鍖 + _resourcesPath = (resourcesPath ?? "localization").Trim('/'); // 瑙勮寖鍖 _httpClient = httpClient; } diff --git a/Atomx.Admin/Atomx.Admin.Client/Services/JsonStringLocalizerFactory.cs b/Atomx.Admin/Atomx.Admin.Client/Services/JsonStringLocalizerFactory.cs index 3fa9ee3..4f44a41 100644 --- a/Atomx.Admin/Atomx.Admin.Client/Services/JsonStringLocalizerFactory.cs +++ b/Atomx.Admin/Atomx.Admin.Client/Services/JsonStringLocalizerFactory.cs @@ -1,5 +1,4 @@ 锘縰sing Microsoft.Extensions.Localization; -using System.Net.Http; namespace Atomx.Admin.Client.Services { @@ -10,7 +9,7 @@ namespace Atomx.Admin.Client.Services public JsonStringLocalizerFactory(string resourcesPath, HttpClient httpClient) { - _resourcesPath = (resourcesPath ?? "Localization").Trim('/'); + _resourcesPath = (resourcesPath ?? "localization").Trim('/'); _httpClient = httpClient; } diff --git a/Atomx.Admin/Atomx.Admin.Client/Services/LanguageProvider.cs b/Atomx.Admin/Atomx.Admin.Client/Services/LanguageProvider.cs index 54c0339..f079134 100644 --- a/Atomx.Admin/Atomx.Admin.Client/Services/LanguageProvider.cs +++ b/Atomx.Admin/Atomx.Admin.Client/Services/LanguageProvider.cs @@ -1,6 +1,5 @@ 锘縰sing Microsoft.AspNetCore.Components; using Microsoft.JSInterop; -using System.Globalization; namespace Atomx.Admin.Client.Services { @@ -29,19 +28,6 @@ namespace Atomx.Admin.Client.Services if (_currentLanguage != value) { _currentLanguage = value; - - // 璁剧疆鍏ㄥ眬绾跨▼鏂囧寲锛岀‘淇 IStringLocalizer 绛夊湪闅忓悗鐨勬覆鏌撲腑璇诲彇鍒版柊鏂囧寲 - try - { - var ci = new CultureInfo(value); - CultureInfo.DefaultThreadCurrentCulture = ci; - CultureInfo.DefaultThreadCurrentUICulture = ci; - } - catch - { - // 蹇界暐鏃犳晥 culture 瀛楃涓 - } - OnLanguageChanged?.Invoke(); } } @@ -59,8 +45,10 @@ namespace Atomx.Admin.Client.Services public async Task InitializeAsync() { // 灏濊瘯浠庢湰鍦板瓨鍌ㄨ幏鍙栦繚瀛樼殑璇█ + Console.WriteLine("灏濊瘯浠庢湰鍦板瓨鍌ㄨ幏鍙栦繚瀛樼殑璇█ Initializing LanguageProvider..."); try { + var savedLanguage = await _jsRuntime.InvokeAsync("localStorage.getItem", "preferred-language"); if (!string.IsNullOrEmpty(savedLanguage) && SupportedLanguages.Contains(savedLanguage)) { @@ -85,6 +73,7 @@ namespace Atomx.Admin.Client.Services /// public async Task ChangeLanguageAsync(string languageCode) { + Console.WriteLine("鍒囨崲璇█ ChangeLanguageAsync to " + languageCode); if (SupportedLanguages.Contains(languageCode) && CurrentLanguage != languageCode) { CurrentLanguage = languageCode; @@ -99,7 +88,8 @@ namespace Atomx.Admin.Client.Services // 蹇界暐閿欒 } - // setter 宸茶Е鍙 OnLanguageChanged + // 閫氱煡璇█宸叉洿鏀 + OnLanguageChanged?.Invoke(); } } diff --git a/Atomx.Admin/Atomx.Admin/Atomx.Admin.csproj b/Atomx.Admin/Atomx.Admin/Atomx.Admin.csproj index f0a8f68..208dc39 100644 --- a/Atomx.Admin/Atomx.Admin/Atomx.Admin.csproj +++ b/Atomx.Admin/Atomx.Admin/Atomx.Admin.csproj @@ -35,10 +35,10 @@ - + Always - + Always diff --git a/Atomx.Admin/Atomx.Admin/Components/App.razor b/Atomx.Admin/Atomx.Admin/Components/App.razor index e7646c1..04d229a 100644 --- a/Atomx.Admin/Atomx.Admin/Components/App.razor +++ b/Atomx.Admin/Atomx.Admin/Components/App.razor @@ -13,11 +13,11 @@ - + - + diff --git a/Atomx.Admin/Atomx.Admin/Middlewares/RequestCultureMiddleware.cs b/Atomx.Admin/Atomx.Admin/Middlewares/RequestCultureMiddleware.cs new file mode 100644 index 0000000..ae2cc9c --- /dev/null +++ b/Atomx.Admin/Atomx.Admin/Middlewares/RequestCultureMiddleware.cs @@ -0,0 +1,72 @@ +锘縰sing System.Globalization; +using Microsoft.AspNetCore.Http; + +namespace Atomx.Admin.Middlewares +{ + /// + /// 浠庤姹傝矾寰勭殑棣栨鎻愬彇鐭瑷鐮侊紙濡 /zh/ 鎴 /en/锛夛紝 + /// 灏嗗叾鏄犲皠涓 Culture锛堝 zh-Hans / en-US锛夛紝璁剧疆绾跨▼ Culture锛 + /// 骞跺湪缁х画绠$嚎鍓嶆妸璇ユ浠 Request.Path 涓Щ闄や互渚胯矾鐢卞尮閰嶃 + /// + public class RequestCultureMiddleware + { + private readonly RequestDelegate _next; + + // 鍙墿灞曠煭鐮佷笌瀹屾暣 culture 鐨勬槧灏 + private static readonly Dictionary ShortToCulture = new(StringComparer.OrdinalIgnoreCase) + { + { "zh", "zh-Hans" }, + { "en", "en-US" } + }; + + public RequestCultureMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context) + { + var path = context.Request.Path.Value ?? "/"; + var trimmed = path.Trim('/'); + if (!string.IsNullOrEmpty(trimmed)) + { + var segments = trimmed.Split('/', StringSplitOptions.RemoveEmptyEntries); + var first = segments.FirstOrDefault(); + if (!string.IsNullOrEmpty(first) && ShortToCulture.TryGetValue(first, out var cultureName)) + { + // 璁剧疆绾跨▼ Culture锛堝奖鍝嶅悗缁鐞嗕腑 IStringLocalizer / 鏃ユ湡鏍煎紡绛夛級 + try + { + var ci = new CultureInfo(cultureName); + CultureInfo.DefaultThreadCurrentCulture = ci; + CultureInfo.DefaultThreadCurrentUICulture = ci; + } + catch + { + // 蹇界暐闈炴硶 culture + } + + // 灏嗚姹傝矾寰勫幓鎺夐娈佃瑷鍓嶇紑锛屼究浜庡悗缁矾鐢卞尮閰嶏紙渚嬪 /zh/account -> /account锛 + var remaining = segments.Length > 1 ? "/" + string.Join('/', segments.Skip(1)) : "/"; + + var originalPath = context.Request.Path; + context.Request.Path = new PathString(remaining); + + try + { + await _next(context); + } + finally + { + // 鎭㈠鍘熷 path锛堜互闃插叾浠栦腑闂翠欢渚濊禆锛 + context.Request.Path = originalPath; + } + + return; + } + } + + await _next(context); + } + } +} diff --git a/Atomx.Admin/Atomx.Admin/Program.cs b/Atomx.Admin/Atomx.Admin/Program.cs index 7fb184b..4fb1109 100644 --- a/Atomx.Admin/Atomx.Admin/Program.cs +++ b/Atomx.Admin/Atomx.Admin/Program.cs @@ -64,7 +64,7 @@ builder.Services.AddHttpClientApiService(builder.Configuration["WebApi:ServerUrl builder.Services.AddSingleton(sp => { var env = sp.GetRequiredService(); - var resourcesPath = Path.Combine(env.ContentRootPath, "Localization"); + var resourcesPath = Path.Combine(env.WebRootPath, "localization"); // 尝试从 DI 获取 IHttpClientFactory(若存在则优先使用),否则回退到 new HttpClient() var httpFactory = sp.GetService(); HttpClient httpClient; @@ -194,6 +194,10 @@ app.UseResponseCompression(); // 使用命名的 CORS 策略 app.UseCors("DefaultCors"); +//// 注册 RequestCultureMiddleware,使外部带短码的请求先设置 Culture 并剥离前缀 +//app.UseMiddleware(); + + app.UseStaticFiles(new StaticFileOptions { OnPrepareResponse = ctx => diff --git a/Atomx.Admin/Atomx.Admin/Localization/en-US.json b/Atomx.Admin/Atomx.Admin/wwwroot/localization/en-US.json similarity index 100% rename from Atomx.Admin/Atomx.Admin/Localization/en-US.json rename to Atomx.Admin/Atomx.Admin/wwwroot/localization/en-US.json diff --git a/Atomx.Admin/Atomx.Admin/Localization/zh-Hans.json b/Atomx.Admin/Atomx.Admin/wwwroot/localization/zh-Hans.json similarity index 100% rename from Atomx.Admin/Atomx.Admin/Localization/zh-Hans.json rename to Atomx.Admin/Atomx.Admin/wwwroot/localization/zh-Hans.json