【发布时间】:2017-10-30 07:33:25
【问题描述】:
我将 Neutronium 库用于基于 HTML 的 C# UI。
我希望能够方便地使用来自 .RESX 资源的字符串,而不是将每个字符串都包装在 ViewModel 的资源访问属性中。
如何更轻松地实现这一目标?
【问题讨论】:
标签: c# wpf neutronium
我将 Neutronium 库用于基于 HTML 的 C# UI。
我希望能够方便地使用来自 .RESX 资源的字符串,而不是将每个字符串都包装在 ViewModel 的资源访问属性中。
如何更轻松地实现这一目标?
【问题讨论】:
标签: c# wpf neutronium
刚刚发布的 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
【讨论】:
您可以使用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
【讨论】:
我不熟悉该框架,但一种选择可能是使用动态类型。警告 - 这些都没有经过测试...
创建一个负责公开字符串资源的“动态”类:
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/… 预先声明属性,它也不起作用