Nancy的本地化没有剃刀视图

目前我在南希使用razor作为我的视图引擎。
我可以在剃刀中访问我的资源文件:

@Text.text.greeting 

但我想切换到不同的视图引擎。
是否有其他可用的视图引擎支持TextResource?
本地化如何在超级简单视图引擎中运行?

或者有没有办法使用模型访问资源?

好问题! 这是我自己需要做的事情。

我设法使用@ Karl-JohanSjögren给你的建议来解决这个问题 – 即我能够创建超级简单视图引擎(SSVE)的扩展。


的背景

SSVE的设计方式使您可以注入额外的“匹配器”,允许您对视图模板进行一些处理,因为它们是为请求的输出而呈现的。

您将注意到SSVE中的以下构造函数(截至2014/05/12),允许您传入其他“匹配器”:

  public SuperSimpleViewEngine(IEnumerable matchers) { this.matchers = matchers ?? Enumerable.Empty(); this.processors = new List> { PerformSingleSubstitutions, PerformContextSubstitutions, PerformEachSubstitutions, PerformConditionalSubstitutions, PerformPathSubstitutions, PerformAntiForgeryTokenSubstitutions, this.PerformPartialSubstitutions, this.PerformMasterPageSubstitutions, }; } 

大多数模板替换在SSVE中工作的基本方法是对视图模板进行非常简单的正则表达式匹配。 如果匹配正则表达式,则调用替换方法,在该方法中发生适当的替换。

例如,SSVE附带的默认PerformSingleSubstitutions处理器/匹配器用于执行基本的“@Model”。 换人。 可能发生以下处理器工作流程:

  • 文本“@ Model.Name”在视图模板中匹配。
  • 替换方法是针对Model参数替换而触发的。
  • 针对动态模型进行一些reflection以获取“名称”属性的值。
  • 然后,使用’Name’属性的值替换视图模板中的’@ Model.Name’字符串。

实施

好了,现在我们已经有了基础,这里是你如何创建自己的翻译匹配器。 🙂

首先,您需要创建一个ISuperSimpleViewEngineMatcher的实现。 下面是我为了说明而创建的一个非常基本的例子:

 internal sealed class TranslateTokenViewEngineMatcher : ISuperSimpleViewEngineMatcher { ///  /// Compiled Regex for translation substitutions. ///  private static readonly Regex TranslationSubstitutionsRegEx; static TranslateTokenViewEngineMatcher() { // This regex will match strings like: // @Translate.Hello_World // @Translate.FooBarBaz; TranslationSubstitutionsRegEx = new Regex( @"@Translate\.(?[a-zA-Z0-9-_]+);?", RegexOptions.Compiled); } public string Invoke(string content, dynamic model, IViewEngineHost host) { return TranslationSubstitutionsRegEx.Replace( content, m => { // A match was found! string translationResult; // Get the translation 'key'. var translationKey = m.Groups["TranslationKey"].Value; // Load the appropriate translation. This could farm off to // a ResourceManager for example. The below implementation // obviously isn't very useful and is just illustrative. :) if (translationKey == "Hello_World") { translationResult = "Hello World!"; } else { // We didn't find any translation key matches so we will // use the key itself. translationResult = translationKey; } return translationResult; }); } } 

好的,所以当上面的匹配器针对我们的视图模板运行时,他们将找到以“@Translate”开头的字符串。 “@Translate”之后的文字。 被认为是我们的翻译密钥。 因此,在’@ Translate.Hello_World’的例子中,翻译键将是’Hello_world’。

匹配发生时,将触发replace方法以查找并返回翻译键的相应翻译。 我当前的示例只返回“Hello_World”键的翻译 – 您当然必须填写自己的机制来进行翻译查找,或许可以使用.net的默认资源管理支持?

匹配器不会自动连接到SSVE,您必须使用Nancy支持的IoCfunction来针对我之前强调的构造函数参数注册匹配器。

为此,您需要覆盖Nancy引导程序中的ConfigureApplicationContainer方法,并添加类似于下面的注册:

 public class MyNancyBootstrapper : DefaultNancyBootstrapper { protected override void ConfigureApplicationContainer(TinyIoCContainer container) { base.ConfigureApplicationContainer(container); // Register the custom/additional processors/matchers for our view // rendering within the SSVE container .Register>( (c, p) => { return new List() { // This matcher provides support for @Translate. tokens new TranslateTokenViewEngineMatcher() }; }); } ... 

最后一步是将您的翻译代币实际添加到您的观看中:

    Translator Test   

@Translate.Hello_World;


正如我所说,这是一个非常基本的示例,您可以将其用作创建满足您需求的实现的基础。 例如,您可以扩展正则表达式匹配器,以考虑您要翻译的目标文化,或者只是简单地使用在您的应用程序中注册的当前线程文化。 您可以根据自己的需要灵活地进行操作。 🙂

我现在制作了自己的解决方案,因为我无法使用资源文件。

在我的模型中,我有一个动态Text对象,其资源使用正确的语言。
(语言取决于当前用户并且是int)

 public dynamic Text { get; private set; } 

一开始我按语言构建静态字典。

 private static Dictionary messages = null; 

我创建了一个ResourceDictionary来填充动态对象:

 public class ResourceDictionary : DynamicObject { private Dictionary dictionary; public ResourceDictionary() { dictionary = new Dictionary(); } public void Add(string key, string value) { dictionary.Add(key, value); } public override bool TryGetMember(GetMemberBinder binder, out object result) { string data; if (!dictionary.TryGetValue(binder.Name, out data)) { throw new KeyNotFoundException("Key not found!"); } result = (string)data; return true; } public override bool TrySetMember(SetMemberBinder binder, object value) { if (dictionary.ContainsKey(binder.Name)) { dictionary[binder.Name] = (string)value; } else { dictionary.Add(binder.Name, (string)value); } return true; } }