تهیه سایتهای چند زبانه و بومی سازی نمایش اطلاعات در ASP.NET MVC
زمانیکه دات نت فریم ورک نیاز به انجام اعمال حساس به مسایل بومی را داشته باشد، ابتدا به مقادیر تنظیم شده دو خاصیت زیر دقت میکند:
الف) System.Threading.Thread.CurrentThread.CurrentCulture
بر این اساس دات نت میتواند تشخیص دهد که برای مثال خروجی متد DateTime.Now.ToString در کانادا و آمریکا باید با هم تفاوت داشته باشند. مثلا در آمریکا ابتدا ماه، سپس روز و در آخر سال نمایش داده میشود و در کانادا ابتدا سال، بعد ماه و در آخر روز نمایش داده خواهد شد. یا نمونهی دیگری از این دست میتواند نحوه نمایش علامت واحد پولی کشورها باشد.
ب) System.Threading.Thread.CurrentThread.CurrentUICulture
مقدار CurrentUICulture بر روی بارگذاری فایلهای مخصوصی به نام Resource، تاثیر گذار است.
این خواص را یا به صورت دستی میتوان تنظیم کرد و یا ASP.NET، این اطلاعات را از هدر Accept-Language دریافتی از مرورگر کاربر به صورت خودکار مقدار دهی میکند. البته برای این منظور نیاز است یک سطر زیر را به فایل وب کانفیگ برنامه اضافه کرد:
<system.web>
<globalization culture="auto" uiCulture="auto" />
یا اگر نیاز باشد تا برنامه را ملزم به نمایش اطلاعات Resource مرتبط با فرهنگ بومی خاصی کرد نیز میتوان در همین قسمت مقادیر culture و uiCulture را دستی تنظیم نمود و یا اگر همانند برنامههایی که چند لینک را بالای صفحه نمایش میدهند که برای مثال به نگارشهای فارسی/عربی/انگلیسی اشاره میکند، اینکار را با کد نویسی نیز میتوان انجام داد:
System.Threading.Thread.CurrentThread.CurrentCulture =
System.Globalization.CultureInfo.CreateSpecificCulture("fa");
جهت آزمایش این مطلب، ابتدا تنظیم globalization فوق را به فایل وب کانفیگ برنامه اضافه کنید. سپس به مسیر زیر در IE مراجعه کنید:
IE -> Tools -> Intenet options -> Genarl tab -> Languages
در اینجا میتوان هدر Accept-Language را مقدار دهی کرد. برای نمونه اگر مقدار زبان پیش فرض را به فرانسه تنظیم کنیم (به عنوان اولین زبان تعریف شده در لیست) و سپس سعی در نمایش مقدار decimal زیر را داشته باشیم:
string.Format("{0:C}", 10.5M)
اگر زبان پیش فرض، انگلیسی آمریکایی باشد، $ نمایش داده خواهد شد و اگر زبان به فرانسه تنظیم شود، یورو در کنار عدد مبلغ نمایش داده میشود.
تا اینجا تنها با تنظیم culture=auto به این نتیجه رسیدهایم. اما سایر قسمتهای صفحه چطور؟ برای مثال برچسبهای نمایش داده شده را چگونه میتوان به صورت خودکار بر اساس Accept-Language مرجح کاربر تنظیم کرد؟ خوشبختانه در دات نت، زیر ساخت مدیریت برنامههای چند زبانه به صورت توکار وجود دارد که در ادامه به بررسی آن خواهیم پرداخت.
آشنایی با ساختار فایلهای Resource
فایلهای Resource یا منبع، در حقیقت فایلهایی هستند مبتنی بر XML با پسوند resx و هدف آنها ذخیره سازی رشتههای متناظر با فرهنگهای مختلف میباشد و برای استفاده از آنها حداقل یک فایل منبع پیش فرض باید تعریف شود. برای نمونه فایل mydata.resx را در نظر بگیرید. برای ایجاد فایل منبع اسپانیایی متناظر، باید فایلی را به نام mydata.es.resx تولید کرد. البته نوع فرهنگ مورد استفاده را کاملتر نیز میتوان ذکر کرد برای مثال mydata.es-mex.resx جهت فرهنگ اسپانیایی مکزیکی بکارگرفته خواهد شد، یا mydata.fr-ca.resx به فرانسوی کانادایی اشاره میکند. سپس مدیریت منابع دات نت فریم ورک بر اساس مقدار CurrentUICulture جاری، اطلاعات فایل متناظری را بارگذاری خواهد کرد. اگر فایل متناظری وجود نداشت، از اطلاعات همان فایل پیش فرض استفاده میگردد.
حین تهیه برنامهها نیازی نیست تا مستقیما با فایلهای XML منابع کار کرد. زمانیکه اولین فایل منبع تولید میشود، به همراه آن یک فایل cs یا vb نیز ایجاد خواهد شد که امکان دسترسی به کلیدهای تعریف شده در فایلهای XML را به صورت strongly typed میسر میکند. این فایلهای خودکار، تنها برای فایل پیش فرض mydata.resx تولید میشوند،از این جهت که تعاریف اطلاعات سایر فرهنگهای متناظر نیز باید با همان کلیدهای فایل پیش فرض آغاز شوند. تنها «مقادیر» کلیدهای تعریف شده در کلاسهای منبع متفاوت هستند.
اگر به خواص فایلهای resx در VS.NET دقت کنیم، نوع Build action آنها به embedded resource تنظیم شده است.
مثالی جهت بررسی استفاده از فایلهای Resource
یک پروژه جدید خالی ASP.NET MVC را آغاز کنید. فایل وب کانفیگ آنرا ویرایش کرده و تنظیمات globalization ابتدای بحث را به آن اضافه کنید. سپس مدل، کنترلر و View متناظر با متد Index آنرا با محتوای زیر به پروژه اضافه نمائید:
namespace MvcApplication19.Models
{
public class Employee
{
public int Id { set; get; }
public string Name { set; get; }
}
}
using System.Web.Mvc;
using MvcApplication19.Models;
namespace MvcApplication19.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
var employee = new Employee { Name = "Name 1" };
return View(employee);
}
}
}
@model MvcApplication19.Models.Employee
@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
<fieldset>
<legend>Employee</legend>
<div class="display-label">
Name
</div>
<div class="display-field">
@Html.DisplayFor(model => model.Name)
</div>
</fieldset>
<fieldset>
<legend>Employee Info</legend>
@Html.DisplayForModel()
</fieldset>
قصد داریم در View فوق بر اساس uiCulture کاربر مراجعه کننده به سایت، برچسب Name را مقدار دهی کنیم. اگر کاربری از ایران مراجعه کند، «نام کارمند» نمایش داده شود و سایر کاربران، «Employee Name» را مشاهده کنند. همچنین این تغییرات باید بر روی متد Html.DisplayForModel نیز تاثیرگذار باشد.
برای این منظور بر روی پوشه Views/Home که محل قرارگیری فایل Index.cshtml فوق است کلیک راست کرده و گزینه Add|New Item را انتخاب کنید. سپس در صفحه ظاهر شده، گزینه «Resources file» را انتخاب کرده و برای مثال نام Index_cshtml.resx را وارد کنید.
به این ترتیب اولین فایل منبع مرتبط با View جاری که فایل پیش فرض نیز میباشد ایجاد خواهد شد. این فایل، به همراه فایل Index_cshtml.Designer.cs تولید میشود. سپس همین مراحل را طی کنید، اما اینبار نام Index_cshtml.fa.resx را حین افزودن فایل منبع وارد نمائید که برای تعریف اطلاعات بومی ایران مورد استفاده قرار خواهد گرفت. فایل دومی که اضافه شده است، فاقد فایل cs همراه میباشد.
اکنون فایل Index_cshtml.resx را در VS.NET باز کنید. از بالای صفحه، به کمک گزینه Access modifier، سطح دسترسی متدهای فایل cs همراه آنرا به public تغییر دهید. پیش فرض آن internal است که برای کار ما مفید نیست. از این جهت که امکان دسترسی به متدهای استاتیک تعریف شده در فایل خودکار Index_cshtml.Designer.cs را در View های برنامه، نخواهیم داشت. سپس دو جفت «نام-مقدار» را در فایل resx وارد کنید. مثلا نام را Name و مقدار آنرا «Employee Name» و سپس نام دیگر را NameIsNotRight و مقدار آنرا «Name is required» وارد نمائید.
در ادامه فایل Index_cshtml.fa.resx را باز کنید. در اینجا نیز دو جفت «نام-مقدار» متناظر با فایل پیش فرض منبع را باید وارد کرد. کلیدها یا نامها یکی است اما قسمت مقدار اینبار باید فارسی وارد شود. مثلا نام را Name و مقدار آنرا «نام کارمند» وارد نمائید. سپس کلید یا نام NameIsNotRight و مقدار «لطفا نام را وارد نمائید» را تنظیم نمائید.
تا اینجا کار تهیه فایلهای منبع متناظر با View جاری به پایان میرسد.
در ادامه با کمک فایل Index_cshtml.Designer.cs که هربار پس از تغییر فایل resx متناظر آن به صورت خودکار توسط VS.NET تولید و به روز میشود، میتوان به کلیدها یا نامهایی که تعریف کردهایم، در قسمتهای مختلف برنامه دست یافت. برای نمونه تعریف کلید Name در این فایل به نحو زیر است:
namespace MvcApplication19.Views.Home {
public class Index_cshtml {
public static string Name {
get {
return ResourceManager.GetString("Name", resourceCulture);
}
}
}
}
بنابراین برای استفاده از آن در هر View ایی تنها کافی است بنویسیم:
@MvcApplication19.Views.Home.Index_cshtml.Name
به این ترتیب بر اساس تنظیمات محلی کاربر، اطلاعات به صورت خودکار از فایلهای Index_cshtml.fa.resx فارسی یا فایل پیش فرض Index_cshtml.resx، دریافت میگردد.
علاوه بر امکان دسترسی مستقیم به کلیدهای تعریف شده در فایلهای منبع، امکان استفاده از آنها توسط data annotations نیز میسر است. در این حالت میتوان مثلا پیغامهای اعتبار سنجی را بومی کرد یا حین استفاده از متد Html.DisplayForModel، بر روی برچسب نمایش داده شده خودکار، تاثیر گذار بود. برای اینکار باید اندکی مدل برنامه را ویرایش کرد:
using System.ComponentModel.DataAnnotations;
namespace MvcApplication19.Models
{
public class Employee
{
[ScaffoldColumn(false)]
public int Id { set; get; }
[Display(ResourceType = typeof(MvcApplication19.Views.Home.Index_cshtml),
Name = "Name")]
[Required(ErrorMessageResourceType = typeof(MvcApplication19.Views.Home.Index_cshtml),
ErrorMessageResourceName = "NameIsNotRight")]
public string Name { set; get; }
}
}
همانطور که ملاحظه میکنید، حین تعریف ویژگیهای Display یا Required، امکان تعریف نام کلاس متناظر با فایل resx خاصی وجود دارد. به علاوه ErrorMessageResourceName به نام یک کلید در این فایل و یا پارامتر Name ویژگی Display نیز به نام کلیدی در فایل منبع مشخص شده، اشاره میکنند. این اطلاعات توسط متدهای Html.DisplayForModel، Html.ValidationMessageFor، Html.LabelFor و امثال آن به صورت خودکار مورد استفاده قرار خواهند گرفت.
نکتهای در مورد کش کردن اطلاعات
در این مثال اگر فیلتر OutputCache را بر روی متد Index تعریف کنیم، حتما نیاز است به هدر Accept-Language نیز دقت داشت. در غیراینصورت تمام کاربران، صرفنظر از تنظیمات بومی آنها، یک صفحه را مشاهده خواهند کرد:
[OutputCache(Duration = 60, VaryByHeader = "Accept-Language")]
public ActionResult Index()