Skip to content

Commit b239535

Browse files
authored
refactor: use context object for i18n head functions (#3353)
1 parent addcb54 commit b239535

File tree

2 files changed

+96
-96
lines changed

2 files changed

+96
-96
lines changed

src/runtime/composables/index.ts

+16-19
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { useRequestHeaders, useCookie as useNuxtCookie, useNuxtApp } from '#imports'
2-
import { ref, computed, watch, onUnmounted, unref } from 'vue'
1+
import { useRequestHeaders, useCookie as useNuxtCookie } from '#imports'
2+
import { ref, computed, watch, onUnmounted } from 'vue'
33
import { parseAcceptLanguage, wrapComposable, runtimeDetectBrowserLanguage } from '../internal'
44
import { DEFAULT_DYNAMIC_PARAMS_KEY, localeCodes, normalizedLocales } from '#build/i18n.options.mjs'
55
import { getActiveHead } from 'unhead'
6-
import { getNormalizedLocales, initCommonComposableOptions } from '../utils'
6+
import { initCommonComposableOptions } from '../utils'
77
import {
8+
creatHeadContext,
89
getAlternateOgLocales,
910
getCanonicalLink,
1011
getCurrentOgLocale,
@@ -18,7 +19,7 @@ import { getComposer } from '../compatibility'
1819
import type { Ref } from 'vue'
1920
import type { Locale } from 'vue-i18n'
2021
import type { resolveRoute } from '../routing/routing'
21-
import type { I18nHeadMetaInfo, I18nHeadOptions, LocaleObject, SeoAttributesOptions } from '#internal-i18n-types'
22+
import type { I18nHeadMetaInfo, I18nHeadOptions, SeoAttributesOptions } from '#internal-i18n-types'
2223
import type { HeadParam } from '../utils'
2324
import type { RouteLocationAsRelativeI18n, RouteLocationRaw, RouteLocationResolvedI18n, RouteMapI18n } from 'vue-router'
2425

@@ -41,12 +42,12 @@ export type SetI18nParamsFunction = (params: Partial<Record<Locale, unknown>>) =
4142
*/
4243
export function useSetI18nParams(seo?: SeoAttributesOptions): SetI18nParamsFunction {
4344
const common = initCommonComposableOptions()
44-
const nuxtApp = useNuxtApp()
4545
const head = getActiveHead()
4646
const router = common.router
4747

48-
const locale = unref(nuxtApp.$i18n.locale)
49-
const locales = getNormalizedLocales(unref(nuxtApp.$i18n.locales))
48+
// @ts-expect-error accepts more
49+
// Hard code to 'id', this is used to replace payload before ssr response
50+
const ctx = creatHeadContext({ key: 'id', seo })
5051
const _i18nParams = ref({})
5152
const experimentalSSR = common.runtimeConfig.public.i18n.experimental.switchLocalePathLinkSSR
5253

@@ -74,9 +75,7 @@ export function useSetI18nParams(seo?: SeoAttributesOptions): SetI18nParamsFunct
7475
stop()
7576
})
7677

77-
const currentLocale: LocaleObject = getNormalizedLocales(locales).find(l => l.code === locale) || { code: locale }
78-
79-
if (!unref(nuxtApp.$i18n.baseUrl)) {
78+
if (!ctx.baseUrl) {
8079
console.warn('I18n `baseUrl` is required to generate valid SEO tag links.')
8180
}
8281

@@ -87,20 +86,18 @@ export function useSetI18nParams(seo?: SeoAttributesOptions): SetI18nParamsFunct
8786
}
8887

8988
// Adding SEO Meta
90-
if (locale && unref(nuxtApp.$i18n.locales)) {
91-
// Hard code to 'id', this is used to replace payload before ssr response
92-
const key = 'id'
93-
89+
if (ctx.locale && ctx.locales) {
9490
// prettier-ignore
9591
metaObject.link.push(
96-
...getHreflangLinks(common, locales, key, seo),
97-
...getCanonicalLink(common, key, seo)
92+
...getHreflangLinks(common, ctx),
93+
...getCanonicalLink(common, ctx)
9894
)
9995

96+
// prettier-ignore
10097
metaObject.meta.push(
101-
...getOgUrl(common, key, seo),
102-
...getCurrentOgLocale(currentLocale, currentLocale.language, key),
103-
...getAlternateOgLocales(locales, currentLocale.language, key)
98+
...getOgUrl(common, ctx),
99+
...getCurrentOgLocale(ctx),
100+
...getAlternateOgLocales(ctx)
104101
)
105102
}
106103

src/runtime/routing/head.ts

+80-77
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,44 @@ import { getRouteBaseName, localeRoute, switchLocalePath } from './routing'
77
import { getComposer } from '../compatibility'
88

99
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'
1117
import type { CommonComposableOptions } from '../utils'
1218

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+
1348
/**
1449
* Returns localized head properties for locale-related aspects.
1550
*
@@ -24,50 +59,44 @@ export function localeHead(
2459
common: CommonComposableOptions,
2560
{ dir = true, lang = true, seo = true, key = 'hid' }: I18nHeadOptions
2661
): I18nHeadMetaInfo {
27-
const { defaultDirection } = useRuntimeConfig().public.i18n
28-
const nuxtApp = useNuxtApp()
29-
3062
const metaObject: Required<I18nHeadMetaInfo> = {
3163
htmlAttrs: {},
3264
link: [],
3365
meta: []
3466
}
3567

36-
const i18nBaseUrl = unref(nuxtApp.$i18n.baseUrl)
37-
if (!i18nBaseUrl) {
68+
const ctx = creatHeadContext({ seo, key })
69+
if (!ctx.baseUrl) {
3870
console.warn('I18n `baseUrl` is required to generate valid SEO tag links.')
3971
}
4072

4173
// 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) {
4375
return metaObject
4476
}
4577

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-
5478
// Adding Direction Attribute
5579
if (dir) {
56-
metaObject.htmlAttrs.dir = currentDir
80+
metaObject.htmlAttrs.dir = ctx.currentDir
5781
}
5882

59-
if (lang && currentLanguage) {
60-
metaObject.htmlAttrs.lang = currentLanguage
83+
if (lang && ctx.currentLanguage) {
84+
metaObject.htmlAttrs.lang = ctx.currentLanguage
6185
}
6286

6387
// 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+
)
6694

95+
// prettier-ignore
6796
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)
71100
)
72101
}
73102

@@ -80,33 +109,25 @@ function getBaseUrl() {
80109
return joinURL(unref(i18n.baseUrl), nuxtApp.$config.app.baseURL)
81110
}
82111

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) {
90113
const { defaultLocale, strategy } = useRuntimeConfig().public.i18n
91114
const links: MetaAttrs[] = []
92115

93116
if (strategy === 'no_prefix') return links
94117

95118
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) {
100121
console.warn('Locale `language` ISO code is required to generate alternate link')
101122
continue
102123
}
103124

104-
const [language, region] = localeLanguage.split('-')
125+
const [language, region] = locale.language.split('-')
105126
if (language && region && (locale.isCatchallLocale || !localeMap.has(language))) {
106127
localeMap.set(language, locale)
107128
}
108129

109-
localeMap.set(localeLanguage, locale)
130+
localeMap.set(locale.language, locale)
110131
}
111132

112133
const strictCanonicals = common.runtimeConfig.public.i18n.experimental.alternateLinkCanonicalQueries === true
@@ -119,15 +140,15 @@ export function getHreflangLinks(
119140

120141
for (const [language, mapLocale] of localeMap.entries()) {
121142
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)
124145
if (canonicalQueryParams && strictCanonicals) {
125146
href = `${href}?${canonicalQueryParams}`
126147
}
127148

128149
if (localePath) {
129150
links.push({
130-
[key]: `i18n-alt-${language}`,
151+
[ctx.key]: `i18n-alt-${language}`,
131152
rel: 'alternate',
132153
href: href,
133154
hreflang: language
@@ -137,15 +158,15 @@ export function getHreflangLinks(
137158

138159
if (defaultLocale) {
139160
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)
142163
if (canonicalQueryParams && strictCanonicals) {
143164
href = `${href}?${canonicalQueryParams}`
144165
}
145166

146167
if (localePath) {
147168
links.push({
148-
[key]: 'i18n-xd',
169+
[ctx.key]: 'i18n-xd',
149170
rel: 'alternate',
150171
href: href,
151172
hreflang: 'x-default'
@@ -156,7 +177,7 @@ export function getHreflangLinks(
156177
return links
157178
}
158179

159-
export function getCanonicalUrl(common: CommonComposableOptions, baseUrl: string, seo: I18nHeadOptions['seo']) {
180+
export function getCanonicalUrl(common: CommonComposableOptions, ctx: HeadContext) {
160181
const route = common.router.currentRoute.value
161182
const currentRoute = localeRoute(common, {
162183
...route,
@@ -165,37 +186,32 @@ export function getCanonicalUrl(common: CommonComposableOptions, baseUrl: string
165186
})
166187

167188
if (!currentRoute) return ''
168-
let href = toAbsoluteUrl(currentRoute.path, baseUrl)
189+
let href = toAbsoluteUrl(currentRoute.path, ctx.baseUrl)
169190

170-
const canonicalQueryParams = getCanonicalQueryParams(common, seo)
191+
const canonicalQueryParams = getCanonicalQueryParams(common, ctx)
171192
if (canonicalQueryParams) {
172193
href = `${href}?${canonicalQueryParams}`
173194
}
174195

175196
return href
176197
}
177198

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)
185201
if (!href) return []
186202

187-
return [{ [key]: 'i18n-can', rel: 'canonical', href }]
203+
return [{ [ctx.key]: 'i18n-can', rel: 'canonical', href }]
188204
}
189205

190-
export function getCanonicalQueryParams(common: CommonComposableOptions, seo: I18nHeadOptions['seo']) {
206+
export function getCanonicalQueryParams(common: CommonComposableOptions, ctx: HeadContext) {
191207
const route = common.router.currentRoute.value
192208
const currentRoute = localeRoute(common, {
193209
...route,
194210
path: undefined,
195211
name: getRouteBaseName(common, route)
196212
})
197213

198-
const canonicalQueries = (isObject(seo) && seo.canonicalQueries) || []
214+
const canonicalQueries = (isObject(ctx.seo) && ctx.seo.canonicalQueries) || []
199215
const currentRouteQueryParams = currentRoute?.query || {}
200216
const params = new URLSearchParams()
201217
for (const queryParamName of canonicalQueries) {
@@ -213,38 +229,25 @@ export function getCanonicalQueryParams(common: CommonComposableOptions, seo: I1
213229
return params.toString() || undefined
214230
}
215231

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)
223234
if (!href) return []
224235

225-
return [{ [key]: 'i18n-og-url', property: 'og:url', content: href }]
236+
return [{ [ctx.key]: 'i18n-og-url', property: 'og:url', content: href }]
226237
}
227238

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 []
234241

235242
// 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) }]
237244
}
238245

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)
245248

246249
return alternateLocales.map(locale => ({
247-
[key]: `i18n-og-alt-${locale.language}`,
250+
[ctx.key]: `i18n-og-alt-${locale.language}`,
248251
property: 'og:locale:alternate',
249252
content: hyphenToUnderscore(locale.language)
250253
}))

0 commit comments

Comments
 (0)