Skip to content

Centralize i18n canonical/hreflang generation and fix blog slug handling#134

Merged
TheMeinerLP merged 2 commits into
mainfrom
claude/fix-language-switching-404-6Afk0
May 16, 2026
Merged

Centralize i18n canonical/hreflang generation and fix blog slug handling#134
TheMeinerLP merged 2 commits into
mainfrom
claude/fix-language-switching-404-6Afk0

Conversation

@TheMeinerLP
Copy link
Copy Markdown
Collaborator

Summary

This PR refactors SEO tag generation to use @nuxtjs/i18n's built-in canonical and hreflang support instead of hand-rolling duplicate logic across multiple composables. It also fixes blog article language switching by publishing per-locale slugs through useSetI18nParams, and adds proper error page handling for missing/unreleased articles.

Key Changes

  • Centralized SEO generation: Moved canonical + hreflang + og:locale generation to layouts/default.vue using useLocaleHead() from @nuxtjs/i18n, eliminating duplicate link tag generation in usePageSeo and useBlogArticle
  • Blog slug translation support: Added useSetI18nParams() integration in useBlogArticle to publish translated slugs per locale, enabling <SwitchLocalePathLink> to resolve correct localized URLs instead of naively reusing the current slug
  • Improved slug resolution: Implemented slugFromUrl() and localeCodeFromHreflang() helpers to extract and map slugs from front-matter alternates and translationKey cross-collection lookups
  • Error page handling: Created error.vue component with proper 404/generic error states; changed useBlogArticle to return a marker instead of throwing, then raise a fatal error after data resolves for reliable SSR error page rendering
  • Async composable: Made useBlogArticle async and awaited in page component to ensure data resolves before rendering
  • Language selector refactor: Updated LanguageSelector.vue to use <SwitchLocalePathLink> instead of manual setLocale + navigateTo, delegating navigation to the i18n component which now handles translated slugs
  • Config update: Changed nuxt.config.ts baseUrl to production domain so SEO tags always reflect real URLs across all environments

Implementation Details

  • Removed alternateLanguages and headLinks computed refs from useBlogArticle — these are now generated app-wide by @nuxtjs/i18n
  • The publishLocaleParams() function in useBlogArticle updates i18n's internal route params whenever article translations are resolved, making the canonical/hreflang tags automatically reflect the correct localized slugs
  • Error handling now distinguishes between missing articles (404) and other failures, with proper HTTP status codes during SSR
  • Removed unused resolveHreflang() and HeadLink type; simplified usePageSeo to only handle page-specific robots/keywords meta

https://claude.ai/code/session_01QsCtq62jJ1pfbxJfMKFm1y

claude added 2 commits May 16, 2026 10:06
…04 page

Blog posts use a different slug per language, so the language switcher
naively reused the current slug and produced a 404 when switching
languages. Publish the per-locale slugs via useSetI18nParams so
switchLocalePath / <SwitchLocalePathLink> resolve the correct localized
URL, and switch LanguageSelector to <SwitchLocalePathLink> to avoid
stale SSR links.

A missing or unreleased article (and any unmatched route) now raises a
fatal 404 instead of silently rendering an empty page, surfaced through
a new localized error.vue.

https://claude.ai/code/session_01QsCtq62jJ1pfbxJfMKFm1y
Search Console reported duplicate / conflicting canonical signals
("Google chose a different canonical"). Canonical and hreflang were
hand-rolled in three places with diverging values: usePageSeo derived
the canonical from route.fullPath (leaking utm/fbclid query params into
infinite canonical variants), useBlogContent emitted its own
frontmatter-driven set, and useSetI18nParams additionally injected the
i18n SEO set on blog pages.

Consolidate on @nuxtjs/i18n as the single source of truth: spread
useLocaleHead({ seo }) app-wide in the default layout so every page
emits exactly one self-referential canonical plus reciprocal
de/en/x-default hreflang. Blog posts feed their translated slugs through
useSetI18nParams, so the i18n-generated tags use the correct
per-language URL. usePageSeo / useBlogContent no longer emit their own
canonical, alternate or og:locale tags. i18n baseUrl is pinned to the
production domain so the tags Google sees are always the real URLs.

https://claude.ai/code/session_01QsCtq62jJ1pfbxJfMKFm1y
@TheMeinerLP TheMeinerLP merged commit 0e11888 into main May 16, 2026
3 of 4 checks passed
@TheMeinerLP TheMeinerLP deleted the claude/fix-language-switching-404-6Afk0 branch May 16, 2026 10:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants