Use localized Razor pages as e-mail templates
Asked Answered
R

2

7

I am developing a .NET Core project where I send Razor pages as e-mail templates. I followed this tutorial: https://scottsauber.com/2018/07/07/walkthrough-creating-an-html-email-template-with-razor-and-razor-class-libraries-and-rendering-it-from-a-net-standard-class-library/ The only thing I couldn't find was how to send a localized e-mail by passing the needed language as a parameter (example):

public async Task<string> RenderViewToStringAsync<TModel>(string viewName, TModel model, string lang)

I found some stuff online about POs and resx files but everything required a Startup implementation. The razor pages is not the main project, the main project is just an API, while the front-end is handled by another Angular project.

How can I implement localization in a Razor Class Library without a Startup file?

This is the project files

enter image description here

Repertory answered 10/2, 2020 at 21:50 Comment(0)
M
5

I see at least two options for you:

  1. Use different resource files for different cultures.
  2. Use different cshtml file for different cultures.

Optoin 1 - Use different resource files for different cultures

Follow these steps:

  1. In API project, register IStringLocalizerFactory and IStringLocalizer<>:

     services.AddSingleton<IStringLocalizerFactory, ResourceManagerStringLocalizerFactory>();
     services.AddScoped(typeof(IStringLocalizer<>), typeof(StringLocalizer<>));
     services.AddScoped<IRegisterAccountService, RegisterAccountService>();
     services.AddScoped<IRazorViewToStringRenderer, RazorViewToStringRenderer>();
    
  2. Create a Resources.Resx file in Razor View Library and set its custom tool to PublicResXFileCodeGenerator. Then for each language, create the resource file like Resources.fa-IR.Resx and clear the custom tool to not generate code for the language files. Then add resource name and value, for example for fa-IR:

     Name        Value            Comment
     =========================================
     Welcome     خوش آمدید              
    
  3. Inject string localizer to the views that you want:

     @using Microsoft.Extensions.Localization
     @inject IStringLocalizer<RazorHtmlEmails.RazorClassLib.SharedResources> SR
    

In above example, RazorHtmlEmails.RazorClassLib is namespace of the resource.

  1. Use SR["resource key in resource file"] whenever you want to show a string from resource file:

     @SR["Welcome"]
    
  2. Add culture as parameter to RenderViewToStringAsync of IRazorViewToStringRenderer:

     Task<string> RenderViewToStringAsync<TModel>
         (string viewName, TModel model, string culture);
    
  3. Add culture to implementation of RenderViewToStringAsync in RazorViewToStringRenderer:

     public async Task<string> RenderViewToStringAsync<TModel>
         (string viewName, TModel model, string culture)
     {
         Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
         Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture);
     ...
    
  4. Use it:

     string body = await _razorViewToStringRenderer.RenderViewToStringAsync(
         "/Views/Emails/ConfirmAccount/ConfirmAccountEmail.cshtml", 
         confirmAccountModel, "fa-IR");
    

Option 2 - Use different cshtml file for different cultures

If you don't want to use resource files and you want to have different cshtml files for different cultures, just use naming convention. For example create a template.fa-IR.cshtml for Persian language and then when rendering, use that view:

string body = await _razorViewToStringRenderer.RenderViewToStringAsync(
    "/Views/Emails/ConfirmAccount/ConfirmAccountEmail.fa-IR.cshtml", 
    confirmAccountModel);
Max answered 2/3, 2020 at 6:48 Comment(3)
will the css render correctly, also will this work with JavaScript?Dittography
@Dittography It works like any other renderer. No matter how you render (using t4, string format, etc.) if you rendered string work as expected, it will work when you render it using Razor as well. For js, it's not expected to work in email (no matter how you render), as it may introduce some vulnerabilities.Max
Thanks @Reza AghaeiDittography
R
2

As an addition to the accepted answer, this is how you can have the translation effect from the resources in the e-mail subject:

Edit RegisterAccountService class like this:

private readonly IStringLocalizer<RazorHtmlEmails.RazorClassLib.SharedResources> _localizer;

Replace RazorHtmlEmails.RazorClassLib.SharedResources with your namespace, then:

public RegisterAccountService(IRazorViewToStringRenderer razorViewToStringRenderer, IStringLocalizer<RazorHtmlEmails.RazorClassLib.SharedResources> localizer)
    {
        _razorViewToStringRenderer = razorViewToStringRenderer;
        _localizer = localizer;
    }

and apply:

SendEmail(toAddresses, "[email protected]", _localizer["My_Email_Subject_Translation"].Value, body);
Repertory answered 5/7, 2020 at 22:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.