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调用登录接口,能够看到英文被正确翻译了: