[ASP Net MVC] 多國語系 - 使用資源檔(Resource)來呈現靜態文字

多國語系 - 使用資源檔(Resource)來呈現靜態文字

前言

 

在處理多國語系網站之靜態文字資料時,可使用資源檔對應出各語系所需顯示的內容。本篇文章將示範如何建立資源檔,了解如何在程式中設定及獲得語系資訊,最後在頁面上呈現正確語系文字。

 

 

建立資源檔

 

首先需要建立各語系資源檔,筆者習慣將資源檔獨立為一個類別庫專案,方便網站上線後若需調整文字時,可以直接抽換該資源檔專案dll,而無須重啟網站服務。資料夾對應約略如下,以方便辨識為目的。

 

clip_image001

 

以Layout為例,建立該預設語系(en-US)資源檔如下

 

clip_image002

 

再建立Layout 日語(ja-JP)語系資源檔如下

 

clip_image003

 

建議使用ResXManager工具來管理資源檔,可以清楚地列出各資源檔及語系文字對應關係。如果不熟悉ResXManager的朋友可參考多國語系 - 使用ResXManager管理資源檔文章,有簡易的重點功能說明。

 

clip_image004

 

 

設定語系資料

 

首先建立網站建置的語系列舉(Enum)類別,以利後續使用

 

public enum LanguageEnum
{
    [Description("en-US")]
    English = 1,
    [Description("ja-JP")]
    Japanese = 2
}

 

再來建立CultureHelper來協助處理相關事項;主要就是要檢核網站支援的語系(GetImplementedCulture),若用戶語系尚未被網站建置時,套用預設語系(GetDefaultCulture)英文來呈現頁面。

 

public static class CultureHelper
{

    /// <summary>
    /// 取得合法語系名稱(尚未實作則給予預設值)
    /// </summary>
    /// <param name="name">語系名稱 (e.g. en-US)</param>
    public static string GetImplementedCulture(string name)
    {
        // give a default culture just in case
        string cultureName = GetDefaultCulture();

        // check if it's implemented
        if (EnumHelper.TryGetValueFromDescription<LanguageEnum>(name))
            cultureName = name;

        return cultureName;
    }

    /// <summary>
    /// 取得預設 語系名稱
    /// </summary>
    /// <returns></returns>
    public static string GetDefaultCulture()
    {
        return LanguageEnum.English.GetDescription();
    }

    /// <summary>
    /// 取得目前 語系
    /// </summary>
    /// <returns></returns>
    public static LanguageEnum GetCurrentLanguage()
    {
        var currentCulture = Thread.CurrentThread.CurrentCulture.Name;

        // get implemented culture name
        currentCulture = GetImplementedCulture(currentCulture);

        // get language by implemented culture name
        return EnumHelper.GetValueFromDescription<LanguageEnum>(currentCulture);
    }
}

 

另附上CultureHelper中所使用到的Enum擴充功能及EnumHelper方法

 

public static class EnumExtension
{

    /// <summary>
    /// 取得Enum的描述標籤內容
    /// </summary>
    /// <returns></returns>
    public static string GetDescription(this Enum self)
    {
        FieldInfo fi = self.GetType().GetField(self.ToString());
        DescriptionAttribute[] attributes = null;

        if (fi != null)
        { 
            attributes = 
                (DescriptionAttribute[])fi.GetCustomAttributes( typeof(DescriptionAttribute), false);

            if (attributes != null && attributes.Length > 0)
                return attributes[0].Description;
        }
      
        return self.ToString();
    }
}

public class EnumHelper
{
    /// <summary>
    /// 透過標籤描述內容反射出Enum數值
    /// </summary>
    /// <typeparam name="T">Enum類別</typeparam>
    /// <param name="description">Enum描述標籤</param>
    /// <returns>Enum數值</returns>
    public static T GetValueFromDescription<T>(string description)
    {
        var type = typeof(T);
        if (!type.IsEnum) throw new InvalidOperationException();

        foreach (var field in type.GetFields())
        {
            var attribute = Attribute.GetCustomAttribute(field,
                typeof(DescriptionAttribute)) as DescriptionAttribute;
            if (attribute != null)
            {
                if (attribute.Description == description)
                    return (T)field.GetValue(null);
            }
            else
            {
                if (field.Name == description)
                    return (T)field.GetValue(null);
            }
        }

        throw new ArgumentException("Not found.", "description");
    }

    /// <summary>
    /// 是否能透過標籤描述內容反射出Enum數值
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="description"></param>
    /// <returns></returns>
    public static bool TryGetValueFromDescription<T>(string description)
    {
        bool isOkToGetValueFromDescription = true;

        try
        {
            GetValueFromDescription<T>(description);
        }
        catch (Exception)
        { isOkToGetValueFromDescription = false; }


        return isOkToGetValueFromDescription;
    }

}

 

此時就可以設定使用者的偏好語系於Cookie中,提供後續網站語系識別的依據。以下在HomeController中建立一個 SetCulture Action 來處理這段事務。

 

public ActionResult SetCulture(string culture, string returnUrl) 
{ 
	// Validate input 
	culture = CultureHelper.GetImplementedCulture(culture); 

	// Save culture in a cookie 
	HttpCookie cookie = Request.Cookies["_culture"]; 

	if (cookie != null) 
	{ 
	    // update cookie value 
	    cookie.Value = culture;    
	} 
	else 
	{ 
	    // create cookie value 
	    cookie = new HttpCookie("_culture"); 
	    cookie.Value = culture; 
	    cookie.Expires = DateTime.Now.AddYears(1); 
	} 

    Response.Cookies.Add(cookie); 
	return Redirect(returnUrl); 
}  

 

 

取得語系資料

 

剛剛我們將語系資訊放置在Cookie中,因此在伺服端接收到Request時就可以查看該Cookie資訊,並且依據該資訊來設定調整目前的CurrentCulture及CurrentUICulture。

 

public class MvcApplication : System.Web.HttpApplication
{
	protected void Application_Start()
	{
		AreaRegistration.RegisterAllAreas();
		FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
		RouteConfig.RegisterRoutes(RouteTable.Routes);
		BundleConfig.RegisterBundles(BundleTable.Bundles);
	}
	
	protected void Application_BeginRequest(Object sender, EventArgs e)
	{
		HttpCookie cultureCookie = Request.Cookies["_culture"];
		if (cultureCookie != null)
		{
			// get culture name
			var cultureInfoName = CultureHelper.GetImplementedCulture(cultureCookie.Value);
			
			// set culture
			System.Threading.Thread.CurrentThread.CurrentCulture =
			new System.Globalization.CultureInfo(cultureInfoName);
			System.Threading.Thread.CurrentThread.CurrentUICulture =
			new System.Globalization.CultureInfo(cultureInfoName);
			
		}
	}
}

 

 

使用資源檔

 

在View中使用資源檔的方式很簡單。首先參考剛建立的Resource專案,然後利用Razor語法來輸出資源檔內的文字資料。以下是 Views/Shared/_layout.cshtml 中利用資源檔來顯示聯絡方式Title文字的示意代碼。

 

clip_image001[5]

 

對應的顯示畫面如下 (預設en-US語系)

 

clip_image002[5]

 

 

切換語系實作測試

 

建立2個ActionLink來切換語系

 

clip_image003[5]

clip_image004[5]

 

點選日本語 - 正確顯示日文Title

 

clip_image005

 

點選English - 正確顯示英文Title

 

clip_image002[6]

 

 

參考資訊

 

http://afana.me/post/aspnet-mvc-internationalization.aspx


希望此篇文章可以幫助到需要的人

若內容有誤或有其他建議請不吝留言給筆者喔 !