Using a Wrapper Class for Shared Resources in ASP.NET 8 Localization

Contents

  1. Overview of the Alternative Approach
  2. Setting Up Shared Resources
  3. Creating Wrapper Helper Classes
  4. Configuring Localization Services
  5. Selecting Culture with Cookies
  6. Using Localization in Controllers
  7. Using Localization in Views
  8. Execution Results

1. Overview of the Alternative Approach

This article demonstrates a variant of the solution presented in the previous part for handling multilingual applications with a single .resx file in ASP.NET 8. The key difference is the introduction of wrapper classes that encapsulate the IStringLocalizer and IHtmlLocalizer objects, providing an abstraction layer. While the previous article directly injected IStringLocalizer<SharedResource>, here we create custom interfaces and implementations to acheive the same goal. This pattern is widely referenced in community resources ([7], [8], [9]) and offers flexibility in how localizers are consumed.

2. Setting Up Shared Resoucres

By default, ASP.NET Core expects separate .resx files per controller or view. The shared resource approach consolidates all localized strings into a single file. A marker class SharedResource is used to group these resources.

Marker Class

// SharedResource.cs
namespace SharedResources02
{
    public class SharedResource
    {
    }
}

This class requires no members; it exists solely for type identification. The namespace should match the root application namespace (typically the assembly name). Placement in any folder is acceptable, provided the namespace is consistent.

3. Creating Wrapper Helper Classes

To abstract the localizer creation, we define interfaces and implementations for both IStringLocalizer and IHtmlLocalizer wrappers.

Interface and Implementation for String Localizer

// ISharedStringLocalizer.cs
using Microsoft.Extensions.Localization;

namespace SharedResources02
{
    public interface ISharedStringLocalizer
    {
        LocalizedString this[string key] { get; }
        LocalizedString GetLocalizedString(string key);
    }
}

// SharedStringLocalizer.cs
using System.Reflection;
using Microsoft.Extensions.Localization;

namespace SharedResources02
{
    public class SharedStringLocalizer : ISharedStringLocalizer
    {
        private readonly IStringLocalizer _localizer;

        public SharedStringLocalizer(IStringLocalizerFactory factory)
        {
            var type = typeof(SharedResource);
            var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName ?? string.Empty);
            _localizer = factory.Create("SharedResource", assemblyName.Name ?? string.Empty);
        }

        public LocalizedString this[string key] => _localizer[key];

        public LocalizedString GetLocalizedString(string key)
        {
            return _localizer[key];
        }
    }
}

Interface and Implementation for HTML Localizer

// ISharedHtmlLocalizer.cs
using Microsoft.AspNetCore.Mvc.Localization;

namespace SharedResources02
{
    public interface ISharedHtmlLocalizer
    {
        LocalizedHtmlString this[string key] { get; }
        LocalizedHtmlString GetLocalizedString(string key);
    }
}

// SharedHtmlLocalizer.cs
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Localization;

namespace SharedResources02
{
    public class SharedHtmlLocalizer : ISharedHtmlLocalizer
    {
        private readonly IHtmlLocalizer _localizer;

        public SharedHtmlLocalizer(IHtmlLocalizerFactory factory)
        {
            var type = typeof(SharedResource);
            var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName ?? string.Empty);
            _localizer = factory.Create("SharedResource", assemblyName.Name ?? string.Empty);
        }

        public LocalizedHtmlString this[string key] => _localizer[key];

        public LocalizedHtmlString GetLocalizedString(string key)
        {
            return _localizer[key];
        }
    }
}

These wrappers delegate calls to the underlying IStringLocalizer or IHtmlLocalizer instances created by the factory, ensuring all translations come from the shared resource file.

4. Configuring Localization Services

In Program.cs, configure localization services and middleware, and register the wrapper classes as singletons.

// Program.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using Microsoft.AspNetCore.Localization;

var builder = WebApplication.CreateBuilder(args);

// Localization services
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
builder.Services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    var supportedCultures = new[] { "en", "fr", "de", "it" };
    options.SetDefaultCulture(supportedCultures[0])
        .AddSupportedCultures(supportedCultures)
        .AddSupportedUICultures(supportedCultures);
});

// Register wrapper services
builder.Services.AddSingleton<ISharedStringLocalizer, SharedStringLocalizer>();
builder.Services.AddSingleton<ISharedHtmlLocalizer, SharedHtmlLocalizer>();

