ASP.NET Core搭建多层网站架构【13-扩展之支持全球化和本地化多语言】
2020/02/03, ASP.NET Core 3.1, VS2019, ResXManager
摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构【13-扩展之支持全球化和本地化多语言】
使用资源管理多语言文件实现网站本地化支持多语言显示
官方文档请点击:ASP.NET Core 全球化和本地化
本章节介绍了使用资源管理多语言文件实现网站本地化支持多语言显示
说明:本文的方法是采取所有的语言资源都在同一个地方,例如在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"

在MS.WebCore类库的SiteSetting.cs类中,对应添加网站默认语言的成员变量public string DefaultLanguage { get; set; }:

说明:
- 网站配置中添加了默认语言(DefaultLanguage)配置,默认为简体中文zh-Hans
- zh-Hans表示简体中文,未区分地区,如果要精确可以使用zh-Hans-CN表示中国大陆地区使用的简体中文,zh-Hans-SG则表示新加坡地区使用的简体中文,标准文档
- 本文还将建立其他两种语言:zh-Hant繁体中文、en英语(也都是未区分地区的)
添加语言资源文件
在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
});
}
}
}
说明:
- 指定了支持的语言列表:"zh-Hans","zh-Hant","en"
- SetCurrentUICulture方法用于修改当前UI线程语言,例如一开始默认设定了中文,然后想切换成英文,就使用该方法
- GetSpecificLanguageString方法是不切换UI线程语言的情况下,获取其他语言的翻译内容
- AddMultiLanguages是封装了多语言服务
- 其中AddLocalization是给服务添加本地化
- AddSingleton是注册获取语言资源提供器为单例,默认取的是SharedResource资源内容,所以后文在构造器中获取IStringLocalizer,用它取到的语言都是从SharedResource语言资源文件中读取到的
- UseMultiLanguage方法是注册多语言中间件,其中设定了支持的语言列表,根据网站配置设定了默认的语言
使用
注册服务
在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
});
说明:
- services.AddMultiLanguages();是添加多语言本地化支持
- services.AddControllers原本就有了,但要在后面追加AddDataAnnotationsLocalization方法,是给DataAnnotations本地化,依旧是注册SharedResource语言文件,这样一来,ViewModel中的注解也都能支持多语言了
在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,打错了,但是不影响结果)

说明:
- 只维护了LoginViewModel注解上的简中和繁中,未维护英文
- 资源文件resx中,左边是名称,即获取语言的代号,右边是值,获取对应的语言值,也就是意味着各个语言资源文件的名称都是相同的,仅仅是值不一样
- 默认不附带语言时,获取到的是网站默认语言 简体中文
- 使用culture来指定需要显示的语言
- 使用culture=zh-hant来指定显示 繁体中文,在维护了繁体中文时,则会显示出来
- 指定显示为英文时,由于英文资源未维护,所以culture=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,将简体中文那部分选择,复制出来,粘贴到谷歌翻译中:

选取好要翻译的目标语言后,将翻译后的内容复制回Excel:

最后在ResXManager插件中,把Excel文件导入回来:

可以看到需要翻译的资源都出来了:

说明:
- 这个插件也支持调用api自动翻译,但是需要api key,可以自行研究下
多语言都维护好后,启动项目,打开Postman调用登陆接口,可以看到英文被正确翻译了:

