- Kentico Xperience 13
Localizing 404 page in Kentico Xperience 13
In this post I will provide you with code that will localize your 404 page in your Kentico Xperience 13 website.
The problem
In my Xperience website, I am using 2 cultures. When I was about to create a 404 page, I didn't want to re-invent the wheel. Therefore, I took out the 404 page implementation from the Dancing Goat site which is a sample site that comes with Kentico Xperience in one package.
Regrettably, I found out that the implementation is not ready for localization and must be adjusted to show localized strings. With the kind help of Kentico's support, I was able to find a solution.
Please note, that this solution is valid for URL structure where
- URL paths for the default culture do not contain culture code (i.e.
/clanky
forcs-CZ
default culture) - URL paths for other cultures contain culture code (i.e.
/en-us/articles
foren-US
culture)
The solution
There are a couple of code files that you have to create.
HttpErrorsController.cs
using Microsoft.AspNetCore.Mvc;
namespace DancingGoat.Controllers
{
public class HttpErrorsController : Controller
{
public IActionResult Error(int code)
{
if (code == 404)
{
return View("NotFound");
}
return StatusCode(code);
}
}
}
NotFoundSiteCultureConstraint.cs
using Kentico.Content.Web.Mvc;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing;
using System;
using System.Globalization;
namespace DancingGoat
{
public class NotFoundSiteCultureConstraint : IRouteConstraint
{
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
{
var cultureCode = values[routeKey]?.ToString();
if (string.IsNullOrEmpty(cultureCode))
{
if (values != null && values.Count == 3 && string.Equals("Error", values["action"].ToString(), StringComparison.OrdinalIgnoreCase) && string.Equals("HttpErrors", values["controller"].ToString(), StringComparison.OrdinalIgnoreCase))
{
var originalTarget = httpContext.Features.Get<IHttpRequestFeature>().RawTarget;
cultureCode = originalTarget.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)[0];
}
if (!string.IsNullOrEmpty(cultureCode))
{
try
{
var cultureInfo = CultureInfo.GetCultureInfo(cultureCode);
}
catch (CultureNotFoundException)
{
cultureCode = null;
}
}
values[routeKey] = cultureCode;
}
return new SiteCultureConstraint().Match(httpContext, route, routeKey, values, routeDirection);
}
}
}
NotFound.cshtml is a view and it should contain your own markup that fits your website. Here I post an example that uses Localization strings. They could be defined in the Xperience Admin in the Localization application.
@{
ViewBag.Title = @ResHelper.GetString("notfoundtitle");
}
<main id="main">
<section class="error">
<div class="error__heading">
<div class="error__text">@ResHelper.GetString("notfoundheading")</div>
</div>
</section>
</main>
In the Startup.cs file, add the following code in the Configure
method after endpoints.Kentico().MapRoutes();
endpoints.MapControllerRoute(
name: "error",
pattern: "/error/{code}",
defaults: new { controller = "HttpErrors", action = "Error" },
constraints: new { culture = new NotFoundSiteCultureConstraint() }
);
And that's it.
Side notes
Considering the Dancing Goat site, the only extra piece in the implementation is the NotFoundSiteCultureConstraint.cs file. What I really like here is the way the originalTarget
is obtained. You can also use that in your controller to add some extra logic to your 404 page.
Example - Redirects article details that are not localized to a listing page.
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using System;
namespace jobs.kentico.com.Web.Controllers
{
public class HttpErrorsController : Controller
{
public IActionResult Error(int code, string culture)
{
var originalTarget = Request.HttpContext.Features.Get<IHttpRequestFeature>().RawTarget;
var path = originalTarget.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
var pathSegment = "";
if (path.Length > 1 && culture == "en-us")
{
pathSegment = path[1];
}
else if (path.Length > 0 && culture == "cs-CZ")
{
pathSegment = path[0];
}
if (code == 404)
{
if (culture == "en-us" && pathSegment == "articles")
{
return LocalRedirect("/en-us/articles");
}
else if (culture == "cs-CZ" && pathSegment == "clanky")
{
return LocalRedirect("/clanky");
}
else
{
return View("NotFound");
}
}
return StatusCode(code);
}
}
}
About the author
Milan Lund is a Full-Stack Web Developer, Solution Architect, and Consultant for Xperience by Kentico projects, working as both a freelancer and a contractor. He specializes in building and maintaining websites using Xperience by Kentico. Milan writes articles based on his project experiences to assist both his future self and other developers.
Find out more