You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core多语言路由本地化实现技术咨询

Got it, let's tackle this problem of implementing Laravel-like localized route translations in ASP.NET Core. Since you already have the language route constraint and basic setup working, the key part now is using IApplicationModelConvention alongside route parameter transformers to rewrite route segments based on the active language. Here's a step-by-step breakdown tailored to your setup:


Implement Localized Route Translations with IApplicationModelConvention

1. Set Up Route Translation Resources

First, create localized resources to map original route segments to their translated counterparts. We'll use .resx files for this:

  • Create a Resources folder in your project
  • Add RouteTranslations.en.resx (base language):
    NameValue
    Productproduct
    Detailsdetails
    Aboutabout
  • Add RouteTranslations.fr.resx (French translations):
    NameValue
    Productproduit
    Detailsdétails
    Aboutà-propos

Register localization services in Program.cs:

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
builder.Services.AddHttpContextAccessor(); // Needed to access current request culture

2. Build Route Parameter Transformers

These transformers handle translating route segments when generating URLs (outbound) and converting translated segments back to original values for routing (inbound).

public class LocalizedControllerTransformer : IOutboundParameterTransformer, IInboundParameterTransformer
{
    private readonly IStringLocalizer<RouteTranslations> _localizer;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly Dictionary<string, string> _reverseTranslationMap;

    public LocalizedControllerTransformer(IStringLocalizer<RouteTranslations> localizer, IHttpContextAccessor httpContextAccessor)
    {
        _localizer = localizer;
        _httpContextAccessor = httpContextAccessor;
        // Pre-build reverse map for inbound translation lookup
        _reverseTranslationMap = _localizer.GetAllStrings()
            .GroupBy(s => s.Value)
            .ToDictionary(g => g.Key, g => g.First().Name);
    }

    public string TransformOutbound(object value)
    {
        if (value == null) return null;
        
        // Strip "Controller" suffix from controller name
        var controllerName = value.ToString();
        var baseName = controllerName.EndsWith("Controller") 
            ? controllerName[0..^10] 
            : controllerName;

        // Get active culture from request
        var httpContext = _httpContextAccessor.HttpContext;
        var culture = httpContext?.Request.RouteValues["culture"]?.ToString() 
            ?? Thread.CurrentThread.CurrentCulture.Name;

        // Return translated segment
        using var cultureScope = new CultureInfo(culture);
        return _localizer[baseName].Value;
    }

    public object TransformInbound(string value)
    {
        // Convert translated segment back to original controller name
        if (_reverseTranslationMap.TryGetValue(value, out var originalName))
        {
            return $"{originalName}Controller";
        }
        // Fallback if translation not found
        return $"{value}Controller";
    }
}

// Repeat similar logic for action segments if needed
public class LocalizedActionTransformer : IOutboundParameterTransformer, IInboundParameterTransformer
{
    private readonly IStringLocalizer<RouteTranslations> _localizer;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly Dictionary<string, string> _reverseTranslationMap;

    public LocalizedActionTransformer(IStringLocalizer<RouteTranslations> localizer, IHttpContextAccessor httpContextAccessor)
    {
        _localizer = localizer;
        _httpContextAccessor = httpContextAccessor;
        _reverseTranslationMap = _localizer.GetAllStrings()
            .GroupBy(s => s.Value)
            .ToDictionary(g => g.Key, g => g.First().Name);
    }

    public string TransformOutbound(object value)
    {
        if (value == null) return null;
        
        var actionName = value.ToString();
        var httpContext = _httpContextAccessor.HttpContext;
        var culture = httpContext?.Request.RouteValues["culture"]?.ToString() 
            ?? Thread.CurrentThread.CurrentCulture.Name;

        using var cultureScope = new CultureInfo(culture);
        return _localizer[actionName].Value;
    }

    public object TransformInbound(string value)
    {
        if (_reverseTranslationMap.TryGetValue(value, out var originalName))
        {
            return originalName;
        }
        return value;
    }
}

3. Implement IApplicationModelConvention

This convention will attach our transformers to controller and action route parameters at application startup.

public class LocalizedRouteConvention : IApplicationModelConvention
{
    private readonly IServiceProvider _serviceProvider;

    public LocalizedRouteConvention(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void Apply(ApplicationModel application)
    {
        // Resolve dependencies from service provider
        var localizer = _serviceProvider.GetRequiredService<IStringLocalizer<RouteTranslations>>();
        var httpContextAccessor = _serviceProvider.GetRequiredService<IHttpContextAccessor>();
        
        var controllerTransformer = new LocalizedControllerTransformer(localizer, httpContextAccessor);
        var actionTransformer = new LocalizedActionTransformer(localizer, httpContextAccessor);

        // Attach transformers to all controller and action route parameters
        foreach (var controller in application.Controllers)
        {
            foreach (var selector in controller.Selectors)
            {
                var controllerParam = selector.AttributeRouteModel.TemplateParameters
                    .FirstOrDefault(p => p.Name == "controller");
                if (controllerParam != null)
                {
                    controllerParam.OutboundTransformer = controllerTransformer;
                    controllerParam.InboundTransformer = controllerTransformer;
                }

                var actionParam = selector.AttributeRouteModel.TemplateParameters
                    .FirstOrDefault(p => p.Name == "action");
                if (actionParam != null)
                {
                    actionParam.OutboundTransformer = actionTransformer;
                    actionParam.InboundTransformer = actionTransformer;
                }
            }
        }
    }
}

4. Register the Convention and Route Constraint

Update Program.cs to add your convention and existing language route constraint:

builder.Services.AddControllersWithViews(options =>
{
    // Register your existing language route constraint
    options.RouteOptions.ConstraintMap.Add("culture", typeof(LanguageRouteConstraint));
    // Add the localized route convention
    options.Conventions.Add(serviceProvider => new LocalizedRouteConvention(serviceProvider));
});

// Configure request localization (adjust supported cultures as needed)
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    var supportedCultures = new[] { "en", "fr" };
    options.DefaultRequestCulture = new RequestCulture("en");
    options.SupportedCultures = supportedCultures.Select(c => new CultureInfo(c)).ToList();
    options.SupportedUICultures = supportedCultures.Select(c => new CultureInfo(c)).ToList();
});

5. Define Your Controllers with Localized Routes

Use standard route attributes with the culture constraint:

[Route("{culture:culture}/[controller]")]
public class ProductController : Controller
{
    [Route("[action]/{id:int}")]
    public IActionResult Details(int id)
    {
        // Generate localized URL: Url.Action("Details", "Product", new { culture = "fr" }) returns /fr/produit/détails/1
        return View();
    }
}

[Route("{culture:culture}/[controller]")]
public class AboutController : Controller
{
    public IActionResult Index()
    {
        // URL for French culture: /fr/à-propos
        return View();
    }
}

How It Works

  • Inbound Routing: When a user visits /fr/produit/détails/1, the inbound transformers convert produit back to ProductController and détails back to Details, routing to the correct action.
  • Outbound URL Generation: When you call Url.Action or use tag helpers like <a asp-action="Details" asp-controller="Product" asp-route-culture="fr">, the outbound transformers translate route segments to the active language.

内容的提问来源于stack exchange,提问作者frank_lbt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:22:47