@@ -3,6 +3,7 @@ import { getTranslations } from "next-intl/server"
33
44import { DEFAULT_OG_IMAGE , SITE_URL } from "@/lib/constants"
55
6+ import { getTranslatedLocales } from "./translatedLocales"
67import { isLocaleValidISO639_1 } from "./translations"
78import { getFullUrl } from "./url"
89
@@ -43,6 +44,7 @@ export const getMetadata = async ({
4344 image,
4445 author,
4546 noIndex = false ,
47+ translatedLocales,
4648} : {
4749 locale : string
4850 slug : string [ ]
@@ -52,36 +54,56 @@ export const getMetadata = async ({
5254 image ?: string
5355 author ?: string
5456 noIndex ?: boolean
57+ translatedLocales ?: string [ ]
5558} ) : Promise < Metadata > => {
5659 const slugString = slug . join ( "/" )
5760 const t = await getTranslations ( { locale, namespace : "common" } )
5861
5962 const description = descriptionProp || t ( "site-description" )
6063 const siteTitle = t ( "site-title" )
6164
62- // Set canonical URL w/ language path to avoid duplicate content
63- const url = getFullUrl ( locale , slugString )
65+ // Auto-detect translated locales if not provided
66+ const finalTranslatedLocales =
67+ translatedLocales ?? ( await getTranslatedLocales ( slugString , "intl" ) )
68+
69+ const isCurrentPageTranslated = finalTranslatedLocales . includes ( locale )
70+
71+ // Set canonical URL
72+ // If current locale is NOT translated, set canonical to English version
73+ const canonicalLocale = isCurrentPageTranslated
74+ ? locale
75+ : routing . defaultLocale
76+ const url = getFullUrl ( canonicalLocale , slugString )
6477
6578 // Set x-default URL for hreflang
6679 const xDefault = getFullUrl ( routing . defaultLocale , slugString )
6780
6881 /* Set fallback ogImage based on path */
6982 const ogImage = image || getOgImage ( slug )
7083
84+ // Only include hreflang alternates if the current page is translated
85+ // Untranslated pages should not have hreflang tags
86+ const localesForHreflang = isCurrentPageTranslated
87+ ? routing . locales . filter (
88+ ( loc ) =>
89+ finalTranslatedLocales . includes ( loc ) && isLocaleValidISO639_1 ( loc )
90+ )
91+ : [ ]
92+
7193 const base : Metadata = {
7294 title,
7395 description,
7496 metadataBase : new URL ( SITE_URL ) ,
7597 alternates : {
7698 canonical : url ,
77- languages : {
78- "x-default" : xDefault ,
79- ... Object . fromEntries (
80- routing . locales
81- . filter ( isLocaleValidISO639_1 )
82- . map ( ( locale ) => [ locale , getFullUrl ( locale , slugString ) ] )
83- ) ,
84- } ,
99+ ... ( localesForHreflang . length > 0 && {
100+ languages : {
101+ "x-default" : xDefault ,
102+ ... Object . fromEntries (
103+ localesForHreflang . map ( ( loc ) => [ loc , getFullUrl ( loc , slugString ) ] )
104+ ) ,
105+ } ,
106+ } ) ,
85107 } ,
86108 openGraph : {
87109 title,
@@ -117,5 +139,9 @@ export const getMetadata = async ({
117139 return { ...base , robots : { index : false } }
118140 }
119141
142+ if ( ! isCurrentPageTranslated ) {
143+ return { ...base , robots : { index : true , follow : true } }
144+ }
145+
120146 return base
121147}
0 commit comments