【问题标题】:Localization in Nancy without the razor viewengine没有剃刀视图引擎的南希本地化
【发布时间】:2014-03-21 13:42:56
【问题描述】:

目前我在 Nancy 中使用 razor 作为我的视图引擎。
我可以像这样在 razor 中访问我的资源文件:

@Text.text.greeting

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

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

【问题讨论】:

标签: c# razor nancy


【解决方案1】:

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

根据@Karl-Johan Sjögren 向您提出的建议,我设法解决了这个问题 - 即我能够创建超级简单视图引擎 (SSVE) 的扩展。


背景

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

您会注意到 SSVE 中的以下构造函数(截至 2014 年 5 月 12 日),它允许您传入额外的“匹配器”:

    public SuperSimpleViewEngine(IEnumerable<ISuperSimpleViewEngineMatcher> matchers)
    {
        this.matchers = matchers ?? Enumerable.Empty<ISuperSimpleViewEngineMatcher>();

        this.processors = new List<Func<string, object, IViewEngineHost, string>>
        {
            PerformSingleSubstitutions,
            PerformContextSubstitutions,
            PerformEachSubstitutions,
            PerformConditionalSubstitutions,
            PerformPathSubstitutions,
            PerformAntiForgeryTokenSubstitutions,
            this.PerformPartialSubstitutions,
            this.PerformMasterPageSubstitutions,
        };
    }

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

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

  • 文本“@Model.Name”在视图模板中匹配。
  • 为模型参数替换触发替换方法。
  • 为了获取“名称”属性的值,动态模型会发生一些反射。
  • 然后使用“名称”属性的值替换视图模板中的“@Model.Name”字符串。

实施

好的,现在我们已经有了基础,下面是您可以创建自己的翻译匹配器的方法。 :)

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

internal sealed class TranslateTokenViewEngineMatcher :
    ISuperSimpleViewEngineMatcher
{
    /// <summary>
    ///   Compiled Regex for translation substitutions.
    /// </summary>
    private static readonly Regex TranslationSubstitutionsRegEx;

    static TranslateTokenViewEngineMatcher()
    {
        // This regex will match strings like:
        // @Translate.Hello_World
        // @Translate.FooBarBaz;
        TranslationSubstitutionsRegEx =
            new Regex(
                @"@Translate\.(?<TranslationKey>[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 支持的 IoC 功能来针对我之前强调的构造函数参数注册匹配器。

为此,您需要覆盖 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<IEnumerable<ISuperSimpleViewEngineMatcher>>(
                (c, p) =>
                {
                    return new List<ISuperSimpleViewEngineMatcher>()
                    {
                        // This matcher provides support for @Translate. tokens
                        new TranslateTokenViewEngineMatcher()
                    };
                });
    }

    ...

最后一步是将您的翻译标记实际添加到您的视图中:

<!-- index.sshtml -->
<html>
    <head>
        <title>Translator Test</title>
    </head>
    <body>
        <h1>@Translate.Hello_World;<h1>
    </body>
</html>

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

【讨论】:

  • 您设置 TranslationSubstitutionsRegEx 属性的原因是什么?我已经设置了自己的匹配器,并且能够为我的正则表达式创建一个公共构造函数和一个实例属性......事实上你有一个静态构造函数和静态属性让我有点困惑......
  • 只是一个小的优化。不需要存在多个实例。正则表达式的单个静态实例可以在 TranslateTokenViewEngineMatcher 的所有实例之间共享。不能说这是否值得优化。我不知道南希的内幕。
【解决方案2】:

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

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

public dynamic Text { get; private set; }

一开始我为每种语言构建一个静态字典。

private static Dictionary<int, dynamic> messages = null;

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

public class ResourceDictionary : DynamicObject
{
    private Dictionary<string, string> dictionary;

    public ResourceDictionary()
    {
        dictionary = new Dictionary<string, string>();
    }

    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;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-07
    相关资源
    最近更新 更多