From 1461506a909d455336ee45c7ee996b55d2b8f278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20T=C3=B8gersen=20=28Delegate=29?= Date: Fri, 20 Oct 2023 15:23:24 +0200 Subject: [PATCH] feat: Added support for format templated strings. Uses regex to capture index from pattern: '{index[,alignment][:formatString]}' --- src/Atc.XUnit/GlobalUsings.cs | 1 + .../AssemblyLocalizationResourcesHelper.cs | 22 ++++++++++++------- ...ssemblyLocalizationResourcesHelperTests.cs | 4 ++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Atc.XUnit/GlobalUsings.cs b/src/Atc.XUnit/GlobalUsings.cs index 8d768888..1d0156b3 100644 --- a/src/Atc.XUnit/GlobalUsings.cs +++ b/src/Atc.XUnit/GlobalUsings.cs @@ -8,6 +8,7 @@ global using System.Runtime.InteropServices; global using System.Text; global using System.Text.Json; +global using System.Text.RegularExpressions; global using Atc.CodeDocumentation; global using Atc.Helpers; diff --git a/src/Atc.XUnit/Internal/AssemblyLocalizationResourcesHelper.cs b/src/Atc.XUnit/Internal/AssemblyLocalizationResourcesHelper.cs index 4507713f..3e900069 100644 --- a/src/Atc.XUnit/Internal/AssemblyLocalizationResourcesHelper.cs +++ b/src/Atc.XUnit/Internal/AssemblyLocalizationResourcesHelper.cs @@ -1,9 +1,9 @@ -// ReSharper disable LoopCanBeConvertedToQuery -// ReSharper disable ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator namespace Atc.XUnit.Internal; internal static class AssemblyLocalizationResourcesHelper { + private static readonly Lazy ArgIndexCaptureRegex = new Lazy(() => new Regex(@"\{(\d+)", RegexOptions.Multiline, TimeSpan.FromMilliseconds(10))); + public static Dictionary>> CollectMissingTranslations( Assembly assembly, IList cultureNames) @@ -295,15 +295,21 @@ internal static bool ValidateKeySuffixWithPlaceholders( } var maxIndex = -1; - for (var i = 0; i < value.Length - 2; i++) + var formatArgsIndex = ArgIndexCaptureRegex.Value + .Matches(value) + .Select(x => x.Groups[1].Value) + .ToList(); + + foreach (var indexString in formatArgsIndex) { - if (value[i] != '{' || !char.IsDigit(value[i + 1]) || value[i + 2] != '}') + if (NumberHelper.TryParseToInt(indexString, out var index)) { - continue; + maxIndex = System.Math.Max(maxIndex, index); + } + else + { + return false; } - - var index = int.Parse(value[i + 1].ToString(), GlobalizationConstants.EnglishCultureInfo); - maxIndex = System.Math.Max(maxIndex, index); } if (string.IsNullOrEmpty(suffix)) diff --git a/test/Atc.XUnit.Tests/Internal/AssemblyLocalizationResourcesHelperTests.cs b/test/Atc.XUnit.Tests/Internal/AssemblyLocalizationResourcesHelperTests.cs index 441ddc64..9895c5ab 100644 --- a/test/Atc.XUnit.Tests/Internal/AssemblyLocalizationResourcesHelperTests.cs +++ b/test/Atc.XUnit.Tests/Internal/AssemblyLocalizationResourcesHelperTests.cs @@ -17,6 +17,8 @@ public class AssemblyLocalizationResourcesHelperTests [InlineData(false, "Foo2", "Hello {0} world")] [InlineData(false, "Foo2", "Hello {0} world {0}")] [InlineData(true, "Foo2", "Hello {0} world {1}")] + [InlineData(true, "Foo3", "Hello {0:format} world {1,10:format} {2,10}")] + [InlineData(true, "Foo11", "{0:format}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}")] public void ValidateKeySuffixWithPlaceholdersTest(bool expected, string key, string value) { Assert.Equal(expected, AssemblyLocalizationResourcesHelper.ValidateKeySuffixWithPlaceholders(key, value)); @@ -37,6 +39,8 @@ public void ValidateKeySuffixWithPlaceholdersTest(bool expected, string key, str [InlineData(false, "Foo2", "Hello {0} world", null)] [InlineData(false, "Foo2", "Hello {0} world {0}", null)] [InlineData(true, "Foo2", "Hello {0} world {1}", null)] + [InlineData(true, "Foo3", "Hello {0:format} world {1,10:format} {2,10}", null)] + [InlineData(true, "Foo11", "{0:format}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}", null)] public void ValidateKeySuffixWithPlaceholdersTest_AllowSuffixTerms(bool expected, string key, string value, string? allowSuffixTerm) { List? allowSuffixTerms = null;