Question

I am going to include the code how I have it. I felt like what I had should work because I got no squiggly lines. lol But I end up with a Redirect Loop.

First off I got the basic implementation from Nadeem Afana's blog. I don't think the CultureHelper.cs or the BaseController.cs may be needed to implement a returnUrl functionality, but since they are part of the code I am including them here so that you don't have to dig through his website to look for it. I have tried to eliminate scrolling for you as well.

HERE IS MY CODE:

Helpers/CultureHelper.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;

namespace xxxUSA.Helpers
{
    public static class CultureHelper
    {
        // Valid cultures REMOVED SOME CODES FOR BREVITY
        private static readonly List<string> _validCultures = new List<string> 
        { "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", "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" };

        private static readonly List<string> _cultures = new List<string> {
            "en-US",  // first culture is the DEFAULT
            "es", // Spanish NEUTRAL culture
            "ar"  // Arabic NEUTRAL culture
        };

        public static bool IsRightToLeft()
        {
            return 
            System.Threading.Thread.CurrentThread
            .CurrentCulture.TextInfo.IsRightToLeft;
        }

        public static string GetImplementedCulture(string name)
        {
            // make sure it's not null
            if (string.IsNullOrEmpty(name))
                return GetDefaultCulture(); // return Default culture

            if (_validCultures.Where(c => c.Equals(name, 
                StringComparison.InvariantCultureIgnoreCase)).Count() == 0)
                return GetDefaultCulture(); // return Default if invalid

            if (_cultures.Where(c => c.Equals(name, 
                StringComparison.InvariantCultureIgnoreCase)).Count() > 0)
                return name; // accept it

            var n = GetNeutralCulture(name);
            foreach (var c in _cultures)
                if (c.StartsWith(n))
                    return c;

            return GetDefaultCulture(); // return Default if no match
        }

        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.Length < 2)
                return name;

            return name.Substring(0, 2);
        }
    }
}

Controllers/BaseController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Mvc;
using xxxUSA.Helpers;

namespace xxxUSA.Controllers
{
    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] 
                : null; // obtain it from HTTP header AcceptLanguages

            // Validate culture name
            cultureName = CultureHelper.GetImplementedCulture(cultureName);

            // Modify current thread's cultures            
            Thread.CurrentThread.CurrentCulture = new 
            System.Globalization.CultureInfo(cultureName);
            Thread.CurrentThread.CurrentUICulture = 
            Thread.CurrentThread.CurrentCulture;

            return base.BeginExecuteCore(callback, state);
        }
    }
}

OK, now we get into where I have modified some things. I will comment on anything I added

Controllers/HomeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using xxxUSA.Helpers;
using xxxUSA.Models;

namespace xxxUSA.Controllers
{
    public class HomeController : BaseController
    {

    ApplicationDbContext _db = new ApplicationDbContext();

    public ActionResult Index()
    {
        return View();
    }

    public ActionResult About()
    {
        return View();
    }

    public ActionResult Contact()
    {
        return View();
    }

                                                  //I ADDED THIS LAST PARAMETER
    public ActionResult SetCulture(string culture, string returnUrl)
    {
        // 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);


        //THIS WORKS
        return Redirect(returnUrl);

        //THIS DOES NOT
        if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
        {
            return Redirect(returnUrl);
        }
        else
        {
            return RedirectToAction("Index", "Home");
        }



    }

Now for the code on the Home Page

Views/Home/Index.cshtml

@{
    var culture = System.Threading.Thread.CurrentThread
    .CurrentUICulture.Name.ToLowerInvariant();
    ViewBag.Title = culture;
}

