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 ...
随机推荐
- android 获取webview内容真实高度(webview上下可滚动距离)
正常获取: mainWebView.getContentHeight()//获取html高度 mainWebView.getScale()//手机上网页缩放比例 mainWebView.getHeig ...
- Linux find命令:在目录中查找文件(超详解)
find 是 Linux 中强大的搜索命令,不仅可以按照文件名搜索文件,还可以按照权限.大小.时间.inode 号等来搜索文件.但是 find 命令是直接在硬盘中进行搜索的,如果指定的搜索范围过大,f ...
- 解决win10状态栏的搜索框无法搜索本地应用或无反应
今天突然出现的问题,在状态栏左下角的搜索框搜索OneNote没有任何反应. 1.首先,打开管理员命令窗口,win+x,可以看到弹出一个窗口,打开windows Powershell(管理员)如图 2, ...
- python多进程编程学习笔记
摘自[https://www.cnblogs.com/chenhuabin/p/10070996.html] by 奥辰 赞
- vs 安装svn插件
在很多互联网开发的团队里面,用到的代码管理器都是SVN,svn目前有客户端和集成到VS里面两种(不清楚分类是否正确).客户端的在这里我就不写了,我目前用到比较多的都是集成到VS里面的,而且目前用着还是 ...
- FreeRTOS学习笔记2:列表
list.h 列表结构List_t 列表:主要看三个 xLIST:1.5检查列表完整性的.但是需要开启.默认不开启 2:记录列表中列表项的 数量:3:记录当前列表项 索引号:4:列表中的最后一个 列表 ...
- Java-POJ1002-487-3279(含c++代码)
Java 的读入还不熟练,解决不了空行的问题,还是只能用c++ A掉,唉~ 之后要把这个坑补掉 解决了,开心(*^▽^*)以下是AC的Java代码 以下是C++代码 #include<cstdi ...
- 图片上传 一张展示,base64图片获取
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 【 Struts2 过滤器】
LoginInterceptor package k.util; import com.opensymphony.xwork2.ActionInvocation; import com.opensym ...
- FromBase64String 输入的不是有效的 Base-64 字符串,因为它包含非 Base-64 字符、两个以上的填充字符,或者填充字符间包含非法字符
js前台: <input id="upload_img_input" v-on:change="onFileChange" type="file ...