add jobs
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
using Microsoft.Extensions.Localization;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Atomx.Admin.Client.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于 ILocalizationProvider 的 IStringLocalizer 实现:
|
||||
/// 使用 JSON 文件中的键值,未找到返回 key 本身。
|
||||
/// 名称改为 JsonStringLocalizer 避免与框架的 StringLocalizer 冲突。
|
||||
/// </summary>
|
||||
public class JsonStringLocalizer<T> : IStringLocalizer<T>
|
||||
{
|
||||
private readonly ILocalizationProvider _provider;
|
||||
|
||||
public JsonStringLocalizer(ILocalizationProvider provider)
|
||||
{
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
public LocalizedString this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = _provider.GetString(name);
|
||||
if (value == null)
|
||||
{
|
||||
// Avoid synchronous blocking during server prerender. Start background load and return key.
|
||||
try
|
||||
{
|
||||
_ = _provider.LoadCultureAsync(_provider.CurrentCulture);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
var result = value ?? name;
|
||||
return new LocalizedString(name, result, resourceNotFound: result == name);
|
||||
}
|
||||
}
|
||||
|
||||
public LocalizedString this[string name, params object[] arguments]
|
||||
{
|
||||
get
|
||||
{
|
||||
var fmt = _provider.GetString(name);
|
||||
if (fmt == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = _provider.LoadCultureAsync(_provider.CurrentCulture);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
var format = fmt ?? name;
|
||||
var value = string.Format(format, arguments);
|
||||
return new LocalizedString(name, value, resourceNotFound: format == name);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
|
||||
{
|
||||
var list = new List<LocalizedString>();
|
||||
var providerType = _provider.GetType();
|
||||
var currentProp = providerType.GetProperty("CurrentCulture");
|
||||
var culture = currentProp?.GetValue(_provider) as string ?? string.Empty;
|
||||
var cacheField = providerType.GetField("_cache", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (!string.IsNullOrEmpty(culture) && cacheField?.GetValue(_provider) is Dictionary<string, Dictionary<string, string>> cache && cache.TryGetValue(culture, out var dict))
|
||||
{
|
||||
foreach (var kv in dict)
|
||||
{
|
||||
list.Add(new LocalizedString(kv.Key, kv.Value, resourceNotFound: false));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public IStringLocalizer WithCulture(CultureInfo culture)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,80 +505,5 @@ namespace Atomx.Admin.Client.Services
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD> ILocalizationProvider <20><> IStringLocalizer ʵ<>֣<EFBFBD>
|
||||
/// ʹ<><CAB9> JSON <20>ļ<EFBFBD><C4BC>еļ<D0B5>ֵ<EFBFBD><D6B5>δ<EFBFBD>ҵ<EFBFBD><D2B5><EFBFBD><EFBFBD><EFBFBD> key <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// <20><><EFBFBD>Ƹ<EFBFBD>Ϊ JsonStringLocalizer <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܵ<EFBFBD> StringLocalizer <20><>ͻ<EFBFBD><CDBB>
|
||||
/// </summary>
|
||||
public class JsonStringLocalizer<T> : IStringLocalizer<T>
|
||||
{
|
||||
private readonly ILocalizationProvider _provider;
|
||||
|
||||
public JsonStringLocalizer(ILocalizationProvider provider)
|
||||
{
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
public LocalizedString this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = _provider.GetString(name);
|
||||
if (value == null)
|
||||
{
|
||||
// Avoid synchronous blocking during server prerender. Start background load and return key.
|
||||
try
|
||||
{
|
||||
_ = _provider.LoadCultureAsync(_provider.CurrentCulture);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
var result = value ?? name;
|
||||
return new LocalizedString(name, result, resourceNotFound: result == name);
|
||||
}
|
||||
}
|
||||
|
||||
public LocalizedString this[string name, params object[] arguments]
|
||||
{
|
||||
get
|
||||
{
|
||||
var fmt = _provider.GetString(name);
|
||||
if (fmt == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = _provider.LoadCultureAsync(_provider.CurrentCulture);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
var format = fmt ?? name;
|
||||
var value = string.Format(format, arguments);
|
||||
return new LocalizedString(name, value, resourceNotFound: format == name);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
|
||||
{
|
||||
var list = new List<LocalizedString>();
|
||||
var providerType = _provider.GetType();
|
||||
var currentProp = providerType.GetProperty("CurrentCulture");
|
||||
var culture = currentProp?.GetValue(_provider) as string ?? string.Empty;
|
||||
var cacheField = providerType.GetField("_cache", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (!string.IsNullOrEmpty(culture) && cacheField?.GetValue(_provider) is Dictionary<string, Dictionary<string, string>> cache && cache.TryGetValue(culture, out var dict))
|
||||
{
|
||||
foreach (var kv in dict)
|
||||
{
|
||||
list.Add(new LocalizedString(kv.Key, kv.Value, resourceNotFound: false));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public IStringLocalizer WithCulture(CultureInfo culture)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Atomx.Admin.Client.Services
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<WasmLocalizationProvider> _logger;
|
||||
private readonly ILocalStorageService _localStorage;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private static readonly ConcurrentDictionary<string, Dictionary<string, string>> _cache = new();
|
||||
private readonly HashSet<string> _loadingCultures = new();
|
||||
private string _currentCulture = "zh-Hans";
|
||||
@@ -21,12 +22,13 @@ namespace Atomx.Admin.Client.Services
|
||||
private const string LocalizationStorageKey = "Localization_{0}";
|
||||
private const string LocalizationVersionKey = "LocalizationVersion_{0}";
|
||||
|
||||
public WasmLocalizationProvider(IJSRuntime jsRuntime, HttpClient httpClient, ILogger<WasmLocalizationProvider> logger, ILocalStorageService localStorage)
|
||||
public WasmLocalizationProvider(IJSRuntime jsRuntime, HttpClient httpClient, ILogger<WasmLocalizationProvider> logger, ILocalStorageService localStorage, ILocalizationService localizationService)
|
||||
{
|
||||
_jsRuntime = jsRuntime;
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
_localStorage = localStorage;
|
||||
_localizationService = localizationService;
|
||||
}
|
||||
|
||||
public string CurrentCulture => _currentCulture;
|
||||
@@ -51,14 +53,43 @@ namespace Atomx.Admin.Client.Services
|
||||
if (_isInitialized) return;
|
||||
|
||||
await LoadCultureAsync(_currentCulture);
|
||||
|
||||
// ensure thread cultures and notify localization service
|
||||
try
|
||||
{
|
||||
var ci = new CultureInfo(_currentCulture);
|
||||
CultureInfo.DefaultThreadCurrentCulture = ci;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = ci;
|
||||
_localizationService.SetLanguage(ci);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "Failed to set culture after initialize: {Culture}", _currentCulture);
|
||||
}
|
||||
|
||||
_isInitialized = true;
|
||||
LanguageChanged?.Invoke(this, _currentCulture);
|
||||
}
|
||||
|
||||
public async Task SetCultureAsync(string cultureShortOrFull)
|
||||
{
|
||||
_currentCulture = MapToFullCulture(cultureShortOrFull);
|
||||
var full = MapToFullCulture(cultureShortOrFull);
|
||||
_currentCulture = full;
|
||||
|
||||
await LoadCultureAsync(_currentCulture);
|
||||
|
||||
try
|
||||
{
|
||||
var ci = new CultureInfo(_currentCulture);
|
||||
CultureInfo.DefaultThreadCurrentCulture = ci;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = ci;
|
||||
_localizationService.SetLanguage(ci);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "Failed to set culture in SetCultureAsync: {Culture}", _currentCulture);
|
||||
}
|
||||
|
||||
LanguageChanged?.Invoke(this, _currentCulture);
|
||||
}
|
||||
|
||||
@@ -101,6 +132,20 @@ namespace Atomx.Admin.Client.Services
|
||||
if (cachedVersion == serverVersion && cachedData != null)
|
||||
{
|
||||
_logger.LogInformation("Localization data for {Culture} is up-to-date.", cultureFull);
|
||||
|
||||
// ensure thread cultures and notify localization service when using cached data
|
||||
try
|
||||
{
|
||||
var ci = new CultureInfo(cultureFull);
|
||||
CultureInfo.DefaultThreadCurrentCulture = ci;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = ci;
|
||||
_localizationService.SetLanguage(ci);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "Failed to set culture after loading from cache: {Culture}", cultureFull);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -115,6 +160,19 @@ namespace Atomx.Admin.Client.Services
|
||||
await _localStorage.SetItemAsync(localDataKey, dict);
|
||||
|
||||
_logger.LogInformation("Loaded localization file for {Culture} from server and updated local storage.", cultureFull);
|
||||
|
||||
// ensure thread cultures and notify localization service after fetching
|
||||
try
|
||||
{
|
||||
var ci = new CultureInfo(cultureFull);
|
||||
CultureInfo.DefaultThreadCurrentCulture = ci;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = ci;
|
||||
_localizationService.SetLanguage(ci);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "Failed to set culture after fetching: {Culture}", cultureFull);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -127,6 +185,9 @@ namespace Atomx.Admin.Client.Services
|
||||
_loadingCultures.Remove(cultureFull);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify listeners that the culture has been loaded
|
||||
LanguageChanged?.Invoke(this, cultureFull);
|
||||
}
|
||||
|
||||
private string MapToFullCulture(string culture)
|
||||
|
||||
Reference in New Issue
Block a user