[WP7] 在 Windows Phone 讀取多語系資源的方法及輔助工具

經過不眠不休地多方嘗試及網路爬文的結果, 我發現在 Windows Phone 開發環境之下, 對於 Silverlight 多語系的支援方法其實和在 Web Form 或 Windows Form 真的是差不多的。但是其中仍然有一些小小的差異, 如果排列組合略有差池, 就得不到想要的結果...

經過不眠不休地多方嘗試及網路爬文的結果, 我發現在 Windows Phone 開發環境之下, 對於 Silverlight 多語系的支援方法其實和在 Web Form 或 Windows Form 真的是差不多的。但是其中仍然有一些小小的差異, 如果排列組合略有差池, 就得不到想要的結果。

經過測試, 我終於可以成功讀取自建 Resource 檔案的內容了。依照以下步驟, 我們就可以讀取到我們想要的資料:

  1. 從方案總管中, 在專案之下建立一個名為 Resources 的資料夾。
     
  2. 在上述資料夾中新增兩個資源檔案, 請分別命名為 AppResources.resx 與 AppResources.zh-TW.resx。
     
  3. 請特別注意, 你必須把上述兩個檔案的「建置動作」(從屬性視窗中) 設定為「內嵌資源」:


     
  4. 分別在上述兩個資源檔中加入同樣名稱的字串, 例如在 AppResources.resx 檔案中加入名稱為 "MyKey", 值為 "Content" 的字串, 在 AppResources.zh-TW.resx 檔案中加入名稱為 "MyKey", 值為 "內容"  的字串 :


     


    同時要特別記得把這兩個資源檔的「存取修飾詞」從 Internal 變更為 Public (這個選項位在上圖上方「移除資源」選項目的右邊)。
     
  5. 接著, 我們就可以在程式中取得其值了:

    using System.Resources;
    using System.Reflection;
    ...
    ResourceManager rm = new ResourceManager("MyApp.Resources.AppResources", Assembly.Load("MyApp"));
    string output = _rm.GetString("MyKey");

    在這裡 MyApp 是我的專案名稱, 而且請注意在 "MyApp.Resources.AppResources" 這個路徑裡不可以把 ".resx" 副檔名加上去。

請留意這兩個資源檔案的命名方式 (如果你會寫 ASP.NET 的多語系網站的話, 應該對它很熟悉)。例如, 如果你的原始資源檔案名為 A.resx, 那麼它的台灣中文版的資源檔案名稱必須是 A.zh-TW.resx。如果你不遵照這個命名慣例, 就無法套用 .Net 內建的 Globalization 方式取得對應的資源。不是不可以, 而是你必須採用其它比較麻煩的方法來處理, 而那並不是本文所將提及的。

在第五個步驟中, 你從資源檔案中取到的 MyKey 這個鍵的值將是 "Content"。這是因為目前 Emulator 的預設 Culture 是英文的緣故。如果我們要取得中文的對應值, 請先參考我在「[WP7] 在開發時期測試 WP7 多語系的方法」一文中描述的步驟, 把 Emulator 的預設語系變更成 zh-TW, 再回頭來執行上述程式時, 即可讀取到 "內容" 這個值。

除了英文與台灣中文, 如果你要增加其它語系的資源檔案的話, 例如簡體中文和日文, 就再加入 AppResources.zh-CN.resx 跟 AppResources.ja-JP.resx 就可以了。

有一點值得稍為留意, 依照 MSDN 對 CultureInfo 的說明, zh-Hant 與 zh-Hanc 這兩個中性文化照理說應該也是可以支援的, 不過經實際測試, 我們並不能指定 CurrentCulture 和 CurrentUICulture 為 zh-Hant 或是 zh-Hanc, 否則在執行時會發生 "Value does not fall within the expected range." 錯誤。而且, 雖然 zh-Hant 是 zh-TW 的 parent culture, 我們也不能把資源檔取名為 AppResources.zh-Hant.resx, 否則這個資源檔就等於不存在一樣。你一定要把台灣中文的資源檔命名為 AppResources.zh-TW.resx。

依照慣例, 我還是把我的輔助工具程式貢獻給大家:

using System;
using System.Net;
using System.IO;
using System.Resources;
using System.Reflection;

namespace Johnny
{
    public class ResourceHelper
    {
        public string Folder;
        public string Filepath;
        public string LastError;

        private const string _appName = "MyApp";
        private ResourceManager _rm;

        private string path
        {
            get
            {
                if (string.IsNullOrEmpty(Folder) || string.IsNullOrEmpty(Filepath))
                    throw new Exception("ResourceHelper: Must specify Folder and Filepath!");
                return string.Format("{0}.{1}.{2}", _appName, regulatePath(Folder), regulatePath(Filepath));
            }
        }

        /// <summary>
        /// Internal utility that removes the leading "/"
        /// </summary>
        private string regulatePath(string input)
        {
            return (input.Trim().StartsWith("/")) ? regulatePath(input.Substring(1)) : input.Trim();
        }

        /// <summary>
        /// Constructor, specify the folder and path of the resource file, using the system locale that is detected
        /// </summary>
        /// <param name="folder">The folder where the resource file stays</param>
        /// <param name="filepath">The path of the resource file - do not incldue ".resx"</param>
        public ResourceHelper(string folder, string filepath)
        {
            Folder = folder;
            Filepath = filepath;
            _rm = new ResourceManager(path, Assembly.Load(_appName));
        }

        /// <summary>
        /// Read from the resource specified
        /// </summary>
        /// <param name="output">The content read</param>
        /// <returns>True if successful, or else false</returns>
        public bool Read(string key, out string output)
        {
            if (string.IsNullOrEmpty(path))
            {
                LastError = string.Format("ResourceHelper.Read() failed. Resource file not found.");
                output = null;
                return false;
            }

            try
            {
                output = _rm.GetString(key);
                return true;
            }
            catch (Exception ex)
            {
                LastError = string.Format("ResourceHelper.Read() failed. Error: {0}", ex.Message);
                output = null;
                return false;
            }
        }
    }
}

 


Dev 2Share @ 點部落