diff --git a/zero/Configuration/ZeroOptions.cs b/zero/Configuration/ZeroOptions.cs index 483bf31a..d469e274 100644 --- a/zero/Configuration/ZeroOptions.cs +++ b/zero/Configuration/ZeroOptions.cs @@ -9,6 +9,9 @@ public class ZeroOptions : IZeroOptions /// public string Version { get; set; } + /// + public string Language { get; set; } = "en_US"; + /// public TimeSpan TokenExpiration { get; set; } @@ -43,6 +46,11 @@ public interface IZeroOptions /// string Version { get; set; } + /// + /// ISO Code for language + /// + public string Language { get; set; } + /// /// Expiration of a generated change token for an entity /// diff --git a/zero/Localization/ConfigurationLocalizer.cs b/zero/Localization/ConfigurationLocalizer.cs index 6e3a9431..4fa20668 100644 --- a/zero/Localization/ConfigurationLocalizer.cs +++ b/zero/Localization/ConfigurationLocalizer.cs @@ -8,12 +8,9 @@ public class ConfigurationLocalizer : Localizer private IConfiguration _configuration; - public ConfigurationLocalizer(IConfiguration configuration, IOptionsMonitor options) : base() + public ConfigurationLocalizer(IConfiguration configuration, ICultureResolver cultureResolver, IOptionsMonitor options) : base(cultureResolver) { _configuration = configuration; - - // set default language - Language(_configuration.GetValue("Zero:Localization:Default", "de")); } diff --git a/zero/Localization/CultureChangeMessage.cs b/zero/Localization/CultureChangeMessage.cs new file mode 100644 index 00000000..5f73d7f6 --- /dev/null +++ b/zero/Localization/CultureChangeMessage.cs @@ -0,0 +1,8 @@ +using System.Globalization; + +namespace zero.Localization; + +public class CultureChangeMessage : IMessage +{ + public CultureInfo Culture { get; set; } +} \ No newline at end of file diff --git a/zero/Localization/CultureResolver.cs b/zero/Localization/CultureResolver.cs index c612145a..bbf98a8a 100644 --- a/zero/Localization/CultureResolver.cs +++ b/zero/Localization/CultureResolver.cs @@ -1,57 +1,117 @@ using FluentValidation; +using Microsoft.AspNetCore.Connections.Features; using Microsoft.Extensions.Logging; using System.Globalization; +using System.Linq.Expressions; using System.Security.Claims; namespace zero.Localization; public class CultureResolver : ICultureResolver { + /// + public CultureInfo Current { get; protected set; } + protected ILogger Logger { get; private set; } + protected IMessageAggregator MessageAggregator { get; private set; } - public CultureResolver(ILogger logger) + + public CultureResolver(ILogger logger, IMessageAggregator messageAggregator) { Logger = logger; + MessageAggregator = messageAggregator; } /// public Task Resolve(IZeroContext context) { - //var session = context.Store.Session(); - //Language language = await session.Query().FirstOrDefaultAsync(); - string isoCode = "de-AT"; + if (!TryConvert(context.Options.Language, out CultureInfo culture)) + { + culture = CultureInfo.CurrentCulture; + } + Set(culture); + + return Task.FromResult(culture); + } + + + /// + public bool TryConvert(string isoCode, out CultureInfo culture) + { try { - CultureInfo culture = CultureInfo.CreateSpecificCulture(isoCode); + culture = CultureInfo.CreateSpecificCulture(isoCode.Replace('_', '-')); if (culture.ThreeLetterISOLanguageName.IsNullOrEmpty()) { throw new Exception("ThreeLetterISOLanguageName is empty"); } + if (culture.ThreeLetterISOLanguageName == "ivl") + { + throw new Exception("Invariant language is not allowed"); + } - CultureInfo.CurrentCulture = culture; - CultureInfo.CurrentUICulture = culture; - ValidatorOptions.Global.LanguageManager.Culture = culture; + return true; } catch (Exception ex) { - Logger.LogError(ex, "Could not create culture from Language code {code}", isoCode); - return Task.FromResult(CultureInfo.CurrentCulture); + Logger.LogWarning(ex, "Could not create culture from Language code {code}", isoCode); + culture = null; + return false; } - - return Task.FromResult(CultureInfo.CurrentCulture); + } + + + /// + public void Set(CultureInfo culture) + { + CultureInfo.CurrentCulture = culture; + CultureInfo.CurrentUICulture = culture; + ValidatorOptions.Global.LanguageManager.Culture = culture; + Current = culture; + MessageAggregator.Publish(new CultureChangeMessage() + { + Culture = culture + }); + } + + + /// + public void Subscribe(Expression> handle) + { + MessageAggregator.Subscribe(handle); } } public interface ICultureResolver { + /// + /// Current culture + /// + CultureInfo Current { get; } + /// /// Resolves the current application from either the backoffice user (in case it is backoffice request) /// or the domain (in case it is frontend request). /// Task Resolve(IZeroContext context); + + /// + /// Tries to convert an ISO code to a culture + /// + bool TryConvert(string isoCode, out CultureInfo culture); + + /// + /// Set a new culture for this request + /// + void Set(CultureInfo culture); + + /// + /// Subscribe to culture change + /// + void Subscribe(Expression> handle); } diff --git a/zero/Localization/Localizer.cs b/zero/Localization/Localizer.cs index e2956c30..d6f51af2 100644 --- a/zero/Localization/Localizer.cs +++ b/zero/Localization/Localizer.cs @@ -1,23 +1,30 @@ using System.Collections.Concurrent; +using System.Globalization; using System.Reflection; namespace zero.Localization; public abstract class Localizer : ILocalizer { - /// - public string LanguageCode { get; private set; } = "de"; - - protected ConcurrentDictionary Cache { get; private set; } = new(); + protected ICultureResolver CultureResolver { get; private set; } - /// - public ILocalizer Language(string languageCode) + protected string LanguageCode { get; set; } + + + public Localizer(ICultureResolver cultureResolver) { - Cache = new(); - LanguageCode = languageCode; - return this; + CultureResolver = cultureResolver; + CultureResolver.Subscribe(msg => OnCultureChange(msg.Culture)); + OnCultureChange(CultureResolver.Current); + } + + + Task OnCultureChange(CultureInfo culture) + { + LanguageCode = culture.Name.Split(new char[] { '_', '-' })[0]; + return Task.CompletedTask; } @@ -97,16 +104,6 @@ public abstract class Localizer : ILocalizer public interface ILocalizer { - /// - /// Current language code - /// - string LanguageCode { get; } - - /// - /// Set language for localizer - /// - ILocalizer Language(string languageCode); - /// /// ///