diff --git a/src/OrchardCore/OrchardCore.Localization.Abstractions/ILocalizationManager.cs b/src/OrchardCore/OrchardCore.Localization.Abstractions/ILocalizationManager.cs
index fde185debd6..76a842b8b6d 100644
--- a/src/OrchardCore/OrchardCore.Localization.Abstractions/ILocalizationManager.cs
+++ b/src/OrchardCore/OrchardCore.Localization.Abstractions/ILocalizationManager.cs
@@ -12,5 +12,5 @@ public interface ILocalizationManager
///
/// The .
/// A for the specified culture.
- CultureDictionary GetDictionary(CultureInfo culture);
+ Task GetDictionaryAsync(CultureInfo culture);
}
diff --git a/src/OrchardCore/OrchardCore.Localization.Abstractions/ITranslationProvider.cs b/src/OrchardCore/OrchardCore.Localization.Abstractions/ITranslationProvider.cs
index 734c9f3e51b..c72a10f7b66 100644
--- a/src/OrchardCore/OrchardCore.Localization.Abstractions/ITranslationProvider.cs
+++ b/src/OrchardCore/OrchardCore.Localization.Abstractions/ITranslationProvider.cs
@@ -10,5 +10,5 @@ public interface ITranslationProvider
///
/// The culture name.
/// The that will contains all loaded translations.
- void LoadTranslations(string cultureName, CultureDictionary dictionary);
+ Task LoadTranslationsAsync(string cultureName, CultureDictionary dictionary);
}
diff --git a/src/OrchardCore/OrchardCore.Localization.Core/LocalizationManager.cs b/src/OrchardCore/OrchardCore.Localization.Core/LocalizationManager.cs
index 8fd61b47b66..e64028087de 100644
--- a/src/OrchardCore/OrchardCore.Localization.Core/LocalizationManager.cs
+++ b/src/OrchardCore/OrchardCore.Localization.Core/LocalizationManager.cs
@@ -33,9 +33,9 @@ public LocalizationManager(
}
///
- public CultureDictionary GetDictionary(CultureInfo culture)
+ public async Task GetDictionaryAsync(CultureInfo culture)
{
- var cachedDictionary = _cache.GetOrCreate(CacheKeyPrefix + culture.Name, k => new Lazy(() =>
+ var cachedDictionary = await _cache.GetOrCreateAsync(CacheKeyPrefix + culture.Name, async (e) =>
{
var rule = _defaultPluralRule;
@@ -50,12 +50,12 @@ public CultureDictionary GetDictionary(CultureInfo culture)
var dictionary = new CultureDictionary(culture.Name, rule ?? _defaultPluralRule);
foreach (var translationProvider in _translationProviders)
{
- translationProvider.LoadTranslations(culture.Name, dictionary);
+ await translationProvider.LoadTranslationsAsync(culture.Name, dictionary);
}
return dictionary;
- }, LazyThreadSafetyMode.ExecutionAndPublication));
+ });
- return cachedDictionary.Value;
+ return cachedDictionary;
}
}
diff --git a/src/OrchardCore/OrchardCore.Localization.Core/OrchardCore.Localization.Core.csproj b/src/OrchardCore/OrchardCore.Localization.Core/OrchardCore.Localization.Core.csproj
index 252b3fd7c7c..fa9070a6894 100644
--- a/src/OrchardCore/OrchardCore.Localization.Core/OrchardCore.Localization.Core.csproj
+++ b/src/OrchardCore/OrchardCore.Localization.Core/OrchardCore.Localization.Core.csproj
@@ -14,6 +14,7 @@
+
diff --git a/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PoFilesTranslationsProvider.cs b/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PoFilesTranslationsProvider.cs
index 73b7be45549..6aa65b5ba43 100644
--- a/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PoFilesTranslationsProvider.cs
+++ b/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PoFilesTranslationsProvider.cs
@@ -21,21 +21,21 @@ public PoFilesTranslationsProvider(ILocalizationFileLocationProvider poFileLocat
}
///
- public void LoadTranslations(string cultureName, CultureDictionary dictionary)
+ public async Task LoadTranslationsAsync(string cultureName, CultureDictionary dictionary)
{
foreach (var fileInfo in _poFilesLocationProvider.GetLocations(cultureName))
{
- LoadFileToDictionary(fileInfo, dictionary);
+ await LoadFileToDictionaryAsync(fileInfo, dictionary);
}
}
- private void LoadFileToDictionary(IFileInfo fileInfo, CultureDictionary dictionary)
+ private async Task LoadFileToDictionaryAsync(IFileInfo fileInfo, CultureDictionary dictionary)
{
if (fileInfo.Exists && !fileInfo.IsDirectory)
{
using var stream = fileInfo.CreateReadStream();
using var reader = new StreamReader(stream);
- dictionary.MergeTranslations(_parser.Parse(reader));
+ dictionary.MergeTranslations(await _parser.ParseAsync(reader).ToListAsync());
}
}
}
diff --git a/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PoParser.cs b/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PoParser.cs
index 10f5157f6e0..df46d222fc1 100644
--- a/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PoParser.cs
+++ b/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PoParser.cs
@@ -26,12 +26,12 @@ static PoParser()
/// The .
/// A list of culture records.
#pragma warning disable CA1822 // Mark members as static
- public IEnumerable Parse(TextReader reader)
+ public async IAsyncEnumerable ParseAsync(TextReader reader)
#pragma warning restore CA1822 // Mark members as static
{
var entryBuilder = new DictionaryRecordBuilder();
string line;
- while ((line = reader.ReadLine()) != null)
+ while ((line = await reader.ReadLineAsync()) != null)
{
(var context, var content) = ParseLine(line);
diff --git a/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PortableObjectStringLocalizer.cs b/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PortableObjectStringLocalizer.cs
index 9f0085c1eae..a6f238bcf5c 100644
--- a/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PortableObjectStringLocalizer.cs
+++ b/src/OrchardCore/OrchardCore.Localization.Core/PortableObject/PortableObjectStringLocalizer.cs
@@ -66,10 +66,11 @@ public virtual LocalizedString this[string name]
public virtual IEnumerable GetAllStrings(bool includeParentCultures)
{
var culture = CultureInfo.CurrentUICulture;
+ var localizedStrings = includeParentCultures
+ ? GetAllStringsFromCultureHierarchyAsync(culture)
+ : GetAllStringsAsync(culture);
- return includeParentCultures
- ? GetAllStringsFromCultureHierarchy(culture)
- : GetAllStrings(culture);
+ return localizedStrings.ToEnumerable();
}
///
@@ -106,9 +107,9 @@ public virtual (LocalizedString, object[]) GetTranslation(string name, params ob
}
}
- private IEnumerable GetAllStrings(CultureInfo culture)
+ private async IAsyncEnumerable GetAllStringsAsync(CultureInfo culture)
{
- var dictionary = _localizationManager.GetDictionary(culture);
+ var dictionary = await _localizationManager.GetDictionaryAsync(culture);
foreach (var translation in dictionary.Translations)
{
@@ -116,35 +117,35 @@ private IEnumerable GetAllStrings(CultureInfo culture)
}
}
- private List GetAllStringsFromCultureHierarchy(CultureInfo culture)
+ private async IAsyncEnumerable GetAllStringsFromCultureHierarchyAsync(CultureInfo culture)
{
var currentCulture = culture;
- var allLocalizedStrings = new List();
+ var resourcesNames = new HashSet();
do
{
- var localizedStrings = GetAllStrings(currentCulture);
+ var localizedStrings = await GetAllStringsAsync(currentCulture).ToListAsync();
if (localizedStrings != null)
{
foreach (var localizedString in localizedStrings)
{
- if (!allLocalizedStrings.Any(ls => ls.Name == localizedString.Name))
+ if (!resourcesNames.Contains(localizedString.Name))
{
- allLocalizedStrings.Add(localizedString);
+ resourcesNames.Add(localizedString.Name);
+
+ yield return localizedString;
}
}
}
currentCulture = currentCulture.Parent;
} while (currentCulture != currentCulture.Parent);
-
- return allLocalizedStrings;
}
protected string GetTranslation(string[] pluralForms, CultureInfo culture, int? count)
{
- var dictionary = _localizationManager.GetDictionary(culture);
+ var dictionary = _localizationManager.GetDictionaryAsync(culture).GetAwaiter().GetResult();
var pluralForm = count.HasValue ? dictionary.PluralRule(count.Value) : 0;
@@ -187,7 +188,7 @@ protected string GetTranslation(string name, string context, CultureInfo culture
string ExtractTranslation()
{
- var dictionary = _localizationManager.GetDictionary(culture);
+ var dictionary = _localizationManager.GetDictionaryAsync(culture).GetAwaiter().GetResult();
if (dictionary != null)
{
diff --git a/test/OrchardCore.Tests/Localization/LocalizationManagerTests.cs b/test/OrchardCore.Tests/Localization/LocalizationManagerTests.cs
index 83dfede050d..459db02f096 100644
--- a/test/OrchardCore.Tests/Localization/LocalizationManagerTests.cs
+++ b/test/OrchardCore.Tests/Localization/LocalizationManagerTests.cs
@@ -24,57 +24,57 @@ public LocalizationManagerTests()
_memoryCache = new MemoryCache(new MemoryCacheOptions());
}
- [Fact]
- public void GetDictionaryReturnsDictionaryWithPluralRuleAndCultureIfNoTranslationsExists()
- {
- _translationProvider.Setup(o => o.LoadTranslations(
- It.Is(culture => culture == "cs"),
- It.IsAny())
- );
+ [Fact]
+ public async Task GetDictionaryReturnsDictionaryWithPluralRuleAndCultureIfNoTranslationsExists()
+ {
+ _translationProvider.Setup(o => o.LoadTranslationsAsync(
+ It.Is(culture => culture == "cs"),
+ It.IsAny())
+ );
var manager = new LocalizationManager(new[] { _pluralRuleProvider.Object }, new[] { _translationProvider.Object }, _memoryCache);
- var dictionary = manager.GetDictionary(CultureInfo.GetCultureInfo("cs"));
+ var dictionary = await manager.GetDictionaryAsync(CultureInfo.GetCultureInfo("cs"));
Assert.Equal("cs", dictionary.CultureName);
Assert.Equal(PluralizationRule.Czech, dictionary.PluralRule);
}
- [Fact]
- public void GetDictionaryReturnsDictionaryWithTranslationsFromProvider()
- {
- var dictionaryRecord = new CultureDictionaryRecord("ball", "míč", "míče", "míčů");
- _translationProvider
- .Setup(o => o.LoadTranslations(It.Is(culture => culture == "cs"), It.IsAny()))
- .Callback((culture, dictioanry) => dictioanry.MergeTranslations(new[] { dictionaryRecord }));
+ [Fact]
+ public async Task GetDictionaryReturnsDictionaryWithTranslationsFromProvider()
+ {
+ var dictionaryRecord = new CultureDictionaryRecord("ball", "míč", "míče", "míčů");
+ _translationProvider
+ .Setup(o => o.LoadTranslationsAsync(It.Is(culture => culture == "cs"), It.IsAny()))
+ .Callback((culture, dictioanry) => dictioanry.MergeTranslations(new[] { dictionaryRecord }));
var manager = new LocalizationManager([_pluralRuleProvider.Object], [_translationProvider.Object], _memoryCache);
- var dictionary = manager.GetDictionary(CultureInfo.GetCultureInfo("cs"));
- var key = new CultureDictionaryRecordKey { MessageId = "ball" };
+ var dictionary = await manager.GetDictionaryAsync(CultureInfo.GetCultureInfo("cs"));
+ var key = new CultureDictionaryRecordKey { MessageId = "ball" };
dictionary.Translations.TryGetValue(key, out var translations);
Assert.Equal(translations, dictionaryRecord.Translations);
}
- [Fact]
- public void GetDictionarySelectsPluralRuleFromProviderWithHigherPriority()
- {
- PluralizationRuleDelegate csPluralRuleOverride = n => ((n == 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 0);
+ [Fact]
+ public async Task GetDictionarySelectsPluralRuleFromProviderWithHigherPriority()
+ {
+ PluralizationRuleDelegate csPluralRuleOverride = n => ((n == 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 0);
var highPriorityRuleProvider = new Mock();
highPriorityRuleProvider.SetupGet(o => o.Order).Returns(-1);
highPriorityRuleProvider.Setup(o => o.TryGetRule(It.Is(culture => culture.Name == "cs"), out csPluralRuleOverride)).Returns(true);
- _translationProvider.Setup(o => o.LoadTranslations(
- It.Is(culture => culture == "cs"),
- It.IsAny())
- );
+ _translationProvider.Setup(o => o.LoadTranslationsAsync(
+ It.Is(culture => culture == "cs"),
+ It.IsAny())
+ );
var manager = new LocalizationManager([_pluralRuleProvider.Object, highPriorityRuleProvider.Object], [_translationProvider.Object], _memoryCache);
- var dictionary = manager.GetDictionary(CultureInfo.GetCultureInfo("cs"));
+ var dictionary = await manager.GetDictionaryAsync(CultureInfo.GetCultureInfo("cs"));
Assert.Equal(dictionary.PluralRule, csPluralRuleOverride);
}
diff --git a/test/OrchardCore.Tests/Localization/PoParserTests.cs b/test/OrchardCore.Tests/Localization/PoParserTests.cs
index 1fa026943f8..e992c26bb62 100644
--- a/test/OrchardCore.Tests/Localization/PoParserTests.cs
+++ b/test/OrchardCore.Tests/Localization/PoParserTests.cs
@@ -6,28 +6,28 @@ namespace OrchardCore.Tests.Localization;
public class PoParserTests
{
[Fact]
- public void ParseRetursSimpleEntry()
+ public async Task ParseRetursSimpleEntry()
{
// msgid "Unknown system error"
// msgstr "Error desconegut del sistema"
- var entries = ParseText("SimpleEntry");
+ var entries = await ParseTextAsync("SimpleEntry");
Assert.Equal("Unknown system error", entries[0].Key);
Assert.Equal("Error desconegut del sistema", entries[0].Translations[0]);
}
[Fact]
- public void ParseIgnoresEntryWithoutTranslation()
+ public async Task ParseIgnoresEntryWithoutTranslation()
{
// "msgid "Unknown system error"
// "msgstr ""
- var entries = ParseText("EntryWithoutTranslation");
+ var entries = await ParseTextAsync("EntryWithoutTranslation");
Assert.Empty(entries);
}
[Fact]
- public void ParseIgnoresPoeditHeader()
+ public async Task ParseIgnoresPoeditHeader()
{
// # Translation of kstars.po into Spanish.
// # This file is distributed under the same license as the kdeedu package.
@@ -48,25 +48,25 @@ public void ParseIgnoresPoeditHeader()
// msgid "Unknown system error"
// msgstr "Error desconegut del sistema"
- var entries = ParseText("PoeditHeader");
+ var entries = await ParseTextAsync("PoeditHeader");
Assert.True(entries.Length == 1);
Assert.True(entries[0].Translations.Length == 1);
}
[Fact]
- public void ParseSetsContext()
+ public async Task ParseSetsContext()
{
// msgctxt "OrchardCore.Localization"
// msgid "Unknown system error"
// msgstr "Error desconegut del sistema"
- var entries = ParseText("EntryWithContext");
+ var entries = await ParseTextAsync("EntryWithContext");
Assert.Equal("OrchardCore.Localization|Unknown system error", entries[0].Key, ignoreCase: true);
}
[Fact]
- public void ParseIgnoresComments()
+ public async Task ParseIgnoresComments()
{
// # translator-comments
// #. extracted-comments
@@ -79,38 +79,38 @@ public void ParseIgnoresComments()
// msgid "Unknown system error"
// msgstr "Error desconegut del sistema"
- var entries = ParseText("EntryWithComments");
+ var entries = await ParseTextAsync("EntryWithComments");
Assert.Equal("OrchardCore.Localization|Unknown system error", entries[0].Key, ignoreCase: true);
Assert.Equal("Error desconegut del sistema", entries[0].Translations[0]);
}
[Fact]
- public void ParseOnlyTrimsLeadingAndTrailingQuotes()
+ public async Task ParseOnlyTrimsLeadingAndTrailingQuotes()
{
// msgid "\"{0}\""
// msgstr "\"{0}\""
- var entries = ParseText("EntryWithQuotes");
+ var entries = await ParseTextAsync("EntryWithQuotes");
Assert.Equal("\"{0}\"", entries[0].Key);
Assert.Equal("\"{0}\"", entries[0].Translations[0]);
}
[Fact]
- public void ParseHandleUnclosedQuote()
+ public async Task ParseHandleUnclosedQuote()
{
// msgctxt "
// msgid "Foo \"{0}\""
// msgstr "Foo \"{0}\""
- var entries = ParseText("EntryWithUnclosedQuote");
+ var entries = await ParseTextAsync("EntryWithUnclosedQuote");
Assert.Equal("Foo \"{0}\"", entries[0].Key);
}
[Fact]
- public void ParseHandlesMultilineEntry()
+ public async Task ParseHandlesMultilineEntry()
{
// msgid ""
// "Here is an example of how one might continue a very long string\n"
@@ -119,26 +119,26 @@ public void ParseHandlesMultilineEntry()
// "Here is an example of how one might continue a very long translation\n"
// "for the common case the string represents multi-line output."
- var entries = ParseText("EntryWithMultilineText");
+ var entries = await ParseTextAsync("EntryWithMultilineText");
Assert.Equal("Here is an example of how one might continue a very long string\nfor the common case the string represents multi-line output.", entries[0].Key);
Assert.Equal("Here is an example of how one might continue a very long translation\nfor the common case the string represents multi-line output.", entries[0].Translations[0]);
}
[Fact]
- public void ParsePreservesEscapedCharacters()
+ public async Task ParsePreservesEscapedCharacters()
{
// msgid "Line:\t\"{0}\"\n"
// msgstr "Line:\t\"{0}\"\n"
- var entries = ParseText("EntryWithEscapedCharacters");
+ var entries = await ParseTextAsync("EntryWithEscapedCharacters");
Assert.Equal("Line:\t\"{0}\"\n", entries[0].Key);
Assert.Equal("Line:\t\"{0}\"\n", entries[0].Translations[0]);
}
[Fact]
- public void ParseReadsPluralTranslations()
+ public async Task ParseReadsPluralTranslations()
{
// msgid "book"
// msgid_plural "books"
@@ -146,7 +146,7 @@ public void ParseReadsPluralTranslations()
// msgstr[1] "knihy"
// msgstr[2] "knih"
- var entries = ParseText("EntryWithPlural");
+ var entries = await ParseTextAsync("EntryWithPlural");
Assert.Equal("book", entries[0].Key);
Assert.Equal("kniha", entries[0].Translations[0]);
@@ -155,7 +155,7 @@ public void ParseReadsPluralTranslations()
}
[Fact]
- public void ParseReadsPluralAndMultilineText()
+ public async Task ParseReadsPluralAndMultilineText()
{
// msgid ""
// "Here is an example of how one might continue a very long string\n"
@@ -170,7 +170,7 @@ public void ParseReadsPluralAndMultilineText()
// "Here are examples of how one might continue a very long translation\n"
// "for the common case the string represents multi-line output."
- var entries = ParseText("EntryWithPluralAndMultilineText");
+ var entries = await ParseTextAsync("EntryWithPluralAndMultilineText");
Assert.Equal("Here is an example of how one might continue a very long string\nfor the common case the string represents multi-line output.", entries[0].Key);
Assert.Equal("Here is an example of how one might continue a very long translation\nfor the common case the string represents multi-line output.", entries[0].Translations[0]);
@@ -178,7 +178,7 @@ public void ParseReadsPluralAndMultilineText()
}
[Fact]
- public void ParseReadsMultipleEntries()
+ public async Task ParseReadsMultipleEntries()
{
// #. "File {0} does not exist"
// msgctxt "OrchardCore.FileSystems.Media.FileSystemStorageProvider"
@@ -190,7 +190,7 @@ public void ParseReadsMultipleEntries()
// msgid "Directory {0} does not exist"
// msgstr "Složka {0} neexistuje"
- var entries = ParseText("MultipleEntries");
+ var entries = await ParseTextAsync("MultipleEntries");
Assert.Equal(2, entries.Length);
@@ -201,13 +201,14 @@ public void ParseReadsMultipleEntries()
Assert.Equal("Složka {0} neexistuje", entries[1].Translations[0]);
}
- private static CultureDictionaryRecord[] ParseText(string resourceName)
+ private static async Task ParseTextAsync(string resourceName)
{
var parser = new PoParser();
var testAssembly = typeof(PoParserTests).Assembly;
using var resource = testAssembly.GetManifestResourceStream("OrchardCore.Tests.Localization.PoFiles." + resourceName + ".po");
using var reader = new StreamReader(resource);
- return parser.Parse(reader).ToArray();
+
+ return (await parser.ParseAsync(reader).ToListAsync()).ToArray();
}
}
diff --git a/test/OrchardCore.Tests/Localization/PortableObjectStringLocalizerTests.cs b/test/OrchardCore.Tests/Localization/PortableObjectStringLocalizerTests.cs
index e3d6712c832..4c0fb44d3a1 100644
--- a/test/OrchardCore.Tests/Localization/PortableObjectStringLocalizerTests.cs
+++ b/test/OrchardCore.Tests/Localization/PortableObjectStringLocalizerTests.cs
@@ -342,8 +342,8 @@ public void LocalizerWithContextShouldCallGetDictionaryOncePerCulture(string cul
// Act
var translation = localizer["Hello"];
- // Assert
- _localizationManager.Verify(lm => lm.GetDictionary(It.IsAny()), Times.Exactly(expectedCalls));
+ // Assert
+ _localizationManager.Verify(lm => lm.GetDictionaryAsync(It.IsAny()), Times.Exactly(expectedCalls));
Assert.Equal("Hello", translation);
}
@@ -359,8 +359,9 @@ private void SetupDictionary(string cultureName, IEnumerable o.GetDictionary(It.Is(c => c.Name == cultureName))).Returns(dictionary);
- }
+ _localizationManager.Setup(o => o.GetDictionaryAsync(It.Is(c => c.Name == cultureName)))
+ .ReturnsAsync(dictionary);
+ }
public class PortableObjectLocalizationStartup
{