【问题标题】:How to localize Neutronium-based UI using .RESX files?如何使用 .RESX 文件本地化基于 Neutronium 的 UI?
【发布时间】:2017-10-30 07:33:25
【问题描述】:

我将 Neutronium 库用于基于 HTML 的 C# UI。

我希望能够方便地使用来自 .RESX 资源的字符串,而不是将每个字符串都包装在 ViewModel 的资源访问属性中。

如何更轻松地实现这一目标?

【问题讨论】:

    标签: c# wpf neutronium


    【解决方案1】:

    刚刚发布的 Neutronium v​​.1.0.0 还有另一种可能。 使用 vue-i18n 的解决方案具有基础设施和 T4 模板,可将 .resx 文件转换为 JSON 文件,然后由 webpack 以 javascript 导入。

    配置(install.js 文件):

    import VueI18n from 'vue-i18n'
    import messages from './Dictionary'
    
    function install(vue) {
        //Call vue use here if needed
        vue.use(VueI18n);
    }
    
    function vueInstanceOption() {
        const i18n = new VueI18n({
            locale: 'fr-FR', // set locale
            messages, // set locale messages
        });
    
        //Return vue global option here, such as vue-router, vue-i18n, mix-ins, .... 
        return {i18n}
    }
    
    export {
        install,
        vueInstanceOption
    }
    

    t4 模板:

    <#@ template debug="false" hostspecific="true" language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Globalization" #>
    <#@ import namespace="System.Resources" #>
    <#@ import namespace="System.Reflection" #>
    <#@ import namespace="System.Collections" #>
    <#@ import namespace="System.IO" #>
    <#@ import namespace="System.Collections.Generic" #>
    <#@ output extension=".json" #>
    <# 
        Langages = new [] { "en-US", "fr-FR", "pt-BR" };
        Resources = new []{"Resource"};
    #>
    {
     <# var first = true;
     PushIndent("    ");
     foreach (var langage in Langages) {
        if (!first){
            WriteLine("},");
        }
        first =false;
        WriteLine($@"""{langage}"":{{");
        PushIndent("    ");
        foreach(var resource in Resources){
            WriteLine($@"""{resource}"":{{");
            PushIndent("    ");
            var dic = GetDictionary(resource, langage);
            DisplayDictionary(dic);
            PopIndent();
            WriteLine("}");
        }
        PopIndent();
     } 
     WriteLine("}");
     PopIndent();
     #>
    }
    <#+
    private string[] Langages;
    
    private string[] Resources;
    
    private void DisplayDictionary(IDictionary<string,string> dictionary){
        var first = true;
        foreach(var entry in dictionary){
            if (!first){
                WriteLine(",");
            }
            first = false;
            Write($@"""{entry.Key}"":""{entry.Value}""");
        }
        WriteLine("");
    }
    
    private IDictionary<string,string> GetDictionary(string resourceName, string langage){
        var path = this.Host.ResolveAssemblyReference("$(TargetPath)");
        var asm = Assembly.LoadFrom(path);
        var resourceManager = new ResourceManager("Example.Option.CFx.Vue." + resourceName, asm);
        var rs = resourceManager.GetResourceSet(new CultureInfo(langage), true, true);
        return rs.Cast<DictionaryEntry>().ToDictionary(dicEntry => (string)dicEntry.Key, dicEntry => (string)dicEntry.Value);            
    }
    #>
    

    那么你就可以在所有文件中正常使用本地化了。

    例如:App.vue

    <template>
      <div>
        <h1>{{ $t("Resource.Welcome") }}</h1>
        <h2>{{ $t("Resource.MyFriend") }}</h2>     
      </div>
    </template>
    

    完整代码here

    【讨论】:

      【解决方案2】:

      您可以使用ResourceManager.GetResourceSet 转换字典中的资源,然后在 ViewModel 中使用该字典

      这是与即将发布的 1.0.0 版本兼容的完整解决方案

      视图模型:

      public class MainViewModel : ViewModelBase
      {
         private Dictionary<string, string> _Localization;
         public Dictionary<string, string> Localization
         {
             get { return _Localization; }
             private set { Set(ref _Localization, value, nameof(Localization)); }
         }
      
         private string _Langage;
         public string Langage
         {
              get { return _Langage; }
              set
              {
                  if (Set(ref _Langage, value, nameof(Langage)))
                  {
                       UpdateLangage();
                  }           
              }
         }
      
         private void UpdateLangage()
         {
             var rs = Resource.ResourceManager
                              .GetResourceSet(new CultureInfo(_Langage), true, true);
             Localization = rs.Cast<DictionaryEntry>()
                              .ToDictionary(dicEntry => (string)dicEntry.Key, dicEntry => (string)dicEntry.Value);            
         }
      
         public string[] Langages => new[] {"en-US", "pt-BR", "fr-FR"};
      
         public MainViewModel()
         {
           Langage = "en-US";
         }
      }
      

      Vue 文件:

       <template>
              <div id="app" class="fluid container">
                  <div id="main-menu" class="jumbotron logo">
                      <img src="./assets/logo.png">
                      <p>{{Localization.NeutroniumLocalizationExample}}</p>
                  </div>
      
                  <div class="col-md-2">
                      <span>{{Localization.Language}}</span>
                      <select v-model="Langage">
                          <option v-for="lang in Langages" :value="lang">{{lang}}</option>
                      </select>
                  </div>
      
                  <div class="col-md-10">
                      <h1>{{Localization.Hello}}</h1>
                      <h2>{{Localization.Welcome}}</h2>
                      <h3>{{Localization.MyFriend}}</h3>
                  </div>
              </div>
          </template>
      

      完整解决方案here

      【讨论】:

        【解决方案3】:

        我不熟悉该框架,但一种选择可能是使用动态类型。警告 - 这些都没有经过测试...

        创建一个负责公开字符串资源的“动态”类:

        public class ResourceProvider : DynamicObject
        {
            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                var resourceName = binder.Name;
        
                result = MyResources.ResourceManager.GetString(resourceName);
                // You'll want some error checking here, and return false if the resource does not exist 
                // (or throw an exception, return a default string, etc).
                return true;
            }
        }
        

        MyResources 是 resx 文件的名称。如果资源文件位于单独的项目中,请记住将资源文件的访问修饰符组合框从 Internal 更改为 Public)。

        接下来,在您的 VM(或 VM 基类,如果您在其他视图中需要此功能)中,创建上述类的实例并将其公开为“动态”属性类型,例如

        public dynamic ViewResources { get; private set; }
        
        ...
        
        ViewResources = new ResourceProvider();
        

        在您的 HTML 中,虽然我不知道该框架的确切语法,但您现在应该能够使用以下表达式进行绑定:

        "ViewResources.Hello"
        

        (其中“Hello”是 resx 文件中字符串资源的名称)。

        这也适用于您的 VM C#​​ 代码,即

        var s = ViewResources.Hello;
        

        【讨论】:

        • DynamicObject 的巧妙用法,但 Neutronium 内部似乎不支持对此类对象进行反射。因此,即使使用 GetDynamicMemberNames().github.com/NeutroniumCore/Neutronium/blob/… 预先声明属性,它也不起作用
        • 我目前正在考虑将资源作为 stringKey-stringValue 对的列表传递给 Neutronium(它当然支持这一点),然后使用属性填充 JS 端 ViewModel。如果可行,我将发布我自己的解决方案。
        • Neutronium 当前不适合与动态对象一起使用。也就是说,可以改进它以接受实现 GetMemberNames 的动态对象,而建议的解决方案并非如此。
        • Neutronium 1.0 版将支持动态对象转换,前提是它们实现了 GetMemberNames。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-06
        相关资源
        最近更新 更多