3

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.

Asons
  • 84,923
  • 12
  • 110
  • 165
  • If it doesn’t sound like a “real” route but an alias for something else may be better off using either IIS url rewrite module or this middleware https://learn.microsoft.com/en-us/aspnet/core/fundamentals/url-rewriting?view=aspnetcore-2.1&tabs=aspnetcore2x – boateng Sep 13 '18 at 18:19
  • @numbtongue Thanks for your comment. As the app might run in other environment than e.g. IIS, I need to avoid such solution. Using middleware to do a rewrite feels less proper than what I have already, and am hoping that there will be a built-in option to solve this. – Asons Sep 13 '18 at 18:30
  • It would be nice if you can put them in a web.config where you could specify (although may confuse future developer) https://stackoverflow.com/a/40220739/3254405 – boateng Sep 13 '18 at 18:36
  • @numbtongue I wouldn't want to store hundreds of path tags in "web.config", they are stored and read from a db given the chosen language, and with that easy to change. – Asons Sep 13 '18 at 18:43
  • May be only create routes for domains like .com and .no and from there you’ll know which path to use.. – boateng Sep 13 '18 at 18:55
  • @numbtongue Chosen language has nothing to do with which domain is being used. – Asons Sep 13 '18 at 19:33
  • Many large websites use language in query string such as /en/ or /fr/ (not sure if it’s any help) https://www.google.com/amp/s/gunnarpeipman.com/aspnet/aspnet-core-simple-localization/amp/ – boateng Sep 13 '18 at 19:58

1 Answers1

3

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);
Mike Brind
  • 28,238
  • 6
  • 56
  • 88
  • Thank you very much, exactly what I were looking for. I did found the `IPageRouteModelConvention` though am too new to ASP.Net Core to figure out how-to :) – Asons Sep 13 '18 at 19:59
  • Just coincidentally, I have a draft of a blog article about this interface almost ready to go. I might include this use case as an additional example of its application. – Mike Brind Sep 13 '18 at 20:10
  • I think you should, as I am sure this must be a very common issue on how to accomplish what I asked, and simple but very easy to understand samples like the one you gave, gives a very good understanding of how it works. – Asons Sep 13 '18 at 20:18
  • 2
    And I just figured out that if a page has a directive like `@page "{edit?}/{id?}"` one can/need to add that too for it all to work, e.g. `new LocalRoute{Page = "/Pages/Events.cshtml", Versions = new List{"hvaskjer/{edit?}/{id?}" } }` – Asons Sep 13 '18 at 20:29
  • @MikeBrind how's it going with that blog post you mentioned? Is it published? – PussInBoots Mar 28 '19 at 12:29
  • @PussInBoots I published it about 6 months ago: https://www.mikesdotnetting.com/article/327/customising-routing-conventions-in-razor-pages – Mike Brind Mar 28 '19 at 12:47
  • @MikeBrind How do you generate the links (Link to localized version of contact page) to the new localized routes? – PussInBoots Mar 30 '19 at 01:18
  • @MikeBrind how would you add additional versions of a page, or new pages? Since the custom convention is executed once during app startup new pages or versions of a page would not be mapped to any route templates and I would need to restart my app every time I add new pages, e.g. when using a CMS. – PussInBoots May 16 '19 at 18:22