mnfst 0.5.60 → 0.5.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -93,6 +93,21 @@ function initializeLocalizationPlugin() {
93
93
  return document.head?.querySelector('meta[name="manifest:prerendered"][content="1"]') !== null;
94
94
  }
95
95
 
96
+ // Returns the set of locales the prerender actually generated URL paths for.
97
+ // Read from `<meta name="manifest:prerender-locales" content="en,fr,...">`.
98
+ // When the target locale isn't in this set, MPA locale switching should NOT
99
+ // navigate (it would 404) — instead, fall back to the in-page store update
100
+ // so locale-aware data sources (e.g. examples on a localization docs page)
101
+ // can re-render without leaving the current page.
102
+ function getPrerenderLocales() {
103
+ const meta = document.head?.querySelector('meta[name="manifest:prerender-locales"]');
104
+ const content = meta?.getAttribute('content') || '';
105
+ return content
106
+ .split(',')
107
+ .map(s => s.trim())
108
+ .filter(Boolean);
109
+ }
110
+
96
111
  function buildLocaleNavigationUrl(newLang, availableLocales) {
97
112
  const currentUrl = new URL(window.location.href);
98
113
  const pathParts = currentUrl.pathname.split('/').filter(Boolean);
@@ -359,14 +374,44 @@ function initializeLocalizationPlugin() {
359
374
 
360
375
 
361
376
  try {
362
- // In prerendered static output, locale switching must navigate to a locale URL.
363
- // Mutating Alpine store alone won't re-render baked static content.
377
+ // In prerendered static output, locale switching normally navigates
378
+ // to the target locale's URL. But only do this when the target
379
+ // locale was ACTUALLY prerendered — otherwise navigation would 404.
380
+ //
381
+ // When the host site has a single locale (e.g. an English docs site
382
+ // with locale-aware example data on one page), `prerender-locales`
383
+ // contains only that locale. Switching to any other locale falls
384
+ // through to the in-page store update below — locale-aware data
385
+ // re-loads via the `localechange` event and the page reflects the
386
+ // new locale without navigating.
387
+ // Track whether this is a "scoped" change (in-page only on a
388
+ // prerendered single-locale site). Scoped changes must NOT persist
389
+ // to localStorage or update the URL — the locale is a transient
390
+ // example/demo state, not a site-wide preference. Persisting would
391
+ // leak the demo locale to subsequent page loads via localStorage,
392
+ // making `<html lang>` mismatch the actual baked content (English
393
+ // article body with `lang="fr"`) and muddying the SEO signal.
394
+ let isScopedDemoChange = false;
364
395
  if (isPrerenderedStaticBuild()) {
365
- const targetUrl = buildLocaleNavigationUrl(newLang, store.available || []);
366
- if (targetUrl !== window.location.href) {
367
- window.location.assign(targetUrl);
396
+ const prerenderLocales = getPrerenderLocales();
397
+ // Multi-locale site: navigate to the target locale's URL.
398
+ // Single-locale site: there are no other locale URLs to navigate
399
+ // to (the renderer skips the redundant default-locale mirror),
400
+ // so ALL switches must be treated as in-page demos.
401
+ const isMultiLocaleSite = prerenderLocales.length > 1;
402
+ const targetIsPrerendered =
403
+ prerenderLocales.length === 0 ||
404
+ prerenderLocales.includes(newLang);
405
+ if (isMultiLocaleSite && targetIsPrerendered) {
406
+ const targetUrl = buildLocaleNavigationUrl(newLang, store.available || []);
407
+ if (targetUrl !== window.location.href) {
408
+ window.location.assign(targetUrl);
409
+ }
410
+ return true;
368
411
  }
369
- return true;
412
+ // In-page demo: locale-aware data re-renders without navigating.
413
+ // Mark as scoped so we skip URL/localStorage persistence below.
414
+ isScopedDemoChange = true;
370
415
  }
371
416
 
372
417
  // Update store
@@ -382,16 +427,21 @@ function initializeLocalizationPlugin() {
382
427
  console.error('[Manifest Localization] DOM update error:', domError);
383
428
  }
384
429
 
385
- // Update localStorage safely
386
- safeStorage.set('lang', newLang);
430
+ // Update localStorage safely — but skip for scoped demo changes
431
+ // so the next page load doesn't restore a non-prerendered locale.
432
+ if (!isScopedDemoChange) {
433
+ safeStorage.set('lang', newLang);
434
+ }
387
435
 
388
- // Update URL based on current URL state and updateUrl parameter
436
+ // Update URL based on current URL state and updateUrl parameter.
437
+ // Skip entirely for scoped demo changes — adding/replacing a locale
438
+ // prefix would point at a URL that wasn't prerendered (404).
389
439
  try {
390
440
  const currentUrl = new URL(window.location.href);
391
441
  const pathParts = currentUrl.pathname.split('/').filter(Boolean);
392
442
  const hasLanguageInUrl = pathParts[0] && store.available.includes(pathParts[0]);
393
443
 
394
- if (updateUrl || hasLanguageInUrl) {
444
+ if (!isScopedDemoChange && (updateUrl || hasLanguageInUrl)) {
395
445
  // Update URL if:
396
446
  // 1. updateUrl is explicitly true (router navigation, initialization)
397
447
  // 2. OR there's already a language code in the URL (user expects URL to update)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst",
3
- "version": "0.5.60",
3
+ "version": "0.5.61",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "templates/starter",