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")