ASP.Net MVC 앱에서 문화 설정


85

ASP.net MVC 앱에서 Culture / UI Culture를 설정하는 가장 좋은 장소는 무엇입니까?

현재 다음과 같은 CultureController 클래스가 있습니다.

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

다음과 같은 링크가있는 홈페이지의 각 언어에 대한 하이퍼 링크 :

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

잘 작동하지만 더 적절한 방법이 있다고 생각합니다.

다음 ActionFilter http://www.iansuttle.com/blog/post/ASPNET-MVC-Action-Filter-for-Localized-Sites.aspx를 사용하여 문화를 읽고 있습니다. 나는 약간의 MVC 멍청이이므로 올바른 위치에 설정하고 있다고 확신하지 못합니다. web.config 수준에서 수행하고 싶지 않으며 사용자의 선택을 기반으로해야합니다. 또한 브라우저 설정에서 문화를 얻기 위해 http 헤더를 확인하고 싶지 않습니다.

편집하다:

명확하게 말하면 세션 사용 여부를 결정하려는 것이 아닙니다. 나는 그 비트에 만족합니다. 내가 해결하려는 것은 각 Culture에 대한 작업 방법이 설정되어있는 Culture 컨트롤러에서이 작업을 수행하는 것이 가장 좋은지 아니면 MVC 파이프 라인에 더 나은 위치가 있는지 여부입니다.


세션 상태를 사용하여 사용자 문화를 선택하는 것은 좋은 선택이 아닙니다. 가장 좋은 방법은 URL의 일부로 문화포함하는 것 입니다. 이렇게하면 현재 페이지를 다른 문화로 쉽게 "교체"할 수 있습니다.
NightOwl888

답변:


114

지역화 방법을 사용 하고 사용자가 example.com/xx-xx/를 방문 할 때마다 문화와 언어를 설정하는 경로 매개 변수를 추가했습니다.

예:

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

실제 문화 / 언어 설정을 수행하는 필터가 있습니다.

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

    }
}

Internationalization 속성을 활성화하려면 클래스에 추가하기 만하면됩니다.

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

이제 방문자가 http://example.com/de-DE/Home/Index이동할 때마다 독일어 사이트가 표시됩니다.

이 답변이 올바른 방향으로 안내되기를 바랍니다.

또한 여기에서 찾을 수있는 작은 MVC 5 예제 프로젝트를 만들었습니다.

http : // {yourhost} : {port} / en-us / home / index로 이동하여 현재 날짜를 영어 (미국)로 확인하거나 http : // {yourhost} : {port} / de로 변경하세요. 독일어 등의 경우 -de / home / index.


15
또한 다른 언어로 검색 엔진에서 크롤링 할 수있게되었고 사용자가 특정 언어로 URL을 저장하거나 보낼 수 있도록했기 때문에 URL에 lang을 넣는 것을 좋아합니다.
Eduardo Molteni

50
URL에 언어를 추가해도 REST를 위반하지 않습니다. 웹 리소스가 숨겨진 세션 상태에 의존하지 않도록하여이를 준수합니다.
Jace Rhea

4
웹 리소스는 렌더링되는 방식과 같이 숨겨진 상태에 의존하지 않습니다. 웹 서비스로 리소스에 액세스하려면 사용할 언어를 선택해야합니다.
Dave Van den Eynde

4
이 유형의 솔루션에 몇 가지 문제가 있습니다. 유효성 검사 오류 메시지가 번역되지 않았습니다. 문제를 해결하기 위해 global.asax.cs 파일의 Application_AcquireRequestState 함수에 문화를 설정했습니다.
ADH

4
이것을 필터에 넣는 것은 좋은 생각이 아닙니다. 모델 바인딩은 CurrentCulture를 사용하지만 ActionFilter는 모델 바인딩 후에 발생합니다. Global.asax, Application_PreRequestHandlerExecute에서이 작업을 수행하는 것이 좋습니다.
Stefan

38

나는 이것이 오래된 질문이라는 것을 알고 있지만 ModelBinder ( DefaultModelBinder.ResourceClassKey = "MyResource";viewmodel 클래스의 데이터 주석에 표시된 리소스 와 관련하여) 와 관련 하여이 작업을 정말로 원한다면 컨트롤러 또는 심지어 ActionFilter는 너무 늦었습니다. 문화를 설정합니다 .

문화는에서 설정할 수 있습니다 Application_AcquireRequestState. 예를 들면 다음과 같습니다.

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

편집하다

실제로 Alex Adamyan이 블로그 에서 완벽하게 설명한 URL에 따라 문화를 설정 하는 사용자 지정 routehandler 를 사용하는 더 좋은 방법이 있습니다.

해야 할 일은 GetHttpHandler메서드 를 재정의 하고 문화를 설정하는 것입니다.

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

불행하게도 RouteData 등은 "Application_AcquireRequestState"메서드에서 사용할 수 없지만 Controller.CreateActionInvoker ()에 있습니다. 그래서 "보호 된 재정의 IActionInvoker CreateActionInvoker ()"를 제안하고 바로 거기에 CultureInfo를 설정합니다.
Skorunka František 2012 년

그 블로그를 읽었습니다. 쿠키로 진행하면 문제가 있나요? 변경할 권한이 없기 때문에. 친절하게 알려주세요. 이 접근 방식에 문제가 있습니까?
kbvishnu

@VeeKeyBee 사이트가 공개 된 경우 쿠키를 사용할 때 모든 언어가 올바르게 색인화되지 않습니다. 보호 된 사이트의 경우 괜찮을 것입니다.
marapet 2012 년

공개되지 않았습니다. "indexed"라는 단어에 대한 힌트를 주실 수 있습니까?
kbvishnu

1
자신의 질문을하고 SEO에 대해 읽어야합니다. 이것은 원래 질문과 더 이상 관련이 없습니다. webmasters.stackexchange.com/questions/3786/...
marapet

25

나는 다음과 같이 컨트롤러의 Initialize 이벤트에서 그것을 할 것입니다 ...

    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
사용자가 사이트에서 사용할 문화를 지정할 수 있어야하므로 문화 문자열은 const 일 수 없습니다.
NerdFury

2
나는 그것을 이해하지만, 문제는 그것을 설정하는 방법이 아니라 문화를 설정하는 것이 가장 좋은 곳이었습니다.
Jace Rhea

const 대신 다음과 같이 사용할 수 있습니다. var newCulture = new CultureInfo (RouteData.Values ​​[ "lang"]. ToString ());
Nordes 2010

AuthorizeCore는 OnActionExecuting보다 먼저 호출되므로 AuthorizeCore 재정의 된 메서드에 문화 정보가 없습니다. 컨트롤러의 initialize 메서드를 사용하면 특히 사용자 지정 AuthorizeAttribute를 구현하는 경우 더 잘 작동 할 수 있습니다. Initialize 메서드가 AuthorizeCore보다 먼저 호출되기 때문입니다 (AuthorizeCore 내에 문화 정보가 있음).
Nathan R

7

세션은 사용자별로 저장되는 설정이므로 정보를 저장하기에 적합한 장소입니다.

각 잠재적 문화에 대해 다른 작업 방법을 사용하지 않고 문화 문자열을 매개 변수로 사용하도록 컨트롤러를 변경합니다. 페이지에 링크를 추가하는 것은 쉬우 며 새로운 문화가 필요할 때마다 동일한 코드를 반복적으로 작성할 필요가 없습니다.

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>

답변 주셔서 감사합니다. 세션 사용 여부를 속이려는 것이 아닙니다. 나는 그 비트에 만족합니다. 내가 해결하려고하는 것은 설정 될 각 Culture에 대한 작업 방법이있는 Culture 컨트롤러에서이 작업을 수행하는 것이 가장 좋은지 아니면 MVC 파이프 라인에서이 작업을 수행하는 더 좋은 위치가 있는지 여부입니다
ChrisCa

질문에 더 잘 맞는 수정 된 답변을 제공했습니다.
NerdFury

예, 확실히 깨끗하지만 제가 정말로 알고 싶은 것은 이것이 컨트롤러에서 수행되어야하는지 여부입니다. 또는 MVC 파이프 라인에 문화를 설정하는 더 좋은 위치가있는 경우. 또는 ActionFilters, Handlers, Modules 등에서 더 나은 경우
ChrisCa

