Asp.Net Core 混合全球化与本地化支持
前言
? ? ? ?最近的新型冠狀病毒流行讓很多人主動在家隔離,希望疫情能快點(diǎn)消退。武漢加油,中國必勝!
? ? ? ?Asp.Net Core 提供了內(nèi)置的網(wǎng)站國際化(全球化與本地化)支持,微軟還內(nèi)置了基于 resx 資源字符串的國際化服務(wù)組件??梢栽谌腴T教程中找到相關(guān)內(nèi)容。
? ? ? ?但是內(nèi)置實(shí)現(xiàn)方式有一個明顯缺陷,resx 資源是要靜態(tài)編譯到程序集中的,無法在網(wǎng)站運(yùn)行中臨時編輯,靈活性較差。幸好我找到了一個基于數(shù)據(jù)庫資源存儲的組件,這個組件完美解決了 resx 資源不靈活的缺陷,經(jīng)過適當(dāng)?shù)脑O(shè)置,可以在第一次查找資源時順便創(chuàng)建數(shù)據(jù)庫記錄,而我們要做的就是訪問一次相應(yīng)的網(wǎng)頁,讓組件創(chuàng)建好記錄,然后我們?nèi)ゾ庉嬒鄳?yīng)的翻譯字段并刷新緩存即可。
? ? ? ?但是!又是但是,經(jīng)過一段時間的使用,發(fā)現(xiàn)基于數(shù)據(jù)庫的方式依然存在缺陷,開發(fā)中難免有需要刪除并重建數(shù)據(jù)庫,初始化環(huán)境。這時,之前辛辛苦苦編輯的翻譯就會一起灰飛煙滅 (╯‵□′)╯︵┻━┻ 。而 resx 資源卻完美避開了這個問題,這時我就在想,能不能讓他們同時工作,兼顧靈活性與穩(wěn)定性,魚與熊掌兼得。
? ? ? ?經(jīng)過一番摸索,終于得以成功,在此開貼記錄分享。
正文
設(shè)置并啟用國際化服務(wù)組件
? ? ? ?安裝 Nuget 包 Localization.SqlLocalizer,這個包依賴 EF Core 進(jìn)行數(shù)據(jù)庫操作。然后在 Startup 的 ConfigureServices 方法中加入以下代碼注冊? EF Core 上下文:
1 services.AddDbContext<LocalizationModelContext>(options => 2 { 3 options.UseSqlServer(connectionString); 4 }, 5 ServiceLifetime.Singleton, 6 ServiceLifetime.Singleton);? ? ? ?注冊自制的混合國際化服務(wù):
services.AddMixedLocalization(opts => { opts.ResourcesPath = "Resources"; }, options => options.UseSettings(true, false, true, true));? ? ? ?注冊請求本地化配置:
1 services.Configure<RequestLocalizationOptions>(2 options =>3 {4 var cultures = Configuration.GetSection("Internationalization").GetSection("Cultures")5 .Get<List<string>>()6 .Select(x => new CultureInfo(x)).ToList();7 var supportedCultures = cultures;8 9 var defaultRequestCulture = cultures.FirstOrDefault() ?? new CultureInfo("zh-CN"); 10 options.DefaultRequestCulture = new RequestCulture(culture: defaultRequestCulture, uiCulture: defaultRequestCulture); 11 options.SupportedCultures = supportedCultures; 12 options.SupportedUICultures = supportedCultures; 13 });? ? ? ?注冊 MVC 本地化服務(wù):
復(fù)制代碼
1 services.AddMvc() 2 //注冊視圖本地化服務(wù) 3 .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, opts => { opts.ResourcesPath = "Resources"; }) 4 //注冊數(shù)據(jù)注解本地化服務(wù) 5 .AddDataAnnotationsLocalization();? ? ? ?在 appsettings.json 的根對象節(jié)點(diǎn)添加屬性:
復(fù)制代碼
"Internationalization": {"Cultures": ["zh-CN","en-US"] }? ? ? ?在某個控制器加入以下動作:
1 public IActionResult SetLanguage(string lang)2 {3 var returnUrl = HttpContext.RequestReferer() ?? "/Home";4 5 Response.Cookies.Append(6 CookieRequestCultureProvider.DefaultCookieName,7 CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(lang)),8 new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }9 ); 10 11 return Redirect(returnUrl); 12 }? ? ? ?準(zhǔn)備一個頁面調(diào)用這個動作切換語言。然后,大功告成!
? ? ? ?這個自制服務(wù)遵循以下規(guī)則:優(yōu)先查找基于 resx 資源的翻譯數(shù)據(jù),如果找到則直接使用,如果沒有找到,再去基于數(shù)據(jù)庫的資源中查找,如果找到則正常使用,如果沒有找到則按照對服務(wù)的配置決定是否在數(shù)據(jù)庫中生成記錄并使用。
自制混合國際化服務(wù)組件的實(shí)現(xiàn)
? ? ? ?本體:
? ? ? ?注冊輔助擴(kuò)展:
?
? ? ? 原理簡介
? ? ? ?服務(wù)組件利用了 DI 中可以為同一個服務(wù)類型注冊多個實(shí)現(xiàn)類型的特性,并在構(gòu)造方法中注入服務(wù)集合,便可以將注冊的所有實(shí)現(xiàn)注入組件同時使用。要注意主控服務(wù)和工作服務(wù)不能注冊為同一個服務(wù)類型,不然會導(dǎo)致循環(huán)依賴。內(nèi)置的國際化框架已經(jīng)指明了依賴 IStringLocalizerFatory ,必須將主控服務(wù)注冊為 IStringLocalizerFatory,工作服只能注冊為其他類型,不過依然要實(shí)現(xiàn) IStringLocalizerFatory,所以最方便的辦法就是定義一個新服務(wù)類型作為工作服務(wù)類型并繼承 IStringLocalizerFatory。
? ? ? ?想直接體驗(yàn)效果的可以到文章底部訪問我的 Github 下載項目并運(yùn)行。
結(jié)語
? ? ? ?這個組件是在計劃集成 IdentityServer4 管理面板時發(fā)現(xiàn)那個組件使用了 resx 的翻譯,而我的現(xiàn)存項目已經(jīng)使用了數(shù)據(jù)庫翻譯存儲,兩者又不相互兼容的情況下產(chǎn)生的想法。
? ? ? ?當(dāng)時 Localization.SqlLocalizer 舊版本(2.0.4)還存在無法在視圖本地化時正常創(chuàng)建數(shù)據(jù)庫記錄的問題,也是我調(diào)試修復(fù)了 bug 并向原作者提交了拉取請求,原作者也在合并了我的修復(fù)后發(fā)布了新版本。
? ? ? ?這次在集成 IdentityServer4 管理面板時又發(fā)現(xiàn)了 bug,正準(zhǔn)備聯(lián)系原作者看怎么處理。
總結(jié)
以上是生活随笔為你收集整理的Asp.Net Core 混合全球化与本地化支持的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gRPC in ASP.NET Core
- 下一篇: Asp.Net Core Identit