RazorLight icon indicating copy to clipboard operation
RazorLight copied to clipboard

IViewLocalizer in cshtml template

Open AlexTeixeira opened this issue 6 years ago • 18 comments

Hello people,

I'm trying to create an email using Razorlight. In my template, I wan't to inject IViewLocalizer to have a localized email.

My issue is when I render the template, I have a Null Reference if I use the IViewLocalizer

This is normal or I'm doing something wrong ?

Code

@using RazorLight
@inherits TemplatePage<Gardendynamics.Commons.Picaplant.Models.Output.OrderModel>
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title> @Localizer["OrderTitleEmail"]</title>
    <style type="text/css">
        body {margin: 0; padding: 0; min-width: 100%!important;}
        .content {width: 100%; max-width: 800px;}  
        .header {padding: 40px 30px 20px 30px;}
        .body {padding: 10px 20px;}
        .footer {padding: 10px 0px;}
        .orderitem{
            border-collapse:separate; border-spacing:0 5px;
        }
        .orderitem thead tr th{border-bottom: 1px solid black; border-collapse:separate; border-spacing:5px 5px;}
    </style>
</head>

string result = await engine.CompileRenderAsync("OrderCompleteEmail.cshtml", new { Name = "John Doe" });

AlexTeixeira avatar Mar 17 '18 15:03 AlexTeixeira

Do you add RazorLight to IServicecollection?

toddams avatar Mar 18 '18 12:03 toddams

Hi,

Yes I add it to my service collection. Possibly my implementation is incorrect...

I can't access to the code now, I will provide my code late in the day

AlexTeixeira avatar Mar 21 '18 13:03 AlexTeixeira

Hi,

My Code :

internal class GardendynamicsRazorLightEngine
    {
        private static string templatePath = $@"{Directory.GetCurrentDirectory()}/EmailTemplates"; 
        private static IRazorLightEngine engine;

        public static IRazorLightEngine Engine{
            get{
                if(engine == null){
                    engine = new RazorLightEngineBuilder()
                        .UseFilesystemProject(templatePath)
                        .UseMemoryCachingProvider()
                        .Build();
                }

                return engine;
            }
        }
    }

  services.AddSingleton(f =>
            {
                return GardendynamicsRazorLightEngine.Engine;
            });

AlexTeixeira avatar Mar 22 '18 19:03 AlexTeixeira

Any Idea?

AlexTeixeira avatar May 20 '18 13:05 AlexTeixeira

Please, use this extension method

https://github.com/toddams/RazorLight/blob/master/src/RazorLight/Extensions/ServiceCollectionExtensions.cs#L10

toddams avatar Jun 10 '18 16:06 toddams

@toddams I have the same problem as OP. I have used the extension method above and it doesn't work. Usages of 'Localizer[]' throws null reference exception. Using RazorLight 2.0 beta 1.

            services.AddRazorLight(() => {
                return new RazorLightEngineBuilder()
                      .UseFilesystemProject(AppContext.BaseDirectory)
                      .UseMemoryCachingProvider()
                      .Build();
            });

If I do serviceProvider.GetService<IViewLocalizer>() I see that ViewLocalizer is correctly registered.

olivierr91 avatar Jun 19 '18 18:06 olivierr91

Hi, @toddams

Our company really really needs this fix. We would like to help. Do you have any ideia/advice on how to fix it, so we can help you with the implementation?

Thanks!

MarcoNicolodi avatar Oct 08 '18 17:10 MarcoNicolodi

are there any workarounds for this? I suppose you could just put all the localized strings into the ViewBag? Would be a lot of work to do and eventually undo though...

john-luke-laue avatar Dec 26 '18 19:12 john-luke-laue

Having the same problem when using this functionality via FluentEmail.Razor package, found no workaround yet (apart from moving all translatable strings to the model instance and translating it upfront).

atsvetkov avatar Feb 13 '19 18:02 atsvetkov

@atsvetkov, the following workaround adds support for @inject attributes to FluentEmail.Razor:

            services.AddRazorLight(() => new RazorLightEngineBuilder()
                                         .UseMemoryCachingProvider()
                                         .Build());
    public static class FluentEmailExtensions
    {
        public static FluentEmailServicesBuilder AddInjectedRazorRenderer(
            this FluentEmailServicesBuilder builder)
        {
            builder.Services.TryAddSingleton<ITemplateRenderer, InjectedRazorRenderer>();
            return builder;
        }

        public class InjectedRazorRenderer : ITemplateRenderer
        {
            private readonly IRazorLightEngine _engine;

            public InjectedRazorRenderer(IRazorLightEngine engine)
            {
                _engine = engine;
            }

            public string Parse<T>(string template, T model, bool isHtml = true)
            {
                return ParseAsync(template, model, isHtml).GetAwaiter().GetResult();
            }

            public async Task<string> ParseAsync<T>(string template, T model, bool isHtml = true)
            {
                return await _engine.CompileRenderAsync(RazorRenderer.GetHashString(template), template, model);
            }
        }
    }

dabide avatar Jun 10 '19 09:06 dabide

So is this going to be fixed without a workaround? We are really hoping to have localization working...

matthew798 avatar Jul 23 '19 02:07 matthew798

Hey, can I get a update on this? Is it fixed or do I need a workaround?

florisdipt avatar Apr 15 '20 13:04 florisdipt

I am always willing to accept PRs. It always amazes me when I get workarounds posted and people don't want to help create a PR to fix the issue. The issue is that the Builder pattern doesn't register things using .TryAddSingleton Microsoft DI extension method, which is what the workaround accomplishes.

In any event, this isn't something I need for my applications and the path forward seems pretty straightforward to me.

jzabroski avatar Apr 15 '20 13:04 jzabroski

I am also trying to get localization to work in a generated HTML page.
The above workaround is specific to FluentMail and not to RazorLight.

I would be more than happy to make a PR with a fix/workaround for this issue, but I don't know enough about the workings of Razorlight (yet) to know where to find the root cause.

arvdrpoo avatar Jul 16 '20 07:07 arvdrpoo

@arvdrpoo (1) Does the solution in the FAQ work for you? It disables encoding entirely. As for what RazorLight does, it registers Razor compiler Directives and then adds "callbacks" for things like @inject. There are a few tests around RawString if you search the code base.

jzabroski avatar Jul 16 '20 13:07 jzabroski

I had also started to rework some of the DI registration logic, but the complicated part is how to handle the callback logic.

jzabroski avatar Jul 16 '20 13:07 jzabroski

@jzabroski If you mean this part of the ReadMe/FAQ, it doesn't seem to work. Or do you mean something else?

I'll try to take a look at the callbacks, and see what I can find

arvdrpoo avatar Jul 17 '20 11:07 arvdrpoo

I don't know if this is still an issue. I couldn't get the localizer to inject into the view. I have created a workaround by injecting the localizer into the model and exposing it as a model property for use in the view. Works fine for my purposes.

Minimal implementation here: https://github.com/Ric43/RazorLightPoC

Ric43 avatar Aug 28 '20 13:08 Ric43