asp.net core 实现支持多语言

asp.net core 实现支持多语言

Intro

最近有一个外国友人通过邮件联系我,想用我的活动室预约,但是还没支持多语言,基本上都是写死的中文,所以最近想支持一下更多语言,于是有了多语言方面的一些实践

国际化/本地化介绍

国际化(Globalization)和本地化(Localization)是要实现的多语言支持的基础

Globalization is the process of designing and developing applications that function for multiple cultures.

Localization is the process of customizing your application for a given culture and locale.

国际化是要支持处理多种文化,而本地化是要根据某一个文化和区域的来展示相应的处理。

更多关于国际化与本地化的不同可以参考 Stack Overflow 上的讨论 https://stackoverflow.com/questions/2074869/globalization-vs-localization

Localization In Asp.NET Core

微软官方的 Localization 的实现是基于资源文件实现的 ( *.resx),我们也可以扩展支持更多方式,如 JSON/数据库 都是可以的,社区已经有实现的示例,只要是可以提供一个文本源的都是可以的,我们先使用默认的基于资源文件的,下一篇再讲一个自定义实现一个 Localization Provider。

.NET Core Localization 的 核心是 IStringLocalizer,asp.net core 里扩展定义了 IViewLocalizerIHtmlLocalizerIViewLocalizerIHtmlLocalizer 主要是为了处理包含 html 的资源,他们不会对资源进行 html encode,相当于 @Html.Raw 的效果,而 IStringLocalizer 则会被 html encode,除此之外 IViewLocalizer 还会根据当前视图的路径寻找资源文件

来看一个示例:

Razor 页面

浏览器效果:

查看网页源代码:

实际案例

服务注册

注册 Localization 相关服务:

var supportedCultures = new[]
{new CultureInfo("zh"),new CultureInfo("en"),
};
services.Configure<RequestLocalizationOptions>(options =>
{options.DefaultRequestCulture = new RequestCulture("zh");// Formatting numbers, dates, etc.options.SupportedCultures = supportedCultures;// UI strings that we have localized.options.SupportedUICultures = supportedCultures;
});
services.AddLocalization(options => options.ResourcesPath = Configuration.GetAppSetting("ResourcesPath"));

配置视图 Localization(根据需要如果是 WebAPI 就不需要了)

