在.NET Core項目中也是可以使用.resx資源文件,來為程序提供多語言支持。以下我們就以一個.NET Core控制台項目為例,來講解資源文件的使用。
新建一個.NET Core控制台項目,然后我們在其中新建一個.resx資源文件叫DemoResource.resx


注意.resx資源文件默認是Internal訪問級別的,這會導致其它程序集無法訪問資源文件類,所以我們最好將其改為Public訪問級別

然后我們在資源文件DemoResource.resx中定義一個字符串叫"Message",值為"Hello",如下所示:

由於資源文件是支持多語言的,其文件名命名格式如下:
{資源文件名}.{語言文化名稱}.resx
其中{語言文化名稱}就是諸如:zh-CN、en-US、ja-JP等語言字符串,代表了一種特定的語言,例如zh-CN就是簡體中文。
所以現在我們就為資源文件DemoResource.resx再創造兩種語言:
DemoResource.zh-CN.resx,簡體中文資源文件:


DemoResource.ja-JP.resx,日語資源文件:


所以我們現在,就有三個資源文件:
- DemoResource.resx是默認的資源文件,我們將其內部的字符串Message定義為了英文。
- DemoResource.zh-CN.resx是簡體中文資源文件,我們將其內部的字符串Message定義為了簡體中文。
- DemoResource.ja-JP.resx是日語資源文件,我們將其內部的字符串Message定義為了日語。
其實它們代表的都是DemoResource資源文件,只不過是不同的語言版本罷了,現在項目結構如下所示:

好了,現在定義好了資源文件,我們就來看看怎么使用它們。
在.NET Core中.resx資源文件是和線程的語言相關,其主要和當前線程的如下兩個語言屬性相關:
- Thread.CurrentThread.CurrentCulture
- Thread.CurrentThread.CurrentUICulture
如果當前線程的這兩個屬性是什么語言,那么.resx資源文件就會返回對應語言的內容。
首先我們在.NET Core控制台項目的Main方法中,設置當前線程的CurrentCulture和CurrentUICulture為zh-CN:
static void Main(string[] args) { Thread.CurrentThread.CurrentCulture = new CultureInfo("zh-CN"); Thread.CurrentThread.CurrentUICulture = new CultureInfo("zh-CN"); Console.WriteLine($"Message為:{DemoResource.Message}"); Console.WriteLine("按任意鍵結束..."); Console.ReadKey(); }
運行結果如下,我們可以看到顯示的Message為中文"你好"

現在我們將當前線程的CurrentCulture和CurrentUICulture設置為ja-JP:
static void Main(string[] args) { Thread.CurrentThread.CurrentCulture = new CultureInfo("ja-JP"); Thread.CurrentThread.CurrentUICulture = new CultureInfo("ja-JP"); Console.WriteLine($"Message為:{DemoResource.Message}"); Console.WriteLine("按任意鍵結束..."); Console.ReadKey(); }
運行結果如下,我們可以看到顯示的Message為日文"こんにちは"

