feat: add culture route param
This commit is contained in:
parent
b4d8b4636c
commit
55ee3ba5a9
17
oliverbooth.dev/Middleware/CultureRedirectExtensions.cs
Normal file
17
oliverbooth.dev/Middleware/CultureRedirectExtensions.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
namespace OliverBooth.Middleware;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="CultureRedirectMiddleware" />.
|
||||||
|
/// </summary>
|
||||||
|
internal static class CultureRedirectExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the <see cref="CultureRedirectMiddleware" /> to the application's request pipeline.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The application builder.</param>
|
||||||
|
/// <returns>The application builder.</returns>
|
||||||
|
public static IApplicationBuilder UseCultureRedirect(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<CultureRedirectMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
54
oliverbooth.dev/Middleware/CultureRedirectMiddleware.cs
Normal file
54
oliverbooth.dev/Middleware/CultureRedirectMiddleware.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace OliverBooth.Middleware;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redirects requests to the default culture if the culture is not specified in the URL.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class CultureRedirectMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="CultureRedirectMiddleware" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="next">The next request delegate.</param>
|
||||||
|
public CultureRedirectMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes the middleware.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The HTTP context.</param>
|
||||||
|
public async Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
const StringComparison comparison = StringComparison.OrdinalIgnoreCase;
|
||||||
|
|
||||||
|
HttpRequest request = context.Request;
|
||||||
|
PathString requestPath = request.Path;
|
||||||
|
|
||||||
|
if (requestPath.HasValue)
|
||||||
|
{
|
||||||
|
string[] pathSegments = requestPath.Value.Split('/');
|
||||||
|
CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
||||||
|
|
||||||
|
if (pathSegments.Length >= 2 && cultures.Any(CultureMatch))
|
||||||
|
{
|
||||||
|
await _next(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CultureMatch(CultureInfo culture)
|
||||||
|
{
|
||||||
|
string segment = pathSegments[1].Split('-')[0];
|
||||||
|
return string.Equals(culture.TwoLetterISOLanguageName, segment, comparison);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const string defaultCulture = "en";
|
||||||
|
var redirectUrl = $"/{defaultCulture}{requestPath}{request.QueryString}";
|
||||||
|
context.Response.Redirect(redirectUrl);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
@page
|
@page "/{culture=en}/error/{code:int?}"
|
||||||
@model ErrorModel
|
@model ErrorModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Error";
|
ViewData["Title"] = "Error";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@page
|
@page "/{culture=en}"
|
||||||
@model IndexModel
|
@model IndexModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Home page";
|
ViewData["Title"] = "Home page";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@page "/privacy/google-play"
|
@page "/{culture=en}/privacy/google-play"
|
||||||
@model OliverBooth.Pages.Privacy.GooglePlay
|
@model OliverBooth.Pages.Privacy.GooglePlay
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Google Play Privacy Policy";
|
ViewData["Title"] = "Google Play Privacy Policy";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@page
|
@page "/{culture=en}/privacy"
|
||||||
@model OliverBooth.Pages.Privacy.Index
|
@model OliverBooth.Pages.Privacy.Index
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Privacy Policy";
|
ViewData["Title"] = "Privacy Policy";
|
||||||
|
@ -1,8 +1,24 @@
|
|||||||
|
using Microsoft.AspNetCore.Localization;
|
||||||
|
using OliverBooth.Middleware;
|
||||||
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||||
|
builder.Services.AddRazorPages().AddViewLocalization().AddDataAnnotationsLocalization();
|
||||||
builder.Services.AddRouting(options => options.LowercaseUrls = true);
|
builder.Services.AddRouting(options => options.LowercaseUrls = true);
|
||||||
|
|
||||||
|
var supportedCultures = new[]
|
||||||
|
{
|
||||||
|
CultureInfo.GetCultureInfo("en"),
|
||||||
|
CultureInfo.GetCultureInfo("fr")
|
||||||
|
};
|
||||||
|
|
||||||
|
builder.Services.Configure<RequestLocalizationOptions>(options =>
|
||||||
|
{
|
||||||
|
options.DefaultRequestCulture = new RequestCulture(supportedCultures[0]);
|
||||||
|
options.SupportedCultures = supportedCultures;
|
||||||
|
options.SupportedUICultures = supportedCultures;
|
||||||
|
});
|
||||||
|
|
||||||
WebApplication app = builder.Build();
|
WebApplication app = builder.Build();
|
||||||
|
|
||||||
if (!app.Environment.IsDevelopment())
|
if (!app.Environment.IsDevelopment())
|
||||||
@ -17,6 +33,17 @@ app.UseStaticFiles();
|
|||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.UseRequestLocalization(options =>
|
||||||
|
{
|
||||||
|
options.ApplyCurrentCultureToResponseHeaders = true;
|
||||||
|
options.DefaultRequestCulture = new RequestCulture(supportedCultures[0]);
|
||||||
|
options.SupportedCultures = supportedCultures;
|
||||||
|
options.SupportedUICultures = supportedCultures;
|
||||||
|
options.RequestCultureProviders.Insert(0, new RouteCultureProvider(supportedCultures[0]));
|
||||||
|
});
|
||||||
|
app.UseCultureRedirect();
|
||||||
|
app.MapControllerRoute("default", "{culture=en}/{controller=Home}/{action=Index}/{id?}");
|
||||||
app.MapRazorPages();
|
app.MapRazorPages();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
47
oliverbooth.dev/RouteCultureProvider.cs
Normal file
47
oliverbooth.dev/RouteCultureProvider.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Localization;
|
||||||
|
|
||||||
|
namespace OliverBooth;
|
||||||
|
|
||||||
|
internal sealed partial class RouteCultureProvider : IRequestCultureProvider
|
||||||
|
{
|
||||||
|
private static readonly Regex CultureRegex = GetCultureRegex();
|
||||||
|
private readonly CultureInfo _defaultCulture;
|
||||||
|
private readonly CultureInfo _defaultUiCulture;
|
||||||
|
|
||||||
|
public RouteCultureProvider(CultureInfo requestCulture) : this(new RequestCulture(requestCulture))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteCultureProvider(RequestCulture requestCulture)
|
||||||
|
{
|
||||||
|
_defaultCulture = requestCulture.Culture;
|
||||||
|
_defaultUiCulture = requestCulture.UICulture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<ProviderCultureResult?> DetermineProviderCultureResult(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
PathString url = httpContext.Request.Path;
|
||||||
|
|
||||||
|
string defaultCulture = _defaultCulture.TwoLetterISOLanguageName;
|
||||||
|
string defaultUiCulture = _defaultUiCulture.TwoLetterISOLanguageName;
|
||||||
|
|
||||||
|
if (url.ToString().Length <= 1)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new ProviderCultureResult(defaultCulture, defaultUiCulture))!;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[]? parts = httpContext.Request.Path.Value?.Split('/');
|
||||||
|
string requestCulture = parts?[1] ?? string.Empty;
|
||||||
|
|
||||||
|
bool isMatch = CultureRegex.IsMatch(requestCulture);
|
||||||
|
string culture = isMatch ? requestCulture : defaultCulture;
|
||||||
|
string uiCulture = isMatch ? requestCulture : defaultUiCulture;
|
||||||
|
|
||||||
|
return Task.FromResult(new ProviderCultureResult(culture, uiCulture))!;
|
||||||
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex("^[a-z]{2}(-[A-Z]{2})*$")]
|
||||||
|
private static partial Regex GetCultureRegex();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user