Культура по умолчанию в маршрутизации URL при локализации

Недавно я взял на себя огромный проект C# MVC. Он локализован на английский и норвежский, но у меня есть некоторые проблемы с локализацией и с тем, как правильно установить маршрут. Я хочу добиться следующего:

  1. / controller / action -> должен отображаться на язык по умолчанию (норвежский)
  2. / no / controller / action -> должен отображаться на странице на норвежском
  3. / en / controller / action -> должен отображаться на странице на английском языке

Во всех случаях URL должен оставаться прежним. Нужно ли мне устанавливать отдельный нелокализованный маршрут для всех случаев, как я сделал для страницы входа? Вот основная часть кода:

        routes.MapRoute(
            name: "metaRelated",
            url: "related/{type}/{id}",
            defaults: new { controller = "Meta", action = "Related" }
        ).RouteHandler = new MultiCultureMvcRouteHandler();

        routes.MapRoute(
            name: "login",
            url: "Login",
            defaults: new { controller = "Login", action = "Login" }
        ).RouteHandler = new MultiCultureMvcRouteHandler();

        routes.MapRoute(
            name: "loginNoCulture",
            url: "Login",
            defaults: new { controller = "Login", action = "Login", culture = UrlParameter.Optional }
        ).RouteHandler = new MvcRouteHandler();

        routes.MapRoute(
            name: "actionStatus",
            url: "DocumentActions/{ActionStatus}/{DocumentId}",
            defaults: new { controller = "Action", action = "Index", DocumentId = UrlParameter.Optional }
        ).RouteHandler = new MultiCultureMvcRouteHandler();

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        ).RouteHandler = new MultiCultureMvcRouteHandler();


        foreach (Route r in routes)
        {
            if (r.RouteHandler is MultiCultureMvcRouteHandler)
            {
                r.RouteHandler = new MultiCultureMvcRouteHandler();
                r.Url = "{culture}/" + r.Url;

                // Adding default culture 
                if (r.Defaults == null)
                    r.Defaults = new RouteValueDictionary();

                r.Defaults.Add("culture", Culture.No.ToString().ToLower());

                // Adding constraint for culture param
                if (r.Constraints == null)
                    r.Constraints = new RouteValueDictionary();

                r.Constraints.Add("culture", new CultureConstraint(Culture.En.ToString().ToLower(), Culture.No.ToString().ToLower(), Culture.Nl.ToString().ToLower()));
            }
        }

Вот класс MultiCultureMvcRouteHandler:

public class MultiCultureMvcRouteHandler : MvcRouteHandler
    {
        protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            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);
        }
    }

1 ответ

Я столкнулся с необходимостью сделать это. Мой вариант использования был для веб-API .NET 5.

После изучения вариантов (несколько дней биться головой о клавиатуру) я нашел хорошее решение. Я использовал фильтр действий на глобальном уровне.

Вот класс ActionFilter

      /// <summary>
/// This filter sets the Thread.CurrentThread.CurrentCulture To the culture property of the route. 
/// </summary>
public class CultureActionFilter : IAsyncActionFilter
{   
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        try
        {
            if (context.RouteData.Values.ContainsKey("culture"))
                Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(context.RouteData.Values["culture"].ToString());
        }
        catch { }

        await next();
    }
}

Как видите, это довольно просто. Он смотрит, есть ли культура в данных маршрута, а затем пытается проанализировать ее и установить текущую культуру потока.

В Настройках служб

      services.AddControllers(config => config.Filters.Add(new CultureActionFilter()));

 services.Configure<RequestLocalizationOptions>(options =>
        {
            options.DefaultRequestCulture = new RequestCulture(new CultureInfo("en-US-POSIX"), new CultureInfo("en-US"));
            options.SupportedCultures = new List<CultureInfo>(_allowedCultures);
            options.SupportedUICultures = new List<CultureInfo>(_allowedCultures);                
            options.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider());
            services.AddSingleton(options);
        });

        services.AddRouting(options =>
        {
            options.LowercaseUrls = true;
        });

Затем в Настройках

      app.UseRouting();

app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();                                
        });

Теперь вы можете проверить это в контроллере, добавив {culture} к маршруту. Пример

      [Route("v{version:apiVersion}/{culture}/[controller]/[action]")]

Затем вы можете использовать его так в методе.

      [HttpGet]
    public IActionResult Test()
    {
        return Ok(Thread.CurrentThread.CurrentCulture.EnglishName);
    }

Вызовите так: http://localhost:41121/v1/fr-CA/maintenance/test

Возвращает французский (Канада)

Другие вопросы по тегам