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

在Blazor应用中为IStringLocalizer动态注入数据库中的翻译资源

在Blazor应用中为IStringLocalizer动态注入数据库中的翻译资源

当然可以实现!我之前也遇到过类似的需求,下面给你分享几种可行的方案,你可以根据自己的场景选最合适的:

方案一:自定义StringLocalizerProvider(整合到Blazor原生本地化系统)

这种方式能把数据库的翻译完全整合到Blazor的IStringLocalizer体系里,和你现有的resx资源无缝配合,用法和原生的localizer完全一致。

步骤1:定义数据库实体

先创建一个存储翻译的实体类,对应数据库表:

public class TranslationEntry
{
    public int Id { get; set; }
    public string CultureCode { get; set; } = string.Empty; // 比如 "en-GB"、"da-DK"
    public string Key { get; set; } = string.Empty; // 翻译键,比如 "fish"
    public string Value { get; set; } = string.Empty; // 对应语言的翻译值
}

步骤2:实现自定义的StringLocalizer和Provider

我们需要包装原生的IStringLocalizer,在获取翻译时优先查数据库,找不到再用resx的资源:

// 自定义Localizer,负责合并数据库和resx的翻译
public class DbStringLocalizer : IStringLocalizer
{
    private readonly IStringLocalizer _defaultLocalizer;
    private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
    private readonly Dictionary<string, Dictionary<string, string>> _cachedTranslations = new();

    public DbStringLocalizer(IStringLocalizer defaultLocalizer, IDbContextFactory<AppDbContext> dbContextFactory)
    {
        _defaultLocalizer = defaultLocalizer;
        _dbContextFactory = dbContextFactory;
        LoadTranslationsFromDb(); // 应用启动时加载一次数据库翻译
    }

    // 从数据库加载翻译并缓存
    private void LoadTranslationsFromDb()
    {
        using var context = _dbContextFactory.CreateDbContext();
        var translations = context.TranslationEntries.ToList();

        foreach (var entry in translations)
        {
            if (!_cachedTranslations.ContainsKey(entry.CultureCode))
            {
                _cachedTranslations[entry.CultureCode] = new Dictionary<string, string>();
            }
            _cachedTranslations[entry.CultureCode][entry.Key] = entry.Value;
        }
    }

    // 获取单条翻译
    public LocalizedString this[string name]
    {
        get
        {
            var currentCulture = CultureInfo.CurrentUICulture.Name;
            // 先查数据库缓存
            if (_cachedTranslations.TryGetValue(currentCulture, out var cultureTranslations) &&
                cultureTranslations.TryGetValue(name, out var value))
            {
                return new LocalizedString(name, value, resourceNotFound: false);
            }
            // 数据库没找到就用原生resx的翻译
            return _defaultLocalizer[name];
        }
    }

    // 带参数的翻译
    public LocalizedString this[string name, params object[] arguments]
    {
        get
        {
            var localizedString = this[name];
            return !localizedString.ResourceNotFound 
                ? new LocalizedString(name, string.Format(localizedString.Value, arguments), false) 
                : _defaultLocalizer[name, arguments];
        }
    }

    // 获取所有翻译(合并数据库和resx)
    public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
    {
        var defaultStrings = _defaultLocalizer.GetAllStrings(includeParentCultures);
        var currentCulture = CultureInfo.CurrentUICulture.Name;

        if (_cachedTranslations.TryGetValue(currentCulture, out var dbStrings))
        {
            foreach (var (key, value) in dbStrings)
            {
                yield return new LocalizedString(key, value, resourceNotFound: false);
            }
        }

        foreach (var str in defaultStrings)
        {
            yield return str;
        }
    }

    public IStringLocalizer WithCulture(CultureInfo culture)
    {
        return new DbStringLocalizer(_defaultLocalizer.WithCulture(culture), _dbContextFactory);
    }
}

// 自定义Provider,告诉Blazor用我们的DbStringLocalizer
public class DbStringLocalizerProvider : IStringLocalizerProvider
{
    private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
    private readonly IStringLocalizerProvider _defaultProvider;

    public DbStringLocalizerProvider(IDbContextFactory<AppDbContext> dbContextFactory, IStringLocalizerProvider defaultProvider)
    {
        _dbContextFactory = dbContextFactory;
        _defaultProvider = defaultProvider;
    }

    public IStringLocalizer Create(Type resourceSource)
    {
        var defaultLocalizer = _defaultProvider.Create(resourceSource);
        return new DbStringLocalizer(defaultLocalizer, _dbContextFactory);
    }

    public IStringLocalizer Create(string baseName, string location)
    {
        var defaultLocalizer = _defaultProvider.Create(baseName, location);
        return new DbStringLocalizer(defaultLocalizer, _dbContextFactory);
    }
}

步骤3:注册自定义Provider

在Program.cs里替换Blazor默认的本地化Provider:

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

// 替换默认的StringLocalizerProvider为我们自定义的
builder.Services.Replace(ServiceDescriptor.Singleton<IStringLocalizerProvider, DbStringLocalizerProvider>());

方案二:自定义Localizer(简单动态添加资源)

如果只是需要像你提到的那样手动添加资源,或者只需要为特定组件提供动态翻译,可以创建一个自定义的Localizer类,直接注入使用:

public class CustomLocalizer
{
    private readonly IStringLocalizer<SharedResource> _defaultLocalizer;
    private readonly IDbContextFactory<AppDbContext> _dbContextFactory;
    private readonly Dictionary<string, Dictionary<string, string>> _dynamicResources = new();

    public CustomLocalizer(IStringLocalizer<SharedResource> defaultLocalizer, IDbContextFactory<AppDbContext> dbContextFactory)
    {
        _defaultLocalizer = defaultLocalizer;
        _dbContextFactory = dbContextFactory;
        LoadDynamicResourcesFromDb(); // 启动时加载数据库翻译
    }

    private void LoadDynamicResourcesFromDb()
    {
        using var context = _dbContextFactory.CreateDbContext();
        var entries = context.TranslationEntries.ToList();
        foreach (var entry in entries)
        {
            if (!_dynamicResources.ContainsKey(entry.CultureCode))
                _dynamicResources[entry.CultureCode] = new();
            _dynamicResources[entry.CultureCode][entry.Key] = entry.Value;
        }
    }

    // 你想要的手动添加资源的方法
    public void AddResource(string culture, string key, string value)
    {
        if (!_dynamicResources.ContainsKey(culture))
            _dynamicResources[culture] = new();
        _dynamicResources[culture][key] = value;
    }

    // 获取翻译
    public string this[string key]
    {
        get
        {
            var culture = CultureInfo.CurrentUICulture.Name;
            if (_dynamicResources.TryGetValue(culture, out var cultureResources) && cultureResources.TryGetValue(key, out var val))
                return val;
            return _defaultLocalizer[key];
        }
    }
}

然后在Program.cs注册为单例:

builder.Services.AddSingleton<CustomLocalizer>();

之后在Blazor组件里直接注入CustomLocalizer使用即可,比如:

@inject CustomLocalizer Localizer

<p>@Localizer["fish"]</p>

注意事项

  • 缓存策略:因为你说数据库不会频繁更新,所以启动时加载一次缓存就够了。如果需要实时更新,可以添加一个刷新方法,比如定时刷新或者提供一个API触发缓存更新。
  • 键冲突处理:如果数据库和resx有相同的键,上面的方案里数据库的翻译会优先生效,你可以根据需求调整优先级。
  • 文化代码规范:确保数据库里的CultureCode是标准的.NET文化名称(比如"en-GB"、"da-DK"),和Blazor的本地化体系保持一致。

备注:内容来源于stack exchange,提问作者Erik Thysell

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 11:24:32