2016年2月21日 星期日

[MVC] 多國語系開發 - 以Routing rule設定

 MVC
目的


l  實作一Website,由Routing rule(路由規則) 判斷目前網頁之語系。
l  顯示之語系規則順序如下:
(1)     Cookie
(2)     URL (Routing rule)
(3)     Browser default localization setting


Routing rule控制語系的好處在於 使用者可任意切換各種語系來做觀看,而不必去更動瀏覽器的設定。

延伸閱讀


環境

l   Windows 7 Enterprise
l   Visual Studio 2013 update 5
l   MVC 5



實作


Routing Rule

因為本文章的首要目標是以Routing rule判斷使用者瀏覽的語系,所以第一件事情是更新預設的Routing rule.

routes.MapRoute(
       name: "Default", // Route name
       url: "{lang}/{controller}/{action}/{id}", // URL with parameters
       defaults: new { lang = "Default", controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
       namespaces :  new[] { "JB.Sample.MultiLangWeb.Website.Controllers" }
);


 
UI

以下圖之UI 為範例。



l   _Layout.cshtml

<div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("首頁", "Index", "Home")</li>
                    <li>@Html.ActionLink("關於", "About", "Home")</li>
                    <li>@Html.ActionLink("連絡方式", "Contact", "Home")</li>

                    @if (ViewBag.Lang == "ZH")
                    {
                        <li>
                            <a href="~/CN" onclick="Redirect('CN')">簡體中文</a>
                        </li>
                        <li>
                            <a href="~/EN" onclick="Redirect('EN')">英文</a>
                        </li>
                    }
                    else if (ViewBag.Lang == "EN")
                    {
                        <li>
                            <a href="~/ZH" onclick="Redirect('ZH')">Traditional Chinese</a>
                        </li>
                        <li>
                            <a href="~/CN" onclick="Redirect('CN')">Simplified Chinese</a>
                        </li>
                    }
                    else if (ViewBag.Lang == "CN")
                    {
                        <li>
                            <a href="~/ZH" onclick="Redirect('ZH')">繁体中文</a>
                        </li>
                        <li>
                            <a href="~/EN" onclick="Redirect('EN')">英文</a>
                        </li>
                    }
                </ul>
</div>

當點選語言時,Post back 設定Cookie並做轉址。
所以需要在後端Controller加上一Http Post method 並加上Ajax.


Controller : Http Post Method

[HttpPost]
 public HttpResponseMessage SetLocaleCookie(string locale)
 {
            try
            {
                HttpCookie cultureCookie = Request.Cookies["_culture"];
                if (cultureCookie != null)
                {
                    cultureCookie.Value = locale;
                }
                else
                {
                    cultureCookie = new HttpCookie("_culture");
                    cultureCookie.HttpOnly = true;
                    cultureCookie.Value = locale;
                    //cultureCookie.Expires = DateTime.Now.AddMonths(2);
                }

                Response.Cookies.Add(cultureCookie);
            }
            catch (Exception ex)
            {
            }

            return new HttpResponseMessage(HttpStatusCode.OK);
}


Javascript

function Redirect(locale) {

    var postUrl = "Default/Home/SetLocaleCookie";

    $.ajax({
        url: postUrl,
        type: "POST",
        data: JSON.stringify({ locale }),
        contentType: 'application/json; charset=utf-8',
        dataType: "json",
        //timeout: 10000,
        success: function (data, status) {
        },
        error: function (jqXHR, textStatus, err) {
        }
    });
}


到目前為止,我們已經可以讓使用者自由的設定網站的語系Cookie
下一步,我們要在網站首頁 (例如本例的本機開發網址  http://localhost:2609/ ) 判斷要導向到哪個語系的網址。


Home Controller  


由以上程式碼可知道本例支援的語系為:
ü   繁中:http://localhost:2609/ZH
ü   簡中:http://localhost:2609/CN
ü   簡中:http://localhost:2609/EN

判斷邏輯(順序)
(1)     Cookie
(2)     URL (Routing rule)
(3)     Browser default localization setting


public class HomeController : Controller
{
     public ActionResult Index()
     {
            string lang = HttpContext.Request.RequestContext.RouteData.Values["lang"].ToString();
            ViewBag.Lang = lang;


            //判斷是否須轉址 (以使用者的瀏覽器語言設定為主)
            if (lang.Equals("Default"))
            {
                string redirectUri = getRedirectUri(lang);
                if (!string.IsNullOrEmpty(redirectUri))
                {
                    return Redirect(redirectUri);
                }
            }
            return View();
     }

private string getRedirectUri(string locale)
{
            string redirectUri = string.Empty;

            HttpCookie cultureCookie = Request.Cookies["_culture"];

            //若已設定Cookie則以Cookie為主
            if (cultureCookie != null//已設定Culture Cookie,則以Cookie為主
            {
                locale = cultureCookie.Value;
            }
            //若未設定Cookie且未指定網頁語系,則以瀏覽器預設語系為主
            else if (string.IsNullOrEmpty(locale) || locale.Equals("Default"))
            {
                //取得用戶端語言喜好設定(已排序的字串陣列)
                var userLanguages = Request.UserLanguages;

                if (userLanguages.Length > 0)
                {
                    try
                    {
                        string userLang = userLanguages[0];
                        switch (userLang.ToLower())
                        {
                            case "en-us":
                                locale = "EN";
                                break;
                            case "zh-cn":
                                locale = "CN";
                                break;
                            case "zh-tw":
                            default:
                                locale = "ZH";
                                break;
                        }
                    }
                    catch (CultureNotFoundException ex)
                    {
                        //Do something
                    }
                }
            }
            else //已指定網頁語系
            {
                //Do nothing
            }


            //Redirect
            try
            {

                switch (locale.ToUpper())
                {
                    case "EN":
                        redirectUri = "/EN";
                        break;
                    case "CN":
                        redirectUri = "/CN";
                        break;
                    case "ZH":
                    default:
                        redirectUri = "/ZH";
                        break;
                }

                return redirectUri;

            }
            catch (Exception)
            {
                throw;
            }

     }
}


測試
















Reference