ایجاد چند زبانه در ASP.Net MVC

چهارشنبه 24 آذر 1395

اگر کاربران وب سایت شما از نقاط مختلف جهان میباشند ، این کاربران علاقه مند هستند که محتوای وب سایت شما را با زبان خودشان مشاهده کنند . ایجاد یک وب سایت چند زبانه کاره زیاد آسانی نیست ، اما این امر باعث میشود تا مخاطبین بیشتری به وب سایت شما مراجعه کنند .خوشبختانه ، Net Framework. دارای مولفه ای برای پشتیبانی از زبان ها و Culture های مختلف میباشد .

ایجاد چند زبانه در ASP.Net MVC

آموزش تصویری و فارسی ساخت سایت چند زبانه در 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

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید