We should populate "_cultures" manually. The "_cultures" dictionary stores the list of culture names our site supports. The nice part of this utility class is that it serves similar cultures. For example, if a user is visiting our site from the United Kingdom (en-GB), a culture which is not implemented in our site, he or she will see our site in English using "en-US" (English, United States). This way, you don't have to implement all cultures unless you really care about currency, date format, etc. One important thing to mention is that we added all specific cultures for the Spanish language. For example, we are implementing "es" Spanish in general without any region where a date looks like this "30/07/2011". Now suppose a nerd is coming from Chile (es-CL), it would be very nice display dates in their culture format (30-07-2011) instead of the neutral one. It does not matter if we don’t have a resource file for any of these cultures. ResourceManager can pick a neutral culture when it cannot find a specific culture file, this automatic mechanism is called fallback.
Controllers
Visual Studio has created a controller named "HomeCotnroller" for us, so we'll use it for simplicity. Modify the "HomeController.cs" so that it looks like below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| 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"); } } |
The "SetCulture" action allows the user to change their current culture and stores it in a cookie called "_culture". We are not restricted to cookies, we could instead save the culture name in Session or elsewhere, but cookies are really lightweight since they do not take any type of space on server side.
Creating a View Template
Now we will implement the View associated with the HomeController's Index action. First delete the existing Index.cshtml file under "Views/Home" folder. Now to implement the view right-click within the "HomeController.Index()" method and select the "Add View" command to create a view template for our home page:
Click OK and replace the existing view if any. When we click the "Add" button, a view template of our "Create" view (which renders the form) is created. Modify it so it looks like below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
| @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>} |
The javascript code simply post back the form to set the culture. The "selected" helper is used to mark the appropriate culture radio button as checked.
Of course, we should not forget about partial views too
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| @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>} |
Left-to-right or Right-to-left
HTML supports rtl languages too, so we need to make sure our main HTML tag has the appropriate direction. Modify the _Layout.cshtml file to look like the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| <!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> |
Now we need to provide basically two sets of CSS and JS files: One for left-to-right languages and one for right-to-left languages. Because MVC default template is using Bootstrap, we need to install the RTL version of bootstrap files. For this, open the package manager console (aka Nuget) by choosing Tools -> Library Package Manager -> Package Manager Console and type:
Install-Package Twitter.Bootstrap.RTL
Make sure you have the two files bootstrap-rtl.js and bootstrap-rtl.css
We need to create two bundles: one for RTL and one for LTR. Modify the file BundleConfig.cs and add the following:
1
2
3
4
5
6
7
| 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")); |
Try It Out
Run the website now. Notice that client side validation is working nicely. Click on radio buttons to switch between cultures, and notice how right-to-left language is showing correctly. Using separate views allowed us to control how to position elements, and have made our views clean and readable.
English
Spanish
Arabic
No comments:
Post a Comment