過去開發系統多語系大多都是製作多個語系UI,隨著前端Framework越來越強大
通常只要一個UI就可以應付各種語系版面,這次以.NET Framework 4.8 Web MVC 搭配 Resource 進行多語系擴充
參考了以下大大的文章
- https://dotblogs.com.tw/wasichris/2015/06/20/151608
- https://blog.miniasp.com/post/2010/01/05/ASPNET-MVC-Developer-Note-Part-15-Globalization-and-Localization
- https://afana.me/archive/2011/01/14/aspnet-mvc-internationalization.aspx/
Web.config
<?xml version="1.0"?> <configuration> <system.web> <!-- 語系設定 culture: 國家文化特性 (ex: 日期,數字和貨幣格式等) UICulture: 預設語系; 之後會依照用戶設定語系為主(保存在Cookies) --> <globalization culture="auto" uiCulture="auto" enableClientBasedCulture="true" /> </system.web> </configuration>
Global.asax
- 預設語系為客戶端瀏覽器
- 客戶端選擇語系保存在
Request.Cookies["Lang"],期限設定1年 - 客戶端更換瀏覽器語,語系需重新選擇
protected void Application_BeginRequest(Object sender, EventArgs e)
{
HttpCookie cultureCookie = Request.Cookies["Lang"];
// 取得語系檔名稱,若Cookies無設定,預設抓用戶瀏覽器語系
var cultureInfoName = CultureHelper.GetImplementedCulture((cultureCookie == null) ? Request.UserLanguages[0] : cultureCookie.Value);
// 設定語系
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureInfoName);
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(cultureInfoName);
}
建立類別庫 GlobalResources
Resource與實作專案目錄對照,可看團隊要用哪種方式來定義目錄結構,這樣比較好查找語系資源檔


EnumHelper.cs
using System;
using System.ComponentModel;
namespace GlobalResources
{
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;
}
}
}
CultureHelper.cs
增加多語系相關方法
using System.Threading;
namespace GlobalResources
{
/// <summary>
/// 語系相關
/// </summary>
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<Language>(name))
cultureName = name;
return cultureName;
}
/// <summary>
/// 取得預設 語系名稱
/// </summary>
/// <returns></returns>
public static string GetDefaultCulture()
{
return Language.English.GetDescription();
}
/// <summary>
/// 取得目前 語系
/// </summary>
/// <returns></returns>
public static Language GetCurrentLanguage()
{
var currentCulture = Thread.CurrentThread.CurrentCulture.Name;
// get implemented culture name
currentCulture = GetImplementedCulture(currentCulture);
// get language by implemented culture name
return EnumHelper.GetValueFromDescription<Language>(currentCulture);
}
}
}
EnumExtension.cs
列舉(Enum)反射相關方法
using System;
using System.ComponentModel;
using System.Reflection;
namespace GlobalResources
{
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();
}
}
}
Language.cs
語系列舉,這邊定義英文、中文、日文
using System.ComponentModel;
namespace GlobalResources
{
/// <summary>
/// 語系清單
/// </summary>
public enum Language
{
/// <summary>
/// 英文
/// </summary>
[Description("en-US")]
English = 1,
/// <summary>
/// 繁體中文
/// </summary>
[Description("zh-TW")]
TraditionalChinese = 2,
/// <summary>
/// 日文
/// </summary>
[Description("ja-JP")]
Japanese = 3
}
}
LocalizedDescriptionAttribute.cs
擴充列舉(Enum)屬性多語化
using System;
using System.ComponentModel;
using System.Resources;
namespace GlobalResources
{
/// <summary>
/// 屬性描述多語系
/// </summary>
public class LocalizedDescriptionAttribute : DescriptionAttribute
{
private readonly string _resourceKey;
private readonly ResourceManager _resource;
public LocalizedDescriptionAttribute(string resourceKey, Type resourceType)
{
_resource = new ResourceManager(resourceType);
_resourceKey = resourceKey;
}
public override string Description
{
get
{
string displayName = _resource.GetString(_resourceKey);
return string.IsNullOrEmpty(displayName)
? string.Format("[[{0}]]", _resourceKey)
: displayName;
}
}
}
}
語系資源檔 Resource 管理
建議安裝 Visual Studio 擴充工具 ResXManager進行多語系Resource管理



使用工具他會自動建置相關對應檔案,只要存檔就會立刻觸發執行T4 Template產生程式碼
- 英文: Resource.resx
- 中文: Resource.zh-TW.resx
- 日文: Resource.ja-JP.resx
程式碼語系替換


