Localizes OrchardCore apps — UI strings (IStringLocalizer/IViewLocalizer S, T, H), PO files and pluralization, content translation (LocalizationPart, culture picker), and supported-culture configuration. Use when the user needs to translate strings, add a culture, set up PO files, or make content items multilingual.
75
93%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
This skill guides you through OrchardCore localization following project conventions.
Three layers, often confused:
IStringLocalizer (S), IViewLocalizer (T), IHtmlLocalizer (H). Translations live in PO files.LocalizationPart and the OrchardCore.ContentLocalization module.| Goal | Layer |
|---|---|
| Translate a label/message in code or a view | UI localization (S/T/H) + PO file |
| Pluralize ("1 item" / "{0} items") | S.Plural(...) + PO msgid_plural |
| Translate a blog post into French | Content localization (LocalizationPart) |
| Add a supported language / set default | Localization settings (LocalizationSettings) |
| Translate data-driven values (e.g. taxonomy terms) | Data Localization (DataLocalization module) |
| Localizer | Convention | Where |
|---|---|---|
IStringLocalizer<T> | field S | drivers, services, controllers |
IViewLocalizer | property T | Razor views (already on RazorPage) |
IHtmlLocalizer<T> | field H | when the translation contains HTML |
public sealed class AliasPartDisplayDriver : ContentPartDisplayDriver<AliasPart>
{
internal readonly IStringLocalizer S;
public AliasPartDisplayDriver(IStringLocalizer<AliasPartDisplayDriver> localizer)
=> S = localizer;
}In Razor: @T["Welcome to Orchard Core"]. In a class: S["A value is required for {0}.", name].
S.Plural(count, "{0} book", "{0} books");
// IHtmlLocalizer: H.Plural(count, "{0} book", "{0} books")PO files are discovered (in order) from each module's Localization/, a global /Localization, the tenant's App_Data/Sites/{tenant}/Localization, and culture folders. A PO entry:
msgctxt "OrchardCore.Admin.Views.AdminDashboard"
msgid "Welcome to Orchard Core"
msgstr "Bienvenue sur Orchard Core"msgctxt = {Namespace}.{Class} (services) or {Namespace}.{ViewPath} (views). Plurals:
msgctxt "OrchardCore.AdminMenu.Views.Menu.List"
msgid "1 item"
msgid_plural "{0} items"
msgstr[0] "{0} élément"
msgstr[1] "{0} éléments"See references/localization.md for the full PO discovery order and headers.
Enable OrchardCore.ContentLocalization (Content Localization). Attach LocalizationPart to the content type (admin or recipe). It carries:
public class LocalizationPart : ContentPart, ILocalizable
{
public string LocalizationSet { get; set; } // links all translations
public string Culture { get; set; } // this version's culture
}On create, the part handler generates a unique LocalizationSet and sets the default culture automatically.
From the editor's Localization, "Translate" creates a sibling content item sharing the same LocalizationSet with a different Culture. Programmatically:
var translated = await _contentLocalizationManager.LocalizeAsync(contentItem, "fr-FR");IContentLocalizationManager fetches across a set:
await _contentLocalizationManager.GetContentItemAsync(localizationSet, "fr");
await _contentLocalizationManager.GetItemsForSetAsync(localizationSet);Liquid: {{ Model.ContentItem.Content.LocalizationPart.LocalizationSet | localization_set: "en" }}. The Content Culture Picker feature adds a front-end language switcher and a ContentRequestCultureProvider that derives the culture from the localized route.
Recipe settings step:
{
"name": "settings",
"LocalizationSettings": {
"DefaultCulture": "en-US",
"SupportedCultures": [ "en-US", "fr-FR", "es-ES" ],
"FallBackToParentCulture": true
}
}The Localization module reads these at startup and calls app.UseRequestLocalization(...) with the configured default + supported cultures.
| Symbol | Type | Output |
|---|---|---|
S | IStringLocalizer<T> | plain string |
T | IViewLocalizer | encoded view string |
H | IHtmlLocalizer<T> | HTML-safe string |
| Member | Purpose |
|---|---|
IContentLocalizationManager.LocalizeAsync(item, culture) | create a translation |
.GetContentItemAsync(set, culture) | one translation |
.GetItemsForSetAsync(set) | all translations |
.GetItemsForSetsAsync(sets, culture) | batch |
ILocalizationService.GetDefaultCultureAsync() / GetSupportedCulturesAsync() | culture config |
| Provider | Picks culture from |
|---|---|
ContentRequestCultureProvider | the localized content route (added first) |
AdminCookieCultureProvider | admin_culture_* cookie, admin paths only |
| cookie / querystring / Accept-Language | ASP.NET Core defaults |
localization_set: "fr" — translated item; switch_culture_url — culture-switch URL.
msgctxt must exactly match the namespace+class (or namespace+view path). Wrong context → string stays untranslated.IStringLocalizer<T> context comes from T's type — inject the localizer typed to the class that owns the strings.LocalizationSet links translations; never share one set across unrelated items, and don't overwrite it.SupportedCultures before content can be translated into it.AddDataAnnotationsPortableObjectLocalization() (the Localization module registers it).references/localization.md — PO discovery order, contexts, plural forms, content localization internals, startup wiringsrc/docs/reference/modules/Localize/README.md (repo)src/docs/reference/modules/ContentLocalization/README.md (repo)src/docs/reference/modules/DataLocalization/README.md (repo)AGENTS.md (repo root) — build commandsb0e1e44
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.