然后,我們將當前線程的CurrentCulture和CurrentUICulture設置為fr-FR,代表法語:
static void Main(string[] args) { Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR"); Console.WriteLine($"Message為:{DemoResource.Message}"); Console.WriteLine("按任意鍵結束..."); Console.ReadKey(); }
那么現在結果是什么呢,如下所示:

可能很多同學會覺得很奇怪為什么Message顯示的是英語"Hello"。其實道理很簡單,因為我們沒有定義DemoResource.fr-FR.resx這個法語資源文件啊,所以在當前線程的CurrentCulture和CurrentUICulture為fr-FR時,調用DemoResource.Message時,.NET Core只好使用DemoResource默認資源文件DemoResource.resx的內容,所以DemoResource.Message輸出的是英文"Hello"。
其實資源文件類DemoResource也是可以通過設置其Culture屬性來指定使用某一種特定的語言,如下代碼所示,雖然我們設置當前線程的CurrentCulture和CurrentUICulture為ja-JP,但是由於我們設置了DemoResource.Culture為zh-CN:
static void Main(string[] args) { Thread.CurrentThread.CurrentCulture = new CultureInfo("ja-JP"); Thread.CurrentThread.CurrentUICulture = new CultureInfo("ja-JP"); DemoResource.Culture = new CultureInfo("zh-CN"); Console.WriteLine($"Message為:{DemoResource.Message}"); Console.WriteLine("按任意鍵結束..."); Console.ReadKey(); }
所以最后顯示的Message為中文"你好"

Async和Await模式對線程語言的影響
有的同學可能會想.NET Core中的Async和Await模式,會對Thread.CurrentThread.CurrentCulture和Thread.CurrentThread.CurrentUICulture這兩個線程的語言屬性產生影響嗎。
我們來看看如下代碼:
/// <summary> /// 測試Async和Await模式,是否會對Thread.CurrentThread.CurrentCulture和Thread.CurrentThread.CurrentUICulture產生影響 /// </summary> static void AsyncAwaitThreadCulture() { //設置主線程的CurrentCulture和CurrentUICulture為語言ja-JP Thread.CurrentThread.CurrentCulture = new CultureInfo("ja-JP"); Thread.CurrentThread.CurrentUICulture = new CultureInfo("ja-JP"); Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>主線程的CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); //通過Task來啟動第一層線程 Task.Run(async () => { Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>第一層線程的CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); //通過Task來啟動第二層線程 Task task = Task.Run(() => { Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>第二層線程的CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); //通過Thread來啟動第三層線程 Thread th = new Thread(new ThreadStart(() => { Thread.Sleep(3000); Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>第三層線程的CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); })); th.IsBackground = true; th.Start(); th.Join();//阻塞第二層線程,直到第三層線程th結束 }); Thread.Sleep(1000); Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>await之前CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); await task;//await,直到第二層線程結束 Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>await之后CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); }).Wait();//阻塞主線程,直到第一層線程執行完畢 }
運行結果如下所示:

我們在AsyncAwaitThreadCulture方法中,將主線程的CurrentCulture和CurrentUICulture設置為了ja-JP,結果可以發現后續啟動的線程其CurrentCulture和CurrentUICulture也都為ja-JP
現在我們設置主線程的CurrentCulture和CurrentUICulture為ja-JP,但是將第一層線程的CurrentCulture和CurrentUICulture改為zh-CN
/// <summary> /// 測試Async和Await模式,是否會對Thread.CurrentThread.CurrentCulture和Thread.CurrentThread.CurrentUICulture產生影響 /// </summary> static void AsyncAwaitThreadCulture() { //設置主線程的CurrentCulture和CurrentUICulture為語言ja-JP Thread.CurrentThread.CurrentCulture = new CultureInfo("ja-JP"); Thread.CurrentThread.CurrentUICulture = new CultureInfo("ja-JP"); Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>主線程的CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); //通過Task來啟動第一層線程 Task.Run(async () => { //將第一層線程的CurrentCulture和CurrentUICulture改為zh-CN Thread.CurrentThread.CurrentCulture = new CultureInfo("zh-CN"); Thread.CurrentThread.CurrentUICulture = new CultureInfo("zh-CN"); Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>第一層線程的CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); //通過Task來啟動第二層線程 Task task = Task.Run(() => { Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>第二層線程的CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); //通過Thread來啟動第三層線程 Thread th = new Thread(new ThreadStart(() => { Thread.Sleep(3000); Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>第三層線程的CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); })); th.IsBackground = true; th.Start(); th.Join();//阻塞第二層線程,直到第三層線程th結束 }); Thread.Sleep(1000); Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>await之前CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); await task;//await,直到第二層線程結束 Console.WriteLine($"Thread id:{Thread.CurrentThread.ManagedThreadId.ToString()}=>await之后CurrentCulture是{Thread.CurrentThread.CurrentCulture.ToString()}, CurrentUICulture是{Thread.CurrentThread.CurrentUICulture.ToString()}"); }).Wait();//阻塞主線程,直到第一層線程執行完畢 }
現在運行結果如下:

我們可以看到從第一層線程開始,后續啟動線程的CurrentCulture和CurrentUICulture都為zh-CN了
這說明在.NET Core中,默認情況下線程的CurrentCulture和CurrentUICulture屬性是由啟動它的線程來決定的,上面的結果很明顯由於第一層線程的CurrentCulture和CurrentUICulture為zh-CN,所以由第一層線程啟動的后續線程(第二層和第三層線程)也都為zh-CN。所以在.NET Core中要設置線程的CurrentCulture和CurrentUICulture屬性,最簡單的辦法就是在根線程(主線程)上設置CurrentCulture和CurrentUICulture的語言即可。
最后如果是在ASP.NET Core中,只需要寫一個中間件(Middleware),來更改主線程的CurrentCulture和CurrentUICulture屬性為特定語言,即可實現.resx資源文件的全局利用,當然ASP.NET Core中也有一套自帶的資源文件匹配規則,這里大家覺得怎么用起來方便怎么用即可。
