Skip to main content

umbraco-multilingual

Set up and debug multilingual Umbraco 17+ Razor sites, where content exists in more than one language (culture). Covers languages and fallback, vary-by-culture Document Types, Culture and Hostnames (/de or a domain per country) without breaking existing URLs, block level variance for Block List/Grid and rich text blocks, dictionary items for hardcoded view text, a language switcher and hreflang tags, and cultures such as zh-HK missing from the Languages dropdown. Also diagnoses blank translated fields, controllers or background jobs returning the wrong culture, and migrations or packages whose imported languages or dictionary items don't go live. Trigger: "add Danish to the site", "make the site multilingual", "add a language switcher", "hreflang tags", "translate the blocks per language", "the German page shows English text", "zh-HK isn't in the languages list". SKIP: non-Umbraco apps, Umbraco 13 and earlier, translating the backoffice UI or extension labels, and headless / Content Delivery API front ends.

Source facts

Repository
umbraco/Umbraco-CMS-Implementation-Skills
Last source activity
October 21, 2026 at 09:06
Detected SKILL.md language
English
Stars
7
Forks
8

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
34 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
umbraco-multilingual
description
Set up and debug multilingual Umbraco 17+ Razor sites, where content exists in more than one language (culture). Covers languages and fallback, vary-by-culture Document Types, Culture and Hostnames (/de or a domain per country) without breaking existing URLs, block level variance for Block List/Grid and rich text blocks, dictionary items for hardcoded view text, a language switcher and hreflang tags, and cultures such as zh-HK missing from the Languages dropdown. Also diagnoses blank translated fields, controllers or background jobs returning the wrong culture, and migrations or packages whose imported languages or dictionary items don't go live. Trigger: "add Danish to the site", "make the site multilingual", "add a language switcher", "hreflang tags", "translate the blocks per language", "the German page shows English text", "zh-HK isn't in the languages list". SKIP: non-Umbraco apps, Umbraco 13 and earlier, translating the backoffice UI or extension labels, and headless / Content Delivery API front ends.
# Multilingual site One content tree, **culture variants** on the nodes, and a **domain per culture** on the site root. That is Umbraco's documented model: one node holds every language version, and the domain the request matches decides which one renders. Build it in this order, since each step depends on the one before. | # | Step | Reference | Ships | |---|---|---|---| | 1 | Languages: default, mandatory, fallback | [languages-and-variants.md](references/languages-and-variants.md) | [`assets/multilingual-package.xml`](assets/multilingual-package.xml) | | 2 | Vary Document Types, properties and blocks by culture | [languages-and-variants.md](references/languages-and-variants.md) | — | | 3 | Culture and Hostnames: a domain per culture | [languages-and-variants.md](references/languages-and-variants.md) | — | | 4 | Render variant values, fallback, dictionary, culture outside a request | [rendering-variants.md](references/rendering-variants.md) | — | | 5 | Language switcher and hreflang | [language-switcher-and-seo.md](references/language-switcher-and-seo.md) | [`languageSwitcher.cshtml`](assets/languageSwitcher.cshtml), [`hreflangLinks.cshtml`](assets/hreflangLinks.cshtml) | | — | Only if a culture is missing from the Languages dropdown | [additional-cultures.md](references/additional-cultures.md) | [`AllowAdditionalCulturesComposer.cs`](assets/AllowAdditionalCulturesComposer.cs) | ### How to decide - Default to one tree with culture variants. Choose a **root per market** only when markets differ in *structure*, not just in wording; see the note in [languages-and-variants.md](references/languages-and-variants.md). - For backoffice steps (1–3), prefer the Umbraco Developer MCP. If it's unavailable, walk the user through the backoffice one step at a time; the order is in [languages-and-variants.md](references/languages-and-variants.md). Don't skip a step because the MCP is missing. - **Headless / Content Delivery API front ends are out of scope for now.** This skill covers Razor sites. ## Version compatibility Targets **Umbraco 17+**. Every API used here (`ILanguageService`, `IDomainService.UpdateDomainsAsync`, `IIsoCodeValidator`, `ICultureService`) exists on 17, with one exception: **language fallback for blocks** (`Value<BlockListModel>(..., fallback: Fallback.ToLanguage)` showing a block not exposed in the current language) needs **17.4+**. On 17.0–17.3 the argument is accepted but unexposed blocks stay hidden, with no error, so check the site's version before relying on it. Umbraco 13 and earlier have different language/domain APIs; don't apply this skill there. ## Best practices - **Never lowercase a culture you pass to Umbraco APIs or emit in markup.** Keep ISO casing (`da-DK`) in code and config, and compare with `StringComparison.OrdinalIgnoreCase`. The one exception is **Examine field names**, whose culture suffixes are stored lowercase (`__Published_da-dk`, `title_da-dk`): build those with `culture.ToLowerInvariant()`, or a query on `__Published_da-DK` silently matches nothing. - **Don't hardcode culture lists.** Read them from `ILanguageService` or `IPublishedContent.Cultures`, so adding a language needs no code change. - **Set a fallback per language and decide mandatory deliberately.** A mandatory language blocks publishing every other culture of a node until it has content. - **Translate blocks with block level variance, not a varying block property.** Three settings, all three stated: the Document Type varies by culture, the Block List/Grid property is invariant (shared), and its element types vary. Every language then shares the same blocks and order. A varying block property gives each language its own list, and the lists drift. - **Vary only what differs.** Shared values (SKUs, dates, images with no text) belong on invariant properties, or editors enter them once per language and they drift apart. - **`Value<string>` returns `""`, not `null`, for a blank value.** A `?? fallback` never fires; use `Fallback.ToLanguage` or `string.IsNullOrWhiteSpace`. - **`Umbraco:CMS:Global:DefaultUILanguage` is the *backoffice* UI language.** The site's default content language is set on the language itself (Settings → Languages → Default language). Changing that default is a separate decision from adding a language: the default is what renders when no domain matches and what `Fallback.ToDefaultLanguage` reads, so when a launch request implies making the new language the default, ask whether that is really intended rather than switching it. - **Every published culture needs a reachable URL.** If a culture has no domain, its pages can't be routed in that culture, and the switcher and hreflang tags have nothing to link to. - **The runtime gate tests the skill's assets, not your copy of them.** When handing over, say the partials or composer were copied from this skill and whether *you* built the project. Don't present the gate as your verification ("validated against Umbraco 17.5") unless you ran it. ## Validation Assertions live in [`evals/evals.json`](evals/evals.json); run them with `umbraco-skill-evaluator`. [`evals/README.md`](evals/README.md) has the project fixture, the run-prompt rules, and the Haiku and Sonnet results (with and without the skill). The runtime gate covers both gated parts: - `examples/culture-variants/` on the blank host (languages, domains, fallback, dictionary, switcher, hreflang, block level variance); - `examples/additional-cultures/` on the Clean host (the composer).
View on GitHub