diff --git a/src/Mocale/Managers/LocalizationManager.cs b/src/Mocale/Managers/LocalizationManager.cs index bd39b9d..c81e8d5 100644 --- a/src/Mocale/Managers/LocalizationManager.cs +++ b/src/Mocale/Managers/LocalizationManager.cs @@ -55,6 +55,8 @@ public async Task SetCultureAsync(CultureInfo culture) CurrentCulture = culture; + UpdateThreadCulture(culture); + currentCultureManager.SetActiveCulture(culture); logger.LogDebug("Updated localization culture to {CultureName}", culture.Name); @@ -105,6 +107,8 @@ private Task InitializeInternal() .Forget(); } + UpdateThreadCulture(CurrentCulture); + return Task.FromResult(true); } @@ -163,4 +167,14 @@ private async Task TryLoadInternalAndExternalTranslations(CultureInfo cult return result.Loaded || localTranslations.Loaded; } + + private static void UpdateThreadCulture(CultureInfo cultureInfo) + { + Thread.CurrentThread.CurrentCulture = cultureInfo; + Thread.CurrentThread.CurrentUICulture = cultureInfo; + CultureInfo.CurrentCulture = cultureInfo; + CultureInfo.CurrentUICulture = cultureInfo; + CultureInfo.DefaultThreadCurrentCulture = cultureInfo; + CultureInfo.DefaultThreadCurrentUICulture = cultureInfo; + } } diff --git a/tests/Mocale.UnitTests/Collections/CollectionDefinitions.cs b/tests/Mocale.UnitTests/Collections/CollectionDefinitions.cs new file mode 100644 index 0000000..59bd015 --- /dev/null +++ b/tests/Mocale.UnitTests/Collections/CollectionDefinitions.cs @@ -0,0 +1,7 @@ +namespace Mocale.UnitTests.Collections; + +[CollectionDefinition(CollectionNames.MocaleLocatorTests, DisableParallelization = true)] +public class MocaleLocatorTestsCollectionDefinition; + +[CollectionDefinition(CollectionNames.ThreadCultureTests, DisableParallelization = true)] +public class ThreadCultureTestsCollectionDefinition; diff --git a/tests/Mocale.UnitTests/Collections/CollectionNames.cs b/tests/Mocale.UnitTests/Collections/CollectionNames.cs index a5b79cc..b7b5527 100644 --- a/tests/Mocale.UnitTests/Collections/CollectionNames.cs +++ b/tests/Mocale.UnitTests/Collections/CollectionNames.cs @@ -3,4 +3,6 @@ namespace Mocale.UnitTests.Collections; public static class CollectionNames { public const string MocaleLocatorTests = "MocaleLocatorCollection"; + + public const string ThreadCultureTests = "ThreadCultureCollection"; } diff --git a/tests/Mocale.UnitTests/Collections/MocaleLocatorTestsCollectionDefinition.cs b/tests/Mocale.UnitTests/Collections/MocaleLocatorTestsCollectionDefinition.cs deleted file mode 100644 index e358e0a..0000000 --- a/tests/Mocale.UnitTests/Collections/MocaleLocatorTestsCollectionDefinition.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Mocale.UnitTests.Collections; - -[CollectionDefinition(CollectionNames.MocaleLocatorTests, DisableParallelization = true)] -public class MocaleLocatorTestsCollectionDefinition; diff --git a/tests/Mocale.UnitTests/Managers/LocalizationManagerTests.cs b/tests/Mocale.UnitTests/Managers/LocalizationManagerTests.cs index e9dd051..58ca66c 100644 --- a/tests/Mocale.UnitTests/Managers/LocalizationManagerTests.cs +++ b/tests/Mocale.UnitTests/Managers/LocalizationManagerTests.cs @@ -6,9 +6,11 @@ using Mocale.Managers; using Mocale.Models; using Mocale.Testing; +using Mocale.UnitTests.Collections; namespace Mocale.UnitTests.Managers; +[Collection(CollectionNames.ThreadCultureTests)] public class LocalizationManagerTests : FixtureBase { #region Setup @@ -22,8 +24,6 @@ public class LocalizationManagerTests : FixtureBase public LocalizationManagerTests() { - mocaleConfiguration = new Mock(); - mocaleConfiguration.SetupGet(m => m.UseExternalProvider) .Returns(true); @@ -31,6 +31,16 @@ public LocalizationManagerTests() .Returns(mocaleConfiguration.Object); } + ~LocalizationManagerTests() + { + Thread.CurrentThread.CurrentCulture = null!; + Thread.CurrentThread.CurrentUICulture = null!; + CultureInfo.CurrentCulture = null!; + CultureInfo.CurrentUICulture = null!; + CultureInfo.DefaultThreadCurrentCulture = null; + CultureInfo.DefaultThreadCurrentUICulture = null; + } + public override ILocalizationManager CreateSystemUnderTest() { return new LocalizationManager( @@ -38,8 +48,7 @@ public override ILocalizationManager CreateSystemUnderTest() configurationManager.Object, logger.Object, translationResolver.Object, - internalTranslatorManager.Object - ); + internalTranslatorManager.Object); } #endregion Setup @@ -563,6 +572,46 @@ public async Task Initialize_WhenExternalProviderNotEnabledAndLocalProviderLoads Times.Never()); } + [Fact] + public async Task InitializeAsync_WhenInitialized_ShouldSetThreadCulturesToActiveCulture() + { + // Arrange + currentCultureManager.Setup(m => m.GetActiveCulture()) + .Returns(new CultureInfo("it-IT")); + + translationResolver.Setup(m => m.LoadLocalTranslations(new CultureInfo("it-IT"))) + .Returns(new TranslationLoadResult() + { + Loaded = true, + Localization = new Localization() + { + CultureInfo = new CultureInfo("it-IT"), + Translations = new Dictionary() + { + { "Hello", "Ciao!" } + } + }, + Source = TranslationSource.WarmCache, + }); + + // This doesn't seem to work when tests run in parallel... + // Assert.NotEqual(new CultureInfo("it-IT"), Thread.CurrentThread.CurrentCulture); + // Assert.NotEqual(new CultureInfo("it-IT"), Thread.CurrentThread.CurrentUICulture); + // Assert.NotEqual(new CultureInfo("it-IT"), CultureInfo.CurrentCulture); + // Assert.NotEqual(new CultureInfo("it-IT"), CultureInfo.CurrentUICulture); + + // Act + var initialized = await Sut.Initialize(); + + // Assert + Assert.True(initialized); + + Assert.Equivalent(new CultureInfo("it-IT"), Thread.CurrentThread.CurrentCulture); + Assert.Equivalent(new CultureInfo("it-IT"), Thread.CurrentThread.CurrentUICulture); + Assert.Equivalent(new CultureInfo("it-IT"), CultureInfo.CurrentCulture); + Assert.Equivalent(new CultureInfo("it-IT"), CultureInfo.CurrentUICulture); + } + [Fact] public async Task SetCultureAsync_WhenSomethingThrows_ShouldLogAndReturnFalse() { @@ -956,6 +1005,55 @@ public async Task SetCultureAsync_WhenExternalProviderNotEnabledAndLocalTranslat internalTranslatorManager.Verify(m => m.RaisePropertyChanged(null), Times.Once); } + [Fact] + public async Task SetCultureAsync_WhenLoadSuccessful_ShouldUpdateThreadCultures() + { + mocaleConfiguration.SetupGet(m => m.UseExternalProvider) + .Returns(false); + + var activeCulture = new CultureInfo("it-IT"); + + currentCultureManager.Setup(m => m.GetActiveCulture()) + .Returns(activeCulture); + + var newCulture = new CultureInfo("fr-FR"); + + var localLoadResult = new TranslationLoadResult() + { + Loaded = true, + Localization = new Localization() + { + CultureInfo = newCulture, + Translations = new Dictionary() + { + { "KeyOne", "Bonjour le monde" }, + }, + }, + Source = TranslationSource.Internal, + }; + + translationResolver.Setup(m => m.LoadLocalTranslations(newCulture)) + .Returns(localLoadResult); + + // This doesn't seem to work when tests run in parallel... + // Assert.NotEqual(new CultureInfo("fr-FR"), Thread.CurrentThread.CurrentCulture); + // Assert.NotEqual(new CultureInfo("fr-FR"), Thread.CurrentThread.CurrentUICulture); + // Assert.NotEqual(new CultureInfo("fr-FR"), CultureInfo.CurrentCulture); + // Assert.NotEqual(new CultureInfo("fr-FR"), CultureInfo.CurrentUICulture); + + // Act + var loaded = await Sut.SetCultureAsync(newCulture); + + // Assert + Assert.True(loaded); + Assert.Equal(newCulture, Sut.CurrentCulture); + + Assert.Equivalent(new CultureInfo("fr-FR"), Thread.CurrentThread.CurrentCulture); + Assert.Equivalent(new CultureInfo("fr-FR"), Thread.CurrentThread.CurrentUICulture); + Assert.Equivalent(new CultureInfo("fr-FR"), CultureInfo.CurrentCulture); + Assert.Equivalent(new CultureInfo("fr-FR"), CultureInfo.CurrentUICulture); + } + #endregion Tests } diff --git a/tests/Mocale.UnitTests/Providers/AppResourceProviderTests.cs b/tests/Mocale.UnitTests/Providers/AppResourceProviderTests.cs index b02fc48..302feca 100644 --- a/tests/Mocale.UnitTests/Providers/AppResourceProviderTests.cs +++ b/tests/Mocale.UnitTests/Providers/AppResourceProviderTests.cs @@ -5,9 +5,11 @@ using Mocale.Exceptions; using Mocale.Models; using Mocale.Providers; +using Mocale.UnitTests.Collections; namespace Mocale.UnitTests.Providers; +[Collection(CollectionNames.ThreadCultureTests)] public class AppResourceProviderTests : FixtureBase { #region Setup @@ -55,6 +57,11 @@ public void Constructor_WhenAppResourcesTypeIsNull_ShouldThrow() public void Constructor_WhenAppResourcesTypeIsNotResx_ShouldReturnNull() { // Arrange + Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; + Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + appResourcesConfigManager.Setup(m => m.Configuration) .Returns(new AppResourcesConfig() { @@ -62,7 +69,10 @@ public void Constructor_WhenAppResourcesTypeIsNotResx_ShouldReturnNull() }); mocaleConfigurationManager.Setup(m => m.Configuration) - .Returns(new MocaleConfiguration()); + .Returns(new MocaleConfiguration() + { + DefaultCulture = new CultureInfo("en-GB"), + }); // Act var values = Sut.GetValuesForCulture(new CultureInfo("en-GB"));