rancher/dashboard PR 19324 (cnotv, branch feature/18898-release-welcome-wizard, 3 commits on master), checked on 2026-10-08. This page covers only the data: where the content lives, how it reaches the modal, and what decides whether the modal shows. The code review comes later.
shell/config/release-welcome.ts, the English text for those keys in en-us.yaml under releaseWelcome.*, and text written directly in the templates (titles, subtitles, URLs). Whether the modal shows depends on one user preference, read-release-welcome, which stores the last minor version the user has seen (for example "2.16"). Each card component reads its list from the config file, then calls t(key) on every entry.
shell/config/release-welcome.ts
WHATS_NEW_FEATURES: { id, titleKey, descriptionKey }[], 4 entriesPRIME_PRODUCTS: string[] of i18n keys, 8 entriesPRIME_BENEFITS: string[] of i18n keys, 4 entriesPRIME_URL, SCC_URL, SUPPORT_HANDBOOK_URL, REGISTRATION_ROUTEshell/assets/translations/en-us.yaml
releaseWelcome.*, 54 new lines{vendor} and {version}whatsNew.features.<id>.title/descriptionprime.* (Go Prime card), registration.* (registration card)shell/utils/release-welcome.ts + shell/store/prefs.ts
read-release-welcome: the last minor version seen, e.g. "2.16"loadManagement (shell/store/index.js:872), only when isRancheraddReleaseNotesNotification, then fetchAndProcessDynamicContent(...) (not awaited), then showReleaseWelcomeIfNew(commit, dispatch, getters). The modal and dynamic content start at the same time and don't depend on each other.shouldShowReleaseWelcome(getters)version = semver.coerce(getVersionData().Version) → "2.16" (none on dev builds → never shows) · not shown in single product mode · shown if read-release-welcome is empty or semver.lt(lastRead, "2.16.0")openReleaseWelcome(commit, dispatch)commit('modal/openModal', { component: ReleaseWelcomeDialog, modalWidth: '900px', closeOnClickOutside: true }), then prefs/set read-release-welcome = "2.16". The read state is saved when the modal opens, not when it closes.ReleaseWelcomeDialog.vue works out the variantisPrime = isRancherPrime() (from /rancherversion RancherPrime) · canRegister = isPrime && isAdminUser && features/get(SCC)WhatsNewCardWHATS_NEW_FEATURES is empty. Each feature renders as t(titleKey) + t(descriptionKey), plus a releaseNotesUrl linkPrimeRegistrationCardcanRegister. Benefits come from PRIME_BENEFITS, and the handbook link is added when benefit === SUPPORT_BENEFITPrimePromoCard!isPrime. Products from PRIME_PRODUCTS as RcTag chips, CTA → PRIME_URL| Install | User | Title | Subtitle | Cards |
|---|---|---|---|---|
| Community | anyone | titleCommunity | subtitleCommunity | What's new + Go Prime |
| Prime | admin + SCC feature | titlePrime | subtitlePrime + "register on the Registration page" link | What's new + Registration |
| Prime | admin without SCC / standard user | titlePrime | subtitlePrime | What's new only |
| Single product (Harvester) / dev build | anyone | never shown, and the user menu item is hidden too | ||
The PR doesn't define this object. This is the content the components put together from ① + ② + the templates, resolved to English. If the modal is going to take content from elsewhere, this is the shape it needs (see page 3). highlighted = text written directly in a template or a constant, not in a list.
{
"title": "Welcome to Rancher Community", // i18n titleCommunity | titlePrime ({vendor})
"subtitle": "Rancher Community v2.16 is up and running. Here's what's new…",
"whatsNew": {
"version": "2.16", // releaseWelcomeVersion()
"releaseNotesUrl": "https://github.com/rancher/rancher/releases/tag/v2.16.0", // store getter
"features": [ // WHATS_NEW_FEATURES → t(titleKey), t(descriptionKey)
{ "id": "navigation", "title": "Improved navigation", "description": "Jump to any cluster with the new switcher…" },
{ "id": "tables", "title": "Smarter tables", "description": "Tables across the UI are super-charged…" },
{ "id": "kubernetes", "title": "Broader Kubernetes support", "description": "Provision and manage clusters on Kubernetes 1.33…" },
{ "id": "hosted-provisioning", "title": "Unified hosted provisioning", "description": "EKS, AKS and GKE provisioning now runs on…" }
]
},
"prime": { // Community only
"title": "Ready for production? Go Prime.",
"description": "Everything in the community edition, plus 24×7 enterprise support…",
"products": ["Rancher Manager", "RKE2, k3k & K3s", "SUSE Continuous Integration & Delivery", "SUSE Security",
"SUSE Storage", "SUSE Virtualization", "SUSE Observability", "Application Collection"],
"cta": { "label": "Explore SUSE Rancher Prime", "url": "https://www.suse.com/products/rancher/" }
},
"registration": { // Prime admin with SCC only
"title": "Register to activate your Prime benefits",
"description": "Registering with SUSE Customer Center unlocks:",
"benefits": [
{ "text": "24×7 support: open cases directly from your account",
"link": { "label": "Support handbook", "url": "https://www.suse.com/support/handbook/" } }, // magic-key match in the template
{ "text": "Application Collection: curated, signed apps ready to deploy" },
{ "text": "Hardened images from the Prime trusted registry" },
{ "text": "Rancher Academy: free certification courses for your team" }
],
"cta": { "label": "Open SUSE Customer Center", "url": "https://scc.suse.com/" }
}
}
Every card calls t() on keys. Dynamic content sends plain text (announcements have title/message strings, not keys), so remote data can't go into these lists as they are. The fix is a step that turns everything into text first (keys → strings for the default, strings as they are for remote), and cards that render text. That step is the object above.
benefit === SUPPORT_BENEFITWhich benefit gets the "Support handbook" link is decided by comparing the benefit's i18n key to a constant in the component. Remote text has no key to compare against, so the data has to carry the link itself: { text, link?: { label, url } }.
The bundled list is "what's new in the release this build ships", and needs editing every minor release. Remote content is shared by every installed version, so each remote entry has to say which version it's for (announcements already do this with a semver version field).
openReleaseWelcomeThe value "2.16" is compared with semver.lt, so patch releases never show the modal again. Content changes don't either: new text in dynamic content for an already-read 2.16 is only seen through the user menu. That's probably fine, and page 3 keeps it.
These depend on the vendor (private label), the running version and the Prime/admin checks, so they belong to the UI rather than to remote content. Page 3 leaves them as i18n and only makes the card contents remote.