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
|
||||
@{
|
||||
ViewData["Title"] = "Error";
|
||||
|
@ -1,4 +1,4 @@
|
||||
@page
|
||||
@page "/{culture=en}"
|
||||
@model IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "Home page";
|
||||
|
@ -1,4 +1,4 @@
|
||||
@page "/privacy/google-play"
|
||||
@page "/{culture=en}/privacy/google-play"
|
||||
@model OliverBooth.Pages.Privacy.GooglePlay
|
||||
@{
|
||||
ViewData["Title"] = "Google Play Privacy Policy";
|
||||
|
@ -1,4 +1,4 @@
|
||||
@page
|
||||
@page "/{culture=en}/privacy"
|
||||
@model OliverBooth.Pages.Privacy.Index
|
||||
@{
|
||||
ViewData["Title"] = "Privacy Policy";
|
||||
|
@ -1,8 +1,24 @@
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using OliverBooth.Middleware;
|
||||
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);
|
||||
|
||||
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();
|
||||
|
||||
if (!app.Environment.IsDevelopment())
|
||||
@ -17,6 +33,17 @@ app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
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.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