                                         //I ADDED THIS LAST PARAMETER
@helper selected(string c, string culture, string returnUrl)
    {
        if (c == culture)
    {
        @:checked="checked"
    }
}

@using (Html.BeginForm("SetCulture", "Home"))
{
@Resources.ChooseYourLanguage &nbsp;&nbsp;

<input name="culture" id="en-us" value="en-us" 
                                        //I ADDED THIS LAST PARAMETER "About"
type="radio" @selected("en-us", culture, "About") /> English 

<input type="Hidden" name="returnUrl" value="About" />

<input name="culture" id="es" value="es"  
                                    //I ADDED THIS LAST PARAMETER "About"
type="radio" @selected("es", culture, "About") /> Español
}

//LOTS OF STUFF LOADS HERE LIKE RESOURCE REFERENCES, GRAPHICS, CONTENT, 
//NOT IMPORTANT FOR THE IMPLEMENTATION REMOVED FOR BREVITY

@section Scripts {
@Scripts.Render("~/bundles/jqueryval")

<script type="text/javascript">
    (function ($) {
        $("input[type = 'radio']").click(function () {
            $(this).parents("form").submit(); // post form
        });

    })(jQuery);
</script> 
}

So the reason I added the "About" was so if I got it to work, It would redirect to the "About" Action instead of back to the same homepage. It doesn't work though.

Can I not overload the checkboxes @Selected Method the way I have?

Also, NOT IMPORTANT NOW, but what if I wanted to add this to other views from other controllers, Could I include another parameter to designate the controller and action it needs to return to so that I don't have to repeat the code in each controller?

I have contacted Nadeem on twitter and he said he would check it out and try and help. I really appreciate his work and seems like a very kind fellow.. Thanks in advance for simply helping in any capacity!

OK so I have a products/Ingredients page where I have stuff coming from the database, here how I hacked that to show the right field from the database. This is a total HACK BEWARE.

Views/Products.cshtml

@model IEnumerable<xxxUSA.Models.Ingredient>

@{
    var culture = System.Threading.Thread.CurrentThread
                  .CurrentUICulture.Name.ToLowerInvariant();
    ViewBag.Title = culture;
}
@helper selected(string c, string culture)
{
    if (c == culture)
    {@:checked="checked"}
}
        <div class="row">
            <div class="large-12 columns">
            @using(Html.BeginForm("SetCulture", "Ingredient"))
{        

  <div class="row">
    <div class="large-6 columns">
      <label>@Resources.ChooseYourLanguage</label>
      <input type="Hidden" name="returnUrl" value="/Prodcuts/" />
      <input name="culture" id="en-us" value="en-us" type="radio" 
                           @selected("en-us", culture) /> English
     <input name="culture" id="es" value="es" type="radio" 
                           @selected("es", culture) /> Español
    </div>
  </div>
}
@{ViewBag.Culture = System.Threading.Thread.CurrentThread
.CurrentUICulture.Name.ToLowerInvariant();}

//Here is where the conditional logic hack I use to show one field 
//or another from the database based on culture

@if (ViewBag.Culture == "en-us") {
@Html.DisplayFor(modelItem => item.IngredientNameEn)
} else { 
@Html.DisplayFor(modelItem => item.IngredientNameEs) }

Basically using that conditional logic anywhere I have data that has a spanish or english version. it's hacky as hell though.

Was it helpful?

Solution

Try this:

public ActionResult SetCulture(string culture, string returnUrl)
    {
        // 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);


       if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
        {
            return Redirect(returnUrl);
        }
        else
        {
            return RedirectToAction("Index", "Home");
        }
    }

OTHER TIPS

I'm also using Nadeem's i18n solution in a project. The way you switch the culture in this project is through clicking on flags (anchor tags in image tags). I added a returnUrl and ended up with a redirect loop. To fix the redirect loop issue, I made a few changes to the code and ended up with this code:

File: BaseController.cs (added this method)

    public ActionResult SetCulture(string culture, string returnUrl)
    {
        // Validate input
        culture = CultureHelper.GetImplementedCulture(culture);

        RouteData.Values["culture"] = culture;

        // Save culture in a cookie
        HttpCookie cookie = Request.Cookies["_culture"];

        if (cookie != null)
        {
            // update cookie value
            cookie.Value = culture;
        }
        else
        {
            cookie = new HttpCookie("_culture");
            cookie.Value = culture;
            cookie.Expires = DateTime.Now.AddYears(1);
        }

        Response.Cookies.Add(cookie);

        return Redirect(returnUrl);
    }

File: _layout.cshtml (flags for selecting culture):

            @{ var daDkUrl = Url.Action("setculture", "base", new { culture = "da-DK", returnUrl = Request.RawUrl }); }
            <a href="@daDkUrl">
                <img src="~/content/images/flag_da.gif" width="16" height="11" alt="Dansk" />
            </a>
            &nbsp;&nbsp;|&nbsp;&nbsp;
            @{ var enUsUrl = Url.Action("setculture", "base", new { culture = "en-US", returnUrl = Request.RawUrl }); }
            <a href="@enUsUrl">
                <img src="~/content/images/flag_en.gif" width="16" height="11" alt="English" />
            </a>

Finally, ensure your controllers inherit from BaseController.cs.

public class TestController : BaseController
{
    //Code
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top