Skip to content

Commit

Permalink
feat: add NuxtLinkLocale component (#2229)
Browse files Browse the repository at this point in the history
* feat: add NuxtLinkLocale component

* refactor: rewrite NuxtLinkLocale sfc as render function

* fix: path resolution for #imports alias

* docs: add components page

* test: add NuxtLinkLocale tests

* fix: hard-coded NuxtLinkLocale props

* docs: change component example to be more realistic

* fix: move nuxi prepare from postinstall to test script

* fix: add dev:prepare script to run before dev and test scripts
  • Loading branch information
BobbieGoede committed Jul 19, 2023
1 parent 53161c6 commit 9dfb024
Show file tree
Hide file tree
Showing 15 changed files with 138 additions and 9 deletions.
52 changes: 52 additions & 0 deletions docs/content/4.API/2.components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Components

Components API for Nuxt i18n module.

## `<NuxtLinkLocale>`
This component is built on top of [`<NuxtLink>`](https://nuxt.com/docs/api/components/nuxt-link#nuxtlink) but changes the default behavior by internally using [`localePath()`](/api/vue#localepath) to make it easier to link to localized routes.

### Examples

#### Basic usage
```vue
<template>
<NuxtLinkLocale to="/">{{ $t('home') }}</NuxtLinkLocale>
</template>
<!-- equivalent to -->
<script setup>
const localePath = useLocalePath()
</script>
<template>
<NuxtLink :to="localePath('/')">{{ $t('home') }}</NuxtLink>
</template>
```

#### Forcing locale resolution
```vue
<template>
<NuxtLinkLocale to="/" locale="nl">{{ $t('home') }}</NuxtLinkLocale>
</template>
<!-- equivalent to -->
<script setup>
const localePath = useLocalePath()
</script>
<template>
<NuxtLink :to="localePath('/', 'nl')">{{ $t('home') }}</NuxtLink>
</template>
```

### Props
This component supports all [props documented for `<NuxtLink>`](https://nuxt.com/docs/api/components/nuxt-link#props) in addition to props described below.

|Prop |Description |
|--- |--- |
|`locale` |Optional prop to force localization using passed Locale, it defaults to the current locale. Identical to `locale` argument of `localePath()`|



File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@
"prepack": "pnpm build",
"release": "bumpp --commit \"release: v%s\" --push --tag",
"changelog": "gh-changelogen --repo=nuxt-community/i18n-module",
"dev": "pnpm build && nuxi dev playground",
"dev": "pnpm dev:prepare && pnpm build && nuxi dev playground",
"dev:build": "nuxi build playground",
"dev:generate": "nuxi generate playground",
"dev:prepare": "nuxi prepare",
"dev:preview": "nuxi preview playground",
"dev:clean": "rm -rf playground/.nuxt playground/dist playground/.output",
"docs:dev": "nuxi dev docs",
Expand All @@ -60,7 +61,7 @@
"format:fix": "pnpm format --write",
"lint": "eslint --cache --ext .js,.ts,.vue,.json .",
"lint:fix": "pnpm lint --fix",
"test": "run-s test:types test:unit test:spec",
"test": "pnpm dev:prepare && run-s test:types test:unit test:spec",
"test:types": "tsc --noEmit",
"test:unit": "vitest run test",
"test:spec": "vitest run specs"
Expand Down
7 changes: 6 additions & 1 deletion playground/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ definePageMeta({
<NuxtLink :to="localePath('/')">Home</NuxtLink> | <NuxtLink :to="localePath({ name: 'about' })">About</NuxtLink> |
<NuxtLink :to="localePath({ name: 'blog' })">Blog</NuxtLink> |
<NuxtLink :to="localePath({ name: 'category-id', params: { id: 'foo' } })">Category</NuxtLink> |
<NuxtLink :to="localePath({ name: 'history' })">History</NuxtLink>
<NuxtLinkLocale :to="{ name: 'history' }" class="history-link">History</NuxtLinkLocale> |
<NuxtLinkLocale :to="'/'" locale="ja" activeClass="link-active">Home (Japanese)</NuxtLinkLocale>
</nav>
<h2>Current Language: {{ getLocaleName(locale) }}</h2>
<h2>Current Strategy: {{ strategy }}</h2>
Expand Down Expand Up @@ -109,4 +110,8 @@ definePageMeta({
.page-leave-active {
opacity: 0;
}
.link-active {
color: rgb(51, 175, 51);
}
</style>
12 changes: 11 additions & 1 deletion specs/basic_usage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,23 @@ test('basic usage', async () => {
// URL path localizing with `useLocalePath`
expect(await page.locator('#locale-path-usages .name a').getAttribute('href')).toEqual('/')
expect(await page.locator('#locale-path-usages .path a').getAttribute('href')).toEqual('/')
expect(await page.locator('#locale-path-usages .named-with-locale a').getAttribute('href')).toEqual('/')
expect(await page.locator('#locale-path-usages .named-with-locale a').getAttribute('href')).toEqual('/fr')
expect(await page.locator('#locale-path-usages .nest-path a').getAttribute('href')).toEqual('/user/profile')
expect(await page.locator('#locale-path-usages .nest-named a').getAttribute('href')).toEqual('/user/profile')
expect(await page.locator('#locale-path-usages .object-with-named a').getAttribute('href')).toEqual(
'/category/nintendo'
)

// URL path localizing with `NuxtLinkLocale`
expect(await page.locator('#nuxt-link-locale-usages .name a').getAttribute('href')).toEqual('/')
expect(await page.locator('#nuxt-link-locale-usages .path a').getAttribute('href')).toEqual('/')
expect(await page.locator('#nuxt-link-locale-usages .named-with-locale a').getAttribute('href')).toEqual('/fr')
expect(await page.locator('#nuxt-link-locale-usages .nest-path a').getAttribute('href')).toEqual('/user/profile')
expect(await page.locator('#nuxt-link-locale-usages .nest-named a').getAttribute('href')).toEqual('/user/profile')
expect(await page.locator('#nuxt-link-locale-usages .object-with-named a').getAttribute('href')).toEqual(
'/category/nintendo'
)

// Language switching path localizing with `useSwitchLocalePath`
expect(await page.locator('#switch-locale-path-usages .switch-to-en a').getAttribute('href')).toEqual('/')
expect(await page.locator('#switch-locale-path-usages .switch-to-fr a').getAttribute('href')).toEqual('/fr')
Expand Down
27 changes: 26 additions & 1 deletion specs/fixtures/basic_usage/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function onClick() {
<NuxtLink :to="localePath('/')">{{ $t('home') }}</NuxtLink>
</li>
<li class="named-with-locale">
<NuxtLink :to="localePath('index', 'en')">Homepage in English</NuxtLink>
<NuxtLink :to="localePath('index', 'fr')">Homepage in French</NuxtLink>
</li>
<li class="nest-path">
<NuxtLink :to="localePath('/user/profile')">Route by path to: {{ $t('profile') }}</NuxtLink>
Expand All @@ -54,6 +54,31 @@ function onClick() {
</NuxtLink>
</li>
</ul>
</section>
<section id="nuxt-link-locale-usages">
<h3>NuxtLinkLocale</h3>
<ul>
<li class="name">
<NuxtLinkLocale :to="'index'">{{ $t('home') }}</NuxtLinkLocale>
</li>
<li class="path">
<NuxtLinkLocale :to="'/'">{{ $t('home') }}</NuxtLinkLocale>
</li>
<li class="named-with-locale">
<NuxtLinkLocale :to="'index'" :locale="'fr'">Homepage in French</NuxtLinkLocale>
</li>
<li class="nest-path">
<NuxtLinkLocale :to="'/user/profile'">Route by path to: {{ $t('profile') }}</NuxtLinkLocale>
</li>
<li class="nest-named">
<NuxtLinkLocale :to="'user-profile'">Route by name to: {{ $t('profile') }}</NuxtLinkLocale>
</li>
<li class="object-with-named">
<NuxtLinkLocale :to="{ name: 'category-slug', params: { slug: category.slug } }">
{{ category.title }}
</NuxtLinkLocale>
</li>
</ul>
</section>
<section id="switch-locale-path-usages">
<h3>switchLocalePath</h3>
Expand Down
6 changes: 6 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
isNuxt2,
isNuxt3,
getNuxtVersion,
addComponent,
addPlugin,
addTemplate,
addImports,
Expand Down Expand Up @@ -298,6 +299,11 @@ export default defineNuxtModule<NuxtI18nOptions>({
const vueI18nPath = await resolveVueI18nAlias(pkgModulesDir, nuxt, pkgMgr)
debug('vueI18nPath for auto-import', vueI18nPath)

await addComponent({
name: 'NuxtLinkLocale',
filePath: resolve(runtimeDir, 'components/NuxtLinkLocale')
})

await addImports([
{ name: 'useI18n', from: vueI18nPath },
...[
Expand Down
30 changes: 30 additions & 0 deletions src/runtime/components/NuxtLinkLocale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useLocalePath } from '#i18n'
import { defineComponent, computed, defineNuxtLink, h } from '#imports'

import type { PropType } from 'vue'
import type { RawLocation, RouteLocation } from '@intlify/vue-router-bridge'

const NuxtLinkLocale = defineNuxtLink({ componentName: 'NuxtLinkLocale' })

export default defineComponent({
name: 'NuxtLinkLocale',
props: {
...NuxtLinkLocale.props,
to: {
type: [String, Object] as PropType<RawLocation | RouteLocation>,
default: undefined,
required: false
},
locale: {
type: String as PropType<string>,
default: undefined,
required: false
}
},
setup(props, { slots }) {
const localePath = useLocalePath()
const resolvedPath = computed(() => (props.to != null ? localePath(props.to, props.locale) : props.to))

return () => h(NuxtLinkLocale, { ...props, to: resolvedPath }, slots.default)
}
})
2 changes: 1 addition & 1 deletion src/runtime/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
isSSG
} from '#build/i18n.options.mjs'

import type { NuxtApp } from '#imports'
import type { NuxtApp } from '#app'
import type { I18nOptions, Locale, VueI18n, LocaleMessages, DefineLocaleMessage } from 'vue-i18n'
import type { Route, RouteLocationNormalized, RouteLocationNormalizedLoaded, LocaleObject } from 'vue-i18n-routing'
import type { DeepRequired } from 'ts-essentials'
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/plugins/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {

import type { Composer, I18nOptions, Locale } from 'vue-i18n'
import type { LocaleObject, ExtendProperyDescripters, VueI18nRoutingPluginOptions } from 'vue-i18n-routing'
import type { NuxtApp } from '#imports'
import type { NuxtApp } from '#app'

type GetRouteBaseName = typeof getRouteBaseName
type LocalePath = typeof localePath
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import type {
PrefixableOptions,
SwitchLocalePathIntercepter
} from 'vue-i18n-routing'
import type { NuxtApp } from '#imports'
import type { NuxtApp } from '#app'
import type { I18n, Locale, FallbackLocale, LocaleMessages, DefineLocaleMessage } from 'vue-i18n'
import type { NuxtI18nOptions, DetectBrowserLanguageOptions, RootRedirectOptions } from '#build/i18n.options.mjs'
import type { DetectLocaleContext } from '#build/i18n.internal.mjs'
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
"paths": {
"#app": ["./node_modules/nuxt/dist/app"],
"#imports": ["./node_modules/nuxt/app.d.ts"],
"#imports": ["./.nuxt/imports.d.ts"],
"#pages": ["./node_modules/nuxt/dist/pages/runtime/index.d.ts"],
"#head": ["./node_modules/nuxt/dist/index.d.ts"],
"#i18n": ["./src/runtime/composables.ts"],
Expand Down

0 comments on commit 9dfb024

Please sign in to comment.