内容目录
在 ABP 中,默认只有本地 JSON 语言处理,但是在业务上我们可能有好多定制需求。本文介绍如何自己根据 redis 实现一个多语言处理,通过 redis 取得语言信息。
ABP 官方文档:https://docs.abp.io/en/abp/latest/Localization
ABP 是这样配置多语言的:
services.Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<TestResource>("en") //Define the resource by "en" default culture
.AddVirtualJson("/Localization/Resources/Test") //Add strings from virtual json files
.AddBaseTypes(typeof(AbpValidationResource)); //Inherit from an existing resource
});
在这个基础上,我们来实现直接的多语言处理。
首先要实现 ILocalizationResourceContributor 接口,提供多语言字符串查询。
笔者使用 FreeRedis 实现本地缓存和动态变更缓存。
在 redis 中,我们设置这样的 key 格式:language:{语言名称}
,比如 language:zh-CN
,然后 Key 使用 hash 类型,里面存储字符串关键字和对应语言翻译。
RedisResourceOptions 随便写,比如 Redis key 前缀。
public class RedisLocalizationResource : ILocalizationResourceContributor
{
private readonly RedisResourceOptions _options;
private readonly RedisClient.DatabaseHook _db;
private readonly ConcurrentDictionary<string, string> _languages = new ConcurrentDictionary<string, string>();
private readonly string _keyPrefix;
internal RedisLocalizationResource(RedisClient.DatabaseHook db, RedisResourceOptions options)
{
_options = options;
_keyPrefix = options.KeyPrefix;
_db = db;
}
/// <summary>
/// 属于动态获取语言信息
/// </summary>
public bool IsDynamic => true;
/// <summary>
/// 填充,IsDynamic = false 才会用到
/// </summary>
public void Fill(string cultureName, Dictionary<string, LocalizedString> dictionary)
{
var hash = _db.HGetAll($"{_keyPrefix}:{cultureName}");
foreach (var item in hash)
{
dictionary.Add(item.Key, new LocalizedString(item.Key, item.Value));
}
}
/// <summary>
/// 填充,IsDynamic = false 才会用到
/// </summary>
public async Task FillAsync(string cultureName, Dictionary<string, LocalizedString> dictionary)
{
var hash = await _db.HGetAllAsync($"{_keyPrefix}:{cultureName}");
foreach (var item in hash)
{
dictionary.Add(item.Key, new LocalizedString(item.Key, item.Value));
}
}
/// <summary>
/// 获取本地化字符串
/// </summary>
/// <param name="cultureName">语言名称</param>
/// <param name="name">key</param>
/// <returns></returns>
public LocalizedString GetOrNull(string cultureName, string name)
{
var key = GetLanguageKey(cultureName);
if (key == default) return null!;
var value = _db.HGet(key, name);
if (!string.IsNullOrEmpty(value))
return new LocalizedString(name, value);
return new LocalizedString(name, name);
}
/// <summary>
/// 支持的语言
/// </summary>
/// <returns></returns>
public async Task<IEnumerable<string>> GetSupportedCulturesAsync()
{
await Task.CompletedTask;
var languageKeys = new List<string>();
List<string> languages = new List<string>();
foreach (var keys in _db.Scan(_keyPrefix, 20, null))
{
languageKeys.AddRange(keys);
}
foreach (var key in languageKeys)
{
var language = key.Split(":").LastOrDefault();
if (language == null) continue;
languages.Add(language);
}
return languages;
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void Initialize(LocalizationResourceInitializationContext context)
{
_languages.Clear();
var languageKeys = new List<string>();
foreach (var keys in _db.Scan(_keyPrefix, 20, null))
{
languageKeys.AddRange(keys);
}
foreach (var key in languageKeys)
{
var language = key.Split(":").LastOrDefault();
if (language == null) continue;
_languages.TryAdd(language, key);
}
}
private DateTime _currentTime = DateTime.Now;
private string? GetLanguageKey(string cultureName)
{
var key = $"{_keyPrefix}:{cultureName}";
// 本地没有这个 key
if (!_languages.ContainsKey(cultureName))
{
// 5 s 内只能检测重新检查一次 key
if (DateTime.Now - _currentTime > TimeSpan.FromSeconds(5))
{
_currentTime = DateTime.Now;
var exist = _db.Exists(key);
if (!exist) return default;
_languages.TryAdd(cultureName, key);
}
else return null;
}
return key;
}
}
然后编写扩展,注入这个自定义语言服务。
/// <summary>
/// 动态多语言扩展配置
/// </summary>
public static class LocalizationExtensions
{
public static TLocalizationResource AddRedisResource<TLocalizationResource>(
[NotNull] this TLocalizationResource localizationResource,
int dbIndex = 0,
string keyPrefix = "language")
where TLocalizationResource : LocalizationResourceBase
{
ArgumentNullException.ThrowIfNull(keyPrefix);
keyPrefix = keyPrefix.ToLower();
var db = RedisHelper.Client.GetDatabase(dbIndex);
// 这里使用 FreeRedis 动态更新本地缓存,提供响应速度和性能
db.UseClientSideCaching(new ClientSideCachingOptions
{
// 客户端缓存容量
Capacity = 20,
// 过滤
KeyFilter = key => key.StartsWith(keyPrefix),
// 检查长时间不使用的缓存
CheckExpired = (key, dt) => DateTime.Now.Subtract(dt) > TimeSpan.FromSeconds(5),
});
localizationResource.Contributors.Add(new RedisLocalizationResource(db, new RedisResourceOptions(dbIndex, keyPrefix)));
return localizationResource;
}
}
然后直接使用即可:
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
// 设置默认语言
.Add<TestResource>("en")
// 配置 redis 信息
.AddRedisResource(dbIndex: 0, keyPrefix: "language");
});
文章评论