剃刀页面本地化 - SEO友好的URL

这是一个系列中的第四篇文章,探讨了ASP.NET核心razor页面应用程序中的本地化的各个方面。本文专注于本地化的SEO方面,因为它们有关URL管理,以及如何利用RoutedAtareQuestCultureProvider来帮助您管理。

The 第一篇文章 如何与今天福彩字谜总汇合作, and discussed the default RequestCultureProvider components that are used to determine the culture for the current request - 与查询字符串,cookie和请求标头一起使用的组件。在该文章中使用的方法,用于演示设置今天福彩字谜总汇的请求依赖于设置查询字符串值以生成特定于语言环境的URL。虽然它有效,但这不是推荐的方法。 谷歌推荐一些其他方法,其中一个涉及利用子目录 特定于地的网站内容的版本。子目录应命名为每个版本所代表的今天福彩字谜总汇代码,所以您最终结束了 以特定今天福彩字谜总汇命名的URL段: mydomain.com/en/about and mydomain.com/it/about etc.

这一切都很好而且很好,但它将导致一个有很多重复的应用程序,有效地为每个支持的今天福彩字谜总汇提供全新版本 在每个子目录中。 ASP.NET核心比这更聪明。 它支持概念 route data - 作为URL中作为段传递的应用程序数据。 ASP.NET. 核心还提供了与路由数据一起使用的请求今天福彩字谜总汇提供者 - the RouteDataRequestCultureProvider. This article builds on the 在以前的文章中介绍的应用程序展示如何制作 最佳使用此组件。

本文中的应用程序是相同的 以前的文章。它是使用标准剃刀页面3.1项目建造的 模板没有身份验证。本文中的许多概念都是 最初介绍在以前的文章中,所以你应该读第一个 如果你还没有。

您需要将表示今天福彩字谜总汇的路由数据项添加到所有路由。您可以单独将其作为路由模板应用于路由模板,但该方法并不是真正可扩展。更好的方法是使用一个 PageRouteModelConvention. 为默认情况下的所有页面添加其他路由模板 application. The following PageRouteModelConvention. creates a new attribute route for each existing route 通过组合命名的路由数据项来组合占位符 culture 使用现有属性路由。这 Order property of the AttributeRouteModel is set to -1 to 确保首先处理它:

using Microsoft.AspNetCore.Mvc.ApplicationModels;
 
namespace Localisation.RouteModelConventions
{
    public class CultureTemplatePageRouteModelConvention. : IPageRouteModelConvention.
    {
        public void Apply(PageRouteModel model)
        {
            var selectorCount = model.Selectors.Count;
 
            for (var i = 0; i < selectorCount; i++)
            {
                var selector = model.Selectors[i];
 
                model.Selectors.Add(new SelectorModel
                {
                    AttributeRouteModel = new AttributeRouteModel
                    { 
                        Order = -1,
                        Template = AttributeRouteModel.CombineTemplates("{culture?}", selector.AttributeRouteModel.Template),
                    }
                });
            }
        }
    }
}

按照惯例,所有请求秘密保障员查找物品 a key of culture and/or ui-culture by default to 建立当前要求的今天福彩字谜总汇。你可以改变这个 configuration.

这 custom PageRouteModelConvention. is registered in ConfigureServices as RazorpagesOptions的一部分:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages(options => {
        options.Conventions.Add(new CultureTemplatePageRouteModelConvention.());
    });
}

如果在此阶段运行应用程序,则可以测试新的 属性路由通过手动插入今天福彩字谜总汇代码来完成

剃刀页面的本地化

路线已正确解决,但请求的今天福彩字谜总汇不是 affected by the presence of the culture 路线数据 value. The RouteDataRequestCultureProvider 需要注册为部分 RequestLocalizationOptionsConfigureServices:

services.Configure<RequestLocalizationOptions>(options =>
{
    var supportedCultures = new[]
    {
        new CultureInfo("en"),
        new CultureInfo("de"),
        new CultureInfo("fr"),
        new CultureInfo("es"),
        new CultureInfo("ru"),
        new CultureInfo("ja"),
        new CultureInfo("ar"),
        new CultureInfo("zh"),
        new CultureInfo("en-GB")
    };
    options.DefaultRequestCulture = new RequestCulture("en-GB");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;
    options.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider { Options = options });
});

上面的代码来自系列中的第一篇文章,增加了 注册路线数据请求今天福彩字谜总汇提供者的最终行,并将其插入其中 第一个请求提供商用于今天福彩字谜总汇匹配。现在,如果你运行 再次应用程序,您应该看到请求提供商工作:

剃刀页面的本地化

使用标记助词生成链接

页面顶部的链接由 anchor tag helpers 在布局页面中。在剃刀页面2.2之前,锚标标签 助手将利用 ambient route values 生成URL。如果是目标页面和 当前请求在其属性路由中共享相同的路由值,值 从当前请求将在URL生成中自动重复使用。 This is very useful when every page shares the culture route value 如在这个例子中。然而,这种行为也有一些不必要的副作用, 并且很大程度上被删除了 随着在ASP.NET Core 2.2中引入端点路由。环境路线 现在仅使用目标页面所指定的目标页面 asp-page 锚标记中的属性与当前请求相同。 您可以通过导航到联系人页面来查看此功能,选择今天福彩字谜总汇(法语, 在我的情况下)并查看所生成的HTML为链接:

请注意,只有联系页面的锚标记包含环境路径 今天福彩字谜总汇价值。它不包含在主页生成的链接中 或隐私页面。需要明确添加培养路线值:

<ul class="navbar-nav flex-grow-1">
    <li class="nav-item">
        <a class="nav-link text-dark" asp-route-culture="@Context.Request.RouteValues["culture"]" asp-page="/Index">@localizer.Get("Home")</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-route-culture="@Context.Request.RouteValues["culture"]" asp-page="/Contact">@localizer.Get("Contact")</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-route-culture="@Context.Request.RouteValues["culture"]" asp-page="/Privacy">@localizer.Get("Privacy")</a>
    </li>
</ul>

现在所有链接都包括今天福彩字谜总汇:

但是,就像手动生成路线模板的前景 每个页面,这对于大型应用程序同样不可规划。你可以做什么 而是实现自己的锚标签帮助,从而得出 框架版本并负责 添加今天福彩字谜总汇路线值 如图所示 this Github issue.

标签帮助的代码跟随:

[HtmlTargetElement("a", Attributes = ActionAttributeName)]
[HtmlTargetElement("a", Attributes = ControllerAttributeName)]
[HtmlTargetElement("a", Attributes = AreaAttributeName)]
[HtmlTargetElement("a", Attributes = PageAttributeName)]
[HtmlTargetElement("a", Attributes = PageHandlerAttributeName)]
[HtmlTargetElement("a", Attributes = FragmentAttributeName)]
[HtmlTargetElement("a", Attributes = HostAttributeName)]
[HtmlTargetElement("a", Attributes = ProtocolAttributeName)]
[HtmlTargetElement("a", Attributes = RouteAttributeName)]
[HtmlTargetElement("a", Attributes = RouteValuesDictionaryName)]
[HtmlTargetElement("a", Attributes = RouteValuesPrefix + "*")]
public class CultureAnchorTagHelper : AnchorTagHelper
{
    public CultureAnchorTagHelper(IHttpContextAccessor contextAccessor, IHtmlGenerator generator) :
        base(generator)
    {
        this.contextAccessor = contextAccessor;
    }
 
    private const string ActionAttributeName = "asp-action";
    private const string ControllerAttributeName = "asp-controller";
    private const string AreaAttributeName = "asp-area";
    private const string PageAttributeName = "asp-page";
    private const string PageHandlerAttributeName = "asp-page-handler";
    private const string FragmentAttributeName = "asp-fragment";
    private const string HostAttributeName = "asp-host";
    private const string ProtocolAttributeName = "asp-protocol";
    private const string RouteAttributeName = "asp-route";
    private const string RouteValuesDictionaryName = "asp-all-route-data";
    private const string RouteValuesPrefix = "asp-route-";
    private const string Href = "href";
 
    private readonly IHttpContextAccessor contextAccessor;
    private readonly string defaultRequestCulture = "en";
 
    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        var culture = (string)contextAccessor.HttpContext.Request.RouteValues["culture"];
 
        if (culture != null && culture != defaultRequestCulture)
        {
            RouteValues["culture"] = culture;
        }
 
        base.Process(context, output);
    }
}

然后您需要从中删除现有的锚标标签助手 应用,这是通过的 应用RemovetAghelper指令_ViewImports.

@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.AnchorTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers

Finally, 如果您一直关注整个系列的文章,那么看 剃刀页面中的本地化,您可能希望更改今天福彩字谜总汇切换器中的客户端代码 viewComponent在第一篇文章中介绍。它目前适用于查询字符串请求今天福彩字谜总汇 提供者,所以它需要修改以使用URL段而不是查询字符串 value:

<script>
    var segments = location.pathname.split('/');
    var el = document.getElementById("culture-options");
    el.addEventListener("change", () => {
        var culture = el.options[el.selectedIndex].value;
        if (segments.length > 2) {
            segments[1] = culture;
            location.href = segments.join('/');
        } else {
            location.href = '/' + culture + location.pathname;
        }
    });
</script>

Summary

The RouteDataRequestCultureProvider is an essential component 如果您想遵循以最佳方式工作的建议 razor页面应用程序中的特定于地的URL。它没有注册为一个 默认的请求今天福彩字谜总汇提供商,所以需要配置它 分别地。本文还显示了如何使用PageRouteModelConvention. 集中管理在应用程序上的特定区域特定路由,以及如何 规避在ASP.NET核心2.2中删除环境路径值,仍在使用 一个锚标记帮助程序,用于生成包含所有页面的当前今天福彩字谜总汇的路由 in the application.