公司業務遍佈全球各地,對應業務系統國際化就是瓜熟蒂落的事情。最近就接手了一批新老系統的國際化任務,這裏把一些探索經驗、案例記錄下來。自己改造和探索過程包括.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區的時間,其餘本地時間向中國時間進行轉換的作法,思路、解決方案是一致的。