141 lines
5.6 KiB
C#
141 lines
5.6 KiB
C#
using Microsoft.Extensions.Localization;
|
||
using System.Globalization;
|
||
using System.Net.Http;
|
||
using System.Text.Json;
|
||
|
||
namespace Atomx.Admin.Client.Services
|
||
{
|
||
public class JsonStringLocalizer : IStringLocalizer
|
||
{
|
||
private readonly string _resourcesPath;
|
||
private readonly Dictionary<string, Dictionary<string, string>> _resourcesCache = new();
|
||
private readonly object _lock = new();
|
||
private readonly HttpClient? _httpClient;
|
||
|
||
// resourcesPath 应为相对于站点根的“目录”名称,例如 "Localization"
|
||
// 在 Blazor WebAssembly 场景下,会使用注入的 HttpClient 从 wwwroot/Localization/{culture}.json 获取资源
|
||
public JsonStringLocalizer(string resourcesPath, HttpClient? httpClient = null)
|
||
{
|
||
_resourcesPath = (resourcesPath ?? "Localization").Trim('/'); // 规范化
|
||
_httpClient = httpClient;
|
||
}
|
||
|
||
public LocalizedString this[string name]
|
||
{
|
||
get
|
||
{
|
||
var value = GetString(name);
|
||
return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
|
||
}
|
||
}
|
||
|
||
public LocalizedString this[string name, params object[] arguments]
|
||
{
|
||
get
|
||
{
|
||
var format = GetString(name);
|
||
var value = string.Format(format ?? name, arguments);
|
||
return new LocalizedString(name, value, resourceNotFound: format == null);
|
||
}
|
||
}
|
||
|
||
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
|
||
{
|
||
var culture = CultureInfo.CurrentUICulture.Name;
|
||
var resources = LoadResources(culture);
|
||
return resources.Select(r => new LocalizedString(r.Key, r.Value, false));
|
||
}
|
||
|
||
private string? GetString(string name)
|
||
{
|
||
var culture = CultureInfo.CurrentUICulture.Name;
|
||
var resources = LoadResources(culture);
|
||
|
||
// 尝试当前文化
|
||
if (resources.TryGetValue(name, out var value))
|
||
return value;
|
||
|
||
// 如果还找不到,尝试英文作为后备
|
||
if (culture != "en-US")
|
||
{
|
||
var enResources = LoadResources("en-US");
|
||
if (enResources.TryGetValue(name, out var enValue))
|
||
return enValue;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private Dictionary<string, string> LoadResources(string culture)
|
||
{
|
||
lock (_lock)
|
||
{
|
||
if (_resourcesCache.TryGetValue(culture, out var cachedResources))
|
||
return cachedResources;
|
||
|
||
var resources = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||
var fileName = $"{culture}.json";
|
||
|
||
// 在浏览器(WASM)环境下,通过 HttpClient 从静态资源目录获取
|
||
if (OperatingSystem.IsBrowser() && _httpClient != null)
|
||
{
|
||
try
|
||
{
|
||
// 构造相对 URL,例如 "Localization/zh-Hans.json"
|
||
var relativeUrl = $"{_resourcesPath}/{fileName}";
|
||
var json = _httpClient.GetStringAsync(relativeUrl).ConfigureAwait(false).GetAwaiter().GetResult();
|
||
var jsonResources = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
|
||
if (jsonResources != null)
|
||
{
|
||
foreach (var item in jsonResources)
|
||
{
|
||
resources[item.Key] = item.Value;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"Error loading localization file {fileName} via HttpClient: {ex.Message}");
|
||
}
|
||
|
||
_resourcesCache[culture] = resources;
|
||
return resources;
|
||
}
|
||
|
||
// 非浏览器(例如 Server 或在 prerender 阶段)尝试从文件系统读取。
|
||
// 尝试几种可能的路径:基路径为 AppContext.BaseDirectory 或当前工作目录,或直接使用传入的路径。
|
||
try
|
||
{
|
||
var candidates = new[]
|
||
{
|
||
Path.Combine(AppContext.BaseDirectory, _resourcesPath, fileName),
|
||
Path.Combine(Directory.GetCurrentDirectory(), _resourcesPath, fileName),
|
||
Path.Combine(_resourcesPath, fileName),
|
||
};
|
||
|
||
var filePath = candidates.FirstOrDefault(File.Exists);
|
||
|
||
if (!string.IsNullOrEmpty(filePath))
|
||
{
|
||
var json = File.ReadAllText(filePath);
|
||
var jsonResources = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
|
||
if (jsonResources != null)
|
||
{
|
||
foreach (var item in jsonResources)
|
||
{
|
||
resources[item.Key] = item.Value;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"Error loading localization file {fileName} from disk: {ex.Message}");
|
||
}
|
||
|
||
_resourcesCache[culture] = resources;
|
||
return resources;
|
||
}
|
||
}
|
||
}
|
||
} |