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

前言

选项模式 Options 是Dotnet非常重要的一个基础概念,在应用开发过程中很多Service都关联着其 Options。

我们有个AI Agent使用 Options 来配置AI的一部分功能,原需求是只需要支持英文语言,现需求改为要支持其它共6种语言。我决定开发一个类库,使 Options 完整地得到多语言支持。

设计思路

1 具体语言的Configuration

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

2 简化的 json 配置文件

未简化前的 AIOptions/zh-CN.json

{ 	"OptionsLocalization": { 		"AIOptions": { 			"zh-CN": { 				"ModelId": "gemini2.5",                 "Prompt": "你好世界" 			} 		} 	} } 

期待简化后的 AIOptions/zh-CN.json

{	  	"ModelId": "gemini2.5",     "Prompt": "你好世界" } 

3 语言区域别名化的 Options

// 选项绑定到别名化的配置 // 如果是默认语言区域,则注册成别名为Options.DefaultName services.Configure<AIOptions>("zh-CN", configuration.GetSection("OptionsLocalization:AIOptions:zh-CN")); 
// 使用别名获取选项 IOptionsMonitor<AIOptions>().Get("zh-CN"); 

4 支持父语言区域回退

假设注册"en"默认语言和zh语言

services.Configure<AIOptions>("zh", zhSection); services.Configure<AIOptions>(Options.DefaultName, enSection); 

现在前端的语言区域为"zh-CN",IOptionsMonitor<AIOptions>().Get("zh-CN")会存在以下问题:

  1. zh-CN不存在,要回退到zh-Hans
  2. zh-Hans不存在,要回退到zh
  3. zh下的ModelId没有配置项,要回退使用默认的en下的ModelId

我们需要实现自定义的IOptionsFactory<TOptions>,把指定的语言区域别名的AIOptions构建正确。

sealed class CultureOptionsFactory<TOptions> : IOptionsFactory<TOptions>      where TOptions : class, new() {     private readonly IConfigureOptions<TOptions>[] _setups;     private readonly IPostConfigureOptions<TOptions>[] _postConfigures;     private readonly IValidateOptions<TOptions>[] _validations;      public CultureOptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures, IEnumerable<IValidateOptions<TOptions>> validations)     {         _setups = setups as IConfigureOptions<TOptions>[] ?? setups.ToArray();         _postConfigures = postConfigures as IPostConfigureOptions<TOptions>[] ?? postConfigures.ToArray();         _validations = validations as IValidateOptions<TOptions>[] ?? validations.ToArray();     }       public TOptions Create(string name)     {         var defaultOptions = this.CreateOptions(Options.DefaultName, default);         if (string.IsNullOrEmpty(name))         {             return defaultOptions;         }          var culture = CultureInfo.GetCultureInfo(name);         var cultureStack = new Stack<CultureInfo>();          cultureStack.Push(culture);         while (culture.Parent.Name.AsSpan().Length > 0)         {             culture = culture.Parent;             cultureStack.Push(culture);         }          var options = defaultOptions;         while (cultureStack.TryPop(out var next))         {             options = this.CreateOptions(next.Name, options);         }          return options;     }       private TOptions CreateOptions(string name, TOptions? options)     {         if (options == null)         {             options = new TOptions();         }          foreach (var setup in _setups)         {             if (setup is IConfigureNamedOptions<TOptions> namedSetup)             {                 namedSetup.Configure(name, options);             }             else if (name == Options.DefaultName)             {                 setup.Configure(options);             }         }          foreach (var post in _postConfigures)         {             post.PostConfigure(name, options);         }          if (_validations != null)         {             var failures = new List<string>();             foreach (var validate in _validations)             {                 var result = validate.Validate(name, options);                 if (result != null && result.Failed)                 {                     failures.AddRange(result.Failures);                 }             }             if (failures.Count > 0)             {                 throw new OptionsValidationException(name, typeof(TOptions), failures);             }         }          return options;     } } 

工程实现

                            -----------------                             |               |  JsonConfigurationSource -> | Configuration |  -> IOptionsLocalizer<TOptions>                             |               |                             ----------------- 

项目地址:
OptionsLocalization

发表评论

评论已关闭。

相关文章