@@ -11,28 +11,37 @@ interface Props {
1111 description? : string ;
1212 authors? : { name: string ; github? : string }[];
1313 image? : string ;
14+ /** Canonical pathname override, for pages served as a copy of another URL. */
15+ canonicalPathname? : string ;
1416}
1517
1618const {
1719 title,
1820 description = ' Fast, unopinionated, minimalist web framework for Node.js' ,
1921 authors,
2022 image,
23+ canonicalPathname,
2124} = Astro .props ;
2225
2326const pageTitle = title
2427 ? ` ${title } · Express.js `
2528 : ' Express.js · Node.js web application framework' ;
2629
27- const canonicalUrl = new URL (Astro .url .pathname , Astro .site || Astro .url .origin );
28-
2930const pathname = Astro .url .pathname .replace (/ \/ $ / , ' ' );
3031const segments = pathname .split (' /' ).filter (Boolean );
3132const langSegment = segments [0 ] || ' en' ;
3233const restSegments = segments .slice (1 );
3334const isApi =
3435 restSegments [0 ] === ' api' || (restSegments [1 ] === ' api' && / ^ \d x$ / .test (restSegments [0 ] ?? ' ' ));
3536const isBlogOrApi = restSegments [0 ] === ' blog' || isApi ;
37+
38+ // Blog and API content is not translated, so their canonical is the English
39+ // page and they emit no hreflang alternates.
40+ let canonicalPath = canonicalPathname ?? Astro .url .pathname ;
41+ if (isBlogOrApi ) {
42+ canonicalPath = replaceLanguageInPath (canonicalPath , ' en' );
43+ }
44+ const canonicalUrl = new URL (canonicalPath , Astro .site || Astro .url .origin );
3645// Build the OG image URL:
3746// - Home pages (no rest segments) use a per-language image: /og/home-{lang}.png
3847// - Blog and API pages strip the lang prefix since their content is not translated: /og/blog-{slug}.png
@@ -90,18 +99,19 @@ const lang = getLangFromUrl(Astro.url);
9099 <link rel =" canonical" href ={ canonicalUrl } />
91100
92101 {
93- languageCodes .map ((altLang ) => (
94- <link
95- rel = " alternate"
96- hreflang = { altLang }
97- href = {
98- new URL (
99- replaceLanguageInPath (Astro .url .pathname , altLang ),
100- Astro .site || Astro .url .origin
101- )
102- }
103- />
104- ))
102+ ! isBlogOrApi &&
103+ [' x-default' , ... languageCodes ].map ((altLang ) => (
104+ <link
105+ rel = " alternate"
106+ hreflang = { altLang }
107+ href = {
108+ new URL (
109+ replaceLanguageInPath (canonicalPath , altLang === ' x-default' ? ' en' : altLang ),
110+ Astro .site || Astro .url .origin
111+ )
112+ }
113+ />
114+ ))
105115 }
106116
107117 <title >{ pageTitle } </title >
0 commit comments