Skip to content

Commit 3fbce60

Browse files
committedJun 1, 2024··
Add typed localizers
1 parent 180ea48 commit 3fbce60

9 files changed

+157
-65
lines changed
 

‎Directory.Build.props

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
<PropertyGroup>
99
<Version>2.0.0</Version>
10-
<PackageVersion>2.0.0-rc.2</PackageVersion>
1110
</PropertyGroup>
1211

1312
<PropertyGroup>

‎README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ IHost host = Host.CreateDefaultBuilder()
4343
{
4444
services.AddStringLocalizer(b =>
4545
{
46-
b.FromResource(assembly, "Lepo.i18n.Resources.Test", new("pl-PL"));
47-
b.FromResource(assembly, "Lepo.i18n.Resources.Test", new("en-US"));
46+
b.FromResource<Translations>(new("pl-PL"));
47+
b.FromYaml(assembly, "Lepo.i18n.Resources.Translations-en.yaml", new("en-US"));
4848
});
4949
}
5050
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// This Source Code Form is subject to the terms of the MIT License.
2+
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
3+
// Copyright (C) Leszek Pomianowski and Lepo.i18n Contributors.
4+
// All Rights Reserved.
5+
6+
namespace Lepo.i18n.DependencyInjection;
7+
8+
/// <summary>
9+
/// Provides a dependency injection-based implementation of the <see cref="LocalizationBuilder"/> class.
10+
/// </summary>
11+
/// <remarks>
12+
/// This class uses the .NET Core dependency injection framework to register localization services.
13+
/// It allows the registration of resources for specific cultures using the <see cref="FromResource{TResource}"/> method.
14+
/// </remarks>
15+
public class DependencyInjectionLocalizationBuilder(IServiceCollection services)
16+
: LocalizationBuilder
17+
{
18+
/// <inheritdoc />
19+
public override void FromResource<TResource>(CultureInfo culture)
20+
{
21+
base.FromResource<TResource>(culture);
22+
23+
_ = services.AddSingleton<
24+
IStringLocalizer<TResource>,
25+
ProviderBasedStringLocalizer<TResource>
26+
>();
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// This Source Code Form is subject to the terms of the MIT License.
2+
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
3+
// Copyright (C) Leszek Pomianowski and Lepo.i18n Contributors.
4+
// All Rights Reserved.
5+
6+
namespace Lepo.i18n.DependencyInjection;
7+
8+
/// <summary>
9+
/// Provides a provider-based implementation of the <see cref="IStringLocalizer{TResource}"/> interface.
10+
/// </summary>
11+
/// <typeparam name="TResource">The type of the resource used for localization.</typeparam>
12+
/// <remarks>
13+
/// This class uses an <see cref="ILocalizationProvider"/> to retrieve localization sets,
14+
/// and an <see cref="ILocalizationCultureManager"/> to manage the current culture.
15+
/// </remarks>
16+
public class ProviderBasedStringLocalizer<TResource>(
17+
ILocalizationProvider localizations,
18+
ILocalizationCultureManager cultureManager
19+
) : IStringLocalizer<TResource>
20+
{
21+
/// <inheritdoc />
22+
public LocalizedString this[string name] => this[name, []];
23+
24+
/// <inheritdoc />
25+
public LocalizedString this[string name, params object[] arguments] =>
26+
LocalizeString(name, arguments);
27+
28+
/// <inheritdoc />
29+
public IEnumerable<LocalizedString> GetAllStrings(bool _)
30+
{
31+
return localizations
32+
.GetLocalizationSet(
33+
cultureManager.GetCulture(),
34+
typeof(TResource).FullName?.ToLower()
35+
)
36+
?.Strings.Select(x => new LocalizedString(x.Key, x.Value ?? x.Key)) ?? [];
37+
}
38+
39+
/// <summary>
40+
/// Fills placeholders in a string with the provided values.
41+
/// </summary>
42+
/// <param name="name">The string with placeholders.</param>
43+
/// <param name="placeholders">The values to fill the placeholders with.</param>
44+
/// <returns>The string with filled placeholders.</returns>
45+
private LocalizedString LocalizeString(string name, object[] placeholders)
46+
{
47+
return new LocalizedString(
48+
name,
49+
FillPlaceholders(
50+
GetAllStrings(true).FirstOrDefault(x => x.Name == name) ?? name,
51+
placeholders
52+
)
53+
);
54+
}
55+
56+
/// <summary>
57+
/// Fills placeholders in a string with the provided values.
58+
/// </summary>
59+
/// <param name="value">The string with placeholders.</param>
60+
/// <param name="placeholders">The values to fill the placeholders with.</param>
61+
/// <returns>The string with filled placeholders.</returns>
62+
private static string FillPlaceholders(string value, object[] placeholders)
63+
{
64+
for (int i = 0; i < placeholders.Length; i++)
65+
{
66+
value = value.Replace($"{{{i}}}", placeholders[i].ToString());
67+
}
68+
69+
return value;
70+
}
71+
}

‎src/Lepo.i18n.DependencyInjection/ServiceCollectionExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public static IServiceCollection AddStringLocalizer(
2121
Action<LocalizationBuilder> configure
2222
)
2323
{
24-
LocalizationBuilder builder = new();
24+
DependencyInjectionLocalizationBuilder builder = new(services);
2525

2626
configure(builder);
2727

‎src/Lepo.i18n/LocalizationBuilder.cs

+48-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// Copyright (C) Leszek Pomianowski and Lepo.i18n Contributors.
44
// All Rights Reserved.
55

6+
using Lepo.i18n.IO;
7+
68
namespace Lepo.i18n;
79

810
/// <summary>
@@ -18,7 +20,7 @@ public class LocalizationBuilder
1820
/// Builds an <see cref="ILocalizationProvider"/> using the current culture and localizations.
1921
/// </summary>
2022
/// <returns>An <see cref="ILocalizationProvider"/> with the current culture and localizations.</returns>
21-
public ILocalizationProvider Build()
23+
public virtual ILocalizationProvider Build()
2224
{
2325
return new LocalizationProvider(
2426
selectedCulture ?? CultureInfo.CurrentCulture,
@@ -30,7 +32,7 @@ public ILocalizationProvider Build()
3032
/// Sets the culture for the <see cref="LocalizationBuilder"/>.
3133
/// </summary>
3234
/// <param name="culture">The culture to set.</param>
33-
public void SetCulture(CultureInfo culture)
35+
public virtual void SetCulture(CultureInfo culture)
3436
{
3537
selectedCulture = culture;
3638
}
@@ -40,7 +42,7 @@ public void SetCulture(CultureInfo culture)
4042
/// </summary>
4143
/// <param name="localization">The localization set to add.</param>
4244
/// <exception cref="InvalidOperationException">Thrown when a localization set for the same culture already exists in the collection.</exception>
43-
public void AddLocalization(LocalizationSet localization)
45+
public virtual void AddLocalization(LocalizationSet localization)
4446
{
4547
if (
4648
localizations.Any(x =>
@@ -56,4 +58,47 @@ public void AddLocalization(LocalizationSet localization)
5658

5759
_ = localizations.Add(localization);
5860
}
61+
62+
/// <summary>
63+
/// Adds localized strings from a resource in the calling assembly to the <see cref="LocalizationBuilder"/>.
64+
/// </summary>
65+
/// <typeparam name="TResource">The type of the resource.</typeparam>
66+
/// <param name="builder">The <see cref="LocalizationBuilder"/> to add the localized strings to.</param>
67+
/// <param name="culture">The culture for which the localized strings are provided.</param>
68+
/// <returns>The <see cref="LocalizationBuilder"/> with the added localized strings.</returns>
69+
public virtual void FromResource<TResource>(CultureInfo culture)
70+
{
71+
Type resourceType = typeof(TResource);
72+
string? resourceName = resourceType.FullName;
73+
74+
if (resourceName is null)
75+
{
76+
return;
77+
}
78+
79+
FromResource(resourceType.Assembly, resourceName, culture);
80+
}
81+
82+
/// <summary>
83+
/// Adds localized strings from a resource with the specified base name in the specified assembly to the <see cref="LocalizationBuilder"/>.
84+
/// </summary>
85+
/// <param name="builder">The <see cref="LocalizationBuilder"/> to add the localized strings to.</param>
86+
/// <param name="assembly">The assembly that contains the resource.</param>
87+
/// <param name="baseName">The base name of the resource.</param>
88+
/// <param name="culture">The culture for which the localized strings are provided.</param>
89+
/// <returns>The <see cref="LocalizationBuilder"/> with the added localized strings.</returns>
90+
/// <exception cref="LocalizationBuilderException">Thrown when the resource cannot be found.</exception>
91+
public virtual void FromResource(Assembly assembly, string baseName, CultureInfo culture)
92+
{
93+
LocalizationSet? localizationSet = LocalizationSetResourceParser.Parse(
94+
assembly,
95+
baseName,
96+
culture
97+
);
98+
99+
if (localizationSet is not null)
100+
{
101+
AddLocalization(localizationSet);
102+
}
103+
}
59104
}

‎src/Lepo.i18n/LocalizationBuilderExtensions.cs

+3-54
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
// Copyright (C) Leszek Pomianowski and Lepo.i18n Contributors.
44
// All Rights Reserved.
55

6-
using Lepo.i18n.IO;
7-
86
namespace Lepo.i18n;
97

108
/// <summary>
@@ -80,75 +78,26 @@ public static LocalizationBuilder FromResource<TResource>(
8078
string culture
8179
)
8280
{
83-
return builder.FromResource<TResource>(new CultureInfo(culture));
84-
}
85-
86-
/// <summary>
87-
/// Adds localized strings from a resource in the calling assembly to the <see cref="LocalizationBuilder"/>.
88-
/// </summary>
89-
/// <typeparam name="TResource">The type of the resource.</typeparam>
90-
/// <param name="builder">The <see cref="LocalizationBuilder"/> to add the localized strings to.</param>
91-
/// <param name="culture">The culture for which the localized strings are provided.</param>
92-
/// <returns>The <see cref="LocalizationBuilder"/> with the added localized strings.</returns>
93-
public static LocalizationBuilder FromResource<TResource>(
94-
this LocalizationBuilder builder,
95-
CultureInfo culture
96-
)
97-
{
98-
Type resourceType = typeof(TResource);
99-
string? resourceName = resourceType.FullName;
100-
101-
if (resourceName is null)
102-
{
103-
return builder;
104-
}
105-
106-
return builder.FromResource(resourceType.Assembly, resourceName, culture);
107-
}
81+
builder.FromResource<TResource>(new CultureInfo(culture));
10882

109-
/// <summary>
110-
/// Adds localized strings from a resource with the specified base name in the specified assembly to the <see cref="LocalizationBuilder"/>.
111-
/// </summary>
112-
/// <param name="builder">The <see cref="LocalizationBuilder"/> to add the localized strings to.</param>
113-
/// <param name="baseName">The base name of the resource.</param>
114-
/// <param name="culture">The culture for which the localized strings are provided.</param>
115-
/// <returns>The <see cref="LocalizationBuilder"/> with the added localized strings.</returns>
116-
/// <exception cref="LocalizationBuilderException">Thrown when the resource cannot be found.</exception>
117-
public static LocalizationBuilder FromResource(
118-
this LocalizationBuilder builder,
119-
string baseName,
120-
CultureInfo culture
121-
)
122-
{
123-
return builder.FromResource(Assembly.GetCallingAssembly(), baseName, culture);
83+
return builder;
12484
}
12585

12686
/// <summary>
12787
/// Adds localized strings from a resource with the specified base name in the specified assembly to the <see cref="LocalizationBuilder"/>.
12888
/// </summary>
12989
/// <param name="builder">The <see cref="LocalizationBuilder"/> to add the localized strings to.</param>
130-
/// <param name="assembly">The assembly that contains the resource.</param>
13190
/// <param name="baseName">The base name of the resource.</param>
13291
/// <param name="culture">The culture for which the localized strings are provided.</param>
13392
/// <returns>The <see cref="LocalizationBuilder"/> with the added localized strings.</returns>
13493
/// <exception cref="LocalizationBuilderException">Thrown when the resource cannot be found.</exception>
13594
public static LocalizationBuilder FromResource(
13695
this LocalizationBuilder builder,
137-
Assembly assembly,
13896
string baseName,
13997
CultureInfo culture
14098
)
14199
{
142-
LocalizationSet? localizationSet = LocalizationSetResourceParser.Parse(
143-
assembly,
144-
baseName,
145-
culture
146-
);
147-
148-
if (localizationSet is not null)
149-
{
150-
builder.AddLocalization(localizationSet);
151-
}
100+
builder.FromResource(Assembly.GetCallingAssembly(), baseName, culture);
152101

153102
return builder;
154103
}

‎src/Lepo.i18n/Translator.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public static bool LoadLanguages(
138138
}
139139
else
140140
{
141-
_ = builder.FromResource(
141+
builder.FromResource(
142142
applicationAssembly,
143143
languageResource.Value,
144144
new CultureInfo(languageResource.Key.Replace("_", "-"))

‎tests/Lepo.i18n.UnitTests/LocalizationBuilderTests.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public void FromResource_ShouldThrowException_WhenResourceSetIsMissing()
1414
{
1515
LocalizationBuilder builder = new();
1616

17-
Func<LocalizationBuilder> action = () =>
17+
Action action = () =>
1818
builder.FromResource(
1919
Assembly.GetExecutingAssembly(),
2020
"Lepo.i18n.UnitTests.Resources.Invalid",
@@ -29,7 +29,7 @@ public void FromResource_ShouldLoadResourceFromNamespace()
2929
{
3030
LocalizationBuilder builder = new();
3131

32-
_ = builder.FromResource<Test>(new CultureInfo("en-US"));
32+
builder.FromResource<Test>(new CultureInfo("en-US"));
3333

3434
ILocalizationProvider provider = builder.Build();
3535

@@ -44,7 +44,7 @@ public void FromResource_ShouldLoadResource()
4444
{
4545
LocalizationBuilder builder = new();
4646

47-
_ = builder.FromResource(
47+
builder.FromResource(
4848
Assembly.GetExecutingAssembly(),
4949
"Lepo.i18n.UnitTests.Resources.Test",
5050
new CultureInfo("en-US")

0 commit comments

Comments
 (0)
Please sign in to comment.