You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为ASP.NET Core本地化配置SDK与消费端双资源文件夹?

Dual Localization Resource Paths for SDK + Consuming App

Great question! I’ve tackled exactly this scenario before when building an SDK that needed to support both its own embedded localization resources and custom ones from the consuming application. The default AddLocalization setup only supports a single resource path, so we need to customize the localization infrastructure a bit to make this work. Here's a step-by-step solution:

Core Idea

The default ResourceManagerStringLocalizerFactory only scans one resource path. To support two paths (SDK + app), we’ll create a custom string localizer factory that loads resources from both locations, with the consuming app’s resources taking priority (so apps can override SDK strings if needed).

Step 1: Build a Combined String Localizer

First, create a custom IStringLocalizer implementation that combines two localizers—one for the SDK, one for the app. This lets us check the app’s resources first, then fall back to the SDK’s if the key isn’t found:

public class CombinedStringLocalizer : IStringLocalizer
{
    private readonly IStringLocalizer _appLocalizer;
    private readonly IStringLocalizer _sdkLocalizer;

    public CombinedStringLocalizer(IStringLocalizer appLocalizer, IStringLocalizer sdkLocalizer)
    {
        _appLocalizer = appLocalizer;
        _sdkLocalizer = sdkLocalizer;
    }

    public LocalizedString this[string name]
    {
        get
        {
            var appString = _appLocalizer[name];
            return appString.ResourceNotFound ? _sdkLocalizer[name] : appString;
        }
    }

    public LocalizedString this[string name, params object[] arguments]
    {
        get
        {
            var appString = _appLocalizer[name, arguments];
            return appString.ResourceNotFound ? _sdkLocalizer[name, arguments] : appString;
        }
    }

    public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
    {
        // Merge strings, keeping app strings if there's a duplicate key
        return _appLocalizer.GetAllStrings(includeParentCultures)
            .Concat(_sdkLocalizer.GetAllStrings(includeParentCultures))
            .GroupBy(s => s.Name)
            .Select(g => g.First());
    }

    public IStringLocalizer WithCulture(CultureInfo culture)
    {
        return new CombinedStringLocalizer(
            _appLocalizer.WithCulture(culture), 
            _sdkLocalizer.WithCulture(culture));
    }
}

Step 2: Create a Custom Localizer Factory

Next, we’ll replace the default factory to create our combined localizer. This factory will generate both the SDK and app localizers, then wrap them in our CombinedStringLocalizer:

public class DualResourceLocalizerFactory : ResourceManagerStringLocalizerFactory
{
    private readonly IStringLocalizer _sdkLocalizer;

    public DualResourceLocalizerFactory(
        IOptions<LocalizationOptions> localizationOptions, 
        ILoggerFactory loggerFactory) 
        : base(localizationOptions, loggerFactory)
    {
        // Initialize the SDK's localizer using a core type from your SDK
        // This ensures we load resources embedded in the SDK assembly
        _sdkLocalizer = base.CreateStringLocalizer(typeof(YourSdkCoreClass));
    }

    public override IStringLocalizer CreateStringLocalizer(Type resourceSource)
    {
        // Create the app's localizer using the consumer's resource type
        var appLocalizer = base.CreateStringLocalizer(resourceSource);
        // Return the combined localizer with app priority
        return new CombinedStringLocalizer(appLocalizer, _sdkLocalizer);
    }

    public override IStringLocalizer CreateStringLocalizer(string baseName, string location)
    {
        var appLocalizer = base.CreateStringLocalizer(baseName, location);
        return new CombinedStringLocalizer(appLocalizer, _sdkLocalizer);
    }
}

Step 3: Update BaseStartup to Use the Custom Factory

Modify your BaseStartup to configure localization and replace the default factory with our custom one. This keeps the consumer’s ability to set their own ResourcesPath:

public abstract class BaseStartup
{
    public virtual void ConfigureServices(IServiceCollection services)
    {
        // Set default app resource path, consumers can override this
        services.AddLocalization(options => options.ResourcesPath = "Resources");

        // Replace the default factory with our dual-path factory
        services.AddSingleton<IStringLocalizerFactory, DualResourceLocalizerFactory>();

        // Add other SDK services here...
    }
}

Step 4: Consumer App Setup

Consuming apps just need to inherit BaseStartup—they can optionally override the ResourcesPath if they use a different directory for their resources:

public class Startup : BaseStartup
{
    public override void ConfigureServices(IServiceCollection services)
    {
        base.ConfigureServices(services);

        // Optional: Override the app's resource path
        services.Configure<LocalizationOptions>(options => 
        {
            options.ResourcesPath = "AppLocalizationResources";
        });

        // Add app-specific services here...
    }
}

Key Notes

  • Resource Priority: App resources always take precedence—if an app defines a string with the same key as the SDK, the app’s version will be used.
  • SDK Resource Setup: Make sure your SDK’s resource files are marked as Embedded Resource in their project properties, so they’re included in the SDK assembly.
  • Compatibility: This works seamlessly with the standard IStringLocalizer injection—consumers don’t need to change how they use localization in their code.

内容的提问来源于stack exchange,提问作者eestein

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:19:52