公司业务遍布全球各地,对应业务系统国际化就是瓜熟蒂落的事情。最近就接手了一批新老系统的国际化任务,这里把一些探索经验、案例记录下来。自己改造和探索过程包括.NET MVC的,以及.NET CORE WEB API的,但这里旧版MVC的就不描述了,重点介绍netcore下的国际化方案。国际化重点在于多语言支持,以及多时区支持,本文就从这两个方面入手。前端
预设:有一个先后端分离的系统,前端由i18n负责多语言支持,后端不渲染视图,提供api返回数据给前端。web
Demo解决方案截图:数据库
如上解决方案截图,Common.Resource是多语言资源工程,ExceptionHandlerTest是示例web api项目,Service是api项目依赖的服务工程。之因此这么设计场景,是为了探索资源文件放在单独工程下,以及非Web Api工程中的多语言方案,这点在官方教程中基本是没有的。后端
先来看demo要干的事情:HomeController中有个SayHello方法,此方法调用HomeService中的SayHello方法返回欢迎语信息,咱们要作的就是对HomeService中返回的欢迎语进行语言协商。下边来看看具体怎么实现:api
以支持中英文为例,定义以下图资源文件,步骤与FX下的很相似。浏览器
惟一的重大区别,是若是你但愿在单独工程中放置资源配置,那就添加一个单独类代码文件,假如你的资源是Common.en.rex,那对应类就应该是Common,这点在跨程序集寻找资源文件中相当重要,官网文档中可没有描述这相当重要的一点,别问我怎么知道的, 问就是看core底层源码。。。服务器
资源文件中定义的资源配置项以下:cookie
1)注册本地化服务及HomeService服务前后端分离
HomeService必须使用容器解析,不然core底层无法注入多语言基础服务到咱们的组件,那你就只能手动传入。运维
2)注册本地化中间件
1)HomeService中注入IStringLocalizer服务
2)SayHello方法引用多语言配置项
1)默认访问
不作任何设置,系统也无设置对应cookie状况下,netcore直接取浏览器语言环境设置,就是下图这个地方:
假如咱们将浏览器语言环境改为英文,那默认状况下系统就会选取英文了。
2)经过查询字符串切换语言
如上图,咱们使用netcore规定的culture=en格式向后端传递语言环境信息。具体语言环境选择优先级是这样的:查询字符串 > cookie > 浏览器语言环境设置,这在官网有详细介绍,看底层源码也证明了这个。基于cookie选取语言环境时候,cookie名称是能够修改的,我实际项目就是如此,官网文档也有介绍,这里不作赘述。
预设1:HomeController中有两个方法,GetTime返回服务端或数据库中存储的UTC时间,系统根据客户本地时区自动转换成其对应时间;SetTime方法接收客户本地时区下的时间,转换成UTC时间存入服务器或数据库
预设2:系统支持中国东八区时间及印度东5区时间
/// <summary> /// 日期转换 /// </summary> public class DateTimeOffsetJsonConverter : JsonConverter<DateTimeOffset> { private TimeZoneInfo chinaZoneInfo = TimeZoneInfo.CreateCustomTimeZone("zh", TimeSpan.FromHours(8), "中国时区", "China time zone"); private TimeZoneInfo indiaZoneInfo = TimeZoneInfo.CreateCustomTimeZone("en-IN", TimeSpan.FromHours(5), "印度时区", "India time zone"); public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var currentZoneInfo = Thread.CurrentThread.CurrentCulture.Name.Contains("zh") ? chinaZoneInfo : indiaZoneInfo; //var time1 = DateTimeOffset.Parse(reader.GetString()); //var time2 = time1.ToOffset(currentZoneInfo.BaseUtcOffset); var time1 = new DateTimeOffset(DateTime.Parse(reader.GetString()), currentZoneInfo.BaseUtcOffset); var time2 = time1.ToUniversalTime(); //var time3 = time2.ToUniversalTime(); return time2; } public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) { var currentZoneInfo = Thread.CurrentThread.CurrentCulture.Name.Contains("zh") ? chinaZoneInfo : indiaZoneInfo; writer.WriteStringValue(value.ToOffset(currentZoneInfo.BaseUtcOffset).ToString("yyyy-MM-dd HH:mm:ss")); } }
如上所述,自定义时间序列化转换器,读取时间时,根据客户语言环境匹配其对应时区,时区中有对应UTC偏离时间信息,据此转换成UTC时间;序列化写入时候,一样根据语言环境匹配时区信息,将服务器端的UTC时间按照时区偏离转换成本地时间返给客户端。
1)获取服务器时间
其中currentTime是模拟服务器上或数据库中取出来的UTC时间,而后什么不作直接返回,具体时间转换交由时间转换器负责。下边看效果:
中文环境时间:
能够看到,原始UTC时间2019-07-15 08:30:00在中国东八区8个小时偏离下,返给客户端变成了16:30:00,即中国本地时间;
英文环境:
当语言环境切换为英文,则匹配到印度东5区时区信息,UTC时间2019-07-15 08:30:00转换成印度本地时间2019-07-15 13:30:00。
2)写入时间到服务器
一样的,接收到客户端时间后,咱们业务代码层不作任何设置,交由时间转换器去负责,具体看效果:
中文环境:
传入本地时间2019-07-15 16:30:00,到了服务器,时间以下:
能够看到,中国东八区时间2019-07-15 16:30:00在服务器上转换成UTC时间2019-07-15 08:30:00;
一样的本地时间,但语言环境为英语:
能够看到,印度东5区的本地时间2019-07-15 16:30:00到服务器,转换成UTC时间2019-07-15 11:30:00。
系统国际化的重点,在于语言环境国际化,以及多时区自适应,解决这两点,剩下就不是啥问题了。关于时区,这里是以服务器及数据库中统一保存UTC时间为例,但也有必定麻烦,好比你须要后台维护数据,尤为是直接在数据库中维护这种,就须要作本地时间和UTC时间的手动处理,除非你是英国人,身处英国,用英国的时区。针对这点能够作对应发散,例如假如系统中文用户占多数,运维也主要是中国员工,那就能够采起服务器或数据库统一存储中国东8区的时间,其余本地时间向中国时间进行转换的作法,思路、解决方案是一致的。