사용자가 선택할 기회가 없기 때문에 핸들러와 모듈은 의미가 없습니다. 사용자가 선택을 한 다음 컨트롤러에서 수행되는 사용자 선택을 처리 할 수있는 방법이 필요합니다.
NerdFury

동의, 핸들러 및 모듈은 사용자 상호 작용을 허용하기 위해 일찍입니다. 그러나 나는 MVC를 처음 접했기 때문에 이것이 파이프 라인에서 설정하기에 가장 좋은 장소인지 확실하지 않습니다. 잠시 후 다른 말을 듣지 못하면 답변을 수락하겠습니다. 추신 : 매개 변수를 Action 메서드에 전달하는 데 사용한 구문이 작동하지 않는 것 같습니다. 컨트롤러가 정의되어 있지 않으므로 기본 컨트롤러를 사용합니다 (이 경우 올바른 컨트롤러가 아님). 그리고 또 다른 과부하에게 적합하도록이 표시 나던
ChrisCa

6

최고의 장소는 당신의 질문입니다. 가장 좋은 위치는 Controller.Initialize 메서드 내부 입니다. MSDN은 생성자 이후 및 작업 메서드 전에 호출된다고 씁니다. OnActionExecuting을 재정의하는 것과는 반대로 Initialize 메서드에 코드를 배치하면 클래스와 속성에 대한 모든 사용자 지정 데이터 주석 및 특성을 지역화 할 수 있습니다.

예를 들어 내 지역화 논리는 내 사용자 지정 컨트롤러에 삽입 된 클래스에서 나옵니다. 생성자 후에 Initialize가 호출되기 때문에이 개체에 액세스 할 수 있습니다. 스레드의 문화 할당을 수행 할 수 있으며 모든 오류 메시지가 올바르게 표시되지는 않습니다.

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

로직이 내가 제공 한 예제와 같은 클래스 내에 있지 않더라도 URL 및 HttpContext 및 기본적으로 가능한 모든 구문 분석을 수행 할 수 있는 RouteData 를 가질 수 있는 RequestContext에 액세스 할 수 있습니다.


이것은 내 HTML5 Telerik ReportLocalization!에서 작동합니다. 감사합니다 @Patrick Desjardins에
CoderRoller

4

예를 들어 "pt.mydomain.com"과 같은 하위 도메인을 사용하여 포르투갈어를 설정하는 경우 Application_AcquireRequestState는 후속 캐시 요청에서 호출되지 않기 때문에 작동하지 않습니다.

이를 해결하기 위해 다음과 같은 구현을 제안합니다.

  1. 다음과 같이 VaryByCustom 매개 변수를 OutPutCache에 추가합니다.

    [OutputCache(Duration = 10000, VaryByCustom = "lang")]
    public ActionResult Contact()
    {
        return View("Contact");
    }
    
  2. global.asax.cs에서 함수 호출을 사용하여 호스트에서 문화를 가져옵니다.

    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        System.Threading.Thread.CurrentThread.CurrentUICulture = GetCultureFromHost();
    }
    
  3. getCultureFromHost 함수를 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. 마지막으로이 함수도 사용하려면 GetVaryByCustomString (...)을 재정의합니다.

    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 함수는 캐시되지 않은 호출에서 호출되어 콘텐츠를 생성하고 캐시 할 수 있습니다. GetVaryByCustomString은 캐시 된 호출에서 콘텐츠가 캐시에서 사용 가능한지 확인하기 위해 호출되며,이 경우에는 새 요청에 대해 변경되었을 수있는 현재 문화 정보에만 의존하는 대신 들어오는 호스트 도메인 값을 다시 검사합니다. 우리는 하위 도메인을 사용하고 있습니다).


4

1 : 사용자 지정 특성을 만들고 다음과 같이 메서드를 재정의합니다.

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에서 FilterConfig.cs를 찾아이 속성을 추가합니다. (이것은 전체 응용 프로그램에서 작동합니다)

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

그게 다야!

전체 애플리케이션 대신 각 컨트롤러 / 액션에 대한 문화를 정의하려면이 속성을 다음과 같이 사용할 수 있습니다.

[Culture]
public class StudentsController : Controller
{
}

또는:

[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
이것이 최선의 방법 인 이유를 설명해주십시오.
Max Leske 2013 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.