services.AddControllersWithViews().AddNewtonsoftJson(options =>{options.SerializerSettings.ContractResolver = new DefaultContractResolver();options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; // 设置时区为 UTCoptions.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;}).AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix,opts => { opts.ResourcesPath = Configuration.GetAppSetting("ResourcesPath"); }).AddDataAnnotationsLocalization().SetCompatibilityVersion(CompatibilityVersion.Latest);

中间件配置:

app.UseRequestLocalization();

逻辑代码中使用示例:

IStringLocalizerIHtmlLocalizer / IViewLocalizer 都可以直接从依赖注入服务中获取, IStringLocalizerIHtmlLocalizer 推荐使用强类型的方式,也就是下面示例的使用方式,使用方式和 ILogger 类似

public async Task<ActionResult> MakeReservation([FromBody]ReservationViewModel model,[FromHeader]string captcha,[FromHeader]string captchaType,[FromServices]IStringLocalizer<HomeController> localizer)
{var result = new ResultModel<bool>();var isCodeValid = await HttpContext.RequestServices.GetService<CaptchaVerifyHelper>().ValidateVerifyCodeAsync(captchaType, captcha);if (!isCodeValid){result.Status = ResultStatus.RequestError;result.ErrorMsg = localizer["InvalidCaptchaInfo"];return Json(result);}

在视图中使用示例:

localizer["data"] 返回的是一个 LocalizedString,实现了隐式转换为 string, 有的时候可能需要强制转一下string, 或者使用 Value 属性

@inject IViewLocalizer viewLocalizer
viewLocalizer["About"]
@Html.ActionLink((string)viewLocalizer["About"], "About", "Home")
@Html.ActionLink(viewLocalizer["About"].Value, "About", "Home")

资源文件配置:

资源文件的配置和文件的结构类似,下面是一个示例

准备的来说是和类型的 FullName 有关系,一般的项目名称就是程序集名称,就是根命名空间,文件名称就是类型名称,所以一般情况下资源文件的位置和类型的位置是一致的,但是如果文件和类型名称不符合,那就要按照类型的 FullName 来找,视图也是类似的,如果根命名空间不是程序集名称,也是可以配置的具体的参考文档

Controllers.HomeController => Controllers/HomeController.zh.resx/ Controllers/HomeController.en.resx

Resource nameDot or path naming
Resources/Controllers.HomeController.fr.resxDot
Resources/Controllers/HomeController.fr.resxPath


  • Resources/Views/Home/About.fr.resx


  • Resources/Views.Home.About.fr.resx




实际项目中的资源文件示例:

实际访问效果:https://reservation.weihanli.xyz/

默认的中文界面:

英文界面:

只是做了几个前台的示例,还有很多地方没改

Docker 部署

现在的项目是基于 docker + k8s 部署的,所以支持 docker 部署很重要

要支持多语言,需要安装 ICU 相关的包,(这个可不是 996.icu 的 icu 哈,如果不装真的有可能导致 996.icu)

RUN apk add --no-cache icu-libs # 安装 icu-libs
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT false # 配置 Globalization

完整的 dockerfile 可以参考:https://github.com/dotnet/dotnet-docker/blob/cb7a9c35dacf6d34fcf7bab7995e60faef55f61f/samples/dotnetapp/Dockerfile.alpine-x64-globalization

More

.net core 的设计真的是很灵活,很优美,基于资源文件的本地化,感觉不太方便,使用资源文件的化可能就只能使用 VS 编辑了,虽然也是纯文本的,基于 xml 但是编辑起来不如界面看着编辑舒服,如果使用 json 之类的,就比较简单明了,编辑起来也比较方便,所以想把资源文件替换成 JSON 文件

下次分享一篇基于 JSON 的 Localization Provider 的实现

Reference

  • https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-3.1

  • https://github.com/dotnet/extensions/tree/master/src/Localization

  • https://github.com/dotnet/aspnetcore/tree/master/src/Middleware/Localization

  • https://stackoverflow.com/questions/2074869/globalization-vs-localization

  • https://github.com/WeihanLi/ActivityReservation

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/312340.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++成员变量和成员函数分开存储

在C中&#xff0c;类内的成员变量和成员函数分开存储 只有非静态成员变量才属于类的对象上 首先先看一下空对象占用多少空间&#xff1a; #include <iostream> using namespace std; //成员变量和成员函数是分开存储的class Person {};void test01() {Person p;cout &l…

dotnet 将C#编译为wasm让前端html使用

其实 dotnet 是全栈的首选&#xff0c;原因是因为可以开发的方向太多&#xff0c;比如大本营PC端&#xff0c;以及后台。还有移动端&#xff0c;包括 IOS 和安卓端。现在还能用来写前端&#xff0c;本文就来告诉大家如何在前端使用现有的C#代码&#xff0c;通过 WebAssembly 使…

python目标跟踪精度曲线图_Python+opencv3.4+Kalman滤波在视频中跟踪绘制运动目标,Pythonopencv34kalman,卡尔曼滤波,实现,物体,追踪,和,轨迹...

实验环境&#xff1a;Python3.6OpenCV3.4pycharm2019代码实现&#xff1a;首先是一个简单的不用kalman滤波的运动目标追踪代码这里可以根据需要进行摄像头运动目标识别&#xff0c;只要把camera cv2.VideoCapture(./video/yellow_ball.mp4)# 改成camera cv2.VideoCapture(0) …

C++this指针的用途

this指针的用途&#xff1a; 1.当形参和成员变量同名时&#xff0c;可用this指针来区分。 2.在类的非静态成员函数中返回对象本身&#xff0c;可使用return *this 每一个非静态成员函数只会诞生一份函数实例&#xff0c;也就是说多个同类型的对象会共用一块代码&#xff0c;那…

【实战 Ids4】小技巧篇:自定义登录页操作

今天的内容很简单&#xff0c;1分钟就能看完&#xff0c;5分钟就能学会&#xff0c;但是却是在我们平时开发中必须要学会的一个小知识点&#xff0c;我就不让大家走弯路了&#xff0c;直接看操作。在平时的IdentityServer4开发中呢&#xff0c;我们都是根据官方的Demo来操作一遍…

mysql_result函数用不了_mysql_result()函数怎么在PHP中使用

mysql_result()函数怎么在PHP中使用发布时间&#xff1a;2020-12-21 16:30:03来源&#xff1a;亿速云阅读&#xff1a;69作者&#xff1a;Leah这篇文章给大家介绍mysql_result()函数怎么在PHP中使用&#xff0c;内容非常详细&#xff0c;感兴趣的小伙伴们可以参考借鉴&#xff…

Asp.Net Core下的开源任务调度平台ScheduleMaster

从何说起2017年初的时候&#xff0c;由于当时项目需要做了一个乞丐版定时调度系统&#xff0c;那时候只在单机上实现了核心的调度功能。做这个玩意之前也调研了社区中开源的解决方案&#xff0c;找了几个实地部署试跑了一下&#xff0c;其实都很不错。但那时候我们有个问题就是…

python如何加密字符串_Python实现对字符串的加密解密方法示例

本文实例讲述了Python实现对字符串的加密解密方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;需求是是要将密码存在数据库里&#xff0c;所以要加密解密是可逆的&#xff0c;在数据库里不要有特殊字符&#xff0c;防止数据库备份和恢复中出错。安装PyCrypto&#…

C++友元

友元的目的就是让一个函数或者类访问另一个类中私有成员 友元的关键字&#xff1a;friend 友元的三种实现&#xff1a; 1.全局函数做友元 2.类做友元 3.成员函数做友元 一.全局函数做友元 #include <iostream> using namespace std; #include <cstring>class Bu…

C#录制视频

这是一个使用C#语言制作的录制框架&#xff0c;支持录制桌面&#xff0c;多屏&#xff0c;声音&#xff0c;摄像头&#xff0c;某个应用程序的界面1.安装使用此框架需要安装扩展包Kogel.Record,可以Nuget上搜索或者使用Nuget命令Install-Package Kogel.Record安装完成包后会出现…

C++加号运算符重载

运算符重载概念&#xff1a; 对已有的运算符重新定义&#xff0c;赋予其另一种功能&#xff0c;以适应不同的数据类型 加号运算符重载&#xff1a; 1.成员函数重载加号&#xff1a; #include <iostream> using namespace std;//加号运算符重载//1.成员函数重载号class …

python编程小案例_用Python3编程写第一个小案例!-Go语言中文社区

用Python3编程第一步&#xff01;今天博主跟大家聊一聊如何使用Python3编程第一步&#xff01;&#xff01;不喜勿喷&#xff0c;如有建议欢迎补充、讨论&#xff01;Come on&#xff01;在前面的几篇文章中我们已经学习了一些Python3 的基本语法知识&#xff0c;我们尝试来写一…

UnitTest in .NET(Part 5)

Photo &#xff1a;UnitTesting文 | Edison Zhou上一篇我们学习了单元测试的核心技术&#xff1a;存根、模拟对象和隔离框架&#xff0c;它们是我们进行高质量单元测试的技术基础。本篇会集中在管理和组织单元测试的技术&#xff0c;以及如何确保在真实项目中进行高质量的单元测…

C++左移运算符重载

作用&#xff1a;可以输出自定义数据类型 代码如下&#xff1a; #include <iostream> using namespace std; //左移运算符重载class Person {public:int m_A;int m_B;};ostream &operator<<(ostream &cout, Person &p) { //本质 operator<<(cou…

java 大小写_java中如何进行大小写字母转换?

展开全部1.创建工程&#xff0c;或使用已有工程&#xff0c;在工程下创建包&#xff0c;包内新建一个类&#xff0c;我e69da5e887aa3231313335323631343130323136353331333365653262命名为Cases类&#xff0c;大家根据自己喜好随便命名&#xff0c;但请保持类名与文件名一致。2…

大量SQL的解决方案——sdmap

大量SQL的解决方案——sdmap最近看到群里面经常讨论大型应用中 SQL的管理办法&#xff0c;有人说用 EF/ EFCore&#xff0c;但很多人不信任它生成 SQL的语句&#xff1b;有人说用 Dapper&#xff0c;但将 SQL写到代码中有些人觉得不合适&#xff1b;有人提出用存储过程&#xf…

C++递增运算符重载

作用&#xff1a;通过重载递增运算符&#xff0c;实现自己定义的数据类型 代码如下&#xff1a; #include <iostream> using namespace std;//重载递增运算符//自定义类型 class MyInteger {friend ostream &operator<<(ostream &cout, MyInteger myint)…

java 最小堆_堆排序 最大堆 最小堆 Java 实现

堆一点疑惑&#xff0c;堆排序是就地排序&#xff0c;所以空间复杂度是 O(1)。但是&#xff0c;比如我有一个数组&#xff0c;建立一个最小堆&#xff0c;然后每次取出最小堆的顶点。建立最小堆需要额外空间&#xff1f;不深究了&#xff0c;归并排序需要额外空间。堆是完全二叉…

过去10年技术人员有哪些状态改变?

现在已经是2020年&#xff0c;我们已经进入了下一个10年&#xff0c;我们都应该回顾、复盘一下过去十年技术的发展&#xff0c;以及未来技术能做什么&#xff1f;如何更好的应用技术&#xff1f;我个人也是一个从事技术10年以上的老兵了&#xff0c;对技术人员来说其实不外乎要…

java解压中文乱码_java使用解压zip文件,文件名乱码解决方案

File outFileDir new File(outDir);if (!outFileDir.exists()) {boolean isMakDir outFileDir.mkdirs();if (isMakDir) {log.info("创建压缩目录成功");}}ZipFile zip new ZipFile(zipFile, "gbk");for (Enumeration enumeration zip.getEntries(); en…