面向云技术架构 - 痴者工良

  • 首页
  • 工良写的电子书
    • Istio 入门教程
    • kubernetes 教程
    • 从 C# 入门 Kafka
    • 多线程和异步
    • 动态编程-反射、特性、AOP
    • 表达式树
  • 本站文章导航
  • 隐私政策
愿有人陪你颠沛流离
遇到能让你付出的事物或者人,都是一种运气。
能遇到,就该珍惜。或许你们最终没能在一起,但你会切实地感受到力量。
正因为这样,那段相遇才变得有价值,才没有辜负这世间的每一段相遇。
  1. 首页
  2. .NET
  3. 正文

ABP 自定义实现多语言处理

2023年7月27日 256点热度 1人点赞 0条评论
内容纲要

在 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");
            });
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: abp 处理 实现 自定义 语言
最后更新:2023年7月27日

痴者工良

高级程序员劝退师

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2023 whuanle.cn. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

粤ICP备18051778号

粤公网安备 44030902003257号