From b74bf7da2b250e30514a162421eb2ccdf68a1fe9 Mon Sep 17 00:00:00 2001 From: Wezz Balk Date: Wed, 30 Oct 2024 16:18:47 +0100 Subject: [PATCH 1/2] Add method to transform a class to a translated dictionary --- .../ILocalizationProvider.cs | 8 +++++ .../LocalizationProvider.cs | 29 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/DbLocalizationProvider/ILocalizationProvider.cs b/src/DbLocalizationProvider/ILocalizationProvider.cs index 99002415..c5c39caf 100644 --- a/src/DbLocalizationProvider/ILocalizationProvider.cs +++ b/src/DbLocalizationProvider/ILocalizationProvider.cs @@ -140,4 +140,12 @@ public interface ILocalizationProvider /// If you need to format the message and substitute placeholders. /// Translation for current language or in invariant language. string GetStringWithInvariantFallback(Expression> resource, params object[] formatArguments); + + /// + /// Converts a localized resource dictionary to a translated dictionary based on the specified type. + /// + /// The type to retrieve localized resources for. + /// A dictionary containing the localized resources translated to the current culture. + /// Thrown when the object does not have a LocalizedResourceAttribute. + IDictionary LocalizedResourceToTranslatedDictionary(Type type); } diff --git a/src/DbLocalizationProvider/LocalizationProvider.cs b/src/DbLocalizationProvider/LocalizationProvider.cs index 508935d3..a314068e 100644 --- a/src/DbLocalizationProvider/LocalizationProvider.cs +++ b/src/DbLocalizationProvider/LocalizationProvider.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Linq.Expressions; using System.Text.RegularExpressions; +using DbLocalizationProvider.Abstractions; using DbLocalizationProvider.Internal; using DbLocalizationProvider.Json; using DbLocalizationProvider.Queries; @@ -348,6 +349,34 @@ public virtual string GetStringByCulture( return GetStringByCulture(resourceKey, culture, formatArguments); } + /// + /// Converts a localized resource dictionary to a translated dictionary based on the specified type. + /// + /// The type to retrieve localized resources for. + /// A dictionary containing the localized resources translated to the current culture. + /// Thrown when the object does not have a LocalizedResourceAttribute. + public IDictionary LocalizedResourceToTranslatedDictionary(Type type) + { + _ = Attribute.GetCustomAttribute(type, typeof(LocalizedResourceAttribute)) ?? throw new ArgumentException($"Object needs to have a {nameof(LocalizedResourceAttribute)} to be converted"); + + return GetLocalizedResourceTranslations(type) + .ToDictionary(k => k.Key, v => v.Value); + } + + internal IEnumerable> GetLocalizedResourceTranslations(Type type) + { + foreach (var property in type.GetProperties()) + { + if (!Array.TrueForAll(Attribute.GetCustomAttributes(property), attribute => attribute is not HiddenAttribute)) + { + continue; + } + + var resourceKey = $"{type.Namespace}.{type.Name}.{property.Name}"; + yield return new(resourceKey, GetString(property.Name)); + } + } + internal static string Format(string message, params object[] formatArguments) { if (formatArguments == null || !formatArguments.Any()) From a949a686399f9f16e3d8000f1072f362ba47efc7 Mon Sep 17 00:00:00 2001 From: Valdis Iljuconoks Date: Sun, 3 Nov 2024 21:25:55 +0200 Subject: [PATCH 2/2] added some unit tests --- .../DictionaryConvertTests/_Tests.cs | 74 +++++++++++++++++++ .../ILocalizationProvider.cs | 28 ++++++- .../Internal/PropertyInfoExtensions.cs | 22 ++++++ .../LocalizationProvider.cs | 48 ++++++++++-- 4 files changed, 165 insertions(+), 7 deletions(-) create mode 100644 Tests/DbLocalizationProvider.Tests/DictionaryConvertTests/_Tests.cs create mode 100644 src/DbLocalizationProvider/Internal/PropertyInfoExtensions.cs diff --git a/Tests/DbLocalizationProvider.Tests/DictionaryConvertTests/_Tests.cs b/Tests/DbLocalizationProvider.Tests/DictionaryConvertTests/_Tests.cs new file mode 100644 index 00000000..f3ff4a71 --- /dev/null +++ b/Tests/DbLocalizationProvider.Tests/DictionaryConvertTests/_Tests.cs @@ -0,0 +1,74 @@ +using DbLocalizationProvider.Internal; +using DbLocalizationProvider.Queries; +using DbLocalizationProvider.Refactoring; +using DbLocalizationProvider.Sync; +using DbLocalizationProvider.Tests.DataAnnotations; +using Microsoft.Extensions.Options; +using System.Collections.Generic; +using DbLocalizationProvider.Abstractions; +using Xunit; + +namespace DbLocalizationProvider.Tests.DictionaryConvertTests; + +public class _Tests +{ + private readonly LocalizationProvider _provider; + + public _Tests() + { + var state = new ScanState(); + var ctx = new ConfigurationContext(); + var wrapper = new OptionsWrapper(ctx); + var keyBuilder = new ResourceKeyBuilder(state, wrapper); + + ctx.TypeFactory + .ForQuery() + .SetHandler() + .ForQuery() + .SetHandler(); + + var queryExecutor = new QueryExecutor(ctx.TypeFactory); + + var expressHelper = new ExpressionHelper(keyBuilder); + _provider = new LocalizationProvider(keyBuilder, + expressHelper, + wrapper, + queryExecutor, + new ScanState()); + } + + [Fact] + public void ConvertToDictionary() + { + var result = _provider.ToDictionary(typeof(ResourceToDictionaryModel)); + + Assert.NotNull(result); + Assert.Single(result); + Assert.True(result.ContainsKey("DbLocalizationProvider.Tests.DictionaryConvertTests.ResourceToDictionaryModel.Property1")); + } + + + [Fact] + public void ConvertToDictionary_HiddenResource_ShouldNotInclude() + { + var result = _provider.ToDictionary(); + + Assert.Single(result); + Assert.True(result.ContainsKey("DbLocalizationProvider.Tests.DictionaryConvertTests.ResourceToDictionaryModelWithHiddenProperty.Property1")); + } +} + +[LocalizedResource] +public class ResourceToDictionaryModel +{ + public string Property1 { get; set; } +} + +[LocalizedResource] +public class ResourceToDictionaryModelWithHiddenProperty +{ + public string Property1 { get; set; } + + [Hidden] + public string Property2 { get; set; } +} diff --git a/src/DbLocalizationProvider/ILocalizationProvider.cs b/src/DbLocalizationProvider/ILocalizationProvider.cs index c5c39caf..fc71aec1 100644 --- a/src/DbLocalizationProvider/ILocalizationProvider.cs +++ b/src/DbLocalizationProvider/ILocalizationProvider.cs @@ -147,5 +147,31 @@ public interface ILocalizationProvider /// The type to retrieve localized resources for. /// A dictionary containing the localized resources translated to the current culture. /// Thrown when the object does not have a LocalizedResourceAttribute. - IDictionary LocalizedResourceToTranslatedDictionary(Type type); + IDictionary ToDictionary(Type type); + + /// + /// Converts a localized resource dictionary to a translated dictionary based on the specified type. + /// + /// The type to retrieve localized resources for. + /// A dictionary containing the localized resources translated to the current culture. + /// Thrown when the object does not have a LocalizedResourceAttribute. + IDictionary ToDictionary(); + + /// + /// Converts a localized resource dictionary to a translated dictionary based on the specified type. + /// + /// The type to retrieve localized resources for. + /// Culture to get translations in. + /// A dictionary containing the localized resources translated to the current culture. + /// Thrown when the object does not have a LocalizedResourceAttribute. + IDictionary ToDictionary(CultureInfo culture); + + /// + /// Converts a localized resource dictionary to a translated dictionary based on the specified type. + /// + /// The type to retrieve localized resources for. + /// Culture to get translations in. + /// A dictionary containing the localized resources translated to the current culture. + /// Thrown when the object does not have a LocalizedResourceAttribute. + IDictionary ToDictionary(Type type, CultureInfo culture); } diff --git a/src/DbLocalizationProvider/Internal/PropertyInfoExtensions.cs b/src/DbLocalizationProvider/Internal/PropertyInfoExtensions.cs new file mode 100644 index 00000000..fbb11fe6 --- /dev/null +++ b/src/DbLocalizationProvider/Internal/PropertyInfoExtensions.cs @@ -0,0 +1,22 @@ +using System; +using System.Linq; +using System.Reflection; +using DbLocalizationProvider.Abstractions; + +namespace DbLocalizationProvider.Internal; + +/// +/// Some extensions for properties +/// +public static class PropertyInfoExtensions +{ + /// + /// Checks whether property is hidden or not. + /// + /// Property to check for. + /// true if property is hidden, otherwise false. + public static bool IsHidden(this PropertyInfo property) + { + return Attribute.GetCustomAttributes(property).FirstOrDefault(a => a is HiddenAttribute) != null; + } +} \ No newline at end of file diff --git a/src/DbLocalizationProvider/LocalizationProvider.cs b/src/DbLocalizationProvider/LocalizationProvider.cs index a314068e..2b642148 100644 --- a/src/DbLocalizationProvider/LocalizationProvider.cs +++ b/src/DbLocalizationProvider/LocalizationProvider.cs @@ -349,31 +349,67 @@ public virtual string GetStringByCulture( return GetStringByCulture(resourceKey, culture, formatArguments); } + /// + /// Converts a localized resource dictionary to a translated dictionary based on the specified type. + /// + /// The type to retrieve localized resources for. + /// A dictionary containing the localized resources translated to the current culture. + /// Thrown when the object does not have a LocalizedResourceAttribute. + public IDictionary ToDictionary() + { + return ToDictionary(typeof(T)); + } + + /// + /// Converts a localized resource dictionary to a translated dictionary based on the specified type. + /// + /// The type to retrieve localized resources for. + /// Culture to get translations in. + /// A dictionary containing the localized resources translated to the current culture. + /// Thrown when the object does not have a LocalizedResourceAttribute. + public IDictionary ToDictionary(CultureInfo culture) + { + return ToDictionary(typeof(T), culture); + } + /// /// Converts a localized resource dictionary to a translated dictionary based on the specified type. /// /// The type to retrieve localized resources for. /// A dictionary containing the localized resources translated to the current culture. /// Thrown when the object does not have a LocalizedResourceAttribute. - public IDictionary LocalizedResourceToTranslatedDictionary(Type type) + public IDictionary ToDictionary(Type type) + { + return ToDictionary(type, _queryExecutor.Execute(new GetCurrentUICulture.Query())); + } + + /// + /// Converts a localized resource dictionary to a translated dictionary based on the specified type. + /// + /// The type to retrieve localized resources for. + /// Culture to get translations in. + /// A dictionary containing the localized resources translated to the current culture. + /// Thrown when the object does not have a LocalizedResourceAttribute. + public IDictionary ToDictionary(Type type, CultureInfo culture) { _ = Attribute.GetCustomAttribute(type, typeof(LocalizedResourceAttribute)) ?? throw new ArgumentException($"Object needs to have a {nameof(LocalizedResourceAttribute)} to be converted"); - return GetLocalizedResourceTranslations(type) + return GetLocalizedResourceTranslations(type, culture) .ToDictionary(k => k.Key, v => v.Value); } - internal IEnumerable> GetLocalizedResourceTranslations(Type type) + internal IEnumerable> GetLocalizedResourceTranslations(Type type, CultureInfo culture) { foreach (var property in type.GetProperties()) { - if (!Array.TrueForAll(Attribute.GetCustomAttributes(property), attribute => attribute is not HiddenAttribute)) + if (property.IsHidden()) { continue; } - var resourceKey = $"{type.Namespace}.{type.Name}.{property.Name}"; - yield return new(resourceKey, GetString(property.Name)); + var resourceKey = _keyBuilder.BuildResourceKey(type, property.Name); + + yield return new(resourceKey, GetString(property.Name, culture)); } }