using System.Globalization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Primitives;
namespace Umbraco.Cms.Web.Common.Localization;
///
/// Base implementation that dynamically adds the determined cultures to the supported cultures.
///
public abstract class DynamicRequestCultureProviderBase : RequestCultureProvider
{
private readonly RequestLocalizationOptions _options;
private readonly Lock _lockerSupportedCultures = new();
private readonly Lock _lockerSupportedUICultures = new();
///
/// Initializes a new instance of the class.
///
/// The request localization options.
protected DynamicRequestCultureProviderBase(RequestLocalizationOptions requestLocalizationOptions)
=> _options = Options = requestLocalizationOptions;
///
public override Task DetermineProviderCultureResult(HttpContext httpContext)
{
ProviderCultureResult? result = GetProviderCultureResult(httpContext);
if (result is not null)
{
// We need to dynamically change the supported cultures since we won't ever know what languages are used since
// they are dynamic within Umbraco. We have to handle this for both UI and Region cultures, in case people run different region and UI languages
// This code to check existence is borrowed from aspnetcore to avoid creating a CultureInfo
// https://github.com/dotnet/aspnetcore/blob/b795ac3546eb3e2f47a01a64feb3020794ca33bb/src/Middleware/Localization/src/RequestLocalizationMiddleware.cs#L165
TryAddLocked(_options.SupportedCultures, result.Cultures, _lockerSupportedCultures, (culture) =>
{
_options.SupportedCultures ??= new List();
_options.SupportedCultures.Add(CultureInfo.GetCultureInfo(culture.ToString()));
});
TryAddLocked(_options.SupportedUICultures, result.UICultures, _lockerSupportedUICultures, (culture) =>
{
_options.SupportedUICultures ??= new List();
_options.SupportedUICultures.Add(CultureInfo.GetCultureInfo(culture.ToString()));
});
return Task.FromResult(result);
}
return NullProviderCultureResult;
}
///
/// Gets the provider culture result.
///
/// The HTTP context.
///
/// The provider culture result.
///
protected abstract ProviderCultureResult? GetProviderCultureResult(HttpContext httpContext);
///
/// Executes the within a double-checked lock when the a culture in does not exist in .
///
/// The supported cultures.
/// The cultures.
/// The locker object to use.
/// The add action to execute.
private static void TryAddLocked(IEnumerable? supportedCultures, IEnumerable cultures, Lock locker, Action addAction)
{
foreach (StringSegment culture in cultures)
{
Func predicate = x => culture.Equals(x.Name, StringComparison.OrdinalIgnoreCase);
if (supportedCultures?.Any(predicate) is not true)
{
lock (locker)
{
if (supportedCultures?.Any(predicate) is not true)
{
addAction(culture);
}
}
}
}
}
}