builder.Services.AddControllersWithViews();

var app = builder.Build();

app.UseRequestLocalization();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=ChangeLanguage}/{id?}");

app.Run();

5. Selecting Culture with Cookies

ASP.NET Core provides several culture providers. This example uses the cookie provider to persist the selected language.

Setting the Culture Cookie

private void SetCultureCookie(HttpContext context, string culture)
{
    if (context == null) throw new ArgumentNullException(nameof(context));
    if (string.IsNullOrEmpty(culture)) throw new ArgumentException("Culture cannot be null or empty");

    context.Response.Cookies.Append(
        CookieRequestCultureProvider.DefaultCookieName,
        CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
        new CookieOptions { Expires = DateTimeOffset.UtcNow.AddMonths(1) }
    );
}

This method can be called from a controller action when the user submits a language selection.

6. Using Localization in Controllers

Inject the wrapper services into the controller constructor and use them to localize strings.

// HomeController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Diagnostics;

namespace SharedResources02.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly ISharedStringLocalizer _stringLocalizer;
        private readonly ISharedHtmlLocalizer _htmlLocalizer;

        public HomeController(
            ILogger<HomeController> logger,
            ISharedStringLocalizer stringLocalizer,
            ISharedHtmlLocalizer htmlLocalizer)
        {
            _logger = logger;
            _stringLocalizer = stringLocalizer;
            _htmlLocalizer = htmlLocalizer;
        }

        public IActionResult LocalizationExample(LocalizationExampleViewModel model)
        {
            model.LocalizedString = _stringLocalizer["Welcome"];
            model.LocalizedHtml = _htmlLocalizer["Welcome"];
            return View(model);
        }

        public IActionResult ChangeLanguage(ChangeLanguageViewModel model)
        {
            if (model.IsSubmit && !string.IsNullOrEmpty(model.SelectedLanguage))
            {
                SetCultureCookie(HttpContext, model.SelectedLanguage);
                return LocalRedirect("/Home/ChangeLanguage");
            }

            PrepareLanguageList(model);
            return View(model);
        }

        private void PrepareLanguageList(ChangeLanguageViewModel model)
        {
            model.Languages = new List<SelectListItem>
            {
                new SelectListItem { Text = "English", Value = "en" },
                new SelectListItem { Text = "German", Value = "de" },
                new SelectListItem { Text = "French", Value = "fr" },
                new SelectListItem { Text = "Italian", Value = "it" }
            };
        }

        private void SetCultureCookie(HttpContext context, string culture)
        {
            context.Response.Cookies.Append(
                CookieRequestCultureProvider.DefaultCookieName,
                CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
                new CookieOptions { Expires = DateTimeOffset.UtcNow.AddMonths(1) }
            );
        }
    }
}

7. Using Localization in Views

Inject the wrapper services directly into the Razor view.

@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Localization
@model LocalizationExampleViewModel
@inject ISharedStringLocalizer StringLocalizer
@inject ISharedHtmlLocalizer HtmlLocalizer

<div style="width:600px">
    <p class="bg-info">
        String from controller: @Model.LocalizedString
    </p>
    <p class="bg-info">
        String from view: @StringLocalizer["Welcome"]
    </p>
    <p class="bg-info">
        HTML from controller: @Model.LocalizedHtml
    </p>
    <p class="bg-info">
        HTML from view: @HtmlLocalizer["Welcome"]
    </p>
</div>

8. Execution Results

When the application runs, the user can select a language from the dropdown. The culture is persisted in a cookie, and all localized strings render according to the selected culture. Debug information in the footer can display the current cookie value for verification.

Complete Code

All code file are provided in the sections above. For a working project, place the .resx files (e.g., SharedResource.en.resx, SharedResource.fr.resx) in the Resources folder.

References

  1. Make ASP.NET Core app content localizable
  2. Localization resources for languages and cultures
  3. Implement a strategy to select language/culture per request
  4. Globalization and localization in ASP.NET Core
  5. Troubleshoot ASP.NET Core localization
  6. ASP.NET Core localization with SharedResource
  7. Adding multiple languages with ASP.NET Core MVC
  8. ASP.NET Core localization: one RESX to rule them all
  9. View localization with single resource file in ASP.NET Core 3.1

Tags: ASP.NET 8 Localization Shared Resources Resx multilingual

Posted on Sat, 05 Sep 2026 16:29:47 +0000 by atticus