From ba407ae795328aba31f2f0f110ee5b88b8842069 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Wed, 26 Nov 2025 11:06:51 +0100 Subject: [PATCH 1/5] feat(Link): automatically use `localePath` from `@nuxtjs/i18n` --- src/runtime/components/Link.vue | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/runtime/components/Link.vue b/src/runtime/components/Link.vue index a07f8f44c2..f6da111b4c 100644 --- a/src/runtime/components/Link.vue +++ b/src/runtime/components/Link.vue @@ -96,8 +96,9 @@ import { computed } from 'vue' import { isEqual } from 'ohash/utils' import { useForwardProps } from 'reka-ui' import { defu } from 'defu' +import { hasProtocol } from 'ufo' import { reactiveOmit } from '@vueuse/core' -import { useRoute, useAppConfig } from '#imports' +import { useRoute, useAppConfig, useNuxtApp } from '#imports' import { mergeClasses } from '../utils' import { tv } from '../utils/tv' import { isPartiallyEqual } from '../utils/link' @@ -115,6 +116,7 @@ defineSlots() const route = useRoute() const appConfig = useAppConfig() as Link['AppConfig'] +const nuxtApp = useNuxtApp() const nuxtLinkProps = useForwardProps(reactiveOmit(props, 'as', 'type', 'disabled', 'active', 'exact', 'exactQuery', 'exactHash', 'activeClass', 'inactiveClass', 'to', 'href', 'raw', 'custom', 'class')) @@ -130,7 +132,23 @@ const ui = computed(() => tv({ }, appConfig.ui?.link || {}) })) -const to = computed(() => props.to ?? props.href) +const to = computed(() => { + const path = props.to ?? props.href + if (!path) return path + + // Skip external links and absolute URLs + if (props.external || (typeof path === 'string' && hasProtocol(path, { acceptRelative: true }))) { + return path + } + + // Use localePath from `@nuxtjs/i18n` if available + const localePath = nuxtApp.$localePath as ((route: RouteLocationRaw) => RouteLocationRaw) | undefined + if (localePath) { + return localePath(path) + } + + return path +}) function isLinkActive({ route: linkRoute, isActive, isExactActive }: any) { if (props.active !== undefined) { From 2a8c9baa670207c787bc3c6711f94298b9869719 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Mon, 8 Dec 2025 14:40:24 +0100 Subject: [PATCH 2/5] Update src/runtime/components/Link.vue Co-authored-by: Bobbie Goede --- src/runtime/components/Link.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/runtime/components/Link.vue b/src/runtime/components/Link.vue index 972cb5b510..e79529e7d8 100644 --- a/src/runtime/components/Link.vue +++ b/src/runtime/components/Link.vue @@ -148,9 +148,9 @@ const to = computed(() => { } // Use localePath from `@nuxtjs/i18n` if available - const localePath = nuxtApp.$localePath as ((route: RouteLocationRaw) => RouteLocationRaw) | undefined - if (localePath) { - return localePath(path) + const localeRoute = nuxtApp.$localeRoute as ((route: RouteLocationRaw) => RouteLocationRaw) | undefined + if (localeRoute) { + return localeRoute(path) } return path From 347457694fd39e85d9c1967e8709f91e2a56a672 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Mon, 8 Dec 2025 14:41:09 +0100 Subject: [PATCH 3/5] Apply suggestion from @benjamincanac --- src/runtime/components/Link.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/components/Link.vue b/src/runtime/components/Link.vue index e79529e7d8..2ba8e076e6 100644 --- a/src/runtime/components/Link.vue +++ b/src/runtime/components/Link.vue @@ -147,7 +147,7 @@ const to = computed(() => { return path } - // Use localePath from `@nuxtjs/i18n` if available + // Use `localeRoute` from `@nuxtjs/i18n` if available const localeRoute = nuxtApp.$localeRoute as ((route: RouteLocationRaw) => RouteLocationRaw) | undefined if (localeRoute) { return localeRoute(path) From c8a70027f408be6a46b7ce4d262c94e821f0ae7e Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Mon, 8 Dec 2025 15:16:47 +0100 Subject: [PATCH 4/5] up --- src/runtime/components/Link.vue | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/runtime/components/Link.vue b/src/runtime/components/Link.vue index 2ba8e076e6..6a0a0da80a 100644 --- a/src/runtime/components/Link.vue +++ b/src/runtime/components/Link.vue @@ -147,10 +147,12 @@ const to = computed(() => { return path } - // Use `localeRoute` from `@nuxtjs/i18n` if available - const localeRoute = nuxtApp.$localeRoute as ((route: RouteLocationRaw) => RouteLocationRaw) | undefined + // Use `$localeRoute` from `@nuxtjs/i18n` if available + const localeRoute = nuxtApp.$localeRoute as ((route: RouteLocationRaw) => RouteLocationRaw | undefined) | undefined if (localeRoute) { - return localeRoute(path) + const localizedPath = localeRoute(path) + // Fallback to original path if localeRoute returns undefined (e.g., route not found) + return localizedPath ?? path } return path From b0b0493213fb205783b0b826242027611394db87 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Mon, 8 Dec 2025 15:34:08 +0100 Subject: [PATCH 5/5] up --- .../6.integrations/4.i18n/1.nuxt.md | 16 ++++++++++++ docs/content/docs/2.components/link.md | 25 ++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/docs/content/docs/1.getting-started/6.integrations/4.i18n/1.nuxt.md b/docs/content/docs/1.getting-started/6.integrations/4.i18n/1.nuxt.md index eb638c0ffd..f32b8b8142 100644 --- a/docs/content/docs/1.getting-started/6.integrations/4.i18n/1.nuxt.md +++ b/docs/content/docs/1.getting-started/6.integrations/4.i18n/1.nuxt.md @@ -157,6 +157,22 @@ const { locale } = useI18n() ``` +#### Automatic link localization :badge{label="Soon" class="align-text-top"} + +When `@nuxtjs/i18n` is installed, the [Link](/docs/components/link) component automatically localizes internal links using the `$localeRoute` helper. This means you don't need to manually wrap your links with `localePath()` or `localeRoute()`. + +```vue + +``` + +::tip +External links and absolute URLs are automatically detected and skip localization. You can still manually use `localePath()` or `localeRoute()` if needed. +:: + :: ### Dynamic direction diff --git a/docs/content/docs/2.components/link.md b/docs/content/docs/2.components/link.md index a0bcdd2bbf..2fea995ca5 100644 --- a/docs/content/docs/2.components/link.md +++ b/docs/content/docs/2.components/link.md @@ -75,9 +75,8 @@ slots: Link :: -## IntelliSense - -If you're using VSCode and wish to get autocompletion for the classes `active-class` and `inactive-class`, you can add the following settings to your `.vscode/settings.json`: +::callout{icon="i-simple-icons-visualstudiocode"} +If you're using the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) extension for VSCode and wish to get autocompletion for the `active-class` and `inactive-class` props, you can add the following settings to your `.vscode/settings.json`: ```json [.vscode/settings.json] { @@ -87,6 +86,26 @@ If you're using VSCode and wish to get autocompletion for the classes `active-cl ] } ``` +:: + +### Locale :badge{label="Soon" class="align-text-top"} + +The Link component automatically integrates with [`@nuxtjs/i18n`](https://i18n.nuxtjs.org/) when installed. Internal links are automatically localized using the `$localeRoute` helper without requiring manual wrapping. + +```vue + +``` + +::tip +You can still manually use `localePath()` or `localeRoute()` if needed. The Link component handles already-localized paths correctly without double-localizing. +:: + +::note{to="/docs/getting-started/integrations/i18n/nuxt#dynamic-locale"} +Learn more about Internationalization in Nuxt UI. +:: ## API