OrchardCoreContrib/OrchardCoreContrib.PoExtractor

Wrong msgctxt Value for .razor files

Closed this issue · 7 comments

Steps to Reproduce

Note: Using the latest develop version of PoExtractor.

Source Code (CultureSelector.razor)

@using Microsoft.Extensions.Localization
@using System.Globalization
@using Domain
@inject IStringLocalizer<CultureSelector> S

<MudSelect ValueChanged="SetCulture"
           T="CultureInfo"
           Value="CultureInfo.CurrentUICulture"
           Label="@S["Language"]" Placeholder="@S["Please Select"]"
           AdornmentIcon="@Icons.Material.Filled.Language"
           Style="width:200px; margin-left:10px;">
    @foreach (var culture in SupportedUiLanguages.Cultures)
    {
        <MudSelectItem Value="@culture" T="CultureInfo">@culture.EnglishName</MudSelectItem>
    }
</MudSelect>

Command Line Arguments

src translations -l "C#" -t "razor"

Generated POT file

#: Portal.Web\Shared\CultureSelector.razor:11
#. Label="@S["Language"]" Placeholder="@S["Please Select"]"
msgctxt "Portal.Web.Shared.CultureSelector.razor"
msgid "Language"
msgstr ""

Problem Description

The problem is that the msgctxt has class name + .razor suffix.
Using OrchardCore.Localization however expects the context not to have this suffix.

Problem Diagnosis

The RazorMetadataProvider performs replacement of the file extension. However, it only considers to replace .cshtml. In the Blazor project, however, the file extension is .razor.

Relevant code is in method public string GetContext(SyntaxNode node).

image

Note: also see: https://stackoverflow.com/questions/55590605/what-is-the-difference-between-razor-and-cshtml-files

Possible Solutions

  • replace multiple file extensions:
    .cshtml and .razor
  • replace the file extension no matter what it is. This works whether the file has an extension or not. Only problem would be, if a file has an extension which is not supposed to be one, let's say foo.bar, and .bar is not supposed to be an extension. Don't think that can happen - or would matter much: class names never have a ., so foo.bar would not be a valid context (class name) in the first place.

Is this still a valid bug? or this one #85?

Is this still a valid bug? or this one #85?

Yes, I'm experiencing both with the latest version 1.0.1 released on 24.01.2023.

Or do you mean I should try out the version from the develop branch?

Edit: I will try the develop version. I just noticed that the release is more than a year old and there's a recent addition to support for file-scope namespaces - and we're using them.

@hishamco
on develop, the first problem with the missing class name is fixed.

Texts in the razor templates are still reported with a msgctxt of ....razor. Tomorrow I'll verify if this actually is a problem and OrchardCore.Localization uses a different msgctxt - or not.

I'm waiting, otherwise please write a failing unit test, so I can fix the issue

For completeness sake here a copy of the original post, where I thought there was more problems - but actually 2 out of 3 are resolved in the upcoming .net 8 based version:


See these examples:

#: Portal.Web\Localization\CommonStrings.cs:11
#. public string SaveChanges => S["Save Changes"];
msgctxt "Portal.Web.Localization."
msgid "Save Changes"
msgstr ""

#: Portal.Web\Pages\Picos\LoadManagement\Multilevel\MultilevelErrorMapper.cs:63
#. return S["Not found"];
msgctxt "Portal.Web.Pages.Picos.LoadManagement.Multilevel.MultilevelErrorMapper"
msgid "Not found"
msgstr ""

#: Portal.Web\Shared\CultureSelector.razor:11
#. Label="@S["Language"]" Placeholder="@S["Please Select"]"
msgctxt "Portal.Web.Shared.CultureSelector.razor"
msgid "Language"
msgstr ""

You can see that the first does not include the class name in the msgctxt while the second does.
The third one has the class name + ".razor" suffix.

I suspect that the msgctxt is generated from the filename/file path, not the actual type name?

Note that passing -t "razor" or leaving it out does not change anything about the generated *.pot file.


CommonStrings.cs

public class CommonStrings(
    IStringLocalizer<CommonStrings> s)
{
    // ReSharper disable once InconsistentNaming required by OrchardCoreContrib
    readonly IStringLocalizer S = s;

    public string Ok => S["OK"];
    public string SaveChanges => S["Save Changes"];
    public string Cancel => S["Cancel"];
}

MultilevelErrorMapper.cs

public class MultilevelErrorMapper
{
    // ReSharper disable once InconsistentNaming required by PoExtractor
    readonly IStringLocalizer S;

    public MultilevelErrorMapper(
        IStringLocalizer<MultilevelErrorMapper> s)
    {
        S = s;
    }

    LocalizedString MapNotFound(
        NotFound _)
    {
        return S["Not found"];
    }
}

CultureSelector.razor

@using Microsoft.Extensions.Localization
@using System.Globalization
@using Domain
@inject IStringLocalizer<CultureSelector> S

<MudSelect ValueChanged="SetCulture"
           T="CultureInfo"
           Value="CultureInfo.CurrentUICulture"
           Label="@S["Language"]" Placeholder="@S["Please Select"]"
           AdornmentIcon="@Icons.Material.Filled.Language"
           Style="width:200px; margin-left:10px;">
    @foreach (var culture in SupportedUiLanguages.Cultures)
    {
        <MudSelectItem Value="@culture" T="CultureInfo">@culture.EnglishName</MudSelectItem>
    }
</MudSelect>

@hishamco
Sorry for the delay - I was super busy at work.
I have now investigated the issue and it's just the fact that Razor support is broken when the razor file does not have a file ending of .cshtml. I have updated the issue entry (first post) to reflect that and have given more information there, including steps to reproduce. The issue should be rather trivial to fix.
If you want me to, I can provide a PR, but would like to request guidance from you as to which solution to choose.
Like I wrote in the opening post I see two easy solutions:

  • replace .cshtml and .razor with string.empty. This one is safe, but of course, if another extension is used, it doesn't work.
  • replace any file extension, no matter what it is. This one stops working when the file has a name of foo.bar but .bar is not supposed to be an extension... so I think it's safe, too, and simpler, too. So I would propose to use this.

IMHO supporting .razor makes sense to me