Skip to content

Commit 92cfda0

Browse files
feat(Link): auto-localize internal links when @nuxtjs/i18n is installed (#5537)
Co-authored-by: Bobbie Goede <bobbiegoede@gmail.com>
1 parent 9c5c0df commit 92cfda0

3 files changed

Lines changed: 61 additions & 6 deletions

File tree

docs/content/docs/1.getting-started/6.integrations/4.i18n/1.nuxt.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,22 @@ const { locale } = useI18n()
161161
</template>
162162
```
163163

164+
#### Automatic link localization :badge{label="Soon" class="align-text-top"}
165+
166+
When `@nuxtjs/i18n` is installed, the [Link](/docs/components/link) component automatically localizes internal links using the `$localePath` helper. This means you don't need to manually wrap your links with `localePath()` or `localeRoute()`.
167+
168+
```vue
169+
<template>
170+
<!-- Automatically becomes /en/about or /fr/about based on current locale -->
171+
<ULink to="/about">About</ULink>
172+
<UButton to="/contact">Contact</UButton>
173+
</template>
174+
```
175+
176+
::tip
177+
External links and absolute URLs are automatically detected and skip localization. You can still manually use `localePath()` or `localeRoute()` if needed.
178+
::
179+
164180
::
165181

166182
### Dynamic direction

docs/content/docs/2.components/link.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,8 @@ slots:
7575
Link
7676
::
7777

78-
## IntelliSense
79-
80-
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`:
78+
::callout{icon="i-simple-icons-visualstudiocode"}
79+
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`:
8180

8281
```json [.vscode/settings.json]
8382
{
@@ -87,6 +86,26 @@ If you're using VSCode and wish to get autocompletion for the classes `active-cl
8786
]
8887
}
8988
```
89+
::
90+
91+
### Locale :badge{label="Soon" class="align-text-top"}
92+
93+
The Link component automatically integrates with [`@nuxtjs/i18n`](https://i18n.nuxtjs.org/) when installed. Internal links are automatically localized using the `$localePath` helper without requiring manual wrapping.
94+
95+
```vue
96+
<template>
97+
<!-- Automatically becomes /en/about or /fr/about based on current locale -->
98+
<ULink to="/about">About</ULink>
99+
</template>
100+
```
101+
102+
::tip
103+
You can still manually use `localePath()` or `localeRoute()` if needed.
104+
::
105+
106+
::note{to="/docs/getting-started/integrations/i18n/nuxt#dynamic-locale"}
107+
Learn more about Internationalization in Nuxt UI.
108+
::
90109

91110
## API
92111

src/runtime/components/Link.vue

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ import { computed } from 'vue'
110110
import { isEqual } from 'ohash/utils'
111111
import { useForwardProps, Slot } from 'reka-ui'
112112
import { defu } from 'defu'
113-
import { reactiveOmit } from '@vueuse/core'
114113
import { hasProtocol } from 'ufo'
115-
import { useRoute, useAppConfig } from '#imports'
114+
import { reactiveOmit } from '@vueuse/core'
115+
import { useRoute, useAppConfig, useNuxtApp } from '#imports'
116116
import { mergeClasses } from '../utils'
117117
import { tv } from '../utils/tv'
118118
import { isPartiallyEqual } from '../utils/link'
@@ -130,6 +130,7 @@ defineSlots<LinkSlots>()
130130
131131
const route = useRoute()
132132
const appConfig = useAppConfig() as Link['AppConfig']
133+
const nuxtApp = useNuxtApp()
133134
134135
const nuxtLinkProps = useForwardProps(reactiveOmit(props, 'as', 'type', 'disabled', 'active', 'exact', 'exactQuery', 'exactHash', 'activeClass', 'inactiveClass', 'to', 'href', 'raw', 'custom', 'class'))
135136
@@ -145,7 +146,26 @@ const ui = computed(() => tv({
145146
}, appConfig.ui?.link || {})
146147
}))
147148
148-
const to = computed(() => props.to ?? props.href)
149+
const to = computed(() => {
150+
const path = props.to ?? props.href
151+
if (!path) return path
152+
153+
// Only localize string paths, leave route objects untouched to preserve state/params
154+
if (typeof path !== 'string') return path
155+
156+
// Skip external links and absolute URLs
157+
if (props.external || hasProtocol(path, { acceptRelative: true })) {
158+
return path
159+
}
160+
161+
// Use `$localePath` from `@nuxtjs/i18n` if available
162+
const localePath = nuxtApp.$localePath as ((route: RouteLocationRaw, locale?: string) => string) | undefined
163+
if (localePath) {
164+
return localePath(path)
165+
}
166+
167+
return path
168+
})
149169
150170
const isInternalLink = computed(() => {
151171
if (!to.value) return false

0 commit comments

Comments
 (0)