Skip to content

Add LocalizedString.Create and LocalizedHtmlString.Create - #19932

Closed
MikeAlhayek wants to merge 2 commits into
OrchardCMS:mainfrom
MikeAlhayek:ma/localized-string-create
Closed

MikeAlhayek wants to merge 2 commits into
OrchardCMS:mainfrom
MikeAlhayek:ma/localized-string-create

Conversation

@MikeAlhayek

@MikeAlhayek MikeAlhayek commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

This PR Depends on OrchardCoreContrib/OrchardCoreContrib.PoExtractor#130

Summary

Some APIs need a LocalizedString or a LocalizedHtmlString, but there is no localizer where the value is created. Today the code repeats the same text for the name and the value:

entry.DisplayName = new LocalizedString(providerName, providerName);

This PR adds a static Create(string name) method to both types, as C# 14 extension members, so that the text is written only once:

entry.DisplayName = LocalizedString.Create(providerName);
var message = LocalizedHtmlString.Create("<strong>Hello</strong>");

Changes

  • LocalizedStringExtensions in the Microsoft.Extensions.Localization namespace, and LocalizedHtmlStringExtensions in the Microsoft.AspNetCore.Mvc.Localization namespace. Both are in OrchardCore.Localization.Abstractions/Extensions, next to the existing Plural extensions, and use the same namespaces as the types. Code that already uses the types can call LocalizedString.Create(...) with no new using directive.

  • Create(name) is the same as new LocalizedString(name, name) and new LocalizedHtmlString(name, name). It does not translate the value, and ResourceNotFound is false.

  • Code that does not use C# 14 can call LocalizedStringExtensions.Create(...) or LocalizedHtmlStringExtensions.Create(...).

  • All 53 built-in DeploymentStep implementations now set their category in the parameterless constructor with LocalizedString.Create(...). The second constructor, which took an IStringLocalizer<TStep> only to set Category, is removed:

    public sealed class IndexProfileDeploymentStep : DeploymentStep
    {
        public IndexProfileDeploymentStep()
        {
            Name = CreateOrUpdateIndexProfileStep.StepKey;
            Category = LocalizedString.Create("Indexing");
        }
    }
  • DeploymentPlanController now translates the category where it is shown, with IStringLocalizerFactory.Create(step.GetType())[step.Category.Name]. IStringLocalizer<TStep> uses the same context (the full name of the step type), so existing PO translations continue to work. Custom steps that still set the category with an IStringLocalizer<TStep> show the same text as before.

  • Docs: a new "Creating a localized string without a localizer" section in reference/modules/Localize/README.md, an example step with a category in the "Extending deployment" section of reference/modules/Deployment/README.md, and a 4.0.0 release notes entry.

Tests

  • New LocalizedStringExtensionsTests and LocalizedHtmlStringExtensionsTests in OrchardCore.Tests.
  • New DeploymentStepCategoryTests. They create steps through DeploymentStepFactory<TStep>, check that the category is set, check that a PO entry with the step type as the context translates it, and check that the result is the same as the old IStringLocalizer<TStep> result.
  • dotnet build src/OrchardCore.Cms.Web -c Release: no warnings or errors.
  • Full OrchardCore.Tests run: 3003 passed, 0 failed, 2 skipped (one Unix-only test, one CI-only test).
  • dotnet test --project test/OrchardCore.Tests -c Release --filter-class "*LocalizedStringExtensionsTests" --filter-class "*LocalizedHtmlStringExtensionsTests" --filter-class "*NullStringLocalizerTests": 31 passed.

Related

The PO extractor will pick up LocalizedString.Create("...") and LocalizedHtmlString.Create("...") when the argument is literal text: OrchardCoreContrib/OrchardCoreContrib.PoExtractor#130

🤖 Generated with Claude Code

Add C# 14 static extension members that create a LocalizedString or a
LocalizedHtmlString where the name is also the value. The extensions
live in the same namespaces as the types so they are discoverable
without an extra using directive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Set the category of every built-in DeploymentStep in its parameterless
constructor with LocalizedString.Create(), and remove the constructors
that took an IStringLocalizer<TStep> only to set the category.

DeploymentPlanController now translates the category by its name with
the step type as the context. This is the same context that
IStringLocalizer<TStep> uses, so existing translations continue to work.

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

Copy link
Copy Markdown
Member Author

Replaced by a stack of PRs so each change can be reviewed on its own: #19933 (add LocalizedString.Create), then #19934 (use it in deployment steps). A third PR for permission descriptions follows.

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.

1 participant