diff --git a/MN.L10n.Benchmark/MN.L10n.Benchmark.csproj b/MN.L10n.Benchmark/MN.L10n.Benchmark.csproj index 471543e..643647c 100644 --- a/MN.L10n.Benchmark/MN.L10n.Benchmark.csproj +++ b/MN.L10n.Benchmark/MN.L10n.Benchmark.csproj @@ -15,4 +15,25 @@ + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + diff --git a/MN.L10n.Benchmark/Program.cs b/MN.L10n.Benchmark/Program.cs index 5b3239b..dfbfee3 100644 --- a/MN.L10n.Benchmark/Program.cs +++ b/MN.L10n.Benchmark/Program.cs @@ -2,6 +2,7 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using MN.L10n; +using MN.L10n.FileProviders; using System.Collections.Concurrent; using System.Reflection; using System.Text; @@ -10,7 +11,7 @@ -BenchmarkRunner.Run(); +BenchmarkRunner.Run(); @@ -19,7 +20,7 @@ public class BenchmarkL10nLanguageProvider : IL10nLanguageProvider { public string GetLanguage() { - return "0"; + return "1"; } } @@ -55,10 +56,10 @@ public class Foo } [MemoryDiagnoser(true)] -[InvocationCount(1_000_000)] -public class SpanTest +[InvocationCount(100_000)] +public class Benchmarks { - [Params("$data$", "Hej $data$ $count$ $many$", "$den här texten inleds med $data$$data2$")] + [Params("$data$", "Hej $data$ $count$ $many$", "$den här texten inleds med $data$$data2$", "Här har vi en längre förklarande text utan dollartecken. Den här skulle t.ex. kunna finnas i ")] public string formatString { get; set; } = ""; public static Foo args = new Foo { data = "Anders" }; @@ -68,9 +69,9 @@ public void GlobalSetup() var dataProvider = new BenchmarkL10nDataProvider(); var stack = new Stack(); - stack.Push("0"); + stack.Push("1"); var items = new Dictionary() { { "___l10nlang", stack } }; - var l10n = L10n.CreateInstance(new BenchmarkL10nLanguageProvider(), dataProvider, () => items); + var l10n = L10n.CreateInstance(new BenchmarkL10nLanguageProvider(), new FileDataProvider(AppDomain.CurrentDomain.BaseDirectory), () => items); dataProvider.SaveL10n(l10n); } @@ -80,6 +81,12 @@ public void GetPhase() L10n._s(formatString, args); } + [Benchmark] + public void GetPhaseWithoutArgs() + { + L10n._s(formatString); + } + [Benchmark] public void FormatNamed() { diff --git a/MN.L10n/L10n.cs b/MN.L10n/L10n.cs index ef585d5..d6c405a 100644 --- a/MN.L10n/L10n.cs +++ b/MN.L10n/L10n.cs @@ -5,6 +5,9 @@ using System.Threading; using System.Threading.Tasks; using MN.L10n.PhraseMetadata; +using System.Reflection; +using System.Text; +using System.Xml; namespace MN.L10n { @@ -140,9 +143,11 @@ internal L10nTranslatedString __getPhrase(string phrase, object args = null) { if (lang.Phrases.TryGetValue(cleanedPhrase, out var phr)) { - if (phr.r.ContainsKey("0")) + string getVal; + + if (phr.r.TryGetValue("0", out getVal)) { - cleanedPhrase = phr.r["0"]; + cleanedPhrase = getVal; } if (isPluralized && lang.AstPluralRule != null) @@ -150,9 +155,9 @@ internal L10nTranslatedString __getPhrase(string phrase, object args = null) // Here there be dragons // Dynamic evaluation to get the phrase to use, based on the pluralization rule specified var phraseIndex = lang.AstPluralRule.Evaluate(GetCount(args)).ToString(); - if (phr.r.ContainsKey(phraseIndex)) + if (phr.r.TryGetValue(phraseIndex, out getVal)) { - cleanedPhrase = phr.r[phraseIndex]; + cleanedPhrase = getVal; } } } @@ -182,41 +187,41 @@ internal L10nTranslatedString __getPhrase(string phrase, object args = null) return FormatNamed(withoutMetadata, args); } + private static ConcurrentDictionary IsPluralizedCache = new ConcurrentDictionary(); + public static bool IsPluralized(object args = null) { if (args == null) return false; - var t = args.GetType(); - foreach (var p in t.GetProperties()) - { - if (p.Name == "__count") return true; - } - return false; + var t = args.GetType(); + return IsPluralizedCache.GetOrAdd(t, t.GetProperty("__count")) is not null; } public static long GetCount(object args = null) { - if (args == null) return 0; - var t = args.GetType(); - foreach (var p in t.GetProperties()) + IsPluralizedCache.TryGetValue(args.GetType(), out var p); + + if(p is not null) { - if (p.Name == "__count") - { - long.TryParse(p.GetValue(args).ToString(), out long __count); - return __count; - } + return Convert.ToInt64(p.GetValue(args)); + } return 0; } + private static ConcurrentDictionary propCache = new ConcurrentDictionary(); + public static L10nTranslatedString FormatNamed(string formatString, object args = null) { if (args == null) return new L10nTranslatedString(formatString); var t = args.GetType(); var tmpVal = formatString; - foreach (var p in t.GetProperties()) + + var props = propCache.GetOrAdd(t, tp => tp.GetProperties()); + + foreach (var p in props) { tmpVal = tmpVal.Replace("$" + p.Name + "$", p.GetValue(args)?.ToString()); } diff --git a/MN.L10n/MN.L10n.csproj b/MN.L10n/MN.L10n.csproj index fcafe24..c50c1b0 100644 --- a/MN.L10n/MN.L10n.csproj +++ b/MN.L10n/MN.L10n.csproj @@ -1,7 +1,7 @@  - net472;netstandard2.0;net8.0;net9.0 + net48;netstandard2.0;net8.0;net9.0 True MultiNet Interactive AB Chris Gårdenberg @@ -29,7 +29,7 @@ Translation package AnyCPU - +