Đặt Văn hóa trong ứng dụng ASP.Net MVC


85

Đâu là nơi tốt nhất để đặt Văn hóa giao diện người dùng / Văn hóa trong ứng dụng ASP.net MVC

Hiện tại tôi có một lớp CultureController trông giống như sau:

public class CultureController : Controller
{
    public ActionResult SetSpanishCulture()
    {
        HttpContext.Session["culture"] = "es-ES";
        return RedirectToAction("Index", "Home");
    }

    public ActionResult SetFrenchCulture()
    {
        HttpContext.Session["culture"] = "fr-FR";
        return RedirectToAction("Index", "Home");
    }
}

và một siêu liên kết cho từng ngôn ngữ trên trang chủ với một liên kết như sau:

<li><%= Html.ActionLink("French", "SetFrenchCulture", "Culture")%></li>
<li><%= Html.ActionLink("Spanish", "SetSpanishCulture", "Culture")%></li>

hoạt động tốt nhưng tôi đang nghĩ có một cách thích hợp hơn để làm điều này.

Tôi đang đọc Văn hóa bằng cách sử dụng ActionFilter sau đây http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx . Tôi là một noob MVC nên không tự tin rằng tôi đang đặt điều này ở đúng vị trí. Tôi không muốn làm điều đó ở cấp độ web.config, nó phải dựa trên sự lựa chọn của người dùng. Tôi cũng không muốn kiểm tra tiêu đề http của họ để lấy văn hóa từ cài đặt trình duyệt của họ.

Biên tập:

Chỉ cần nói rõ - tôi không cố gắng quyết định có sử dụng phiên hay không. Tôi hài lòng với chút đó. Điều tôi đang cố gắng tìm ra là nếu tốt nhất nên thực hiện việc này trong bộ điều khiển Văn hóa có phương pháp hành động cho từng Văn hóa được thiết lập hoặc có nơi nào tốt hơn trong đường dẫn MVC để thực hiện việc này?


Sử dụng trạng thái phiên để chọn văn hóa người dùng không phải là một lựa chọn tốt. Cách tốt nhất là bao gồm văn hóa như một phần của URL , điều này giúp bạn dễ dàng "hoán đổi" trang hiện tại với một nền văn hóa khác.
NightOwl888

Câu trả lời:


114

Tôi đang sử dụng phương pháp bản địa hóa này và đã thêm thông số tuyến đường đặt văn hóa và ngôn ngữ bất cứ khi nào người dùng truy cập example.com/xx-xx/

Thí dụ:

routes.MapRoute("DefaultLocalized",
            "{language}-{culture}/{controller}/{action}/{id}",
            new
            {
                controller = "Home",
                action = "Index",
                id = "",
                language = "nl",
                culture = "NL"
            });

Tôi có một bộ lọc thực hiện cài đặt văn hóa / ngôn ngữ thực tế:

using System.Globalization;
using System.Threading;
using System.Web.Mvc;

public class InternationalizationAttribute : ActionFilterAttribute {

    public override void OnActionExecuting(ActionExecutingContext filterContext) {

        string language = (string)filterContext.RouteData.Values["language"] ?? "nl";
        string culture = (string)filterContext.RouteData.Values["culture"] ?? "NL";

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(string.Format("{0}-{1}", language, culture));
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(string.Format("{0}-{1}", language, culture));

    }
}

Để kích hoạt thuộc tính Internationalization, chỉ cần thêm nó vào lớp của bạn:

[Internationalization]
public class HomeController : Controller {
...

Giờ đây, bất cứ khi nào khách truy cập vào http://example.com/de-DE/Home/Index , trang web của Đức sẽ được hiển thị.

Tôi hy vọng câu trả lời này sẽ hướng bạn đi đúng hướng.

Tôi cũng đã tạo một dự án ví dụ MVC 5 nhỏ mà bạn có thể tìm thấy ở đây

Chỉ cần truy cập http: // {yourhost}: {port} / en-us / home / index để xem ngày hiện tại bằng tiếng Anh (Mỹ) hoặc đổi thành http: // {yourhost}: {port} / de -de / home / index cho etcetera của Đức.


15
Tôi cũng thích đặt ngôn ngữ trong URL, vì công cụ tìm kiếm có thể thu thập thông tin bằng các ngôn ngữ khác nhau và cho phép người dùng lưu hoặc gửi URL với một ngôn ngữ cụ thể.
Eduardo Molteni

50
Thêm ngôn ngữ vào url không vi phạm REST. Thực tế nó tuân theo nó bằng cách làm cho tài nguyên web không phụ thuộc vào trạng thái phiên ẩn.
Jace Rhea

4
Tài nguyên web không phụ thuộc vào trạng thái ẩn, cách nó được hiển thị. Nếu bạn muốn truy cập các tài nguyên như một dịch vụ web, bạn sẽ cần phải chọn một ngôn ngữ để làm điều đó trong.
Dave Van den Eynde

4
Tôi đã gặp một số vấn đề với loại giải pháp này. Thông báo lỗi xác thực không được dịch. Để giải quyết vấn đề, tôi đặt văn hóa trong hàm Application_AcquireRequestState của tệp global.asax.cs.
ADH

4
Đưa cái này vào bộ lọc KHÔNG phải là một ý kiến ​​hay. Liên kết mô hình sử dụng CurrentCulture, nhưng ActionFilter xuất hiện sau liên kết mô hình. Tốt hơn nên làm điều này trong Global.asax, Application_PreRequestHandlerExecute.
Stefan

38

Tôi biết đây là một câu hỏi cũ, nhưng nếu bạn thực sự muốn điều này hoạt động với ModelBinder của bạn (liên quan đến DefaultModelBinder.ResourceClassKey = "MyResource";cũng như các tài nguyên được chỉ ra trong chú thích dữ liệu của các lớp mô hình), bộ điều khiển hoặc thậm chí ActionFilterlà quá muộn để thiết lập văn hóa .

Văn hóa có thể được đặt trong Application_AcquireRequestState, ví dụ:

protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        // For example a cookie, but better extract it from the url
        string culture = HttpContext.Current.Request.Cookies["culture"].Value;

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture);
    }

BIÊN TẬP

Trên thực tế, có một cách tốt hơn là sử dụng trình xử lý định tuyến tùy chỉnh đặt văn hóa theo url, được Alex Adamyan mô tả hoàn hảo trên blog của mình .

Tất cả những gì cần làm là ghi đè GetHttpHandlerphương thức và đặt văn hóa ở đó.

public class MultiCultureMvcRouteHandler : MvcRouteHandler
{
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        // get culture from route data
        var culture = requestContext.RouteData.Values["culture"].ToString();
        var ci = new CultureInfo(culture);
        Thread.CurrentThread.CurrentUICulture = ci;
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
        return base.GetHttpHandler(requestContext);
    }
}

Không may là RouteData, v.v. không có sẵn trong phương thức "Application_AcquireRequestState" nhưng chúng có trong Controller.CreateActionInvoker (). Vì vậy, tôi đề nghị "ghi đè được bảo vệ IActionInvoker CreateActionInvoker ()" và đặt CultureInfo ngay tại đó.
Skorunka František

Tôi đã đọc blog đó. Có vấn đề gì không nếu tôi tiếp tục với cookie? Vì tôi không có quyền thay đổi nó. Vui lòng thông báo cho tôi. có vấn đề gì với cách tiếp cận này không?
kbvishnu 23/10/12

@VeeKeyBee Nếu trang web của bạn ở chế độ công khai, tất cả các ngôn ngữ sẽ không được lập chỉ mục chính xác khi sử dụng cookie, đối với các trang web được bảo vệ, bạn có thể ổn.
marapet 23/10/12

không phải nó không công khai. Bạn có thể vui lòng cho một gợi ý về từ "được lập chỉ mục"?
kbvishnu 23/10/12

1
Bạn nên đặt câu hỏi của riêng bạn và đọc về SEO, điều này không có gì để làm nữa với câu hỏi ban đầu. webmasters.stackexchange.com/questions/3786/…
marapet

25

Tôi sẽ làm điều đó trong sự kiện Khởi tạo của bộ điều khiển như thế này ...

    protected override void Initialize(System.Web.Routing.RequestContext requestContext)
    {
        base.Initialize(requestContext);

        const string culture = "en-US";
        CultureInfo ci = CultureInfo.GetCultureInfo(culture);

        Thread.CurrentThread.CurrentCulture = ci;
        Thread.CurrentThread.CurrentUICulture = ci;
    }

1
chuỗi văn hóa không được là một hằng số, vì người dùng cần có khả năng chỉ định văn hóa họ muốn sử dụng trên trang web.
NerdFury

2
Tôi hiểu điều đó, nhưng câu hỏi đặt ra là ở đâu tốt nhất để thiết lập văn hóa chứ không phải thiết lập nó như thế nào.
Jace Rhea

Thay vì một const, bạn có thể sử dụng một cái gì đó như: var newCulture = new CultureInfo (RouteData.Values ​​["lang"]. ToString ());
Nordes

