.NET本地化扩展:资源未找到时即时翻译实现咨询
实现Blazor .NET 6服务器端SQL本地化+即时翻译方案
核心思路是扩展ASP.NET本地化体系中的IStringLocalizer接口,在获取本地化字符串的环节捕获资源未找到的情况,触发即时翻译逻辑。以下是具体实现步骤:
1. 封装翻译API服务
先把Google/Deepl的翻译API封装成独立服务,便于复用和维护:
public interface ITranslationService { // text: 待翻译文本;sourceLang: 源语言代码;targetLang: 目标语言代码 Task<string> TranslateAsync(string text, string sourceLang, string targetLang); } // Google翻译API实现示例 public class GoogleTranslationService : ITranslationService { private readonly HttpClient _httpClient; private readonly string _apiKey = "你的Google API密钥"; public GoogleTranslationService(HttpClient httpClient) => _httpClient = httpClient; public async Task<string> TranslateAsync(string text, string sourceLang, string targetLang) { try { var url = $"https://translation.googleapis.com/language/translate/v2?q={Uri.EscapeDataString(text)}&source={sourceLang}&target={targetLang}&key={_apiKey}"; var response = await _httpClient.GetAsync(url); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync<GoogleTranslationResponse>(); return result?.Data?.Translations?.FirstOrDefault()?.TranslatedText ?? text; } catch (Exception ex) { // 翻译失败时返回原文本,避免影响业务 return text; } } } // 对应Google API的响应模型 public class GoogleTranslationResponse { public TranslationData Data { get; set; } } public class TranslationData { public List<Translation> Translations { get; set; } } public class Translation { public string TranslatedText { get; set; } }
2. 实现带 fallback 翻译的自定义StringLocalizer
继承或包装你现有的SQL本地化IStringLocalizer实现,在获取字符串时检查资源是否存在,不存在则调用翻译API:
public class SqlWithFallbackTranslateLocalizer : IStringLocalizer { private readonly IStringLocalizer _sqlLocalizer; private readonly ITranslationService _translationService; private readonly IMemoryCache _cache; private readonly ILogger<SqlWithFallbackTranslateLocalizer> _logger; public SqlWithFallbackTranslateLocalizer( IStringLocalizer sqlLocalizer, ITranslationService translationService, IMemoryCache cache, ILogger<SqlWithFallbackTranslateLocalizer> logger) { _sqlLocalizer = sqlLocalizer; _translationService = translationService; _cache = cache; _logger = logger; } // 处理无参数的本地化字符串 public LocalizedString this[string name] { get { var localizedString = _sqlLocalizer[name]; if (!localizedString.ResourceNotFound) return localizedString; // 尝试从缓存获取翻译结果 var cacheKey = $"translation_{name}_{CultureInfo.CurrentUICulture.Name}"; if (_cache.TryGetValue(cacheKey, out string cachedText)) return new LocalizedString(name, cachedText, false); // 调用翻译API(注意:Blazor同步上下文需避免死锁,这里用ConfigureAwait(false)) var targetLang = CultureInfo.CurrentUICulture.Name; var translatedText = _translationService.TranslateAsync(name, "zh-CN", targetLang) .ConfigureAwait(false).GetAwaiter().GetResult(); // 缓存翻译结果,有效期1小时 _cache.Set(cacheKey, translatedText, TimeSpan.FromHours(1)); _logger.LogInformation($"自动翻译资源:{name} -> {targetLang}"); // 可选:将翻译结果存入SQL表,避免后续重复调用API // _sqlLocalizer.SaveTranslation(name, targetLang, translatedText); return new LocalizedString(name, translatedText, false); } } // 处理带参数的本地化字符串 public LocalizedString this[string name, params object[] arguments] { get { var localizedString = _sqlLocalizer[name, arguments]; if (!localizedString.ResourceNotFound) return localizedString; var cacheKey = $"translation_{name}_{CultureInfo.CurrentUICulture.Name}"; if (!_cache.TryGetValue(cacheKey, out string cachedText)) { var targetLang = CultureInfo.CurrentUICulture.Name; cachedText = _translationService.TranslateAsync(name, "zh-CN", targetLang) .ConfigureAwait(false).GetAwaiter().GetResult(); _cache.Set(cacheKey, cachedText, TimeSpan.FromHours(1)); } var formattedText = string.Format(cachedText, arguments); return new LocalizedString(name, formattedText, false); } } // 实现接口其他方法,直接委托给原SQL Localizer public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures) => _sqlLocalizer.GetAllStrings(includeParentCultures); public IStringLocalizer WithCulture(CultureInfo culture) { var sqlLocalizerWithCulture = _sqlLocalizer.WithCulture(culture); return new SqlWithFallbackTranslateLocalizer(sqlLocalizerWithCulture, _translationService, _cache, _logger); } }
3. 注册自定义本地化组件
在Program.cs中替换原有的SQL本地化服务,注入自定义Localizer和翻译服务:
var builder = WebApplication.CreateBuilder(args); // 添加本地化基础配置 builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); // 注册翻译服务 builder.Services.AddHttpClient<ITranslationService, GoogleTranslationService>(); // 注册自定义Localizer(假设你原有的SQL Localizer类型是SqlStringLocalizer) builder.Services.AddScoped<IStringLocalizer>(sp => { var sqlLocalizer = sp.GetRequiredService<SqlStringLocalizer>(); var translationService = sp.GetRequiredService<ITranslationService>(); var cache = sp.GetRequiredService<IMemoryCache>(); var logger = sp.GetRequiredService<ILogger<SqlWithFallbackTranslateLocalizer>>(); return new SqlWithFallbackTranslateLocalizer(sqlLocalizer, translationService, cache, logger); }); // 其他服务注册... var app = builder.Build(); // 配置本地化中间件 var supportedCultures = new[] { "zh-CN", "en-US", "ja-JP" }; var localizationOptions = new RequestLocalizationOptions() .SetDefaultCulture(supportedCultures[0]) .AddSupportedCultures(supportedCultures) .AddSupportedUICultures(supportedCultures); app.UseRequestLocalization(localizationOptions); // 其他中间件配置... app.Run();
关键注意事项
- 异步处理优化:由于Blazor服务器端的同步调用限制,翻译API调用使用
ConfigureAwait(false).GetAwaiter().GetResult()避免死锁,同时建议对高频翻译内容添加缓存。 - 成本控制:添加内存缓存或分布式缓存,减少API调用次数;可选将翻译结果持久化到SQL表,后续请求直接从SQL读取。
- 错误容错:翻译API调用失败时返回原文本,避免影响业务流程;添加日志记录翻译失败情况。
内容的提问来源于stack exchange,提问作者Dr. Lee
相关产品推荐
相关产品推荐