ASP.NET Core搭建多层网站架构【13-扩展之支持全球化和本地化多语言】的更多相关文章
- ASP.NET Core搭建多层网站架构【0-前言】
2020/01/26, ASP.NET Core 3.1, VS2019 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构 目录 0-前言 1-项目结构分层建立 2-公共基 ...
- ASP.NET Core搭建多层网站架构【2-公共基础库】
2020/01/28, ASP.NET Core 3.1, VS2019,Newtonsoft.Json 12.0.3, Microsoft.AspNetCore.Cryptography.KeyDe ...
- ASP.NET Core搭建多层网站架构【1-项目结构分层建立】
2020/01/26, ASP.NET Core 3.1, VS2019 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构[1-项目结构分层建立] 文章目录 此分支项目代码 ...
- ASP.NET Core搭建多层网站架构【3-xUnit单元测试之简单方法测试】
2020/01/28, ASP.NET Core 3.1, VS2019, xUnit 2.4.0 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构[3-xUnit单元测试 ...
- ASP.NET Core搭建多层网站架构【4-工作单元和仓储设计】
2020/01/28, ASP.NET Core 3.1, VS2019, Microsoft.EntityFrameworkCore.Relational 3.1.1 摘要:基于ASP.NET Co ...
- ASP.NET Core搭建多层网站架构【5-网站数据库实体设计及映射配置】
2020/01/29, ASP.NET Core 3.1, VS2019, EntityFrameworkCore 3.1.1, Microsoft.Extensions.Logging.Consol ...
- ASP.NET Core搭建多层网站架构【6-注册跨域、网站核心配置】
2020/01/29, ASP.NET Core 3.1, VS2019, NLog.Web.AspNetCore 4.9.0 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站 ...
- ASP.NET Core搭建多层网站架构【7-使用NLog日志记录器】
2020/01/29, ASP.NET Core 3.1, VS2019, NLog.Web.AspNetCore 4.9.0 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站 ...
- ASP.NET Core搭建多层网站架构【8.1-使用ViewModel注解验证】
2020/01/29, ASP.NET Core 3.1, VS2019 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构[8.1-使用ViewModel注解验证] 使用V ...
随机推荐
- mvc 模板位置
mvc4 模板位置 Common7\IDE\ItemTemplates\CSharp\Web\MVC 4\CodeTemplates mvc5 模板位置 Common7\IDE\Extensions\ ...
- 同一域名的ASP.NET网站实现Session共享
Session共享主要保证两点: 前台Asp_SessionId的Cookie作用域为顶级域名,且值相同 后端Session公用同一源 通过自定义HttpModule可以实现这两个需求 /// < ...
- yii2 场景使用
场景定义 //场景 const SCENARIO_USER_CREATE='0';//前台用户新增 const SCENARIO_USER_UPDATE='1';//前台用户更新 场景配置 publi ...
- spring(二):体系结构&核心模块
Spring框架 帮助管理对象及其依赖关系 提供如通用日志记录.性能统计.安全控制.异常处理等面向切面的能力 帮助管理数据库事务,提供了一套简单的JDBC访问实现,提供与第三方数据访问框架集成(如Hi ...
- UPF set_port_attribute
『set_port_attribute』, 在IEEE 1801-2015 中该命令定义如下,不是所有的工具都支持所有的option: 这个命令用于描述port 在『未知』区域的power 连接情况, ...
- curl模板----php发送post,get请求
function _grab($curl,$ip='',$referer='',$postInfo='',$cookie=''){ $ch = curl_init(); curl_setopt($ch ...
- Bugku-CTF加密篇之affine(y = 17x-8 flag{szzyfimhyzd})
affine y = 17x-8 flag{szzyfimhyzd} 答案格式:flag{*} 来源:第七届山东省大学生网络安全技能大赛
- KM算法模板 hdu 2255
KM算法是在匹配是完备的情况下寻找最优匹配. 首先,先将范围定为最大的情况,如果最大的情况无法满足,就下降一个维度继续匹配. 直到匹配成功. #include<cstdio> #inclu ...
- Java - Test - TestNG: Idea 引入 testng.xml 自动生成插件
1. 概述 Idea 引入自动生成 testng.xml 插件 自动生成 testng.xml 2. 背景 testng 调试 调试 testng, 主要是这两种方法 ide 下直接执行测试 方法 类 ...
- CF1272C
Recently, Norge found a string s=s1s2…sns=s1s2…sn consisting of nn lowercase Latin letters. As an ex ...