AuthorizeCore được gọi trước OnActionExecuting, vì vậy bạn sẽ không có bất kỳ chi tiết văn hóa nào trong phương thức ghi đè AuthorizeCore của mình. Sử dụng phương thức khởi tạo của bộ điều khiển có thể hoạt động tốt hơn, đặc biệt nếu bạn đang triển khai AuthorizeAttribute tùy chỉnh, vì phương thức Khởi tạo được gọi trước AuthorizeCore (bạn sẽ có thông tin chi tiết về văn hóa trong AuthorizeCore).
Nathan R

7

Vì nó là một cài đặt được lưu trữ cho mỗi người dùng, phiên là một nơi thích hợp để lưu trữ thông tin.

Tôi sẽ thay đổi bộ điều khiển của bạn để lấy chuỗi văn hóa làm tham số, thay vì có một phương pháp hành động khác nhau cho từng nền văn hóa tiềm năng. Thêm một liên kết đến trang rất dễ dàng và bạn không cần phải viết lại cùng một đoạn mã bất cứ khi nào yêu cầu một nền văn hóa mới.

public class CultureController : Controller    
{
        public ActionResult SetCulture(string culture)
        {
            HttpContext.Session["culture"] = culture
            return RedirectToAction("Index", "Home");
        }        
}

<li><%= Html.ActionLink("French", "SetCulture", new {controller = "Culture", culture = "fr-FR"})%></li>
<li><%= Html.ActionLink("Spanish", "SetCulture", new {controller = "Culture", culture = "es-ES"})%></li>

cảm ơn vì câu trả lời, tôi không cố gắng phân tích xem có nên sử dụng phiên hay không. Tôi hài lòng với chút đó. Những gì tôi đang cố gắng để làm việc ra là nếu nó là tốt nhất để làm điều này trong một bộ điều khiển văn hóa có một phương pháp hành động cho mỗi văn hóa được thiết lập Hoặc là có một nơi tốt hơn trong các đường ống MVC để làm điều này
ChrisCa

Tôi đã cung cấp một câu trả lời được chỉnh sửa phù hợp hơn với câu hỏi.
NerdFury

Vâng, điều đó chắc chắn là sạch hơn, nhưng điều tôi thực sự muốn biết là liệu điều này có nên được thực hiện trong Bộ điều khiển hay không. Hoặc nếu có một nơi tốt hơn trong đường ống MVC để đặt Văn hóa. Hoặc nếu nó là tốt hơn trong một ActionFilters, Handlers, Modules vv
ChrisCa

Một trình xử lý và mô-đun không có ý nghĩa vì người dùng không có cơ hội thực hiện lựa chọn. Bạn cần một cách để người dùng thực hiện lựa chọn và sau đó xử lý lựa chọn người dùng, việc này sẽ được thực hiện trong bộ điều khiển.
NerdFury 13/10/09

đã đồng ý, trình xử lý và mô-đun sẽ sớm cho phép người dùng tương tác. Tuy nhiên, tôi còn khá mới đối với MVC nên không chắc liệu đây có phải là nơi tốt nhất để đặt nó hay không. Nếu tôi không nghe thấy sau một thời gian, tôi sẽ chấp nhận câu trả lời của bạn. ps rằng cú pháp bạn đã sử dụng để truyền một tham số cho một phương thức Hành động dường như không hoạt động. Nó không có bộ điều khiển được xác định vì vậy chỉ sử dụng bộ điều khiển mặc định (không phải là bộ điều khiển chính xác trong trường hợp này). Và có doesnt dường như quá tải khác phù hợp
ChrisCa

6

Đâu là nơi tốt nhất là câu hỏi của bạn. Nơi tốt nhất là bên trong phương thức Controller.Initialize . MSDN viết rằng nó được gọi sau hàm tạo và trước phương thức hành động. Ngược lại với việc ghi đè OnActionExecuting, việc đặt mã của bạn trong phương thức Khởi tạo cho phép bạn có lợi khi có tất cả các chú thích và thuộc tính dữ liệu tùy chỉnh trên các lớp và trên các thuộc tính của bạn được bản địa hóa.

Ví dụ: logic bản địa hóa của tôi đến từ một lớp được đưa vào bộ điều khiển tùy chỉnh của tôi. Tôi có quyền truy cập vào đối tượng này vì Khởi tạo được gọi sau hàm tạo. Tôi có thể thực hiện việc gán văn hóa của Chủ đề và không có mọi thông báo lỗi được hiển thị chính xác.

 public BaseController(IRunningContext runningContext){/*...*/}

 protected override void Initialize(RequestContext requestContext)
 {
     base.Initialize(requestContext);
     var culture = runningContext.GetCulture();
     Thread.CurrentThread.CurrentUICulture = culture;
     Thread.CurrentThread.CurrentCulture = culture;
 }

