Kültürü bir ASP.Net MVC uygulamasında ayarlama


85

ASP.net MVC uygulamasında Kültür / Kullanıcı Arabirimi Kültürünü ayarlamak için en iyi yer neresidir?

Şu anda aşağıdaki gibi görünen bir CultureController sınıfım var:

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");
    }
}

ve ana sayfadaki her dil için aşağıdaki gibi bir bağlantı içeren bir köprü:

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

iyi çalışıyor ama bunu yapmanın daha uygun bir yolu olduğunu düşünüyorum.

Kültürü aşağıdaki ActionFilter http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx kullanarak okuyorum . Ben biraz MVC noob'um, bu yüzden bunu doğru yere koyduğuma emin değilim. Bunu web.config seviyesinde yapmak istemiyorum, kullanıcının seçimine dayalı olmalı. Ayrıca, tarayıcı ayarlarından kültürü almak için http başlıklarını kontrol etmek istemiyorum.

Düzenle:

Sadece açık olmak gerekirse - seansı kullanıp kullanmamaya karar vermeye çalışmıyorum. Ben bundan biraz memnunum. Anlamaya çalıştığım şey, bunu her Kültür için ayarlanacak bir eylem yöntemine sahip bir Kültür denetleyicisinde yapmak en iyisi mi yoksa MVC işlem hattında bunu yapmak için daha iyi bir yer mi var?


Kullanıcı kültürünü seçmek için oturum durumunu kullanmak iyi bir seçim değildir. En iyi yol, kültürü URL'nin bir parçası olarak eklemektir; bu, geçerli sayfayı başka bir kültürle "değiştirmeyi" kolaylaştırır.
NightOwl888

Yanıtlar:


114

Bu yerelleştirme yöntemini kullanıyorum ve bir kullanıcı example.com/xx-xx/ adresini her ziyaret ettiğinde kültürü ve dili ayarlayan bir rota parametresi ekliyorum

Misal:

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

Gerçek kültür / dil ayarını yapan bir filtrem var:

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));

    }
}

Uluslararasılaştırma özelliğini etkinleştirmek için, onu sınıfınıza eklemeniz yeterlidir:

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

Artık bir ziyaretçi http://example.com/de-DE/Home/Index adresine her gittiğinde Almanca site görüntülenir.

Umarım bu cevap sizi doğru yönde işaret eder.

Ayrıca burada bulabileceğiniz küçük bir MVC 5 örnek proje yaptım

Geçerli tarihi İngilizce (ABD) olarak görmek için http: // {yourhost}: {port} / en-us / home / index adresine gitmeniz veya http: // {yourhost}: {port} / de olarak değiştirmeniz yeterli Almanca vb. için -de / home / index.


15
Ayrıca dili, farklı dillerdeki arama motorları tarafından taranabilir hale geldiğinden ve kullanıcının belirli bir dilde bir URL kaydetmesine veya göndermesine izin verdiği için URL'ye koymayı da seviyorum.
Eduardo Molteni

50
Dilin url'ye eklenmesi REST'i ihlal etmez. Web kaynağını gizli bir oturum durumuna bağımlı hale getirmeyerek ona bağlı kaldığı gerçeği.
Jace Rhea

4
Web kaynağı, işlenme şekli gibi gizli bir duruma bağlı değildir. Kaynağa bir web hizmeti olarak erişmek istiyorsanız, bunu yapmak için bir dil seçmeniz gerekir.
Dave Van den Eynde

4
Bu tür bir çözümle ilgili bazı sorunlarım vardı. Doğrulama hata mesajları tercüme edilmiyordu. Sorunu çözmek için kültürü global.asax.cs dosyasının Application_AcquireRequestState işlevinde ayarladım.
ADH

4
Bunu bir filtreye koymak iyi bir fikir DEĞİLDİR. Model bağlama, CurrentCulture kullanır, ancak ActionFilter model bağlamadan sonra gerçekleşir. Bunu Global.asax, Application_PreRequestHandlerExecute içinde yapmak daha iyidir.
Stefan

38

Bunun eski bir soru olduğunu biliyorum, ancak bunun ModelBinder'inizle çalışmasını gerçekten istiyorsanız ( DefaultModelBinder.ResourceClassKey = "MyResource";görünüm modeli sınıflarının veri ek açıklamalarında belirtilen kaynaklarla ilgili olarak), denetleyici veya hatta an ActionFilteriçin çok geç. kültürü ayarlayın .

