ایجاد چند زبانه در ASP.Net MVC
چهارشنبه 24 آذر 1395اگر کاربران وب سایت شما از نقاط مختلف جهان میباشند ، این کاربران علاقه مند هستند که محتوای وب سایت شما را با زبان خودشان مشاهده کنند . ایجاد یک وب سایت چند زبانه کاره زیاد آسانی نیست ، اما این امر باعث میشود تا مخاطبین بیشتری به وب سایت شما مراجعه کنند .خوشبختانه ، Net Framework. دارای مولفه ای برای پشتیبانی از زبان ها و Culture های مختلف میباشد .
آموزش تصویری و فارسی ساخت سایت چند زبانه در MVC
Globalization و Localization در ASP.Net MVC :
Internationalization (بین المللی کردن) شامل Globalization و Localization میباشد . Globalization فرآیندی میباشد که در طی آن ظاهر و طراحی برنامه شما از Culture ها و فرهنگ های مختلف پشتیبانی میکند . Localization سفارشی کردن یک برنامه برای یک زبان خاص میباشد .
فرمت برای نام Culture عبارت است از : "<languagecode2>-<country/regioncode2>" که در آن
<languagecode2> جزو کدهای زبان میباشد و <country/regioncode2> جزو کدهای Subculture میباشد . مثال ما شامل es-CL برای (Spanish(chile و en-US برای (English(United State میباشد .
Internationalization معمولا با "I18N" مخفف میشود . که در این کلمه مخفف حرف های ابتدایی و انتهایی Internationalization گرفته شده است و عدد 18 نشان دهنده تعداد حرف های ما بین این دو است . همین روش نیز برای Globalization و Localization نیز در نظر گرفته میشود و مخفف های "G11N" و "L10N" بکار گرفته میشود .
ASP.Net دو مقدار از Culture را نگهداری میکند ، Culture و UICulture . مقدار Culture ، نتیجه توابع وابسته به Culture را تخمین میزند ، همانند Date , Number و فرمت پولی مربوط به آن را . اما UICulture اینکه کدام resource باید برای صفحه بارگذاری شود را بوسیله ResourceManager ، تخمین میزند . ResourceManager به دنبال Culture-specific Resource میگردد که بوسیله CurrentUICulture تخمین زده میشود . هر thread در Net. دارای اشیا CurrentCulture و CurrentUICulture میباشد . بنابراین ASP.Net مقادیر اینها را در زبان Render کردن توابع وابسته به Culture بررسی میکند . برای مثال ، اگر فرهنگ Thread جاری (CurrentCulture) روی en-US تنظیم شده باشد ()DateTime.Now.ToLongDateString مقدار "Saturday, January 08, 2011" را نمایش خواهد داد ، اما اگر CurrentCulture بر روی en-CL تنظیم شده باشد نتیجه "sábado, 08 de enero de 2011" خواهد بود .
حال مروری بر مطالبی که تا الان شرح دادیم را خواهیم داشت :
• Globalization - G11N : پردازشی که درآن برنامه را قادر به پشتیبانی از چندین زبان میکند .
•Localization - L10N : پردازشی که در طی آن یک برنامه را برای یک زبان خاص سفارشی سازی میکند .
• Internationalization - I18N : دو تعریف بالا یعنی Globalization و Localization را شرح میدهد .
• Culture : این یک زبان است ، که بصورت اختیاری نیز میتواند یک region هم داشته باشد .
• Local : همانند همان Culture میباشد .
• Neutral culture : فرهنگی میباشد که دارای یک زبان مشخص و خاص است اما Region آن مشخص نیست . برای مثال : "en" , "es"
• specific culture : فرهنگی میباشد که Culture و region آن مشخص است برای مثال : "en-US"
ممکن است این سوال در ذهن شما ایجاد شود که چرا ما به Region احتیاج داریم و Culture به تنهایی برای ما کفایت نمیکند ؟
شما ممکن است که اصلا به region احتیاجی پیدا نکنید . این درست است English در United State با English در United Kingdom یکسان نیست ، اما اگر برنامه شما فقط متن های خوانای انگلیسی برای کاربران این کشورها ارائه کند ، شما به region اصلا احتیاجی نخواهید داشت . مشکل زمانی نمایان میشود که شما قصد استفاده از number , dates و واحد پولی این کشورها را داشته باشید . برای مثال ، خروجی زیر را برای دو Region مختلف از Spanish یعنی Chile و Mexico مقایسه کنید .
int value = 5600; Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("es-CL"); Console.WriteLine(DateTime.Now.ToShortDateString()); Console.WriteLine(value.ToString("c")); Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("es-MX"); Console.WriteLine(DateTime.Now.ToShortDateString()); Console.WriteLine(value.ToString("c")); // Output 26-07-2011 // Date in es-CL, Spanish (Chile) $5.600,00 // Currency in es-CL, Spanish (Chile) 26/07/2011 // Date in es-MX, Spanish (Mexico) $5,600.00 // Currency in es-MX, Spanish (Mexico)
توجه داشته باشید که تاریخ و واحد پولی آنها با هم متفاوت میباشد . جدا کنندگان اعداد هم در دو کشور متفاوت میباشد و اگر region را رعایت نکنید باعث سردرگمی کاربران خواهید شد . ما به Region برای این دست از مشکلات نیازمند هستیم .
حال به سراغ بررسی چگونگی پشتیبانی از زبان های مختلف در ASP.Net MVC میرویم :
در اینجا دو روش اصلی برای ثبت Language و Culture مختلف در برنامه ASP.Net MVC معرفی خواهیم کرد :
1. استفاده از Resource String در تمام View سایت خود .
2. استفاده از View های مختلف برای هر زبان
3. استفاده از ترکیب دو روش بالا
#سوال -- کدام روش بهتر است ؟
این امر بسیار مهم است که کدام کارآمد تر است و سهولت بیشتری در استفاده دارد . برخی از مردم ترجیح میدهند که برای تمام زبان ها از یک View استفاده کنند و آن به این دلیل است که Maintainabilty ( نگهداری و توسعه کدها ) آسان تر است . در حالی که برخی دیگر از مردم معتقدند که استفاده از Resource برای جابجا کردن محتوای سایت و تغییر آن به زبان دیگر ممکن است درهم ریختگی و بی نطمی بوجود بیاورد و ناخوانا باشد . بعضی از پروژه ها توسعه دهنده را مجبور میکنند که از Viewهای مختلف برای هر زبان استفاده کنند . اما گاهی اوقاتم شما مجبور به استفاده از View مختلف برای زبان های دیگر میباشید چون بعضی از آنها rtl و بعضی دیگر ltr میباشند . اگر شما "dir="ltr تنظیم کنید نیز در بعضی مواقع کفایت نمیکند و آن بهم ریختگی ها باز ایجاد خواهد شد . شاید ترکیبی از دو روش بالا بهترین روش باشد ، در این مثال ، برای استفاده از زبان های Spanish , English و Farsi ما مشکلی به استفاده از Resource ها در Layout نخواهیم خورد .
چگونه ASP.Net زبان کاربر را حدس میزند؟!
در هر درخواست HTTP ، یک فیلد در Header وجود دارد که Accept-Language نامیده میشود که زبان مرورگر کاربر را تخمین میزند :
Accept-Language: en-us,en;q=0.5
این بدان معناست که مرورگر ترجیح میدهد که از زبان en-us استفاده کند . اما این نوع های دیگر زبان انگلیسی را نیز میپذیرد . پارامتر "q" میزان تخمین زده شده برای علاقه مندی کاربر در استفاده از این زبان را نشان میدهد . شما میتوانید لیست زبان های مورد استفاده را توسط مرورگر خود مشخص کنید .
Internet Explorer :
FireFox :
روند Globalizing سایت :
ما یک برنامه جدید ASP.Net MVC ایجاد میکنیم و فرآیند Globalizing آن را مرحله به مرحله بررسی خواهیم کرد .
یک پروژه جدید ASP.Net Web Application ایجاد کنید :
ایجاد Model :
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } public string Email { get; set; } public string Biography { get; set; } }
پیام های اعتبارسنجی Internationalization :
Model ما هیچ اعتبارسنجی ای ندارد ، که این امر در برنامه های امروزی عُرف نمی باشد . ما میتوانیم برای اعتبارسنجی از صفت های Data Annotation استفاده کنیم . برای Globize کردن پیام های اعتبارسنجی ، ما به تعریف یکسری پارامترهای دیگر نیاز داریم . "ErrorMessageResourceType" نوع Resource را برای جستجوی پیام خطا ، مشخص میکند . "ErrorMessageResourceName" نام Resource را برای جستجوی پیام خطا ، مشخص میکند . ResourceManager فایل مناسب Resource را بر مبنای CurrentCulture انتخاب میکند .
حال کلاس Person را ویرایش میکنیم و صفت های زیر را به آن اضافه میکنیم :
public class Person { [Display(Name = "FirstName", ResourceType = typeof(Resources.Resources))] [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "FirstNameRequired")] [StringLength(50, ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "FirstNameLong")] public string FirstName { get; set; } [Display(Name = "LastName", ResourceType = typeof(Resources.Resources))] [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "LastNameRequired")] [StringLength(50, ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "LastNameLong")] public string LastName { get; set; } [Display(Name = "Age", ResourceType = typeof(Resources.Resources))] [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "AgeRequired")] [Range(0, 130, ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "AgeRange")] public int Age { get; set; } [Display(Name = "Email", ResourceType = typeof(Resources.Resources))] [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "EmailRequired")] [RegularExpression(".+@.+\\..+", ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "EmailInvalid")] public string Email { get; set; } [Display(Name = "Biography", ResourceType = typeof(Resources.Resources))] public string Biography { get; set; } }
پیامهای اعتبارسنجی Localizing Data Annotation :
به این دلیل که برای اعتبارسنجی در Model ما نیاز به استفاده از Data Annotation داریم ، ما مجبور خواهیم بود که resource string ترجمه شده را برای هر Culture ای که سایت ما پشتیبانی میکند ، اضافه کنیم . در مثال ما ، English , Spanish و Farsi خواهد بود .
ما فایل های Resource را در Assembly جداگاه ذخیره خواهیم کرد ، بنابراین ما میتوانیم از آنها در پروژه های بعدی نیز استفاده بنمائیم .
روی Solution راست کلیک کرده و New Project را انتخاب کنید . در پنجره باز شده Class Library را انتخاب کنید و نام آن را Resources بگذارید .
حال روی پروژه Resources راست کلیک کرده و New Item را بزنید . در پنجره باز شده Resource File را انتخاب کرده و نام آن را Resources.resx بگذارید . این Culture پیش فرض ما خواهد بود (en-US)
توجه داشته باشید که سطح دسترسی آن را Public بگذارید ، در این صورت این فایل در پروژه های دیگر نیز در دسترس خواهد بود .
حال یک Resource جدید با نام Resources.es.resx ایجاد کنید ، مقادیر این فایل همانند زیر میباشد :
حال ، همین کار را برای نسخه فارسی نیز انجام دهید و نام آن را Resources.fa.resx بگذارید .
ما باید از درون برنامه به این Resource ها ارجاع داشته باشیم ، به این صورت ما میتوانیم فایل های Resource را در وب سایت خود داشته باشیم . روی Refrences کلیک راست کرده و Resources را انتخاب کنید
Viewها :
ما باید متن انگلیسی را از تمام View ها Extract کرده و آن را به فایل Resource منتقل کنیم . در اینجا یک ترفند وجود دارد ، بجای تایپ کردن هرباره ی نام NameSpace ، ما میتوانیم آن را به Web.Config اضافه کنیم ، به تصویر دقت کنید :
تخمین Culture :
همانطور که پیش تر گفتیم ، در header یک فیلد با نام Accept-Language وجود دارد که در هربار درخواست مرورگر آن را می فرستد . این فیلد شامل یک لیستی از نام Culture ها میباشد (language-culture) که کاربر بر روی مرورگر خود تنظیم کرده است . مشکل این است که Culture های مشخص شده ، هیچ کدام آن Culture مد نظر کاربر نباشند . پس به همین دلیل ما این امکان را به کاربر میدهیم که بصورت صریح زبان برنامه را انتخاب کند . ما باید گزینه انتخابی کاربر را در Cookie ذخیره کنیم . ما یک Controller با نام Base Controller ایجاد میکنیم که ابتدا محتوای کوکی کاربر را بررسی میکند ، ما از فیلد Accept-language که توسط مرورگر کاربر فرستاده میشود ، استفاده میکنیم .یک Controller ایجاد کرده و همانند زیر نام آن را BaseController بگذارید :
public class BaseController : Controller { protected override IAsyncResult BeginExecuteCore(AsyncCallback callback, object state) { string cultureName = null; // Attempt to read the culture cookie from Request HttpCookie cultureCookie = Request.Cookies["_culture"]; if (cultureCookie != null) cultureName = cultureCookie.Value; else cultureName = Request.UserLanguages != null && Request.UserLanguages.Length > 0 ? Request.UserLanguages[0] : // obtain it from HTTP header AcceptLanguages null; // Validate culture name cultureName = CultureHelper.GetImplementedCulture(cultureName); // This is safe // Modify current thread's cultures Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName); Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; return base.BeginExecuteCore(callback, state); } }
توجه داشته باشید که تمامی Controller های پروژه شما از BaseController ارث بری داشته باشند . Base Controller کوکی را بررسی میکند و اگر مقداری در آن موجود بود آن را در Current Thread Culture قرار میدهد . البته چون این مقدار در سمت کاربر ذخیره میشود در هر بار استفاده باید آن را اعتبارسنجی کنیم . که برای این کار از کلاس helperای به نام CultureHelper استفاده میکنیم . اگر نام Culture معتبر نباشد ، این کلاس یک مقدار پیش فرض برای Culture قرار میدهد .
کلاس CultureHelper :
CultureHelper اساس یک مزیت است که این امکان را به می دهد که نام Culture ای را که ما در سایت خود پیاده سازی کرده ایم را ذخیره کنیم :
public static class CultureHelper { // Valid cultures private static readonly List<string> _validCultures = new List<string> { "af", "af-ZA", "sq", "sq-AL", "gsw-FR", "am-ET", "ar", "ar-DZ", "ar-BH", "ar-EG", "ar-IQ", "ar-JO", "ar-KW", "ar-LB", "ar-LY", "ar-MA", "ar-OM", "ar-QA", "ar-SA", "ar-SY", "ar-TN", "ar-AE", "ar-YE", "hy", "hy-AM", "as-IN", "az", "az-Cyrl-AZ", "az-Latn-AZ", "ba-RU", "eu", "eu-ES", "be", "be-BY", "bn-BD", "bn-IN", "bs-Cyrl-BA", "bs-Latn-BA", "br-FR", "bg", "bg-BG", "ca", "ca-ES", "zh-HK", "zh-MO", "zh-CN", "zh-Hans", "zh-SG", "zh-TW", "zh-Hant", "co-FR", "hr", "hr-HR", "hr-BA", "cs", "cs-CZ", "da", "da-DK", "prs-AF", "div", "div-MV", "nl", "nl-BE", "nl-NL", "en", "en-AU", "en-BZ", "en-CA", "en-029", "en-IN", "en-IE", "en-JM", "en-MY", "en-NZ", "en-PH", "en-SG", "en-ZA", "en-TT", "en-GB", "en-US", "en-ZW", "et", "et-EE", "fo", "fo-FO", "fil-PH", "fi", "fi-FI", "fr", "fr-BE", "fr-CA", "fr-FR", "fr-LU", "fr-MC", "fr-CH", "fy-NL", "gl", "gl-ES", "ka", "ka-GE", "de", "de-AT", "de-DE", "de-LI", "de-LU", "de-CH", "el", "el-GR", "kl-GL", "gu", "gu-IN", "ha-Latn-NG", "he", "he-IL", "hi", "hi-IN", "hu", "hu-HU", "is", "is-IS", "ig-NG", "id", "id-ID", "iu-Latn-CA", "iu-Cans-CA", "ga-IE", "xh-ZA", "zu-ZA", "it", "it-IT", "it-CH", "ja", "ja-JP", "kn", "kn-IN", "kk", "kk-KZ", "km-KH", "qut-GT", "rw-RW", "sw", "sw-KE", "kok", "kok-IN", "ko", "ko-KR", "ky", "ky-KG", "lo-LA", "lv", "lv-LV", "lt", "lt-LT", "wee-DE", "lb-LU", "mk", "mk-MK", "ms", "ms-BN", "ms-MY", "ml-IN", "mt-MT", "mi-NZ", "arn-CL", "mr", "mr-IN", "moh-CA", "mn", "mn-MN", "mn-Mong-CN", "ne-NP", "no", "nb-NO", "nn-NO", "oc-FR", "or-IN", "ps-AF", "fa", "fa-IR", "pl", "pl-PL", "pt", "pt-BR", "pt-PT", "pa", "pa-IN", "quz-BO", "quz-EC", "quz-PE", "ro", "ro-RO", "rm-CH", "ru", "ru-RU", "smn-FI", "smj-NO", "smj-SE", "se-FI", "se-NO", "se-SE", "sms-FI", "sma-NO", "sma-SE", "sa", "sa-IN", "sr", "sr-Cyrl-BA", "sr-Cyrl-SP", "sr-Latn-BA", "sr-Latn-SP", "nso-ZA", "tn-ZA", "si-LK", "sk", "sk-SK", "sl", "sl-SI", "es", "es-AR", "es-BO", "es-CL", "es-CO", "es-CR", "es-DO", "es-EC", "es-SV", "es-GT", "es-HN", "es-MX", "es-NI", "es-PA", "es-PY", "es-PE", "es-PR", "es-ES", "es-US", "es-UY", "es-VE", "sv", "sv-FI", "sv-SE", "syr", "syr-SY", "tg-Cyrl-TJ", "tzm-Latn-DZ", "ta", "ta-IN", "tt", "tt-RU", "te", "te-IN", "th", "th-TH", "bo-CN", "tr", "tr-TR", "tk-TM", "ug-CN", "uk", "uk-UA", "wen-DE", "ur", "ur-PK", "uz", "uz-Cyrl-UZ", "uz-Latn-UZ", "vi", "vi-VN", "cy-GB", "wo-SN", "sah-RU", "ii-CN", "yo-NG" }; // Include ONLY cultures you are implementing private static readonly List<string> _cultures = new List<string> { "en-US", // first culture is the DEFAULT "es", // Spanish NEUTRAL culture "ar" // Arabic NEUTRAL culture }; /// <summary> /// Returns true if the language is a right-to-left language. Otherwise, false. /// </summary> public static bool IsRighToLeft() { return System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.IsRightToLeft; } /// <summary> /// Returns a valid culture name based on "name" parameter. If "name" is not valid, it returns the default culture "en-US" /// </summary> /// <param name="name" />Culture's name (e.g. en-US)</param> public static string GetImplementedCulture(string name) { // make sure it's not null if (string.IsNullOrEmpty(name)) return GetDefaultCulture(); // return Default culture // make sure it is a valid culture first if (_validCultures.Where(c => c.Equals(name, StringComparison.InvariantCultureIgnoreCase)).Count() == 0) return GetDefaultCulture(); // return Default culture if it is invalid // if it is implemented, accept it if (_cultures.Where(c => c.Equals(name, StringComparison.InvariantCultureIgnoreCase)).Count() > 0) return name; // accept it // Find a close match. For example, if you have "en-US" defined and the user requests "en-GB", // the function will return closes match that is "en-US" because at least the language is the same (ie English) var n = GetNeutralCulture(name); foreach (var c in _cultures) if (c.StartsWith(n)) return c; // else // It is not implemented return GetDefaultCulture(); // return Default culture as no match found } /// <summary> /// Returns default culture name which is the first name decalared (e.g. en-US) /// </summary> /// <returns></returns> public static string GetDefaultCulture() { return _cultures[0]; // return Default culture } public static string GetCurrentCulture() { return Thread.CurrentThread.CurrentCulture.Name; } public static string GetCurrentNeutralCulture() { return GetNeutralCulture(Thread.CurrentThread.CurrentCulture.Name); } public static string GetNeutralCulture(string name) { if (!name.Contains("-")) return name; return name.Split('-')[0]; // Read first part only. E.g. "en", "es" } }
ما باید "Culture_" را بصورت دستی پر کنیم . دیکشنری "Culture_" لیستی از نام Culture هایی را که توسط سایت ما پشتیبانی میشود را نگهداری میکند . تمام زیبایی این کلاس مفید در آن است که طریقه ی استفاده از آن همانند Culture میباشد . برای مثال ، اگر کاربری از united Kingdam سایت ما را مشاهده میکندو اگر در سایت ما Culture آن پیاده سازی نشده باشد ، کاربر سایت را با زبان انگلیسی دیگری مثل en-US مشاهده خواهد کرد . برای همین اگر واحد پولی و تاریخ برای شما مهم نمیباشد ، پیاده سازی تمام Culture ها ضرورتی ندارد . ResourceManager زمانی که Culture مشخص را پیدا نمیکند ، یک Neutral Culture را جایگزین آن میکنیم ، این مکانیزم خودکار fallback نامیده میشود .
Controllerها :
Visual Studio بصورت پیش فرض یک Controller با نام Home برای ما ایجاد میکند ، خب ما هم برای سادگی از آن استفاده میکنیم . برای کارامد بودن این Controller آن را همانند زیر ویرایش میکنیم :
public class HomeController : BaseController { [HttpGet] public ActionResult Index() { return View(); } [HttpPost] public ActionResult Index(Person per) { return View(); } public ActionResult SetCulture(string culture) { // Validate input culture = CultureHelper.GetImplementedCulture(culture); // Save culture in a cookie HttpCookie cookie = Request.Cookies["_culture"]; if (cookie != null) cookie.Value = culture; // update cookie value else { cookie = new HttpCookie("_culture"); cookie.Value = culture; cookie.Expires = DateTime.Now.AddYears(1); } Response.Cookies.Add(cookie); return RedirectToAction("Index"); } }
"SetCulture" این امکان را برای کاربران فراهم می آورد که Current Culture خود را عوض کرده و این را در یک کوکی با نام Culture_ ذخیره کنند . ما فقط محدود به Cookie نیستیم و ما میتوانیم به جای آن نام Culture را در Session یا هر جای دیگر ذخیره سازی کنیم ، اما به این دلیل که Cookie ها هیچگونه حجمی در سمت سرور را اشغال نمیکنند ، هنوزم به عنوان سبک ترین و یکی از بهترین راه ها هستند .
ایجاد یک View Template :
حال یک View برای متد Index از HomeController پیاده سازی میکینم .ابتدا View Index موجود را حذف میکنیم . حال با راست کلیک بر روی نام action Methode یک View جدید برای آن ایجاد میکنیم :
بعد از ساخته شدن View ، کدهای زیر را در آن قرار دهید :
@model MvcInternationalization.Models.Person @{ ViewBag.Title = Resources.AddPerson; var culture = System.Threading.Thread.CurrentThread.CurrentUICulture.Name.ToLowerInvariant(); } @helper selected(string c, string culture) { if (c == culture) { @:checked="checked" } } <h2>@Resources.AddPerson</h2> @using(Html.BeginForm("SetCulture", "Home")) { <fieldset> <legend>@Resources.ChooseYourLanguage</legend> <div class="control-group"> <div class="controls"> <label for="en-us"> <input name="culture" id="en-us" value="en-us" type="radio" @selected("en-us", culture) /> English </label> </div> </div> <div class="control-group"> <div class="controls"> <label for="es"> <input name="culture" id="es" value="es" type="radio" @selected("es", culture) /> Español </label> </div> </div> <div class="control-group"> <div class="controls"> <label for="ar"> <input name="culture" id="ar" value="ar" type="radio" @selected("ar", culture) /> العربية </label> </div> </div> </fieldset> } @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <hr /> @Html.ValidationSummary(true) <div class="form-group"> @Html.LabelFor(model => model.FirstName, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.FirstName) @Html.ValidationMessageFor(model => model.FirstName) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.LastName, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.LastName) @Html.ValidationMessageFor(model => model.LastName) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Age, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Age) @Html.ValidationMessageFor(model => model.Age) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Email, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Email) @Html.ValidationMessageFor(model => model.Email) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Biography, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Biography) @Html.ValidationMessageFor(model => model.Biography) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="@Resources.Create" class="btn btn-default" /> </div> </div> </div> } @section Scripts { @Scripts.Render("~/bundles/jqueryval") <script type="text/javascript"> (function ($) { $("input[type = 'radio']").click(function () { $(this).parents("form").submit(); // post form }); })(jQuery); </script> }
JavaScript بصورت خیلی ساده برای تنظیم Culture مقادیر فرم را Post میکند . البته Partial View ها را نباید فراموش کنیم :
@using Microsoft.AspNet.Identity @if (Request.IsAuthenticated) { using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" })) { @Html.AntiForgeryToken() <ul class="nav navbar-nav navbar-right"> <li> @Html.ActionLink(User.Identity.GetUserName(), "Manage", "Account", routeValues: null, htmlAttributes: new { title = "Manage" }) </li> <li><a href="javascript:document.getElementById('logoutForm').submit()">@Resources.LogOff</a></li> </ul> } } else { <ul class="nav navbar-nav navbar-right"> <li>@Html.ActionLink(Resources.Register, "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li> <li>@Html.ActionLink(Resources.LogOn, "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li> </ul> }
چپ به راست یا راست به چپ :
Html از زبان های rtl نیز پشتیبانی میکند . Layout.cshtml_ را بصورت زیر ویرایش کنید :
<!DOCTYPE html> <html lang="@CultureHelper.GetCurrentNeutralCulture()" dir="@(CultureHelper.IsRighToLeft() ? "rtl" : "ltr")"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - ASP.NET MVC Internationalization</title> @Styles.Render("~/Content/css" + (CultureHelper.IsRighToLeft() ? "-rtl" : "")) @Scripts.Render("~/bundles/modernizr") </head> <body> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> @Html.ActionLink("ASP.NET MVC Internationalization", "Index", "Home", null, new { @class = "navbar-brand" }) </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> </ul> @Html.Partial("_LoginPartial") </div> </div> </div> <div class="container body-content"> @RenderBody() <hr /> <footer> <p>@DateTime.Now</p> </footer> </div> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/bootstrap" + (CultureHelper.IsRighToLeft() ? "-rtl" : "")) @RenderSection("scripts", required: false) </body> </html>
حال بصورت اساسی ما نیاز به دو مجموعه فایل های CSS و JS داریم . یکی برای زبان های چپ به راست و دیگری برای زبان های راست به چپ . به این دلیل که قالب پیش فرض MVC از Bootstrap استفاده میکند ، این امر نیاز است که ما قالب rtl آن را نیز نصب کنیم . برای این از طریق Package Manager Console اقدام کرده و کد زیر را در آن تایپ کنید :
Install-Package Twitter.Bootstrap.RTL
توجه داشته باشید که فایل های bootstrap-rtl.js و bootstrap-rtl.css را داشته باشید .
ما باید دو تا bundle ایجاد کنیم ، یکی برای rtl دیگری برای ltr . فایل BundleConfig.cs را همانند زیر ویرایش کنید .
bundles.Add(new ScriptBundle("~/bundles/bootstrap-rtl").Include( "~/Scripts/bootstrap-rtl.js", "~/Scripts/respond.js")); bundles.Add(new StyleBundle("~/Content/css-rtl").Include( "~/Content/bootstrap-rtl.css", "~/Content/site.css"));
حال برنامه را اجرا کنید :
توجه داشته باشید که اعتبارسنجی سمت کاربر به زیبایی هر چه تمام تر کار میکند . بر تغییر Culture روی RadioButton های دلخواه خود کلیک کنید و توجه داشته باشید که قالب شما چگونه چپ به راست و راست به چپ میشود . استفاده از Viewهای جداگانه این امکان را به ما میدهد که موقعیت مکانی مولفه ها را کنترل کنیم و با تغییر Culture بی نظمی در قالب خود نداشته باشیم .
آموزش asp.net mvc
- ASP.net MVC
- 5k بازدید
- 20 تشکر