@@ -7,9 +7,44 @@ import { getRouteBaseName, localeRoute, switchLocalePath } from './routing'
7
7
import { getComposer } from '../compatibility'
8
8
9
9
import type { I18n } from 'vue-i18n'
10
- import type { I18nHeadMetaInfo , MetaAttrs , LocaleObject , I18nHeadOptions } from '#internal-i18n-types'
10
+ import type {
11
+ I18nHeadMetaInfo ,
12
+ MetaAttrs ,
13
+ LocaleObject ,
14
+ I18nHeadOptions ,
15
+ SeoAttributesOptions
16
+ } from '#internal-i18n-types'
11
17
import type { CommonComposableOptions } from '../utils'
12
18
19
+ export type HeadContext = {
20
+ key : string
21
+ seo : boolean | SeoAttributesOptions | undefined
22
+ currentDir : string
23
+ currentLocale : LocaleObject < string >
24
+ currentLanguage : string | undefined
25
+ baseUrl : string
26
+ locale : string
27
+ locales : LocaleObject < string > [ ]
28
+ }
29
+
30
+ export function creatHeadContext ( { key, seo } : Required < Pick < I18nHeadOptions , 'key' | 'seo' > > ) : HeadContext {
31
+ const nuxtApp = useNuxtApp ( )
32
+ const { defaultDirection } = useRuntimeConfig ( ) . public . i18n
33
+ const locale = unref ( nuxtApp . $i18n . locale )
34
+ const locales = getNormalizedLocales ( unref ( nuxtApp . $i18n . locales ) )
35
+ const currentLocale : LocaleObject = locales . find ( l => l . code === locale ) || { code : locale }
36
+ return {
37
+ key,
38
+ seo,
39
+ locale,
40
+ locales,
41
+ currentDir : currentLocale . dir || defaultDirection ,
42
+ currentLocale,
43
+ currentLanguage : currentLocale . language ,
44
+ baseUrl : getBaseUrl ( )
45
+ }
46
+ }
47
+
13
48
/**
14
49
* Returns localized head properties for locale-related aspects.
15
50
*
@@ -24,50 +59,44 @@ export function localeHead(
24
59
common : CommonComposableOptions ,
25
60
{ dir = true , lang = true , seo = true , key = 'hid' } : I18nHeadOptions
26
61
) : I18nHeadMetaInfo {
27
- const { defaultDirection } = useRuntimeConfig ( ) . public . i18n
28
- const nuxtApp = useNuxtApp ( )
29
-
30
62
const metaObject : Required < I18nHeadMetaInfo > = {
31
63
htmlAttrs : { } ,
32
64
link : [ ] ,
33
65
meta : [ ]
34
66
}
35
67
36
- const i18nBaseUrl = unref ( nuxtApp . $i18n . baseUrl )
37
- if ( ! i18nBaseUrl ) {
68
+ const ctx = creatHeadContext ( { seo , key } )
69
+ if ( ! ctx . baseUrl ) {
38
70
console . warn ( 'I18n `baseUrl` is required to generate valid SEO tag links.' )
39
71
}
40
72
41
73
// skip if no locales or baseUrl is set
42
- if ( unref ( nuxtApp . $i18n . locales ) == null || i18nBaseUrl == null ) {
74
+ if ( ctx . locales == null || ctx . baseUrl == null ) {
43
75
return metaObject
44
76
}
45
77
46
- const locale = unref ( nuxtApp . $i18n . locale )
47
- const locales = unref ( nuxtApp . $i18n . locales )
48
- const currentLocale : LocaleObject = getNormalizedLocales ( locales ) . find ( l => l . code === locale ) || {
49
- code : locale
50
- }
51
- const currentLanguage = currentLocale . language
52
- const currentDir = currentLocale . dir || defaultDirection
53
-
54
78
// Adding Direction Attribute
55
79
if ( dir ) {
56
- metaObject . htmlAttrs . dir = currentDir
80
+ metaObject . htmlAttrs . dir = ctx . currentDir
57
81
}
58
82
59
- if ( lang && currentLanguage ) {
60
- metaObject . htmlAttrs . lang = currentLanguage
83
+ if ( lang && ctx . currentLanguage ) {
84
+ metaObject . htmlAttrs . lang = ctx . currentLanguage
61
85
}
62
86
63
87
// Adding SEO Meta
64
- if ( seo && locale && unref ( nuxtApp . $i18n . locales ) ) {
65
- metaObject . link . push ( ...getHreflangLinks ( common , locales , key , seo ) , ...getCanonicalLink ( common , key , seo ) )
88
+ if ( seo && ctx . locale && ctx . locales ) {
89
+ // prettier-ignore
90
+ metaObject . link . push (
91
+ ...getHreflangLinks ( common , ctx ) ,
92
+ ...getCanonicalLink ( common , ctx )
93
+ )
66
94
95
+ // prettier-ignore
67
96
metaObject . meta . push (
68
- ...getOgUrl ( common , key , seo ) ,
69
- ...getCurrentOgLocale ( currentLocale , currentLanguage , key ) ,
70
- ...getAlternateOgLocales ( locales , currentLanguage , key )
97
+ ...getOgUrl ( common , ctx ) ,
98
+ ...getCurrentOgLocale ( ctx ) ,
99
+ ...getAlternateOgLocales ( ctx )
71
100
)
72
101
}
73
102
@@ -80,33 +109,25 @@ function getBaseUrl() {
80
109
return joinURL ( unref ( i18n . baseUrl ) , nuxtApp . $config . app . baseURL )
81
110
}
82
111
83
- export function getHreflangLinks (
84
- common : CommonComposableOptions ,
85
- locales : LocaleObject [ ] ,
86
- key : NonNullable < I18nHeadOptions [ 'key' ] > ,
87
- seo : I18nHeadOptions [ 'seo' ]
88
- ) {
89
- const baseUrl = getBaseUrl ( )
112
+ export function getHreflangLinks ( common : CommonComposableOptions , ctx : HeadContext ) {
90
113
const { defaultLocale, strategy } = useRuntimeConfig ( ) . public . i18n
91
114
const links : MetaAttrs [ ] = [ ]
92
115
93
116
if ( strategy === 'no_prefix' ) return links
94
117
95
118
const localeMap = new Map < string , LocaleObject > ( )
96
- for ( const locale of locales ) {
97
- const localeLanguage = locale . language
98
-
99
- if ( ! localeLanguage ) {
119
+ for ( const locale of ctx . locales ) {
120
+ if ( ! locale . language ) {
100
121
console . warn ( 'Locale `language` ISO code is required to generate alternate link' )
101
122
continue
102
123
}
103
124
104
- const [ language , region ] = localeLanguage . split ( '-' )
125
+ const [ language , region ] = locale . language . split ( '-' )
105
126
if ( language && region && ( locale . isCatchallLocale || ! localeMap . has ( language ) ) ) {
106
127
localeMap . set ( language , locale )
107
128
}
108
129
109
- localeMap . set ( localeLanguage , locale )
130
+ localeMap . set ( locale . language , locale )
110
131
}
111
132
112
133
const strictCanonicals = common . runtimeConfig . public . i18n . experimental . alternateLinkCanonicalQueries === true
@@ -119,15 +140,15 @@ export function getHreflangLinks(
119
140
120
141
for ( const [ language , mapLocale ] of localeMap . entries ( ) ) {
121
142
const localePath = switchLocalePath ( common , mapLocale . code , routeWithoutQuery )
122
- const canonicalQueryParams = getCanonicalQueryParams ( common , seo )
123
- let href = toAbsoluteUrl ( localePath , baseUrl )
143
+ const canonicalQueryParams = getCanonicalQueryParams ( common , ctx )
144
+ let href = toAbsoluteUrl ( localePath , ctx . baseUrl )
124
145
if ( canonicalQueryParams && strictCanonicals ) {
125
146
href = `${ href } ?${ canonicalQueryParams } `
126
147
}
127
148
128
149
if ( localePath ) {
129
150
links . push ( {
130
- [ key ] : `i18n-alt-${ language } ` ,
151
+ [ ctx . key ] : `i18n-alt-${ language } ` ,
131
152
rel : 'alternate' ,
132
153
href : href ,
133
154
hreflang : language
@@ -137,15 +158,15 @@ export function getHreflangLinks(
137
158
138
159
if ( defaultLocale ) {
139
160
const localePath = switchLocalePath ( common , defaultLocale , routeWithoutQuery )
140
- const canonicalQueryParams = getCanonicalQueryParams ( common , seo )
141
- let href = toAbsoluteUrl ( localePath , baseUrl )
161
+ const canonicalQueryParams = getCanonicalQueryParams ( common , ctx )
162
+ let href = toAbsoluteUrl ( localePath , ctx . baseUrl )
142
163
if ( canonicalQueryParams && strictCanonicals ) {
143
164
href = `${ href } ?${ canonicalQueryParams } `
144
165
}
145
166
146
167
if ( localePath ) {
147
168
links . push ( {
148
- [ key ] : 'i18n-xd' ,
169
+ [ ctx . key ] : 'i18n-xd' ,
149
170
rel : 'alternate' ,
150
171
href : href ,
151
172
hreflang : 'x-default'
@@ -156,7 +177,7 @@ export function getHreflangLinks(
156
177
return links
157
178
}
158
179
159
- export function getCanonicalUrl ( common : CommonComposableOptions , baseUrl : string , seo : I18nHeadOptions [ 'seo' ] ) {
180
+ export function getCanonicalUrl ( common : CommonComposableOptions , ctx : HeadContext ) {
160
181
const route = common . router . currentRoute . value
161
182
const currentRoute = localeRoute ( common , {
162
183
...route ,
@@ -165,37 +186,32 @@ export function getCanonicalUrl(common: CommonComposableOptions, baseUrl: string
165
186
} )
166
187
167
188
if ( ! currentRoute ) return ''
168
- let href = toAbsoluteUrl ( currentRoute . path , baseUrl )
189
+ let href = toAbsoluteUrl ( currentRoute . path , ctx . baseUrl )
169
190
170
- const canonicalQueryParams = getCanonicalQueryParams ( common , seo )
191
+ const canonicalQueryParams = getCanonicalQueryParams ( common , ctx )
171
192
if ( canonicalQueryParams ) {
172
193
href = `${ href } ?${ canonicalQueryParams } `
173
194
}
174
195
175
196
return href
176
197
}
177
198
178
- export function getCanonicalLink (
179
- common : CommonComposableOptions ,
180
- key : NonNullable < I18nHeadOptions [ 'key' ] > ,
181
- seo : I18nHeadOptions [ 'seo' ]
182
- ) {
183
- const baseUrl = getBaseUrl ( )
184
- const href = getCanonicalUrl ( common , baseUrl , seo )
199
+ export function getCanonicalLink ( common : CommonComposableOptions , ctx : HeadContext ) {
200
+ const href = getCanonicalUrl ( common , ctx )
185
201
if ( ! href ) return [ ]
186
202
187
- return [ { [ key ] : 'i18n-can' , rel : 'canonical' , href } ]
203
+ return [ { [ ctx . key ] : 'i18n-can' , rel : 'canonical' , href } ]
188
204
}
189
205
190
- export function getCanonicalQueryParams ( common : CommonComposableOptions , seo : I18nHeadOptions [ 'seo' ] ) {
206
+ export function getCanonicalQueryParams ( common : CommonComposableOptions , ctx : HeadContext ) {
191
207
const route = common . router . currentRoute . value
192
208
const currentRoute = localeRoute ( common , {
193
209
...route ,
194
210
path : undefined ,
195
211
name : getRouteBaseName ( common , route )
196
212
} )
197
213
198
- const canonicalQueries = ( isObject ( seo ) && seo . canonicalQueries ) || [ ]
214
+ const canonicalQueries = ( isObject ( ctx . seo ) && ctx . seo . canonicalQueries ) || [ ]
199
215
const currentRouteQueryParams = currentRoute ?. query || { }
200
216
const params = new URLSearchParams ( )
201
217
for ( const queryParamName of canonicalQueries ) {
@@ -213,38 +229,25 @@ export function getCanonicalQueryParams(common: CommonComposableOptions, seo: I1
213
229
return params . toString ( ) || undefined
214
230
}
215
231
216
- export function getOgUrl (
217
- common : CommonComposableOptions ,
218
- key : NonNullable < I18nHeadOptions [ 'key' ] > ,
219
- seo : I18nHeadOptions [ 'seo' ]
220
- ) {
221
- const baseUrl = getBaseUrl ( )
222
- const href = getCanonicalUrl ( common , baseUrl , seo )
232
+ export function getOgUrl ( common : CommonComposableOptions , ctx : HeadContext ) {
233
+ const href = getCanonicalUrl ( common , ctx )
223
234
if ( ! href ) return [ ]
224
235
225
- return [ { [ key ] : 'i18n-og-url' , property : 'og:url' , content : href } ]
236
+ return [ { [ ctx . key ] : 'i18n-og-url' , property : 'og:url' , content : href } ]
226
237
}
227
238
228
- export function getCurrentOgLocale (
229
- currentLocale : LocaleObject ,
230
- currentLanguage : string | undefined ,
231
- key : NonNullable < I18nHeadOptions [ 'key' ] >
232
- ) {
233
- if ( ! currentLocale || ! currentLanguage ) return [ ]
239
+ export function getCurrentOgLocale ( ctx : HeadContext ) {
240
+ if ( ! ctx . currentLocale || ! ctx . currentLanguage ) return [ ]
234
241
235
242
// Replace dash with underscore as defined in spec: language_TERRITORY
236
- return [ { [ key ] : 'i18n-og' , property : 'og:locale' , content : hyphenToUnderscore ( currentLanguage ) } ]
243
+ return [ { [ ctx . key ] : 'i18n-og' , property : 'og:locale' , content : hyphenToUnderscore ( ctx . currentLanguage ) } ]
237
244
}
238
245
239
- export function getAlternateOgLocales (
240
- locales : LocaleObject [ ] ,
241
- currentLanguage : string | undefined ,
242
- key : NonNullable < I18nHeadOptions [ 'key' ] >
243
- ) {
244
- const alternateLocales = locales . filter ( locale => locale . language && locale . language !== currentLanguage )
246
+ export function getAlternateOgLocales ( ctx : HeadContext ) {
247
+ const alternateLocales = ctx . locales . filter ( locale => locale . language && locale . language !== ctx . currentLanguage )
245
248
246
249
return alternateLocales . map ( locale => ( {
247
- [ key ] : `i18n-og-alt-${ locale . language } ` ,
250
+ [ ctx . key ] : `i18n-og-alt-${ locale . language } ` ,
248
251
property : 'og:locale:alternate' ,
249
252
content : hyphenToUnderscore ( locale . language )
250
253
} ) )
0 commit comments