Localized page names with ASP.NET Core 2.1

2020-03-26 06:23发布

问题:

When create a Razor page, e.g. "Events.cshtml", one get its model name set to

@page
@model EventsModel

where the page's name in this case is "Events", and the URL would look like

http://example.com/Events

To be able to use page name's in Norwegian I added the following to the "Startup.cs"

services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
    .AddRazorPagesOptions(options => {
        options.Conventions.AddPageRoute("/Events", "/hvaskjer");
        options.Conventions.AddPageRoute("/Companies", "/bedrifter");
        options.Conventions.AddPageRoute("/Contact", "/kontakt");
});

With this I can also use an URL like this and still serve the "Events" page

http://example.com/hvaskjer

I'm planning to support many more languages and wonder, is this the recommended way to setup localized page name's/route's?, or is there a more proper, correct way to accomplish the same.

I mean, with the above sample, and having 15 pages in 10 languages it gets/feels messy using options.Conventions.AddPageRoute("/Page", "/side"); 150 times.

回答1:

You can do this with the IPageRouteModelConvention interface. It provides access to the PageRouteModel where you can effectively add more templates for routes to match against for a particular page.

Here's a very simple proof of concept based on the following service and model:

public interface ILocalizationService
{
    List<LocalRoute> LocalRoutes();
}
public class LocalizationService : ILocalizationService
{
    public List<LocalRoute> LocalRoutes()
    {
        var routes = new List<LocalRoute>
        {
            new LocalRoute{Page = "/Pages/Contact.cshtml", Versions = new List<string>{"kontakt", "contacto", "contatto" } }
        };
        return routes;
    }
}

public class LocalRoute
{
    public string Page { get; set; }
    public List<string> Versions { get; set; }
}

All it does is provide the list of options for a particular page. The IPageRouteModelConvention implementation looks like this:

public class LocalizedPageRouteModelConvention : IPageRouteModelConvention
{
    private ILocalizationService _localizationService;

    public LocalizedPageRouteModelConvention(ILocalizationService localizationService)
    {
        _localizationService = localizationService;
    }

    public void Apply(PageRouteModel model)
    {
        var route = _localizationService.LocalRoutes().FirstOrDefault(p => p.Page == model.RelativePath);
        if (route != null)
        {
            foreach (var option in route.Versions)
            {
                model.Selectors.Add(new SelectorModel()
                {
                    AttributeRouteModel = new AttributeRouteModel
                    {
                        Template = option
                    }
                });
            }
        }
    }
}

At Startup, Razor Pages build the routes for the application. The Apply method is executed for every navigable page that the framework finds. If the relative path of the current page matches one in your data, an additional template is added for each option.

You register the new convention in ConfigureServices:

services.AddMvc().AddRazorPagesOptions(options =>
{
    options.Conventions.Add(new LocalizedPageRouteModelConvention(new LocalizationService()));
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);