@@ -62,7 +62,7 @@ export function localeHead(
62
62
63
63
// Adding SEO Meta
64
64
if ( seo && locale && unref ( nuxtApp . $i18n . locales ) ) {
65
- metaObject . link . push ( ...getHreflangLinks ( common , locales , key ) , ...getCanonicalLink ( common , key , seo ) )
65
+ metaObject . link . push ( ...getHreflangLinks ( common , locales , key , seo ) , ...getCanonicalLink ( common , key , seo ) )
66
66
67
67
metaObject . meta . push (
68
68
...getOgUrl ( common , key , seo ) ,
@@ -83,7 +83,8 @@ function getBaseUrl() {
83
83
export function getHreflangLinks (
84
84
common : CommonComposableOptions ,
85
85
locales : LocaleObject [ ] ,
86
- key : NonNullable < I18nHeadOptions [ 'key' ] >
86
+ key : NonNullable < I18nHeadOptions [ 'key' ] > ,
87
+ seo : I18nHeadOptions [ 'seo' ]
87
88
) {
88
89
const baseUrl = getBaseUrl ( )
89
90
const { defaultLocale, strategy } = useRuntimeConfig ( ) . public . i18n
@@ -108,25 +109,45 @@ export function getHreflangLinks(
108
109
localeMap . set ( localeLanguage , locale )
109
110
}
110
111
112
+ const strictCanonicals = common . runtimeConfig . public . i18n . experimental . alternateLinkCanonicalQueries === true
113
+ const routeWithoutQuery = strictCanonicals ? common . router . resolve ( { query : { } } ) : undefined
114
+
115
+ // set meta property which is lost on router.resolve
116
+ if ( ! common . runtimeConfig . public . i18n . experimental . switchLocalePathLinkSSR && strictCanonicals ) {
117
+ routeWithoutQuery ! . meta = common . router . currentRoute . value . meta
118
+ }
119
+
111
120
for ( const [ language , mapLocale ] of localeMap . entries ( ) ) {
112
- const localePath = switchLocalePath ( common , mapLocale . code )
121
+ const localePath = switchLocalePath ( common , mapLocale . code , routeWithoutQuery )
122
+ const canonicalQueryParams = getCanonicalQueryParams ( common , seo )
123
+ let href = toAbsoluteUrl ( localePath , baseUrl )
124
+ if ( canonicalQueryParams && strictCanonicals ) {
125
+ href = `${ href } ?${ canonicalQueryParams } `
126
+ }
127
+
113
128
if ( localePath ) {
114
129
links . push ( {
115
130
[ key ] : `i18n-alt-${ language } ` ,
116
131
rel : 'alternate' ,
117
- href : toAbsoluteUrl ( localePath , baseUrl ) ,
132
+ href : href ,
118
133
hreflang : language
119
134
} )
120
135
}
121
136
}
122
137
123
138
if ( defaultLocale ) {
124
- const localePath = switchLocalePath ( common , defaultLocale )
139
+ const localePath = switchLocalePath ( common , defaultLocale , routeWithoutQuery )
140
+ const canonicalQueryParams = getCanonicalQueryParams ( common , seo )
141
+ let href = toAbsoluteUrl ( localePath , baseUrl )
142
+ if ( canonicalQueryParams && strictCanonicals ) {
143
+ href = `${ href } ?${ canonicalQueryParams } `
144
+ }
145
+
125
146
if ( localePath ) {
126
147
links . push ( {
127
148
[ key ] : 'i18n-xd' ,
128
149
rel : 'alternate' ,
129
- href : toAbsoluteUrl ( localePath , baseUrl ) ,
150
+ href : href ,
130
151
hreflang : 'x-default'
131
152
} )
132
153
}
@@ -146,24 +167,9 @@ export function getCanonicalUrl(common: CommonComposableOptions, baseUrl: string
146
167
if ( ! currentRoute ) return ''
147
168
let href = toAbsoluteUrl ( currentRoute . path , baseUrl )
148
169
149
- const canonicalQueries = ( isObject ( seo ) && seo . canonicalQueries ) || [ ]
150
- const currentRouteQueryParams = currentRoute . query
151
- const params = new URLSearchParams ( )
152
- for ( const queryParamName of canonicalQueries ) {
153
- if ( queryParamName in currentRouteQueryParams ) {
154
- const queryParamValue = currentRouteQueryParams [ queryParamName ]
155
-
156
- if ( isArray ( queryParamValue ) ) {
157
- queryParamValue . forEach ( v => params . append ( queryParamName , v || '' ) )
158
- } else {
159
- params . append ( queryParamName , queryParamValue || '' )
160
- }
161
- }
162
- }
163
-
164
- const queryString = params . toString ( )
165
- if ( queryString ) {
166
- href = `${ href } ?${ queryString } `
170
+ const canonicalQueryParams = getCanonicalQueryParams ( common , seo )
171
+ if ( canonicalQueryParams ) {
172
+ href = `${ href } ?${ canonicalQueryParams } `
167
173
}
168
174
169
175
return href
@@ -181,6 +187,32 @@ export function getCanonicalLink(
181
187
return [ { [ key ] : 'i18n-can' , rel : 'canonical' , href } ]
182
188
}
183
189
190
+ export function getCanonicalQueryParams ( common : CommonComposableOptions , seo : I18nHeadOptions [ 'seo' ] ) {
191
+ const route = common . router . currentRoute . value
192
+ const currentRoute = localeRoute ( common , {
193
+ ...route ,
194
+ path : undefined ,
195
+ name : getRouteBaseName ( common , route )
196
+ } )
197
+
198
+ const canonicalQueries = ( isObject ( seo ) && seo . canonicalQueries ) || [ ]
199
+ const currentRouteQueryParams = currentRoute ?. query || { }
200
+ const params = new URLSearchParams ( )
201
+ for ( const queryParamName of canonicalQueries ) {
202
+ if ( queryParamName in currentRouteQueryParams ) {
203
+ const queryParamValue = currentRouteQueryParams [ queryParamName ]
204
+
205
+ if ( isArray ( queryParamValue ) ) {
206
+ queryParamValue . forEach ( v => params . append ( queryParamName , v || '' ) )
207
+ } else {
208
+ params . append ( queryParamName , queryParamValue || '' )
209
+ }
210
+ }
211
+ }
212
+
213
+ return params . toString ( ) || undefined
214
+ }
215
+
184
216
export function getOgUrl (
185
217
common : CommonComposableOptions ,
186
218
key : NonNullable < I18nHeadOptions [ 'key' ] > ,
0 commit comments