diff --git a/packages/vue-i18n-routing/src/compatibles/head.ts b/packages/vue-i18n-routing/src/compatibles/head.ts index 6e0511ac..3a9f7888 100644 --- a/packages/vue-i18n-routing/src/compatibles/head.ts +++ b/packages/vue-i18n-routing/src/compatibles/head.ts @@ -11,7 +11,7 @@ import type { I18nHeadOptions, I18nHeadMetaInfo, MetaAttrs, RoutingProxy } from export function localeHead( this: RoutingProxy, - { addDirAttribute = false, addSeoAttributes = false }: I18nHeadOptions = {} + { addDirAttribute = false, addSeoAttributes = false, identifierAttribute = 'hid' }: I18nHeadOptions = {} ): I18nHeadMetaInfo { const router = this.router const i18n = this.i18n @@ -46,16 +46,22 @@ export function localeHead( metaObject.htmlAttrs.lang = currentLocaleIso } - addHreflangLinks.call(this, locales as LocaleObject[], i18n.__baseUrl, metaObject.link) - addCanonicalLinks.call(this, i18n.__baseUrl, metaObject.link, addSeoAttributes) - addCurrentOgLocale(currentLocale, currentLocaleIso, metaObject.meta) - addAlternateOgLocales(locales as LocaleObject[], currentLocaleIso, metaObject.meta) + addHreflangLinks.call(this, locales as LocaleObject[], i18n.__baseUrl, metaObject.link, identifierAttribute) + addCanonicalLinks.call(this, i18n.__baseUrl, metaObject.link, identifierAttribute, addSeoAttributes) + addCurrentOgLocale(currentLocale, currentLocaleIso, metaObject.meta, identifierAttribute) + addAlternateOgLocales(locales as LocaleObject[], currentLocaleIso, metaObject.meta, identifierAttribute) } return metaObject } -function addHreflangLinks(this: RoutingProxy, locales: LocaleObject[], baseUrl: string, link: MetaAttrs) { +function addHreflangLinks( + this: RoutingProxy, + locales: LocaleObject[], + baseUrl: string, + link: MetaAttrs, + identifierAttribute: NonNullable +) { const router = this.router const { defaultLocale, strategy } = getI18nRoutingOptions(router, this) if (strategy === STRATEGIES.NO_PREFIX) { @@ -84,7 +90,7 @@ function addHreflangLinks(this: RoutingProxy, locales: LocaleObject[], baseUrl: const localePath = switchLocalePath.call(this, mapLocale.code) if (localePath) { link.push({ - hid: `i18n-alt-${iso}`, + [identifierAttribute]: `i18n-alt-${iso}`, rel: 'alternate', href: toAbsoluteUrl(localePath, baseUrl), hreflang: iso @@ -96,7 +102,7 @@ function addHreflangLinks(this: RoutingProxy, locales: LocaleObject[], baseUrl: const localePath = switchLocalePath.call(this, defaultLocale) if (localePath) { link.push({ - hid: 'i18n-xd', + [identifierAttribute]: 'i18n-xd', rel: 'alternate', href: toAbsoluteUrl(localePath, baseUrl), hreflang: 'x-default' @@ -109,6 +115,7 @@ function addCanonicalLinks( this: RoutingProxy, baseUrl: string, link: MetaAttrs, + identifierAttribute: NonNullable, seoAttributesOptions: I18nHeadOptions['addSeoAttributes'] ) { const route = this.route @@ -145,14 +152,19 @@ function addCanonicalLinks( } link.push({ - hid: 'i18n-can', + [identifierAttribute]: 'i18n-can', rel: 'canonical', href }) } } -function addCurrentOgLocale(currentLocale: LocaleObject, currentLocaleIso: string | undefined, meta: MetaAttrs) { +function addCurrentOgLocale( + currentLocale: LocaleObject, + currentLocaleIso: string | undefined, + meta: MetaAttrs, + identifierAttribute: NonNullable +) { const hasCurrentLocaleAndIso = currentLocale && currentLocaleIso if (!hasCurrentLocaleAndIso) { @@ -160,14 +172,19 @@ function addCurrentOgLocale(currentLocale: LocaleObject, currentLocaleIso: strin } meta.push({ - hid: 'i18n-og', + [identifierAttribute]: 'i18n-og', property: 'og:locale', // Replace dash with underscore as defined in spec: language_TERRITORY content: hypenToUnderscore(currentLocaleIso) }) } -function addAlternateOgLocales(locales: LocaleObject[], currentLocaleIso: string | undefined, meta: MetaAttrs) { +function addAlternateOgLocales( + locales: LocaleObject[], + currentLocaleIso: string | undefined, + meta: MetaAttrs, + identifierAttribute: NonNullable +) { const localesWithoutCurrent = locales.filter(locale => { const localeIso = locale.iso return localeIso && localeIso !== currentLocaleIso @@ -175,7 +192,7 @@ function addAlternateOgLocales(locales: LocaleObject[], currentLocaleIso: string if (localesWithoutCurrent.length) { const alternateLocales = localesWithoutCurrent.map(locale => ({ - hid: `i18n-og-alt-${locale.iso}`, + [identifierAttribute]: `i18n-og-alt-${locale.iso}`, property: 'og:locale:alternate', content: hypenToUnderscore(locale.iso!) })) diff --git a/packages/vue-i18n-routing/src/compatibles/types.ts b/packages/vue-i18n-routing/src/compatibles/types.ts index 1adff61c..64ce29d0 100644 --- a/packages/vue-i18n-routing/src/compatibles/types.ts +++ b/packages/vue-i18n-routing/src/compatibles/types.ts @@ -53,6 +53,12 @@ export interface I18nHeadOptions { * @defaultValue false */ addSeoAttributes?: boolean | SeoAttributesOptions + /** + * Identifier attribute of `` tag + * + * @defaultValue 'hid' + */ + identifierAttribute?: string } export type MetaAttrs = Record diff --git a/packages/vue-i18n-routing/src/composables/__test__/__snapshots__/head.test.ts.snap b/packages/vue-i18n-routing/src/composables/__test__/__snapshots__/head.test.ts.snap index 14efe43a..6e8fd1fa 100644 --- a/packages/vue-i18n-routing/src/composables/__test__/__snapshots__/head.test.ts.snap +++ b/packages/vue-i18n-routing/src/composables/__test__/__snapshots__/head.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1 -exports[`useLocaleHead > should be worked > en 1`] = ` +exports[`useLocaleHead > basic > should be worked > en 1`] = ` { "htmlAttrs": { "dir": "ltr", @@ -52,7 +52,7 @@ exports[`useLocaleHead > should be worked > en 1`] = ` } `; -exports[`useLocaleHead > should be worked > ja 1`] = ` +exports[`useLocaleHead > basic > should be worked > ja 1`] = ` { "htmlAttrs": { "dir": "ltr", diff --git a/packages/vue-i18n-routing/src/composables/__test__/head.test.ts b/packages/vue-i18n-routing/src/composables/__test__/head.test.ts index ac745137..c11d78eb 100644 --- a/packages/vue-i18n-routing/src/composables/__test__/head.test.ts +++ b/packages/vue-i18n-routing/src/composables/__test__/head.test.ts @@ -7,46 +7,92 @@ import { createRouter } from '../../extends/router' import { useLocaleHead } from '../head' describe('useLocaleHead', () => { - it('should be worked', async () => { - const i18n = createI18n({ legacy: false, locale: 'en' }) - const router = createRouter(i18n, { - version: 4, - locales: [ - { - code: 'en', - iso: 'en-US', - dir: 'ltr' - }, - { - code: 'ja', - iso: 'ja-JP' + describe('basic', () => { + it('should be worked', async () => { + const i18n = createI18n({ legacy: false, locale: 'en' }) + const router = createRouter(i18n, { + version: 4, + locales: [ + { + code: 'en', + iso: 'en-US', + dir: 'ltr' + }, + { + code: 'ja', + iso: 'ja-JP' + } + ], + baseUrl: 'http://localhost:8080', + routes: [ + { path: '/', name: 'index', component: { template: '
index
' } }, + { path: '/about', name: 'about', component: { template: '
About
' } }, + { path: '/:pathMatch(.*)*', name: 'not-found', component: { template: '
Not Found
' } } + ], + history: createMemoryHistory() + }) + await router.push('/en/about') + + const vm = useSetup(() => { + const route = useRoute() + const router = useRouter() + const i18n = useI18n() + const head = useLocaleHead({ addDirAttribute: true, addSeoAttributes: true, route, router, i18n }) + expect(head.value).toMatchSnapshot(i18n.locale.value) + assert.equal(head.value.htmlAttrs!.lang, 'en-US') + return { + i18n, + head } - ], - baseUrl: 'http://localhost:8080', - routes: [ - { path: '/', name: 'index', component: { template: '
index
' } }, - { path: '/about', name: 'about', component: { template: '
About
' } }, - { path: '/:pathMatch(.*)*', name: 'not-found', component: { template: '
Not Found
' } } - ], - history: createMemoryHistory() + }, [router, i18n]) + + await router.push('/ja') + expect(vm.head).toMatchSnapshot(vm.i18n.locale.value) + assert.equal(vm.head.htmlAttrs!.lang, 'ja-JP') }) - await router.push('/en/about') + }) - const vm = useSetup(() => { - const route = useRoute() - const router = useRouter() - const i18n = useI18n() - const head = useLocaleHead({ addDirAttribute: true, addSeoAttributes: true, route, router, i18n }) - expect(head.value).toMatchSnapshot(i18n.locale.value) - assert.equal(head.value.htmlAttrs!.lang, 'en-US') - return { - i18n, - head - } - }, [router, i18n]) + describe('identifierAttribute option', () => { + it('should be worked', async () => { + const i18n = createI18n({ legacy: false, locale: 'en' }) + const router = createRouter(i18n, { + version: 4, + locales: [ + { + code: 'en', + iso: 'en-US', + dir: 'ltr' + }, + { + code: 'ja', + iso: 'ja-JP' + } + ], + baseUrl: 'http://localhost:8080', + routes: [ + { path: '/', name: 'index', component: { template: '
index
' } }, + { path: '/about', name: 'about', component: { template: '
About
' } }, + { path: '/:pathMatch(.*)*', name: 'not-found', component: { template: '
Not Found
' } } + ], + history: createMemoryHistory() + }) + await router.push('/en/about') - await router.push('/ja') - expect(vm.head).toMatchSnapshot(vm.i18n.locale.value) - assert.equal(vm.head.htmlAttrs!.lang, 'ja-JP') + const vm = useSetup(() => { + const route = useRoute() + const router = useRouter() + const i18n = useI18n() + const head = useLocaleHead({ addSeoAttributes: true, identifierAttribute: 'key', route, router, i18n }) + return { + i18n, + head + } + }, [router, i18n]) + + await router.push('/ja') + for (const m of vm.head.meta || []) { + expect(m).toHaveProperty('key') + } + }) }) }) diff --git a/packages/vue-i18n-routing/src/composables/head.ts b/packages/vue-i18n-routing/src/composables/head.ts index d7522582..b6c9aff7 100644 --- a/packages/vue-i18n-routing/src/composables/head.ts +++ b/packages/vue-i18n-routing/src/composables/head.ts @@ -26,6 +26,7 @@ import type { Ref } from 'vue-demi' export function useLocaleHead({ addDirAttribute = false, addSeoAttributes = false, + identifierAttribute = 'hid', strategy = undefined, defaultLocale = undefined, route = useRoute(), @@ -60,7 +61,7 @@ export function useLocaleHead({ defaultLocale, strategy }, - [{ addDirAttribute, addSeoAttributes }] + [{ addDirAttribute, addSeoAttributes, identifierAttribute }] ) as I18nHeadMetaInfo }