2020/02/03, ASP.NET Core 3.1, VS2019, ResXManagerhtml
摘要:基於ASP.NET Core 3.1 WebApi搭建後端多層網站架構【13-擴展之支持全球化和本地化多語言】
使用資源管理多語言文件實現網站本地化支持多語言顯示git
文章目錄github
此分支項目代碼web
官方文檔請點擊:ASP.NET Core 全球化和本地化數據庫
本章節介紹了使用資源管理多語言文件實現網站本地化支持多語言顯示json
說明:本文的方法是採起全部的語言資源都在同一個地方,例如在MS.WebCore路徑下有SharedResource.zh-Hans.resx
、SharedResource.zh-Hant.resx
兩個語言資源文件,裏面包含了整個網站全部的翻譯,無論是Controller中的翻譯仍是業務Service的翻譯都從SharedResource
中讀取。
而官方的作法是:HomeController的語言資源文件在Resources/Controllers.HomeController.fr.resx
或Resources/Controllers/HomeController.fr.resx
中,HomeService的語言資源文件則可能在Resources/Services.HomeService.fr.resx
或Resources/Services/HomeService.fr.resx
中,各個語言資源文件分散在各處,沒有統一管理,官方語言資源文件命名的規則請查閱文檔後端
在MS.WebApi
應用程序的appsettings.json
的SiteSetting
節點中,添加DefaultLanguage
子節點:"DefaultLanguage": "zh-Hans"
api
在MS.WebCore
類庫的SiteSetting.cs
類中,對應添加網站默認語言的成員變量public string DefaultLanguage { get; set; }
:
架構
說明:mvc
在MS.WebCore
類庫中新建MultiLanguages文件夾,在該文件夾下新建SharedResource.cs
類:
namespace MS.WebCore.MultiLanguages { public class SharedResource { } }
注意是public類型
在MultiLanguages文件夾下新建資源文件SharedResource.zh-Hans.resx
:
若是新建項中找不到資源文件的選項,把MS.WebCore
類庫中的Project Sdk從Microsoft.NET.Sdk
改成Microsoft.NET.Sdk.Web
再試試(記得添加完資源文件再把sdk改回來)
一樣的方式再添加SharedResource.en.resx
、SharedResource.zh-Hant.resx
新建完成後,以下圖所示:
後面的操做中,會主要編輯簡體中文的翻譯內容,而後使用工具同步翻譯繁體中文、英語的翻譯內容
在MultiLanguages文件夾下新建MultiLangExtensions.cs
類:
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using System.Collections.Generic; using System.Globalization; namespace MS.WebCore.MultiLanguages { public static class MultiLangExtensions { /// <summary> /// 支持的語言類型 /// 此處內容要與真實的文件對應 /// </summary> public static readonly List<string> supportLangs = new List<string> { "zh-Hans", "zh-Hant", "en" }; /// <summary> /// 更改當前UI線程語言 /// </summary> /// <param name="name"></param> public static void SetCurrentUICulture(string name) { CultureInfo.CurrentUICulture = new CultureInfo(name, false); } /// <summary> /// 獲取指定語言的文字內容 /// </summary> /// <param name="localizer"></param> /// <param name="specificLang"></param> /// <param name="key"></param> /// <returns></returns> public static string GetSpecificLanguageString(this IStringLocalizer localizer, string specificLang, string key) { #pragma warning disable CS0618 // 類型或成員已過期 return localizer.WithCulture(new CultureInfo(specificLang))[key].ToString(); #pragma warning restore CS0618 // 類型或成員已過期 } /// <summary> /// 添加多語言本地化支持 /// </summary> /// <param name="services"></param> /// <returns></returns> public static IServiceCollection AddMultiLanguages(this IServiceCollection services) { services.AddLocalization(); services.AddSingleton<IStringLocalizer>((sp) => { var sharedLocalizer = sp.GetRequiredService<IStringLocalizer<SharedResource>>(); return sharedLocalizer; }); /* //須要在startup中,AddControllers(webapi)的後面或者AddMVC(webmvc)註冊 .AddDataAnnotationsLocalization(options => { options.DataAnnotationLocalizerProvider = (type, factory) => factory.Create(typeof(SharedResource));//給註解添加本地化資源提供器Localizerprovider }) */ return services; } /// <summary> /// 使用多語言本地化中間件 /// 默認語言在appsetting中設置 /// </summary> /// <param name="app"></param> /// <param name="configuration"></param> /// <returns></returns> public static IApplicationBuilder UseMultiLanguage(this IApplicationBuilder app, IConfiguration configuration) { List<CultureInfo> supportedCultures = new List<CultureInfo>(); foreach (var item in supportLangs) { supportedCultures.Add(new CultureInfo(item)); } return app.UseRequestLocalization(new RequestLocalizationOptions { DefaultRequestCulture = new RequestCulture(configuration.GetSection("SiteSetting:DefaultLanguage").Value), // Formatting numbers, dates, etc. SupportedCultures = supportedCultures, // UI strings that we have localized. SupportedUICultures = supportedCultures }); } } }
說明:
在MS.WebApi
應用程序的Startup.cs
類ConfigureServices中:
//添加多語言本地化支持 services.AddMultiLanguages(); services .AddControllers(options => { options.Filters.Add<ApiResultFilter>(); options.Filters.Add<ApiExceptionFilter>(); }) .AddDataAnnotationsLocalization(options => { options.DataAnnotationLocalizerProvider = (type, factory) => factory.Create(typeof(SharedResource));//給註解添加本地化資源提供器Localizerprovider });
說明:
在MS.WebApi
應用程序的Startup.cs
類Configure中添加app.UseMultiLanguage(Configuration);
:
以LoginViewModel中的翻譯爲例,我暫時添加了LoginViewModel字段註解上的簡體中文、繁體中文翻譯(英語未添加)
啓動項目,打開Postman,選取登錄接口進行測試: localhost:5000/account 、
接着修改接口地址爲: localhost:5000/account?culture=zh-hant 、
再繼續測試未添加的語言:localhost:5000/account?culture=en(圖中是us,打錯了,可是不影響結果)
說明:
至此,ViewModel數據註解已經實現了本地化
繼續維護LoginViewModel中剩下的翻譯到簡體中文、繁體中文資源文件中:
使用IStringLocalizer來獲取語言資源
LoginValidate方法中添加參數IStringLocalizer localizer
,而後使用GetString方法獲取對應的語言:
public async Task<ExecuteResult<UserData>> LoginValidate(IUnitOfWork<MSDbContext> unitOfWork, IMapper mapper, SiteSetting siteSetting, IStringLocalizer localizer) { ExecuteResult<UserData> result = new ExecuteResult<UserData>(); //將登陸用戶查出來 var loginUserInDB = await unitOfWork.GetRepository<UserLogin>().FindAsync(Account); //用戶不存在 if (loginUserInDB is null) { return result.SetFailMessage(localizer.GetString("DataAnnotations_ErrorMessage_NotExist", localizer.GetString("LoginViewModel_DisplayName_Account"))); } //用戶被鎖定 if (loginUserInDB.IsLocked && loginUserInDB.LockedTime.HasValue && (DateTime.Now - loginUserInDB.LockedTime.Value).Minutes < siteSetting.LoginLockedTimeout) { return result.SetFailMessage(localizer.GetString("LoginViewModel_ServiceError_UserLocked", siteSetting.LoginLockedTimeout)); } //密碼正確 if (Crypto.VerifyHashedPassword(loginUserInDB.HashedPassword, Password)) { //密碼正確後才加載用戶信息、角色信息 var userInDB = await unitOfWork.GetRepository<User>().GetFirstOrDefaultAsync( predicate: a => a.Id == loginUserInDB.UserId, include: source => source .Include(u => u.Role)); //若是用戶已失效 if (userInDB.StatusCode != StatusCode.Enable) { return result.SetFailMessage(localizer.GetString("LoginViewModel_ServiceError_UserDisabled")); } //用戶正常、密碼正確,更新相應字段 loginUserInDB.IsLocked = false; loginUserInDB.AccessFailedCount = 0; loginUserInDB.LastLoginTime = DateTime.Now; //提交到數據庫 await unitOfWork.SaveChangesAsync(); //獲得userdata UserData userData = mapper.Map<UserData>(userInDB); return result.SetData(userData); } //密碼錯誤 else { loginUserInDB.AccessFailedCount++;//失敗次數累加 result.SetFailMessage(localizer.GetString("LoginViewModel_ServiceError_LoginError")); //超出失敗次數限制 if (loginUserInDB.AccessFailedCount >= siteSetting.LoginFailedCountLimits) { loginUserInDB.IsLocked = true; loginUserInDB.LockedTime = DateTime.Now; result.SetFailMessage(localizer.GetString("LoginViewModel_ServiceError_UserLocked", siteSetting.LoginLockedTimeout)); } //提交到數據庫 await unitOfWork.SaveChangesAsync(); return result; } }
IStringLocalizer
的使用方法如上所示。
LoginValidate方法是在AccountService中被調用的,因此AccountService中經過構造函數依賴注入的方式獲得IStringLocalizer localizer
:
啓動項目,打開Postman調用登錄接口,能夠看到登錄驗證的返回內容已經取得多語言:
這裏我推薦一個叫ResXManager
的VS插件,用它管理多語言資源:
對着resx文件右擊使用ResXManager打開(打開的前提是文件內至少要有一條語言記錄):
打開後以下圖,多種語言都在一個頁面中顯示出來,選擇某一行能夠直接編輯:
選擇將全部資源導出,保存在本地
打開Excel,將簡體中文那部分選擇,複製出來,粘貼到谷歌翻譯中:
選取好要翻譯的目標語言後,將翻譯後的內容複製回Excel:
最後在ResXManager
插件中,把Excel文件導入回來:
能夠看到須要翻譯的資源都出來了:
說明:
多語言都維護好後,啓動項目,打開Postman調用登錄接口,能夠看到英文被正確翻譯了: