找回密码
 立即注册
首页 业界区 业界 Dotnet选项模式的全球化与本地化

Dotnet选项模式的全球化与本地化

章娅萝 2025-9-28 16:52:45
前言

选项模式 Options 是Dotnet非常重要的一个基础概念,在应用开发过程中很多Service都关联着其 Options。
我们有个AI Agent使用 Options 来配置AI的一部分功能,原需求是只需要支持英文语言,现需求改为要支持其它共6种语言。我决定开发一个类库,使 Options 完整地得到多语言支持。
设计思路

1 具体语言的Configuration

使用OptionsLocalization:{OptionsName}:{culture}做为配置的key前缀,例如AIOptions选项的ModelId属性,在配置里的zh-CN对应的key是OptionsLocalization:AIOptions:zh-CN:ModelId
2 简化的 json 配置文件

未简化前的 AIOptions/zh-CN.json
  1. {
  2.         "OptionsLocalization": {
  3.                 "AIOptions": {
  4.                         "zh-CN": {
  5.                                 "ModelId": "gemini2.5",
  6.                 "Prompt": "你好世界"
  7.                         }
  8.                 }
  9.         }
  10. }
复制代码
期待简化后的 AIOptions/zh-CN.json
  1. {         
  2.         "ModelId": "gemini2.5",
  3.     "Prompt": "你好世界"
  4. }
复制代码
3 语言区域别名化的 Options
  1. // 选项绑定到别名化的配置
  2. // 如果是默认语言区域,则注册成别名为Options.DefaultName
  3. services.Configure("zh-CN", configuration.GetSection("OptionsLocalization:AIOptions:zh-CN"));
复制代码
  1. // 使用别名获取选项
  2. IOptionsMonitor().Get("zh-CN");
复制代码
4 支持父语言区域回退

假设注册"en"默认语言和zh语言
  1. services.Configure("zh", zhSection);
  2. services.Configure(Options.DefaultName, enSection);
复制代码
现在前端的语言区域为"zh-CN",IOptionsMonitor().Get("zh-CN")会存在以下问题:

  • zh-CN不存在,要回退到zh-Hans
  • zh-Hans不存在,要回退到zh
  • zh下的ModelId没有配置项,要回退使用默认的en下的ModelId项
我们需要实现自定义的IOptionsFactory,把指定的语言区域别名的AIOptions构建正确。
  1. sealed class CultureOptionsFactory<TOptions> : IOptionsFactory<TOptions>
  2.      where TOptions : class, new()
  3. {
  4.     private readonly IConfigureOptions<TOptions>[] _setups;
  5.     private readonly IPostConfigureOptions<TOptions>[] _postConfigures;
  6.     private readonly IValidateOptions<TOptions>[] _validations;
  7.     public CultureOptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures, IEnumerable<IValidateOptions<TOptions>> validations)
  8.     {
  9.         _setups = setups as IConfigureOptions<TOptions>[] ?? setups.ToArray();
  10.         _postConfigures = postConfigures as IPostConfigureOptions<TOptions>[] ?? postConfigures.ToArray();
  11.         _validations = validations as IValidateOptions<TOptions>[] ?? validations.ToArray();
  12.     }
  13.     public TOptions Create(string name)
  14.     {
  15.         var defaultOptions = this.CreateOptions(Options.DefaultName, default);
  16.         if (string.IsNullOrEmpty(name))
  17.         {
  18.             return defaultOptions;
  19.         }
  20.         var culture = CultureInfo.GetCultureInfo(name);
  21.         var cultureStack = new Stack<CultureInfo>();
  22.         cultureStack.Push(culture);
  23.         while (culture.Parent.Name.AsSpan().Length > 0)
  24.         {
  25.             culture = culture.Parent;
  26.             cultureStack.Push(culture);
  27.         }
  28.         var options = defaultOptions;
  29.         while (cultureStack.TryPop(out var next))
  30.         {
  31.             options = this.CreateOptions(next.Name, options);
  32.         }
  33.         return options;
  34.     }
  35.     private TOptions CreateOptions(string name, TOptions? options)
  36.     {
  37.         if (options == null)
  38.         {
  39.             options = new TOptions();
  40.         }
  41.         foreach (var setup in _setups)
  42.         {
  43.             if (setup is IConfigureNamedOptions<TOptions> namedSetup)
  44.             {
  45.                 namedSetup.Configure(name, options);
  46.             }
  47.             else if (name == Options.DefaultName)
  48.             {
  49.                 setup.Configure(options);
  50.             }
  51.         }
  52.         foreach (var post in _postConfigures)
  53.         {
  54.             post.PostConfigure(name, options);
  55.         }
  56.         if (_validations != null)
  57.         {
  58.             var failures = new List<string>();
  59.             foreach (var validate in _validations)
  60.             {
  61.                 var result = validate.Validate(name, options);
  62.                 if (result != null && result.Failed)
  63.                 {
  64.                     failures.AddRange(result.Failures);
  65.                 }
  66.             }
  67.             if (failures.Count > 0)
  68.             {
  69.                 throw new OptionsValidationException(name, typeof(TOptions), failures);
  70.             }
  71.         }
  72.         return options;
  73.     }
  74. }
复制代码
工程实现
  1.                             -----------------
  2.                             |               |
  3. JsonConfigurationSource -> | Configuration |  -> IOptionsLocalizer<TOptions>
  4.                             |               |
  5.                             -----------------
复制代码
项目地址:
OptionsLocalization

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册