Skip to content

Localisation

ConsoleMVC uses the standard .NET localisation: .resx resource files read through IStringLocalizer, and the culture of the run for formatting.

Choosing the culture

builder.Localization.Culture = CultureInfo.GetCultureInfo("pl-PL");

Or let users choose, with the ConsoleMVC:Culture setting:

dotnet run -- --ConsoleMVC:Culture=pl-PL
export ConsoleMVC__Culture=pl-PL

The culture is used for the whole run, for formatting (@Model.Total shows 1 234,50 in Polish) and for resources. Without one, the system's culture is used. An unknown culture name stops the application with a clear message.

Your own text

IStringLocalizer<T> is registered for you. Put the resources in a folder and say where:

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
MyApp/
├── SharedResource.cs                 (an empty class that names the resources)
└── Resources/
    ├── SharedResource.resx           (English)
    └── SharedResource.pl.resx        (Polish)
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<MyApp.SharedResource> L

<box title="@L["GreetingTitle"]">
    <text>@L["GreetingIntro"]</text>
</box>
<input name="Name" label="@L["NameLabel"]" required />

Controllers take IStringLocalizer<T> in their constructor in the same way.

The framework's messages

The prompts and messages the framework shows itself (Select an option:, This field is required., the <scroll> status line, binding errors, …) are the properties of FrameworkText. Translate them in your resources, one resource per property name:

builder.Localization.FrameworkTextResource = typeof(SharedResource);
Resource English
MenuPrompt Select an option:
MenuInvalidChoice Invalid choice, please try again.
Required This field is required.
CheckboxRequired, CheckboxAnswer This box must be ticked., Please answer y or n.
YesLetter, NoLetter, YesWord, NoWord y, n, yes, no (shown in [y/N] and accepted as answers)
NotANumber, WholeNumber, DecimalPlace, DecimalPlaces number field messages ({0}: decimals allowed)
NumberRange, NumberAtLeast, NumberAtMost {0} and {1}: the limits
NotADate, DateRange, DateOnOrAfter, DateOnOrBefore date field messages
UnknownChoice, NoMatch, Ambiguous choice field messages ({0}: the answer)
ChooseOne, ChooseSeveral typed choice prompts ({0}: the range)
ScrollStatus Lines {0}-{1} of {2} …
InvalidValue The value '{0}' is not valid for {1}.
FieldInvalid The {0} field is invalid.

Messages without a resource stay in English. You can also set them in code:

builder.Localization.Text = FrameworkText.English with { MenuPrompt = "Choose: " };

Validation messages

builder.Localization.LocalizeValidation = true;
builder.Localization.ValidationResource = typeof(SharedResource); // or leave it out: resources named after each model

An attribute's ErrorMessage is then the name of a resource, and [Display(Name = "…")] is translated too. The translation is formatted like the original, so placeholders keep their meaning:

[Required(ErrorMessage = "NameRequired")]                       // NameRequired: "Podaj {0}."
[StringLength(30, ErrorMessage = "NameTooLong")]                // NameTooLong: "{0} może mieć najwyżej {1} znaków."
[Display(Name = "Name")]                                        // Name: "Imię"
public string Name { get; set; } = "";
Attribute {1} {2}
[StringLength] maximum length minimum length
[Range] minimum maximum
[MinLength], [MaxLength] the length
[Length] minimum length maximum length
[RegularExpression] the pattern
[Compare] the other property

{0} is always the field's (translated) display name. A message without ErrorMessage is translated as a whole when your resources have it. The template does all of this in Polish: try dotnet run -- --ConsoleMVC:Culture=pl-PL.