Skip to content

Extract strings from LocalizedString and LocalizedHtmlString constructors and Create calls - #130

Closed
MikeAlhayek wants to merge 5 commits into
OrchardCoreContrib:mainfrom
MikeAlhayek:localized-string-extractor
Closed

MikeAlhayek wants to merge 5 commits into
OrchardCoreContrib:mainfrom
MikeAlhayek:localized-string-extractor

Conversation

@MikeAlhayek

@MikeAlhayek MikeAlhayek commented Sep 23, 2026 •

Copy link
Copy Markdown

Summary

Some code creates localizable strings directly, without a localizer:

var displayName = new LocalizedString("Foo", "Foo");
var html = new LocalizedHtmlString("Foo", "Foo");

// Single argument form, where the name is also the value.
var shortForm = new LocalizedString("Foo");
var shortHtml = new LocalizedHtmlString("Foo");

// Factory methods, for example C# 14 static extension members.
var created = LocalizedString.Create("Foo");
var createdHtml = LocalizedHtmlString.Create("Foo");

Before this change, the extractor found only S["Foo"], H["Foo"], and T["Foo"]. Strings created with the LocalizedString (Microsoft.Extensions.Localization) or LocalizedHtmlString (Microsoft.AspNetCore.Mvc.Localization) constructors were not added to the POT file.

This PR adds a LocalizedStringExtractor for C# and VB and registers it in CSharpProjectProcessor and VisualBasicProjectProcessor.

Behavior

  • The extractor uses the first (name) argument as the msgid. This is the key that the string is localized by. The second (value) argument and any other arguments are ignored.
  • It matches only when the name argument is a string literal. nameof(...), variables, and other expressions are skipped.
  • It accepts one or more arguments. The single argument form (new LocalizedString("Foo")) is not in .NET today. It is supported so that the extractor is ready for a helper or a future constructor where the name is also the value. A call with no arguments is skipped.
  • It accepts simple, qualified, and global:: type names, for example new Microsoft.Extensions.Localization.LocalizedString(...).
  • It also extracts LocalizedString.Create("...") and LocalizedHtmlString.Create("...") calls, and the LocalizedStringExtensions.Create("...") and LocalizedHtmlStringExtensions.Create("...") forms for callers that do not use C# 14. The type and method names are in LocalizedStringTypes in OrchardCoreContrib.PoExtractor.DotNet, so the C# and VB extractors share them.
  • C#: string concatenation ("a " + "b") is supported. The concatenation logic moved from SingularStringExtractor to an internal ExpressionSyntaxExtensions helper, so both extractors use it.
  • VB: the type name check is case-insensitive, like VB identifiers.
  • Target-typed new("Foo", "Foo") is not supported, because the type cannot be known from syntax alone.

The README "Limitations" section documents the new convention.

Tests

  • New LocalizedStringExtractorTests in the C# and VB test projects, with positive and negative cases for both types, the single argument form, and the Create calls.
  • dotnet test -c Release: all 89 tests pass.
  • Manual end-to-end run: new LocalizedString("From constructor", "From constructor") shows in the generated POT file with the correct msgctxt. LocalizedString.Create("...") and LocalizedHtmlString.Create("...") are also extracted. new LocalizedString(nameof(C), "...") and LocalizedString.Create(nameof(E)) are skipped.

Related

The LocalizedString.Create and LocalizedHtmlString.Create helpers are proposed for Orchard Core in OrchardCMS/OrchardCore#19933

🤖 Generated with Claude Code

MikeAlhayek and others added 2 commits September 23, 2026 13:39
Add a LocalizedStringExtractor for C# and VB that extracts the name
argument of a LocalizedString constructor call when it is a string
literal. This is in addition to the existing S["Foo"] / H["Foo"] /
T["Foo"] conventions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MikeAlhayek MikeAlhayek changed the title Extract strings from new LocalizedString("Foo", "Foo") Extract strings from new LocalizedString(...) and new LocalizedHtmlString(...) Sep 23, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/OrchardCoreContrib.PoExtractor.DotNet.CS/SingularStringExtractor.cs Outdated
Comment thread src/OrchardCoreContrib.PoExtractor.DotNet.CS/LocalizedStringExtractor.cs Outdated
Comment thread README.md Outdated
- Extract LocalizedString.Create("...") and LocalizedHtmlString.Create("...")
  factory calls, including the LocalizedStringExtensions and
  LocalizedHtmlStringExtensions forms.
- Move the type names to LocalizedStringTypes in the shared DotNet project.
- Move TryGetString to an internal ExpressionSyntaxExtensions helper.
- Apply README wording suggestions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MikeAlhayek MikeAlhayek changed the title Extract strings from new LocalizedString(...) and new LocalizedHtmlString(...) Extract strings from LocalizedString and LocalizedHtmlString constructors and Create calls Sep 23, 2026
Comment on lines +49 to +50
LocalizedStringExtensionsTypeName,
LocalizedHtmlStringExtensionsTypeName

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have these been introduced or is it Claude :)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To allow LocalizedString.Create("...") and LocalizedHtmlString.Create("...") so we are not typing the same string twice.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious about the Create method, AFAIK, LocalizedString doesn't have such a method

@MikeAlhayek MikeAlhayek Sep 24, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not. I added it in OC. OrchardCMS/OrchardCore#19933

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked about it during triage and that is a sound approach.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LocalizedString.Create and LocalizedHtmlString.Create accept format
arguments. The first argument is the message ID, and the other
arguments are ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hishamco

hishamco commented Oct 5, 2026

Copy link
Copy Markdown
Member

I think we need to close this after merging OrchardCMS/OrchardCore#19972, right?

@MikeAlhayek

Copy link
Copy Markdown
Author

Yes.

@MikeAlhayek MikeAlhayek closed this Oct 5, 2026
@hishamco

hishamco commented Oct 5, 2026

Copy link
Copy Markdown
Member

I have something to add to what you did guys on these PR, hope to join tonight

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants