From 3fbce60b8a9d0d50232a56b32f040064e81ea1bb Mon Sep 17 00:00:00 2001
From: pomianowski <13592821+pomianowski@users.noreply.github.com>
Date: Sat, 1 Jun 2024 22:59:15 +0200
Subject: [PATCH] Add typed localizers
---
Directory.Build.props | 1 -
README.md | 4 +-
.../DependencyInjectionLocalizationBuilder.cs | 28 ++++++++
...ProviderBasedStringLocalizer{TResource}.cs | 71 +++++++++++++++++++
.../ServiceCollectionExtensions.cs | 2 +-
src/Lepo.i18n/LocalizationBuilder.cs | 51 ++++++++++++-
.../LocalizationBuilderExtensions.cs | 57 +--------------
src/Lepo.i18n/Translator.cs | 2 +-
.../LocalizationBuilderTests.cs | 6 +-
9 files changed, 157 insertions(+), 65 deletions(-)
create mode 100644 src/Lepo.i18n.DependencyInjection/DependencyInjectionLocalizationBuilder.cs
create mode 100644 src/Lepo.i18n.DependencyInjection/ProviderBasedStringLocalizer{TResource}.cs
diff --git a/Directory.Build.props b/Directory.Build.props
index 8c64af9..418e167 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -7,7 +7,6 @@
2.0.0
- 2.0.0-rc.2
diff --git a/README.md b/README.md
index 06e18b5..6dcd173 100644
--- a/README.md
+++ b/README.md
@@ -43,8 +43,8 @@ IHost host = Host.CreateDefaultBuilder()
{
services.AddStringLocalizer(b =>
{
- b.FromResource(assembly, "Lepo.i18n.Resources.Test", new("pl-PL"));
- b.FromResource(assembly, "Lepo.i18n.Resources.Test", new("en-US"));
+ b.FromResource(new("pl-PL"));
+ b.FromYaml(assembly, "Lepo.i18n.Resources.Translations-en.yaml", new("en-US"));
});
}
)
diff --git a/src/Lepo.i18n.DependencyInjection/DependencyInjectionLocalizationBuilder.cs b/src/Lepo.i18n.DependencyInjection/DependencyInjectionLocalizationBuilder.cs
new file mode 100644
index 0000000..2277875
--- /dev/null
+++ b/src/Lepo.i18n.DependencyInjection/DependencyInjectionLocalizationBuilder.cs
@@ -0,0 +1,28 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and Lepo.i18n Contributors.
+// All Rights Reserved.
+
+namespace Lepo.i18n.DependencyInjection;
+
+///
+/// Provides a dependency injection-based implementation of the class.
+///
+///
+/// This class uses the .NET Core dependency injection framework to register localization services.
+/// It allows the registration of resources for specific cultures using the method.
+///
+public class DependencyInjectionLocalizationBuilder(IServiceCollection services)
+ : LocalizationBuilder
+{
+ ///
+ public override void FromResource(CultureInfo culture)
+ {
+ base.FromResource(culture);
+
+ _ = services.AddSingleton<
+ IStringLocalizer,
+ ProviderBasedStringLocalizer
+ >();
+ }
+}
diff --git a/src/Lepo.i18n.DependencyInjection/ProviderBasedStringLocalizer{TResource}.cs b/src/Lepo.i18n.DependencyInjection/ProviderBasedStringLocalizer{TResource}.cs
new file mode 100644
index 0000000..0b2b89f
--- /dev/null
+++ b/src/Lepo.i18n.DependencyInjection/ProviderBasedStringLocalizer{TResource}.cs
@@ -0,0 +1,71 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and Lepo.i18n Contributors.
+// All Rights Reserved.
+
+namespace Lepo.i18n.DependencyInjection;
+
+///
+/// Provides a provider-based implementation of the interface.
+///
+/// The type of the resource used for localization.
+///
+/// This class uses an to retrieve localization sets,
+/// and an to manage the current culture.
+///
+public class ProviderBasedStringLocalizer(
+ ILocalizationProvider localizations,
+ ILocalizationCultureManager cultureManager
+) : IStringLocalizer
+{
+ ///
+ public LocalizedString this[string name] => this[name, []];
+
+ ///
+ public LocalizedString this[string name, params object[] arguments] =>
+ LocalizeString(name, arguments);
+
+ ///
+ public IEnumerable GetAllStrings(bool _)
+ {
+ return localizations
+ .GetLocalizationSet(
+ cultureManager.GetCulture(),
+ typeof(TResource).FullName?.ToLower()
+ )
+ ?.Strings.Select(x => new LocalizedString(x.Key, x.Value ?? x.Key)) ?? [];
+ }
+
+ ///
+ /// Fills placeholders in a string with the provided values.
+ ///
+ /// The string with placeholders.
+ /// The values to fill the placeholders with.
+ /// The string with filled placeholders.
+ private LocalizedString LocalizeString(string name, object[] placeholders)
+ {
+ return new LocalizedString(
+ name,
+ FillPlaceholders(
+ GetAllStrings(true).FirstOrDefault(x => x.Name == name) ?? name,
+ placeholders
+ )
+ );
+ }
+
+ ///
+ /// Fills placeholders in a string with the provided values.
+ ///
+ /// The string with placeholders.
+ /// The values to fill the placeholders with.
+ /// The string with filled placeholders.
+ private static string FillPlaceholders(string value, object[] placeholders)
+ {
+ for (int i = 0; i < placeholders.Length; i++)
+ {
+ value = value.Replace($"{{{i}}}", placeholders[i].ToString());
+ }
+
+ return value;
+ }
+}
diff --git a/src/Lepo.i18n.DependencyInjection/ServiceCollectionExtensions.cs b/src/Lepo.i18n.DependencyInjection/ServiceCollectionExtensions.cs
index ade4080..79c48ae 100644
--- a/src/Lepo.i18n.DependencyInjection/ServiceCollectionExtensions.cs
+++ b/src/Lepo.i18n.DependencyInjection/ServiceCollectionExtensions.cs
@@ -21,7 +21,7 @@ public static IServiceCollection AddStringLocalizer(
Action configure
)
{
- LocalizationBuilder builder = new();
+ DependencyInjectionLocalizationBuilder builder = new(services);
configure(builder);
diff --git a/src/Lepo.i18n/LocalizationBuilder.cs b/src/Lepo.i18n/LocalizationBuilder.cs
index b99bbf8..288082e 100644
--- a/src/Lepo.i18n/LocalizationBuilder.cs
+++ b/src/Lepo.i18n/LocalizationBuilder.cs
@@ -3,6 +3,8 @@
// Copyright (C) Leszek Pomianowski and Lepo.i18n Contributors.
// All Rights Reserved.
+using Lepo.i18n.IO;
+
namespace Lepo.i18n;
///
@@ -18,7 +20,7 @@ public class LocalizationBuilder
/// Builds an using the current culture and localizations.
///
/// An with the current culture and localizations.
- public ILocalizationProvider Build()
+ public virtual ILocalizationProvider Build()
{
return new LocalizationProvider(
selectedCulture ?? CultureInfo.CurrentCulture,
@@ -30,7 +32,7 @@ public ILocalizationProvider Build()
/// Sets the culture for the .
///
/// The culture to set.
- public void SetCulture(CultureInfo culture)
+ public virtual void SetCulture(CultureInfo culture)
{
selectedCulture = culture;
}
@@ -40,7 +42,7 @@ public void SetCulture(CultureInfo culture)
///
/// The localization set to add.
/// Thrown when a localization set for the same culture already exists in the collection.
- public void AddLocalization(LocalizationSet localization)
+ public virtual void AddLocalization(LocalizationSet localization)
{
if (
localizations.Any(x =>
@@ -56,4 +58,47 @@ public void AddLocalization(LocalizationSet localization)
_ = localizations.Add(localization);
}
+
+ ///
+ /// Adds localized strings from a resource in the calling assembly to the .
+ ///
+ /// The type of the resource.
+ /// The to add the localized strings to.
+ /// The culture for which the localized strings are provided.
+ /// The with the added localized strings.
+ public virtual void FromResource(CultureInfo culture)
+ {
+ Type resourceType = typeof(TResource);
+ string? resourceName = resourceType.FullName;
+
+ if (resourceName is null)
+ {
+ return;
+ }
+
+ FromResource(resourceType.Assembly, resourceName, culture);
+ }
+
+ ///
+ /// Adds localized strings from a resource with the specified base name in the specified assembly to the .
+ ///
+ /// The to add the localized strings to.
+ /// The assembly that contains the resource.
+ /// The base name of the resource.
+ /// The culture for which the localized strings are provided.
+ /// The with the added localized strings.
+ /// Thrown when the resource cannot be found.
+ public virtual void FromResource(Assembly assembly, string baseName, CultureInfo culture)
+ {
+ LocalizationSet? localizationSet = LocalizationSetResourceParser.Parse(
+ assembly,
+ baseName,
+ culture
+ );
+
+ if (localizationSet is not null)
+ {
+ AddLocalization(localizationSet);
+ }
+ }
}
diff --git a/src/Lepo.i18n/LocalizationBuilderExtensions.cs b/src/Lepo.i18n/LocalizationBuilderExtensions.cs
index fbab5b9..395010c 100644
--- a/src/Lepo.i18n/LocalizationBuilderExtensions.cs
+++ b/src/Lepo.i18n/LocalizationBuilderExtensions.cs
@@ -3,8 +3,6 @@
// Copyright (C) Leszek Pomianowski and Lepo.i18n Contributors.
// All Rights Reserved.
-using Lepo.i18n.IO;
-
namespace Lepo.i18n;
///
@@ -80,75 +78,26 @@ public static LocalizationBuilder FromResource(
string culture
)
{
- return builder.FromResource(new CultureInfo(culture));
- }
-
- ///
- /// Adds localized strings from a resource in the calling assembly to the .
- ///
- /// The type of the resource.
- /// The to add the localized strings to.
- /// The culture for which the localized strings are provided.
- /// The with the added localized strings.
- public static LocalizationBuilder FromResource(
- this LocalizationBuilder builder,
- CultureInfo culture
- )
- {
- Type resourceType = typeof(TResource);
- string? resourceName = resourceType.FullName;
-
- if (resourceName is null)
- {
- return builder;
- }
-
- return builder.FromResource(resourceType.Assembly, resourceName, culture);
- }
+ builder.FromResource(new CultureInfo(culture));
- ///
- /// Adds localized strings from a resource with the specified base name in the specified assembly to the .
- ///
- /// The to add the localized strings to.
- /// The base name of the resource.
- /// The culture for which the localized strings are provided.
- /// The with the added localized strings.
- /// Thrown when the resource cannot be found.
- public static LocalizationBuilder FromResource(
- this LocalizationBuilder builder,
- string baseName,
- CultureInfo culture
- )
- {
- return builder.FromResource(Assembly.GetCallingAssembly(), baseName, culture);
+ return builder;
}
///
/// Adds localized strings from a resource with the specified base name in the specified assembly to the .
///
/// The to add the localized strings to.
- /// The assembly that contains the resource.
/// The base name of the resource.
/// The culture for which the localized strings are provided.
/// The with the added localized strings.
/// Thrown when the resource cannot be found.
public static LocalizationBuilder FromResource(
this LocalizationBuilder builder,
- Assembly assembly,
string baseName,
CultureInfo culture
)
{
- LocalizationSet? localizationSet = LocalizationSetResourceParser.Parse(
- assembly,
- baseName,
- culture
- );
-
- if (localizationSet is not null)
- {
- builder.AddLocalization(localizationSet);
- }
+ builder.FromResource(Assembly.GetCallingAssembly(), baseName, culture);
return builder;
}
diff --git a/src/Lepo.i18n/Translator.cs b/src/Lepo.i18n/Translator.cs
index 05a5718..4f71f2a 100644
--- a/src/Lepo.i18n/Translator.cs
+++ b/src/Lepo.i18n/Translator.cs
@@ -138,7 +138,7 @@ public static bool LoadLanguages(
}
else
{
- _ = builder.FromResource(
+ builder.FromResource(
applicationAssembly,
languageResource.Value,
new CultureInfo(languageResource.Key.Replace("_", "-"))
diff --git a/tests/Lepo.i18n.UnitTests/LocalizationBuilderTests.cs b/tests/Lepo.i18n.UnitTests/LocalizationBuilderTests.cs
index aacec17..ed60b4d 100644
--- a/tests/Lepo.i18n.UnitTests/LocalizationBuilderTests.cs
+++ b/tests/Lepo.i18n.UnitTests/LocalizationBuilderTests.cs
@@ -14,7 +14,7 @@ public void FromResource_ShouldThrowException_WhenResourceSetIsMissing()
{
LocalizationBuilder builder = new();
- Func action = () =>
+ Action action = () =>
builder.FromResource(
Assembly.GetExecutingAssembly(),
"Lepo.i18n.UnitTests.Resources.Invalid",
@@ -29,7 +29,7 @@ public void FromResource_ShouldLoadResourceFromNamespace()
{
LocalizationBuilder builder = new();
- _ = builder.FromResource(new CultureInfo("en-US"));
+ builder.FromResource(new CultureInfo("en-US"));
ILocalizationProvider provider = builder.Build();
@@ -44,7 +44,7 @@ public void FromResource_ShouldLoadResource()
{
LocalizationBuilder builder = new();
- _ = builder.FromResource(
+ builder.FromResource(
Assembly.GetExecutingAssembly(),
"Lepo.i18n.UnitTests.Resources.Test",
new CultureInfo("en-US")