Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hotfix/improve assembly localization resources helper collect missing translations #293

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/CodeDoc/Atc/Atc.Helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,21 @@ CultureHelper.

<br />

## CultureInfoHelper

>```csharp
>public static class CultureInfoHelper
>```

### Static Methods

#### GetCulturesFromNames
>```csharp
>IList<CultureInfo> GetCulturesFromNames(IEnumerable<string> cultureNames)
>```

<br />

## DataAnnotationHelper

>```csharp
Expand Down
1 change: 1 addition & 0 deletions docs/CodeDoc/Atc/Index.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
- [CardinalDirectionTypeHelper](Atc.Helpers.md#cardinaldirectiontypehelper)
- [CliHelper](Atc.Helpers.md#clihelper)
- [CultureHelper](Atc.Helpers.md#culturehelper)
- [CultureInfoHelper](Atc.Helpers.md#cultureinfohelper)
- [DataAnnotationHelper](Atc.Helpers.md#dataannotationhelper)
- [DateTimeHelper](Atc.Helpers.md#datetimehelper)
- [DateTimeOffsetHelper](Atc.Helpers.md#datetimeoffsethelper)
Expand Down
4 changes: 4 additions & 0 deletions docs/CodeDoc/Atc/IndexExtended.md
Original file line number Diff line number Diff line change
Expand Up @@ -4461,6 +4461,9 @@
- GetLanguageNames(int displayLanguageLcid, List&lt;int&gt; includeOnlyLcids, DropDownFirstItemType dropDownFirstItemType = None)
- GetLanguageNames(int displayLanguageLcid, List&lt;string&gt; includeOnlyCultureNames, DropDownFirstItemType dropDownFirstItemType = None)
- GetSupportedCultures(int displayLanguageLcid = 1033)
- [CultureInfoHelper](Atc.Helpers.md#cultureinfohelper)
- Static Methods
- GetCulturesFromNames(IEnumerable&lt;string&gt; cultureNames)
- [DataAnnotationHelper](Atc.Helpers.md#dataannotationhelper)
- Static Methods
- TryValidate(T data, out IList`1 validationResults, bool validateAllProperties = True)
Expand Down Expand Up @@ -5270,6 +5273,7 @@
- GetBeautifiedName(this Assembly assembly)
- GetExportedTypeByName(this Assembly assembly, string typeName)
- GetFileVersion(this Assembly assembly)
- GetResourceManagers(this Assembly assembly)
- GetTypesInheritingFromType(this Assembly assembly, Type type)
- IsDebugBuild(this Assembly assembly)
- [FieldInfoExtensions](System.Reflection.md#fieldinfoextensions)
Expand Down
4 changes: 4 additions & 0 deletions docs/CodeDoc/Atc/System.Reflection.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ Extensions for the `System.Reflection.Assembly` class.
>```csharp
>Version GetFileVersion(this Assembly assembly)
>```
#### GetResourceManagers
>```csharp
>ResourceManager[] GetResourceManagers(this Assembly assembly)
>```
#### GetTypesInheritingFromType
>```csharp
>Type[] GetTypesInheritingFromType(this Assembly assembly, Type type)
Expand Down
1 change: 1 addition & 0 deletions src/Atc.XUnit/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
global using System.Text.Json;

global using Atc.CodeDocumentation;
global using Atc.Helpers;
global using Atc.Serialization;
global using Atc.XUnit.Internal;

Expand Down
89 changes: 37 additions & 52 deletions src/Atc.XUnit/Internal/AssemblyLocalizationResourcesHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ public static Dictionary<string, Dictionary<string, List<string>>> CollectMissin
Assembly assembly,
IList<string> cultureNames)
{
var resourceManagers = assembly.GetResourceManagers();

var cultures = CultureInfoHelper.GetCulturesFromNames(cultureNames);

var result = new Dictionary<string, Dictionary<string, List<string>>>(StringComparer.Ordinal);
var resourceManagers = GetResourceManagersFromAssembly(assembly);
foreach (var resourceManager in resourceManagers.OrderBy(x => x.BaseName, StringComparer.Ordinal))
foreach (var resourceManager in resourceManagers)
{
result.Add(
resourceManager.BaseName,
CollectMissingTranslationsForResourceManager(
resourceManager,
cultureNames));
cultures));
}

return result;
Expand All @@ -27,43 +30,24 @@ public static Dictionary<string, Dictionary<string, List<string>>> CollectInvali
IList<string> cultureNames,
IList<string>? allowSuffixTerms = null)
{
var resourceManagers = assembly.GetResourceManagers();

var cultures = CultureInfoHelper.GetCulturesFromNames(cultureNames);

var result = new Dictionary<string, Dictionary<string, List<string>>>(StringComparer.Ordinal);
var resourceManagers = GetResourceManagersFromAssembly(assembly);
foreach (var resourceManager in resourceManagers.OrderBy(x => x.BaseName, StringComparer.Ordinal))
foreach (var resourceManager in resourceManagers)
{
result.Add(
resourceManager.BaseName,
CollectInvalidKeySuffixWithPlaceholdersForResourceManager(
resourceManager,
cultureNames,
cultures,
allowSuffixTerms));
}

return result;
}

private static IEnumerable<ResourceManager> GetResourceManagersFromAssembly(
Assembly assembly)
{
var resourceTypes = assembly.GetTypes();
var resourceManagers = new List<ResourceManager>();

foreach (var type in resourceTypes)
{
var property = type.GetProperty("ResourceManager", BindingFlags.Public | BindingFlags.Static);
if (property is null ||
property.PropertyType != typeof(ResourceManager))
{
continue;
}

var resourceManager = (ResourceManager)property.GetValue(null)!;
resourceManagers.Add(resourceManager);
}

return resourceManagers;
}

private static List<string> GetMissingKeysInDefault(
HashSet<string> allKnownKeys,
List<DictionaryEntry> defaultDictionaryEntries)
Expand Down Expand Up @@ -104,7 +88,7 @@ private static List<string> GetInvalidKeySuffixWithPlaceholdersInDefault(

private static Dictionary<string, List<string>> CollectMissingTranslationsForResourceManagerSet(
ResourceManager resourceManager,
IList<string> cultureNames,
IList<CultureInfo> cultures,
ResourceSet resourceSet)
{
var result = new Dictionary<string, List<string>>(StringComparer.Ordinal);
Expand All @@ -115,28 +99,30 @@ private static Dictionary<string, List<string>> CollectMissingTranslationsForRes
continue;
}

if (entry.Value is not string neutralValue)
foreach (var culture in cultures)
{
continue;
}
var resourceSetForCulture = resourceManager.GetResourceSet(
culture,
createIfNotExists: true,
tryParents: false);

foreach (var cultureName in cultureNames)
{
var cultureInfo = new CultureInfo(cultureName);
var translatedValue = resourceManager.GetString(key, cultureInfo);
if (resourceSetForCulture is null)
{
continue;
}

if (!string.IsNullOrEmpty(translatedValue) &&
translatedValue != neutralValue)
var translatedValue = resourceSetForCulture.GetString(key);
if (translatedValue is not null)
{
continue;
}

if (!result.ContainsKey(cultureName))
if (!result.ContainsKey(culture.Name))
{
result.Add(cultureName, new List<string>());
result.Add(culture.Name, new List<string>());
}

result[cultureName].Add(key);
result[culture.Name].Add(key);
}
}

Expand All @@ -145,7 +131,7 @@ private static Dictionary<string, List<string>> CollectMissingTranslationsForRes

private static Dictionary<string, List<string>> CollectInvalidKeySuffixWithPlaceholdersForResourceManagerSet(
ResourceManager resourceManager,
IList<string> cultureNames,
IList<CultureInfo> cultures,
ResourceSet resourceSet,
IList<string>? allowSuffixTerms = null)
{
Expand All @@ -162,10 +148,9 @@ private static Dictionary<string, List<string>> CollectInvalidKeySuffixWithPlace
continue;
}

foreach (var cultureName in cultureNames)
foreach (var culture in cultures)
{
var cultureInfo = new CultureInfo(cultureName);
var translatedValue = resourceManager.GetString(key, cultureInfo);
var translatedValue = resourceManager.GetString(key, culture);

if (string.IsNullOrEmpty(translatedValue) ||
translatedValue == neutralValue)
Expand All @@ -181,12 +166,12 @@ private static Dictionary<string, List<string>> CollectInvalidKeySuffixWithPlace
continue;
}

if (!result.ContainsKey(cultureName))
if (!result.ContainsKey(culture.Name))
{
result.Add(cultureName, new List<string>());
result.Add(culture.Name, new List<string>());
}

result[cultureName].Add(key);
result[culture.Name].Add(key);
}
}

Expand All @@ -195,7 +180,7 @@ private static Dictionary<string, List<string>> CollectInvalidKeySuffixWithPlace

private static Dictionary<string, List<string>> CollectMissingTranslationsForResourceManager(
ResourceManager resourceManager,
IList<string> cultureNames)
IList<CultureInfo> cultures)
{
var resourceSet = resourceManager.GetResourceSet(
CultureInfo.InvariantCulture,
Expand Down Expand Up @@ -223,7 +208,7 @@ private static Dictionary<string, List<string>> CollectMissingTranslationsForRes

var resultForTranslations = CollectMissingTranslationsForResourceManagerSet(
resourceManager,
cultureNames,
cultures,
resourceSet);

foreach (var item in resultForTranslations)
Expand All @@ -236,7 +221,7 @@ private static Dictionary<string, List<string>> CollectMissingTranslationsForRes

private static Dictionary<string, List<string>> CollectInvalidKeySuffixWithPlaceholdersForResourceManager(
ResourceManager resourceManager,
IList<string> cultureNames,
IList<CultureInfo> cultures,
IList<string>? allowSuffixTerms = null)
{
var resourceSet = resourceManager.GetResourceSet(
Expand Down Expand Up @@ -266,7 +251,7 @@ private static Dictionary<string, List<string>> CollectInvalidKeySuffixWithPlace

var resultForTranslations = CollectInvalidKeySuffixWithPlaceholdersForResourceManagerSet(
resourceManager,
cultureNames,
cultures,
resourceSet,
allowSuffixTerms);

Expand Down
19 changes: 15 additions & 4 deletions src/Atc.XUnit/TestResultHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,22 +189,33 @@ public static void AssertOnTestResultsFromMissingTranslationsAndInvalidKeysSuffi
IDictionary<string, Dictionary<string, List<string>>>? missingTranslations,
IDictionary<string, Dictionary<string, List<string>>>? invalidKeysSuffixWithPlaceholders)
{
var assemblyTotalCount = 0;
if (missingTranslations is not null)
{
assemblyTotalCount += missingTranslations.Sum(item => missingTranslations[item.Key].Values.Sum(x => x.Count));
}

if (invalidKeysSuffixWithPlaceholders is not null)
{
assemblyTotalCount += invalidKeysSuffixWithPlaceholders.Sum(item => invalidKeysSuffixWithPlaceholders[item.Key].Values.Sum(x => x.Count));
}

var testResults = new List<TestResult>
{
new($"Assembly: {assemblyName} (???)"),
new($"Assembly: {assemblyName} ({assemblyTotalCount})"),
};

if (missingTranslations is not null)
{
foreach (var item in missingTranslations)
{
var totalCount = missingTranslations[item.Key].Values.Sum(x => x.Count);
if (totalCount == 0)
var resourceTotalCount = missingTranslations[item.Key].Values.Sum(x => x.Count);
if (resourceTotalCount == 0)
{
continue;
}

testResults.Add(new TestResult(isError: false, 1, $"Resource: {item.Key} ({totalCount})"));
testResults.Add(new TestResult(isError: false, 1, $"Resource: {item.Key} ({resourceTotalCount})"));
foreach (var itemForResource in item.Value)
{
testResults.Add(new TestResult(isError: false, 2, $"Missing translation values for '{itemForResource.Key}' ({itemForResource.Value.Count})"));
Expand Down
30 changes: 30 additions & 0 deletions src/Atc/Extensions/Reflection/AssemblyExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// ReSharper disable once CheckNamespace
// ReSharper disable LoopCanBeConvertedToQuery
namespace System.Reflection;

/// <summary>
Expand Down Expand Up @@ -100,4 +101,33 @@ public static Type[] GetTypesInheritingFromType(this Assembly assembly, Type typ
.Where(x => x.IsInheritedFrom(type))
.ToArray();
}

public static ResourceManager[] GetResourceManagers(
this Assembly assembly)
{
if (assembly is null)
{
throw new ArgumentNullException(nameof(assembly));
}

var resourceTypes = assembly.GetTypes();
var resourceManagers = new List<ResourceManager>();

foreach (var type in resourceTypes)
{
var property = type.GetProperty(nameof(ResourceManager), BindingFlags.Public | BindingFlags.Static);
if (property is null ||
property.PropertyType != typeof(ResourceManager))
{
continue;
}

var resourceManager = (ResourceManager)property.GetValue(null)!;
resourceManagers.Add(resourceManager);
}

return resourceManagers
.OrderBy(x => x.BaseName, StringComparer.Ordinal)
.ToArray();
}
}
31 changes: 31 additions & 0 deletions src/Atc/Helpers/CultureInfoHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace Atc.Helpers;

public static class CultureInfoHelper
{
public static IList<CultureInfo> GetCulturesFromNames(
IEnumerable<string> cultureNames)
{
if (cultureNames is null)
{
throw new ArgumentNullException(nameof(cultureNames));
}

var cultures = new List<CultureInfo>();
foreach (var cultureName in cultureNames)
{
if (cultureName.IsDigitOnly())
{
if (NumberHelper.TryParseToInt(cultureName, out var lcid))
{
cultures.Add(new CultureInfo(lcid));
}
}
else
{
cultures.Add(new CultureInfo(cultureName));
}
}

return cultures;
}
}
Loading
Loading