Skip to content

Commit 658df14

Browse files
authored
feat: support defaultDirection option (#1541)
1 parent 62133f3 commit 658df14

File tree

12 files changed

+431
-226
lines changed

12 files changed

+431
-226
lines changed

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ This todo is based on [nuxt/i18n](https://i18n.nuxtjs.org/) docs.
5555
- [x] getBrowserLocale
5656
- [x] finalizePendingLocaleChange
5757
- [x] waitForPendingLocaleChange
58-
- [ ] defaultDirection
58+
- [x] defaultDirection
5959
- [x] defaultLocale
6060
- [x] localeCodes
6161
- [x] locales

docs/content/40.options/2.routing.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ export default {
6464

6565
## `defaultDirection`
6666

67-
::alert{type="warning"}
68-
// TODO:
69-
🚧 This feature is not implemented yet.
70-
::
67+
- type: `string`
68+
- default: `ltr`
69+
70+
The app's default direction. Will only be used when `dir` is not specified.
7171

7272
## `defaultLocale`
7373

docs/content/50.API/2.vue-i18n.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,6 @@ Routing strategy as specified in options.
6363

6464
### defaultDirection
6565

66-
::alert{type="warning"}
67-
// TODO:
68-
🚧 This feature is not implemented yet.
69-
::
70-
7166
- **Type**: `Directions`
7267

7368
Default direction as specified in options.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@
9999
"eslint-config-prettier": "^8.5.0",
100100
"eslint-plugin-prettier": "^4.0.0",
101101
"gh-changelogen": "^0.2.6",
102-
"happy-dom": "^6.0.4",
103102
"jiti": "^1.14.0",
103+
"jsdom": "^20.0.0",
104104
"lint-staged": "^12.1.2",
105105
"npm-run-all": "^4.1.5",
106106
"nuxt": "^3.0.0-rc.9",

specs/fixtures/basic/pages/index.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import LangSwitcher from '../components/LangSwitcher.vue'
77
88
const { t } = useI18n()
99
const localePath = useLocalePath()
10-
const i18nHead = useLocaleHead({ addSeoAttributes: { canonicalQueries: ['page'] }, router: useRouter() })
10+
const i18nHead = useLocaleHead({
11+
addDirAttribute: true,
12+
addSeoAttributes: { canonicalQueries: ['page'] },
13+
router: useRouter()
14+
})
1115
const { data, refresh } = useAsyncData('home', () =>
1216
Promise.resolve({
1317
aboutPath: localePath('about'),
@@ -22,7 +26,8 @@ watchEffect(() => {
2226
useHead({
2327
title: t('home'),
2428
htmlAttrs: {
25-
lang: i18nHead.value.htmlAttrs!.lang
29+
lang: i18nHead.value.htmlAttrs!.lang,
30+
dir: i18nHead.value.htmlAttrs!.dir
2631
},
2732
link: [...(i18nHead.value.link || [])],
2833
meta: [...(i18nHead.value.meta || [])]

specs/fixtures/head/app.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import { useI18n, useLocaleHead } from '#i18n'
55
66
const route = useRoute()
77
const { t } = useI18n()
8-
const head = useLocaleHead({ addSeoAttributes: { canonicalQueries: ['page'] } })
8+
const head = useLocaleHead({ addDirAttribute: true, addSeoAttributes: { canonicalQueries: ['page'] } })
99
const title = computed(() => `Page - ${t(route.meta.title as string)}`)
1010
</script>
1111

1212
<template>
13-
<Html :lang="head.htmlAttrs.lang">
13+
<Html :lang="head.htmlAttrs.lang" :dir="head.htmlAttrs.dir">
1414
<Head>
1515
<Title>{{ title }}</Title>
1616
<template v-for="(link, index) in head.link" :key="index">

specs/helper.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Window } from 'happy-dom'
1+
import { JSDOM } from 'jsdom'
22

33
import type { Page } from 'playwright'
44

@@ -35,7 +35,28 @@ export async function assetLocaleHead(page: Page, headSelector: string) {
3535
}
3636

3737
export function getDom(html: string) {
38-
const window = new Window()
39-
window.document.body.innerHTML = html
40-
return window.document
38+
return new JSDOM(html).window.document
39+
}
40+
41+
export function getDataFromDom(dom: Document, selector: string) {
42+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
43+
return JSON.parse(dom.querySelector(selector)!.textContent!.replace('&quot;', '"'))
44+
}
45+
46+
export async function assertLocaleHeadWithDom(dom: Document, headSelector: string) {
47+
const localeHead = getDataFromDom(dom, headSelector)
48+
const headData = [...localeHead.link, ...localeHead.meta]
49+
for (const head of headData) {
50+
const tag = dom.querySelector(`[hid="${head.hid}"]`)
51+
for (const [key, value] of Object.entries(head)) {
52+
if (key === 'hid') {
53+
continue
54+
}
55+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
56+
const v = tag!.getAttribute(key)
57+
if (v !== value) {
58+
throw new Error(`${key} ${v} !== ${value}`)
59+
}
60+
}
61+
}
4162
}

specs/seo/baseUrl.spec.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { test } from 'vitest'
2+
import { fileURLToPath } from 'node:url'
3+
import { setup, $fetch } from '@nuxt/test-utils'
4+
import { getDom, assertLocaleHeadWithDom } from '../helper'
5+
6+
await setup({
7+
rootDir: fileURLToPath(new URL(`../fixtures/basic`, import.meta.url)),
8+
browser: true,
9+
// overrides
10+
nuxtConfig: {
11+
i18n: {
12+
defaultLocale: 'en',
13+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
14+
baseUrl: (nuxt: any) => {
15+
if (process.server) {
16+
const xOverrideBaseUrl = nuxt.ssrContext.req.headers['x-override-base-url']
17+
console.log('xOverrideBaseUrl', xOverrideBaseUrl)
18+
if (Array.isArray(xOverrideBaseUrl)) {
19+
return xOverrideBaseUrl[0]
20+
}
21+
return xOverrideBaseUrl || ''
22+
}
23+
return ''
24+
}
25+
}
26+
}
27+
})
28+
29+
test('render seo tags with baseUrl', async () => {
30+
const html = await $fetch('/?noncanonical', {
31+
headers: {
32+
'X-Override-Base-Url': 'CUSTOM'
33+
}
34+
})
35+
const dom = getDom(html)
36+
await assertLocaleHeadWithDom(dom, '#home-use-locale-head')
37+
})

specs/seo/metaComponent.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ test('render with meta components', async () => {
2929
// html tag `lang` attriute
3030
expect(await page.getAttribute('html', 'lang')).toMatch('en')
3131

32+
// html tag `dir` attriute
33+
expect(await page.getAttribute('html', 'dir')).toMatch('ltr')
34+
3235
// rendering link tag and meta tag in head tag
3336
await assetLocaleHead(page, '#home-use-locale-head')
3437

specs/seo/useHead.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ await setup({
99
// overrides
1010
nuxtConfig: {
1111
i18n: {
12-
defaultLocale: 'en'
12+
defaultLocale: 'en',
13+
defaultDirection: 'auto'
1314
}
1415
}
1516
})
@@ -31,6 +32,9 @@ test('render with useHead', async () => {
3132
// html tag `lang` attriute
3233
expect(await page.getAttribute('html', 'lang')).toMatch('en')
3334

35+
// html tag `dir` attriute
36+
expect(await page.getAttribute('html', 'dir')).toMatch('auto')
37+
3438
// rendering link tag and meta tag in head tag
3539
await assetLocaleHead(page, '#home-use-locale-head')
3640

src/runtime/vue-i18n-bridge.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import type { ComputedRef } from 'vue-demi'
2-
import type { LocaleObject, Strategies } from 'vue-i18n-routing'
2+
import type { LocaleObject, Strategies, Directions } from 'vue-i18n-routing'
33
import type { BeforeLanguageSwitchHandler, LanguageSwitchedHandler } from '#build/i18n.options.mjs'
44

55
export interface ComposerCustomProperties {
66
strategy: Strategies
77
localeProperties: ComputedRef<LocaleObject>
88
differentDomains: boolean
9+
defaultDirection: Directions
910
setLocale: (locale: string) => Promise<void>
1011
getBrowserLocale: () => string | undefined
1112
getLocaleCookie: () => string | undefined
@@ -23,6 +24,7 @@ declare module 'vue-i18n' {
2324
readonly strategy: Strategies
2425
localeProperties: LocaleObject
2526
readonly differentDomains: boolean
27+
readonly defaultDirection: Directions
2628
setLocale: (locale: string) => Promise<void>
2729
getBrowserLocale: () => string | undefined
2830
getLocaleCookie: () => string | undefined
@@ -47,6 +49,7 @@ declare module 'vue-i18n-bridge' {
4749
readonly strategy: Strategies
4850
localeProperties: LocaleObject
4951
readonly differentDomains: boolean
52+
readonly defaultDirection: Directions
5053
setLocale: (locale: string) => Promise<void>
5154
getBrowserLocale: () => string | undefined
5255
getLocaleCookie: () => string | undefined
@@ -70,6 +73,7 @@ declare module '@intlify/vue-i18n-bridge' {
7073
readonly strategy: Strategies
7174
localeProperties: LocaleObject
7275
readonly differentDomains: boolean
76+
readonly defaultDirection: Directions
7377
setLocale: (locale: string) => Promise<void>
7478
getBrowserLocale: () => string | undefined
7579
getLocaleCookie: () => string | undefined

0 commit comments

Comments
 (0)