micro_bhk
دوشنبه 11 دی 1391, 21:16 عصر
سلام
توی یکی از پروژه هام نیاز به چند زبانه سازی با MVC داشتم، توی سایت برنامه نویس هم مطلبی در این رابطه پیدا نکردم، یه مقاله پیدا کردم که پیاده سازیش رو توضیح داده بود، به همین خاطر اون رو به صورت فارسی گذاشتم که دوستانی که نیاز دارن استفاده کنن.
لازم به ذکره که دقیقا همون مقاله رو ترجمه نکردم، اون چیزهایی که نیاز بود رو تو قالب یه پروژه گذاشتم.
برای توضیحات بیشتر میتونید به منبع اصلی مراجعه کنید:
http://geekswithblogs.net/shaunxu/archive/2010/05/06/localization-in-asp.net-mvc-ndash-3-days-investigation-1-day.aspx
تا جایی که لازم بوده نحوه کار توضیح داده شده.
طبق مثال موجود، برای این کار ما نیاز به 2 پروژه داریم
1. GlobalizationMvc
2. AVAResource
مراحل انجام کار
1. یک پروژه از نوع ASP .NET Mvc 4 Web Application ایجاد میکنیم و اسمش رو GlobalizationMVC4 قرار میدیم.
2. حالا باید پروژه AVAResource رو ایجاد کنیم، برای اینکار از مسیر Files -> Add-> Visual C# -> Windows یه پروژه از نوع Class Library ایجاد میکنیم و فایل Class1.cs رو که نیازی بهش نداریم، پاک میکنیم. این پروژه شامل فایل های Resource مربرط به هر زبان هست.
3. برای اضافه کردن فایل Resource هر زبان، روی پروژه AVAResource راست کلیک و گزینه Add و بعد New Item رو انتخاب میکنیم، از قسمت Visual C# Item -> General گزینه Resource File رو انتخاب میکنیم و اسمش رو Resource قرار میدیم (این فایل رو به عنوان Resource پیش فرض در نظر میگیریم). برای اضافه کردن هر زبان، مراحل قسمت 4 رو انجام میدیم (یا از فایل ایجاد شده یک کپی میگیریم و اسمش رو تغییر میدیم)، یک فایل دیگه برای زبان فارسی اضافه میکنیم
استاندارد نامگذاری زبان ها به اینصورت هست که نام اختصاری زبانهایی رو که میخواید به عنوان پسوند فایل ایجاد شده قرار میدید مثلا برای فارسی Default.aspx.fa.resx یا Default.aspx.fa-IR.resx استفاده کنید.
4. مقدار Name/Value مربوط به هر زبان رو اضافه کنید. ما تو این مثال 3 تا مقدار message، title و rtl (برای تعیین چپ به راست و راست به چپ ) استفاده کردیم
5. برای هر فایل Resource اگر بخوایم به روش این مثال فایل های Resource رو تو یه پروژه جدا قرار بدیم، لازمه که مقدار Access Modifier هر زبان رو برابر Public و تو قسمت Properties مقدار گزینه Bulid Action رو برابر با Embedded Resource قرار بدید.
6. AVAResource رو توی پروژه GlobalizationMVC4 رفرنس میدیم (برای این کار در پروژه GlobalizationMVC4 روی Refrence راست کلیک کرده، گزینه Add Refrence رو انتخاب میکنیم، از لیت پروژه های موجود تو تب Project، AVAResource رو انتخاب میکنیم)
7. توی پوشه App_Start، فایل RouteConfig.cs تیکه کد زیر رو به تابع RegisterRoutes اضافه می کنیم:
routes.MapRoute(
"Localization", // Route name
"{lang}/{controller}/{action}/{id}", // URL with parameters
new {controller = "Home", action = "Index", id = UrlParameter.Optional} // Parameter defaults
);
همونطور که پیداست {lang} اضافه شده، که نشون دهنده این هستش که اولین مقداری که بعد از URL قرار میگیره، برای زبان هست.
8. یک Controller به اسم BaseController ایجاد میکنیم و نوع کلاس رو Abstract قرار میدیم، برای اینکار روی پوشه Controllers راست کلیک کرده، گزینه Add و سپس Controller رو انتخاب میکنیم.
9. توی کنترلر ایجاد شده (BaseController) کدهای زیر رو وارد میکنیم:
protected override bool DisableAsyncSupport
{
get
{
return true;
}
}
protected override void ExecuteCore()
{
if (RouteData.Values["lang"] != null &&
!string.IsNullOrWhiteSpace(RouteData.Values["lang"].ToString()))
{
// set the culture from the route data (url)
var lang = RouteData.Values["lang"].ToString();
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(lang);
}
else
{
// load the culture info from the cookie
var cookie = HttpContext.Request.Cookies["MvcLocalization.CurrentUICulture"];
var langHeader = string.Empty;
if (cookie != null)
{
// set the culture by the cookie content
langHeader = cookie.Value;
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(langHeader);
}
else
{
// set the culture by the location if not speicified
langHeader = HttpContext.Request.UserLanguages[0];
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(langHeader);
}
// set the lang value into route data
RouteData.Values["lang"] = langHeader;
}
// save the location into cookie
HttpCookie _cookie = new HttpCookie("MvcLocalization.CurrentUICulture", Thread.CurrentThread.CurrentUICulture.Name);
_cookie.Expires = DateTime.Now.AddYears(1);
HttpContext.Response.SetCookie(_cookie);
base.ExecuteCore();
}
نکته:
توی MVC 4 برای اینکه بخوایم متد ExecuteCore() رو Override کنیم، لازمه که مقدارد فلاگ DisableAsyncSupport برابر true قرار بدیم برای اینکار DisableAsyncSupport رو Override میکنیم.
تابع ExecuteCore هر بار که Controller ی رو اجرا می کنیم، فراخونی میشه، توی این تابع ما عمل چک کردن URL رو انجام میدیم، اگر که lang وجود داشته باشه، اونرو میخونیم و و ست میکنیم و در نهایت توی Cookie ذخیره میکنیم.
10. هر کنترلی که ایجاد میکنیم باید از کنترلر BaseController ارث برای کنه، مثلا تو این مثال کنترلر Home ما از این کلاس ارث بری کرده:
public class HomeController : BaseController
11. یک کلاس static درست میکنیم به اسم SwitchLanguageHelper، از این کلاس برای ساخت html helper link اختصاصی برای تغییر زبان استفاده می کنیم
کد های زیر رو توی این کلاس کپی می کنیم:
public class Language
{
public string Url { get; set; }
public string ActionName { get; set; }
public string ControllerName { get; set; }
public RouteValueDictionary RouteValues { get; set; }
public bool IsSelected { get; set; }
public MvcHtmlString HtmlSafeUrl
{
get
{
return MvcHtmlString.Create(Url);
}
}
}
public static Language LanguageUrl(this HtmlHelper helper, string cultureName,
string languageRouteName = "lang", bool strictSelected = false)
{
// set the input language to lower
cultureName = cultureName.ToLower();
// retrieve the route values from the view context
var routeValues = new RouteValueDictionary(helper.ViewContext.RouteData. Values);
// copy the query strings into the route values to generate the link
var queryString = helper.ViewContext.HttpContext.Request.QueryString ;
foreach (string key in queryString)
{
if (queryString[key] != null && !string.IsNullOrWhiteSpace(key))
{
if (routeValues.ContainsKey(key))
{
routeValues[key] = queryString[key];
}
else
{
routeValues.Add(key, queryString[key]);
}
}
}
var actionName = routeValues["action"].ToString();
var controllerName = routeValues["controller"].ToString();
// set the language into route values
routeValues[languageRouteName] = cultureName;
// generate the language specify url
var urlHelper = new UrlHelper(helper.ViewContext.RequestContext, helper.RouteCollection);
var url = urlHelper.RouteUrl("Localization", routeValues);
// check whether the current thread ui culture is this language
var current_lang_name = Thread.CurrentThread.CurrentUICulture.Name.ToLower ();
var isSelected = strictSelected ?
current_lang_name == cultureName :
current_lang_name.StartsWith(cultureName);
return new Language()
{
Url = url,
ActionName = actionName,
ControllerName = controllerName,
RouteValues = routeValues,
IsSelected = isSelected
};
}
public static MvcHtmlString LanguageSelectorLink(this HtmlHelper helper,
string cultureName, string selectedText, string unselectedText,
IDictionary<string, object> htmlAttributes, string languageRouteName = "lang", bool strictSelected = false)
{
var language = helper.LanguageUrl(cultureName, languageRouteName, strictSelected);
var link = helper.RouteLink(language.IsSelected ? selectedText : unselectedText,
"Localization", language.RouteValues, htmlAttributes);
return link;
}
12. تقریبا کار تموم شده، توی هر پیجی که می خوایم تغییر زبان انجام بشه (معمولا در Master) تکه کد زیر رو توی کدهای html صفحه قرار میدیم:
@Html.LanguageSelectorLink("en", "[English]", "English", null)
@Html.LanguageSelectorLink("fa", "[فارسی]", "فارسی", null)
به ازای هر زبان، یک LanguageSelectorLink مثل بالا قرار می دیم.
13. برای استفاده از Resource ها طبق حالت های زیر عمل میکنم:
استفاده در View:
@AVAResource.Resource.message
استفاده در کلاس:
AVAResource.Resource.title;
مثلا:
ViewBag.Message = AVAResource.Resource.title;
14. برای راست به چپ یا بالعکس تگ Body رو بصورت زیر تغییر میدیم:
<body dir="@AVAResource.Resource.dir">
کار تمومه.
حجم فایل زیاد بود اون رو هم پیوست کردم. بخاطر محدویدیت در تعداد فایل های پیوست، تو 2تا پست فایل ها رو گذاشتم.
همه فایل های پیوست این پست و پست بعدی رو دانلود کنید و توی یک پوشه قرار بدید، و بعد فایل اول رو Extract کنید.
توی یکی از پروژه هام نیاز به چند زبانه سازی با MVC داشتم، توی سایت برنامه نویس هم مطلبی در این رابطه پیدا نکردم، یه مقاله پیدا کردم که پیاده سازیش رو توضیح داده بود، به همین خاطر اون رو به صورت فارسی گذاشتم که دوستانی که نیاز دارن استفاده کنن.
لازم به ذکره که دقیقا همون مقاله رو ترجمه نکردم، اون چیزهایی که نیاز بود رو تو قالب یه پروژه گذاشتم.
برای توضیحات بیشتر میتونید به منبع اصلی مراجعه کنید:
http://geekswithblogs.net/shaunxu/archive/2010/05/06/localization-in-asp.net-mvc-ndash-3-days-investigation-1-day.aspx
تا جایی که لازم بوده نحوه کار توضیح داده شده.
طبق مثال موجود، برای این کار ما نیاز به 2 پروژه داریم
1. GlobalizationMvc
2. AVAResource
مراحل انجام کار
1. یک پروژه از نوع ASP .NET Mvc 4 Web Application ایجاد میکنیم و اسمش رو GlobalizationMVC4 قرار میدیم.
2. حالا باید پروژه AVAResource رو ایجاد کنیم، برای اینکار از مسیر Files -> Add-> Visual C# -> Windows یه پروژه از نوع Class Library ایجاد میکنیم و فایل Class1.cs رو که نیازی بهش نداریم، پاک میکنیم. این پروژه شامل فایل های Resource مربرط به هر زبان هست.
3. برای اضافه کردن فایل Resource هر زبان، روی پروژه AVAResource راست کلیک و گزینه Add و بعد New Item رو انتخاب میکنیم، از قسمت Visual C# Item -> General گزینه Resource File رو انتخاب میکنیم و اسمش رو Resource قرار میدیم (این فایل رو به عنوان Resource پیش فرض در نظر میگیریم). برای اضافه کردن هر زبان، مراحل قسمت 4 رو انجام میدیم (یا از فایل ایجاد شده یک کپی میگیریم و اسمش رو تغییر میدیم)، یک فایل دیگه برای زبان فارسی اضافه میکنیم
استاندارد نامگذاری زبان ها به اینصورت هست که نام اختصاری زبانهایی رو که میخواید به عنوان پسوند فایل ایجاد شده قرار میدید مثلا برای فارسی Default.aspx.fa.resx یا Default.aspx.fa-IR.resx استفاده کنید.
4. مقدار Name/Value مربوط به هر زبان رو اضافه کنید. ما تو این مثال 3 تا مقدار message، title و rtl (برای تعیین چپ به راست و راست به چپ ) استفاده کردیم
5. برای هر فایل Resource اگر بخوایم به روش این مثال فایل های Resource رو تو یه پروژه جدا قرار بدیم، لازمه که مقدار Access Modifier هر زبان رو برابر Public و تو قسمت Properties مقدار گزینه Bulid Action رو برابر با Embedded Resource قرار بدید.
6. AVAResource رو توی پروژه GlobalizationMVC4 رفرنس میدیم (برای این کار در پروژه GlobalizationMVC4 روی Refrence راست کلیک کرده، گزینه Add Refrence رو انتخاب میکنیم، از لیت پروژه های موجود تو تب Project، AVAResource رو انتخاب میکنیم)
7. توی پوشه App_Start، فایل RouteConfig.cs تیکه کد زیر رو به تابع RegisterRoutes اضافه می کنیم:
routes.MapRoute(
"Localization", // Route name
"{lang}/{controller}/{action}/{id}", // URL with parameters
new {controller = "Home", action = "Index", id = UrlParameter.Optional} // Parameter defaults
);
همونطور که پیداست {lang} اضافه شده، که نشون دهنده این هستش که اولین مقداری که بعد از URL قرار میگیره، برای زبان هست.
8. یک Controller به اسم BaseController ایجاد میکنیم و نوع کلاس رو Abstract قرار میدیم، برای اینکار روی پوشه Controllers راست کلیک کرده، گزینه Add و سپس Controller رو انتخاب میکنیم.
9. توی کنترلر ایجاد شده (BaseController) کدهای زیر رو وارد میکنیم:
protected override bool DisableAsyncSupport
{
get
{
return true;
}
}
protected override void ExecuteCore()
{
if (RouteData.Values["lang"] != null &&
!string.IsNullOrWhiteSpace(RouteData.Values["lang"].ToString()))
{
// set the culture from the route data (url)
var lang = RouteData.Values["lang"].ToString();
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(lang);
}
else
{
// load the culture info from the cookie
var cookie = HttpContext.Request.Cookies["MvcLocalization.CurrentUICulture"];
var langHeader = string.Empty;
if (cookie != null)
{
// set the culture by the cookie content
langHeader = cookie.Value;
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(langHeader);
}
else
{
// set the culture by the location if not speicified
langHeader = HttpContext.Request.UserLanguages[0];
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(langHeader);
}
// set the lang value into route data
RouteData.Values["lang"] = langHeader;
}
// save the location into cookie
HttpCookie _cookie = new HttpCookie("MvcLocalization.CurrentUICulture", Thread.CurrentThread.CurrentUICulture.Name);
_cookie.Expires = DateTime.Now.AddYears(1);
HttpContext.Response.SetCookie(_cookie);
base.ExecuteCore();
}
نکته:
توی MVC 4 برای اینکه بخوایم متد ExecuteCore() رو Override کنیم، لازمه که مقدارد فلاگ DisableAsyncSupport برابر true قرار بدیم برای اینکار DisableAsyncSupport رو Override میکنیم.
تابع ExecuteCore هر بار که Controller ی رو اجرا می کنیم، فراخونی میشه، توی این تابع ما عمل چک کردن URL رو انجام میدیم، اگر که lang وجود داشته باشه، اونرو میخونیم و و ست میکنیم و در نهایت توی Cookie ذخیره میکنیم.
10. هر کنترلی که ایجاد میکنیم باید از کنترلر BaseController ارث برای کنه، مثلا تو این مثال کنترلر Home ما از این کلاس ارث بری کرده:
public class HomeController : BaseController
11. یک کلاس static درست میکنیم به اسم SwitchLanguageHelper، از این کلاس برای ساخت html helper link اختصاصی برای تغییر زبان استفاده می کنیم
کد های زیر رو توی این کلاس کپی می کنیم:
public class Language
{
public string Url { get; set; }
public string ActionName { get; set; }
public string ControllerName { get; set; }
public RouteValueDictionary RouteValues { get; set; }
public bool IsSelected { get; set; }
public MvcHtmlString HtmlSafeUrl
{
get
{
return MvcHtmlString.Create(Url);
}
}
}
public static Language LanguageUrl(this HtmlHelper helper, string cultureName,
string languageRouteName = "lang", bool strictSelected = false)
{
// set the input language to lower
cultureName = cultureName.ToLower();
// retrieve the route values from the view context
var routeValues = new RouteValueDictionary(helper.ViewContext.RouteData. Values);
// copy the query strings into the route values to generate the link
var queryString = helper.ViewContext.HttpContext.Request.QueryString ;
foreach (string key in queryString)
{
if (queryString[key] != null && !string.IsNullOrWhiteSpace(key))
{
if (routeValues.ContainsKey(key))
{
routeValues[key] = queryString[key];
}
else
{
routeValues.Add(key, queryString[key]);
}
}
}
var actionName = routeValues["action"].ToString();
var controllerName = routeValues["controller"].ToString();
// set the language into route values
routeValues[languageRouteName] = cultureName;
// generate the language specify url
var urlHelper = new UrlHelper(helper.ViewContext.RequestContext, helper.RouteCollection);
var url = urlHelper.RouteUrl("Localization", routeValues);
// check whether the current thread ui culture is this language
var current_lang_name = Thread.CurrentThread.CurrentUICulture.Name.ToLower ();
var isSelected = strictSelected ?
current_lang_name == cultureName :
current_lang_name.StartsWith(cultureName);
return new Language()
{
Url = url,
ActionName = actionName,
ControllerName = controllerName,
RouteValues = routeValues,
IsSelected = isSelected
};
}
public static MvcHtmlString LanguageSelectorLink(this HtmlHelper helper,
string cultureName, string selectedText, string unselectedText,
IDictionary<string, object> htmlAttributes, string languageRouteName = "lang", bool strictSelected = false)
{
var language = helper.LanguageUrl(cultureName, languageRouteName, strictSelected);
var link = helper.RouteLink(language.IsSelected ? selectedText : unselectedText,
"Localization", language.RouteValues, htmlAttributes);
return link;
}
12. تقریبا کار تموم شده، توی هر پیجی که می خوایم تغییر زبان انجام بشه (معمولا در Master) تکه کد زیر رو توی کدهای html صفحه قرار میدیم:
@Html.LanguageSelectorLink("en", "[English]", "English", null)
@Html.LanguageSelectorLink("fa", "[فارسی]", "فارسی", null)
به ازای هر زبان، یک LanguageSelectorLink مثل بالا قرار می دیم.
13. برای استفاده از Resource ها طبق حالت های زیر عمل میکنم:
استفاده در View:
@AVAResource.Resource.message
استفاده در کلاس:
AVAResource.Resource.title;
مثلا:
ViewBag.Message = AVAResource.Resource.title;
14. برای راست به چپ یا بالعکس تگ Body رو بصورت زیر تغییر میدیم:
<body dir="@AVAResource.Resource.dir">
کار تمومه.
حجم فایل زیاد بود اون رو هم پیوست کردم. بخاطر محدویدیت در تعداد فایل های پیوست، تو 2تا پست فایل ها رو گذاشتم.
همه فایل های پیوست این پست و پست بعدی رو دانلود کنید و توی یک پوشه قرار بدید، و بعد فایل اول رو Extract کنید.