Ngay cả khi logic của bạn không nằm trong một lớp như ví dụ tôi đã cung cấp, bạn có quyền truy cập vào RequestContext cho phép bạn có URL và HttpContextRouteData mà về cơ bản bạn có thể thực hiện bất kỳ phân tích cú pháp nào có thể.


Điều này hoạt động cho HTML5 Telerik ReportLocalization của tôi !. Cảm ơn @Patrick Desjardins
CoderRoller

4

Nếu sử dụng Tên miền phụ, chẳng hạn như "pt.mydomain.com" để đặt tiếng Bồ Đào Nha chẳng hạn, thì việc sử dụng Application_AcquireRequestState sẽ không hoạt động vì nó không được gọi trong các yêu cầu bộ nhớ cache tiếp theo.

Để giải quyết vấn đề này, tôi đề xuất một cách triển khai như sau:

  1. Thêm tham số VaryByCustom vào OutPutCache như sau:

    [OutputCache(Duration = 10000, VaryByCustom = "lang")]
    public ActionResult Contact()
    {
        return View("Contact");
    }
    
  2. Trong global.asax.cs, lấy văn hóa từ máy chủ lưu trữ bằng cách gọi hàm:

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost();
    }
    
  3. Thêm hàm GetCultureFromHost vào global.asax.cs:

    private CultureInfo GetCultureFromHost()
    {
        CultureInfo ci = new CultureInfo("en-US"); // en-US
        string host = Request.Url.Host.ToLower();
        if (host.Equals("mydomain.com"))
        {
            ci = new CultureInfo("en-US");
        }
        else if (host.StartsWith("pt."))
        {
            ci = new CultureInfo("pt");
        }
        else if (host.StartsWith("de."))
        {
            ci = new CultureInfo("de");
        }
        else if (host.StartsWith("da."))
        {
            ci = new CultureInfo("da");
        }
    
        return ci;
    }
    
  4. Và cuối cùng ghi đè GetVaryByCustomString (...) để sử dụng chức năng này:

    public override string GetVaryByCustomString(HttpContext context, string value)
    {
        if (value.ToLower() == "lang")
        {
            CultureInfo ci = GetCultureFromHost();
            return ci.Name;
        }
        return base.GetVaryByCustomString(context, value);
    }
    

Hàm Application_AcquireRequestState được gọi trên các lệnh gọi không được lưu trong bộ đệm, cho phép nội dung được tạo và lưu vào bộ đệm. GetVaryByCustomString được gọi trong các cuộc gọi được lưu trong bộ nhớ cache để kiểm tra xem nội dung có sẵn trong bộ nhớ cache hay không và trong trường hợp này, chúng tôi kiểm tra giá trị miền máy chủ lưu trữ đến, một lần nữa, thay vì chỉ dựa vào thông tin văn hóa hiện tại, có thể đã thay đổi cho yêu cầu mới (bởi vì chúng tôi đang sử dụng tên miền phụ).


4

1: Tạo thuộc tính tùy chỉnh và ghi đè phương thức như sau:

public class CultureAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
    // Retreive culture from GET
    string currentCulture = filterContext.HttpContext.Request.QueryString["culture"];

    // Also, you can retreive culture from Cookie like this :
    //string currentCulture = filterContext.HttpContext.Request.Cookies["cookie"].Value;

    // Set culture
    Thread.CurrentThread.CurrentCulture = new CultureInfo(currentCulture);
    Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(currentCulture);
    }
}

2: Trong App_Start, tìm FilterConfig.cs, thêm thuộc tính này. (điều này hoạt động cho ứng dụng TOÀN BỘ)

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
    // Add custom attribute here
    filters.Add(new CultureAttribute());
    }
}    

Đó là nó !

Nếu bạn muốn xác định văn hóa cho từng bộ điều khiển / hành động thay vì toàn bộ ứng dụng, bạn có thể sử dụng thuộc tính này như sau:

[Culture]
public class StudentsController : Controller
{
}

Hoặc là:

[Culture]
public ActionResult Index()
{
    return View();
}

0
protected void Application_AcquireRequestState(object sender, EventArgs e)
        {
            if(Context.Session!= null)
            Thread.CurrentThread.CurrentCulture =
                    Thread.CurrentThread.CurrentUICulture = (Context.Session["culture"] ?? (Context.Session["culture"] = new CultureInfo("pt-BR"))) as CultureInfo;
        }

3
Vui lòng giải thích lý do tại sao đây được cho là cách tốt nhất.
Max Leske
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.