Skip to content

Localization

Valerio edited this page Apr 21, 2026 · 1 revision

Resources and Localization

Harpyx keeps user-facing UI text in ASP.NET Core resource files. Razor markup should describe structure and behavior; copy belongs in .resx files organized by area and page.

The WebApp uses the standard ASP.NET Core localization stack:

  • AddLocalization(options => options.ResourcesPath = "Resources")
  • AddViewLocalization() for Razor Page and view text
  • AddDataAnnotationsLocalization() for validation and model metadata

The service registration lives in src/Harpyx.WebApp/Extensions/WebApplicationBuilderExtensions.cs.

Razor Usage

Every Razor Page receives two localizers from src/Harpyx.WebApp/Pages/_ViewImports.cshtml:

@inject IViewLocalizer Localizer
@inject IStringLocalizer<Harpyx.WebApp.Shared.SharedResource> SharedLocalizer

Use Localizer for text owned by the current page:

@{
    ViewData["Title"] = Localizer["PageTitle"].Value;
}

<button class="btn btn-primary">@Localizer["SaveButton"]</button>

Use SharedLocalizer for text intentionally reused across multiple pages, layouts, or partials:

var brandHero = new BrandHeroViewModel(
    SharedLocalizer["BrandHeroTitle"].Value,
    SharedLocalizer["BrandHeroSubtitle"].Value);

This mirrors common ASP.NET Core guidance: page-local strings stay close to the page resource, while cross-page vocabulary goes through a shared marker resource.

Folder Layout

Resource files mirror the Razor UI surface. The folder name should explain the owning area, and the file name should match the page or shared concept.

src/Harpyx.WebApp/
  Resources/
    Pages/
      Index.resx
      Welcome.resx
      Profile/
        Index.resx
    Areas/
      Admin/
        Pages/
          Llm/
            Index.resx
    Shared/
      SharedResource.resx

Examples:

UI file Resource file
Pages/Index.cshtml Resources/Pages/Index.resx
Pages/Welcome.cshtml Resources/Pages/Welcome.resx
Pages/Profile/Index.cshtml Resources/Pages/Profile/Index.resx
Areas/Admin/Pages/Llm/Index.cshtml Resources/Areas/Admin/Pages/Llm/Index.resx
shared brand/layout copy Resources/Shared/SharedResource.resx

The shared resource uses a marker class at src/Harpyx.WebApp/Localization/Shared/SharedResource.cs. The class intentionally has no behavior; it gives IStringLocalizer<T> a stable resource anchor.

Key Naming

Resource keys are semantic identifiers, not the English text itself.

Use PascalCase keys:

PageTitle
SaveButton
DeleteConfirmationTitle
DeleteConfirmationMessage
BrandHeroTitle
BrandHeroSubtitle

Avoid full-sentence keys:

Save changes
Are you sure you want to delete this project?
Welcome to Harpyx!

Semantic keys are more stable when copy changes, easier to search, and friendlier to IDE completion and refactoring.

Ownership Rules

Put a string in the page resource when it belongs to one page:

@Localizer["GoToProjects"]

Put a string in SharedResource.resx when it is deliberately shared:

@SharedLocalizer["BrandHeroTitle"]

Do not move text into SharedResource.resx just because two pages happen to use the same English words. Shared resources should represent shared product language, shared layout copy, or reusable components.

Adding a New Page

When adding a Razor Page:

  1. Create the page resource file under Resources/Pages or Resources/Areas, mirroring the Razor path.
  2. Add semantic PascalCase keys for every user-facing string.
  3. Reference page-owned strings with Localizer["KeyName"].
  4. Reference global/shared copy with SharedLocalizer["KeyName"].
  5. Keep HTML, tag helpers, route names, CSS classes, and icon names out of resource files unless they are true user-facing content.

For example, a new page at Pages/Projects/Create.cshtml should use:

Resources/Pages/Projects/Create.resx

and the Razor markup should read:

<h1>@Localizer["PageTitle"]</h1>
<button type="submit" class="btn btn-primary">@Localizer["CreateProjectButton"]</button>

Validation and Data Annotations

Validation messages and model metadata also use ASP.NET Core localization through AddDataAnnotationsLocalization().

Use explicit resource-backed messages for user-facing validation text. Prefer stable semantic keys when the same validation message appears in multiple places, and keep field-specific copy in the resource owned by that UI/model area.

Clone this wiki locally