Kültür Application_AcquireRequestState, örneğin:

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);
    }

DÜZENLE

Aslında , kültürü URL'ye göre ayarlayan özel bir yönlendirme kullanmanın daha iyi bir yolu var, Alex Adamyan'ın blogunda mükemmel bir şekilde tanımladığı .

Yapılması gereken tek şey GetHttpHandleryöntemi geçersiz kılmak ve kültürü oraya ayarlamaktır.

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);
    }
}

Ne yazık ki RouteData vb. "Application_AcquireRequestState" yönteminde mevcut değil, ancak Controller.CreateActionInvoker () içindedir. Bu yüzden "IActionInvoker CreateActionInvoker () korumalı geçersiz kılmayı" ve CultureInfo'yu tam orada ayarlamanızı öneririm.
Skorunka František

O blogu okudum. Çerez ile devam edersem herhangi bir sorun olur mu? Değiştirmeye iznim olmadığı için. Lütfen beni bilgilendirin. bu yaklaşımla ilgili herhangi bir sorun var mı?
kbvishnu

@VeeKeyBee Siteniz herkese açıksa, tanımlama bilgilerini kullanırken tüm diller düzgün şekilde dizine eklenmez, korumalı siteler için muhtemelen sorun yoktur.
marapet

halka açık değil. Lütfen "indekslenmiş" kelimesi hakkında bir ipucu verebilir misiniz?
kbvishnu

1
Kendi sorunuzu sormalı ve SEO hakkında okumalısınız, bunun artık orijinal soruyla hiçbir ilgisi yok. webmasters.stackexchange.com/questions/3786/…
marapet

25

Bunu denetleyicinin Initialize olayında şöyle yapardım ...

    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
kullanıcının sitede kullanmak istediği kültürü belirtebilmesi gerektiğinden, kültür dizesi bir sabit olamaz.
NerdFury

2
Bunu anlıyorum, ama soru kültürü nasıl yerleştireceğimiz değil, en iyi nerede yerleştireceğiydi.
Jace Rhea

Sabit yerine, aşağıdaki gibi bir şey kullanabilirsiniz: var newCulture = new CultureInfo (RouteData.Values ​​["lang"]. ToString ());
Nordes

AuthorizeCore, OnActionExecuting'den önce çağrılır, bu nedenle AuthorizeCore geçersiz kılınan yönteminizde herhangi bir kültür ayrıntısına sahip olmazsınız. Denetleyicinin başlatma yöntemini kullanmak, özellikle özel bir AuthorizeAttribute uyguluyorsanız daha iyi çalışabilir, çünkü Initialize yöntemi AuthorizeCore'dan önce çağrılır (AuthorizeCore içinde kültür ayrıntılarına sahip olacaksınız).
Nathan R

7

Kullanıcı başına depolanan bir ayar olduğu için oturum, bilgileri depolamak için uygun bir yerdir.

Denetleyicinizi, her potansiyel kültür için farklı bir eylem yöntemine sahip olmak yerine kültür dizesini bir parametre olarak alacak şekilde değiştirirdim. Sayfaya bağlantı eklemek kolaydır ve yeni bir kültür gerektiğinde aynı kodu tekrar tekrar yazmanıza gerek yoktur.

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>

cevap için teşekkürler, seansı kullanıp kullanmamaya karar vermeye çalışmıyorum. Ben bundan biraz memnunum. Çözmeye çalıştığım şey, bunu, ayarlanacak her Kültür için bir eylem yöntemi olan bir Kültür denetleyicisinde yapmak en iyisi mi yoksa MVC ardışık
düzeninde

Soruya daha iyi uyan düzenlenmiş bir yanıt verdim.
NerdFury

Evet, bu kesinlikle daha temiz, ama gerçekten bilmek istediğim şey bunun bir Kontrolörde yapılması gerekip gerekmediğidir. Veya MVC ardışık düzeninde Kültürü ayarlamak için daha iyi bir yer varsa. Veya bir ActionFilters, Handlers, Modules
vs.'de daha iyiyse

Bir işleyici ve modül bir anlam ifade etmiyor çünkü kullanıcının seçim yapma şansı olmadı. Kullanıcının bir seçim yapması ve ardından bir kontrolörde yapılacak kullanıcı seçimini işlemesi için bir yola ihtiyacınız var.
NerdFury

kabul edildi, işleyiciler ve modüller, kullanıcı etkileşimine izin vermek için erkendir. Bununla birlikte, MVC'de oldukça yeniyim, bu yüzden onu ayarlamak için boru hattında en iyi yer olup olmadığından emin değilim. Bir süre sonra aksini duymazsam cevabınızı kabul edeceğim. Bir parametreyi bir Action yöntemine iletmek için kullandığınız sözdiziminin çalışmadığı görülüyor. Tanımlanmış bir denetleyiciye sahip değildir, bu nedenle yalnızca varsayılanı kullanır (bu durumda bu doğru olanı değildir). Ve uygun başka bir aşırı yük yok gibi görünüyor
ChrisCa

6

Sorunuz en iyi yer neresi? En iyi yer Controller.Initialize yönteminin içindedir . MSDN, yapıcıdan sonra ve eylem yönteminden önce çağrıldığını yazar. OnActionExecuting'i geçersiz kılmanın tersine, kodunuzu Initialize yöntemine yerleştirmek, sınıflarınızda ve yerelleştirilecek özelliklerinizde tüm özel veri ek açıklamalarına ve özniteliğine sahip olmanıza olanak tanır.

Örneğin, yerelleştirme mantığım özel denetleyicime enjekte edilen bir sınıftan geliyor. Yapıcıdan sonra Initialize çağrıldığından bu nesneye erişimim var. Thread'in kültür atamasını yapabiliyorum ve her hata mesajının doğru şekilde görüntülenmesini sağlayamıyorum.

 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;
 }

Senin mantık verdiğim örnekteki gibi bir sınıf içerisindeki olmasa bile, sen erişebilir RequestContext URL'yi ve kullanmasına izin HttpContext ve RouteData mümkün ayrıştırma herhangi temelde yapabilirsiniz.


Bu, HTML5 Telerik ReportLocalization'ım için çalışıyor !. Teşekkürler @Patrick Desjardins
CoderRoller

4

Örneğin portekizceyi ayarlamak için "pt.mydomain.com" gibi Subdomainleri kullanıyorsanız, sonraki önbellek isteklerinde çağrılmadığı için Application_AcquireRequestState kullanılması işe yaramaz.

Bunu çözmek için şöyle bir uygulama öneririm:

  1. VaryByCustom parametresini OutPutCache'ye şu şekilde ekleyin:

    [OutputCache(Duration = 10000, VaryByCustom = "lang")]
    public ActionResult Contact()
    {
        return View("Contact");
    }
    
  2. Global.asax.cs içinde, bir işlev çağrısı kullanarak kültürü ana bilgisayardan alın:

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost();
    }
    
  3. GetCultureFromHost işlevini global.asax.cs dosyasına ekleyin:

    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. Ve son olarak, bu işlevi de kullanmak için GetVaryByCustomString (...) öğesini geçersiz kılın:

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

Application_AcquireRequestState işlevi, içeriğin oluşturulmasına ve önbelleğe alınmasına izin veren önbelleğe alınmamış çağrılarda çağrılır. GetVaryByCustomString, içeriğin önbellekte bulunup bulunmadığını kontrol etmek için önbelleğe alınmış çağrılara çağrılır ve bu durumda, yeni istek için değişmiş olabilecek mevcut kültür bilgisine güvenmek yerine, gelen ana bilgisayar etki alanı değerini tekrar inceliyoruz (çünkü alt alanlar kullanıyoruz).


4

1: Özel bir özellik oluşturun ve aşağıdaki gibi yöntemi geçersiz kılın:

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: App_Start'ta FilterConfig.cs'yi bulun, bu niteliği ekleyin. (bu BÜTÜN uygulama için işe yarar)

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

Bu kadar !

Tüm uygulama yerine her bir denetleyici / eylem için kültür tanımlamak istiyorsanız, bu özniteliği şu şekilde kullanabilirsiniz:

[Culture]
public class StudentsController : Controller
{
}

Veya:

[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
Lütfen bunun neden en iyi yol olduğunu açıklayın.
Max Leske
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.