looking for cleaner code when embedding razor call

2019-07-24 17:38发布

问题:

I used to have something like this:

We suggest you read our @Html.ActionLink("help page", "Help", "Home") before
proceeding.

nice and clean. then I decided we needed to internationalise the app. I couldn't figure out a better way to deal with the above than to store the following string in the resource file:

We suggest you read our [HelpPage] before proceeding.

and then on the view I have to do:

@MvcHtmlString.Create(this.Resource("Help").ToString()
   .Replace("[HelpPage]",
       @Html.ActionLink("help page", "Help", "Home").ToString()
   )
)

What other strategies can you use to internationalize using Razor?

this.Resource() is a page extension that calls .GetLocalResourceObject() and returns an MvcHtmlString

回答1:

You should create a separate code-behind method that replaces any [placeholder]s with actual links, then call that helper in Razor views.

This will give you a single place to change the code that fills in the links.



回答2:

I was having the same problem. Instead of using placeholders, I use the same formatting in my resource strings as if I were using String.Format(). An example of using this; my resource strings

LogOnText1 | Please enter your user name and password. {0} if you don't have an account.
LogOnText1Register | Register

and my view (razor):

@MvcHtmlString.Create(String.Format(ViewRes.AccountStrings.LogOnText1,
                      Html.ActionLink(ViewRes.AccountStrings.LogOnText1Register, "Register")))

I think it's a bit cleaner



回答3:

so here's what I ended up writing:

public static class PageExtensions
{
    public static MvcHtmlString Resource(
        this WebViewPage page, string key, 
        Dictionary<string, MvcHtmlString> tokenMap
    ) {
        HttpContextBase http = page.ViewContext.HttpContext;
        string text = (string) http.GetLocalResourceObject(page.VirtualPath, key);
        return new TagReplacer(text, tokenMap).ToMvcHtmlString();
    }

where the tag replacements gets done like this:

public class TagReplacer
{
    Dictionary<string, MvcHtmlString> tokenmap;
    public string Value { get; set; } 

    public TagReplacer(string text, Dictionary<string, MvcHtmlString> tokenMap)
    {
        tokenmap = tokenMap;

        Regex re = new Regex(@"\[.*?\]", RegexOptions.IgnoreCase);
        Value = re.Replace(text, new MatchEvaluator(this.Replacer));
    }

    public string Replacer(Match m)
    {
        return tokenmap[m.Value.RemoveSet("[]")].ToString();
    }

    public MvcHtmlString ToMvcHtmlString()
    {
        return MvcHtmlString.Create(Value);
    }
}

so in my code I can now call it like this:

@{
   Dictionary<string, MvcHtmlString> tagmap = new Dictionary<string, MvcHtmlString>() {
                { "HelpPage", Html.ActionLink("help page", "Help", "Home") }
            };
}

and elsewhere:

@this.Resource("Help", tagmap)

any suggestions for improvement most welcome