feat(Link): auto-localize internal links when @nuxtjs/i18n is installed#5537
feat(Link): auto-localize internal links when @nuxtjs/i18n is installed#5537benjamincanac merged 10 commits intov4from
@nuxtjs/i18n is installed#5537Conversation
|
@BobbieGoede I'd love your feedback on this, would this work? |
commit: |
Co-authored-by: Bobbie Goede <bobbiegoede@gmail.com>
|
@BobbieGoede I have another question, what will happen if users have already passed |
Passing localized paths/routes to You might want to add a prop to the component so users can both opt-in to localization and/or specify the language (something like |
|
watch |
1 similar comment
|
watch |
|
any advance on this? or even any workaround? |
localePath from @nuxtjs/i18n@nuxtjs/i18n is installed
📝 WalkthroughWalkthroughAdds automatic i18n localization to the Link component: when Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/content/docs/2.components/link.md`:
- Around line 102-104: The docs overstate idempotency — the Link component
implementation (Link.vue) simply forwards string paths to $localePath so whether
already-localized paths are preserved depends on the i18n strategy; update the
wording to a conditional claim like "In most configurations the Link component
preserves already-localized paths; if you encounter edge cases use localePath()
manually" and add a short note referencing the Link component's behavior (the
string path forwarding to $localePath) so readers know why strategy differences
matter.
In `@src/runtime/components/Link.vue`:
- Around line 149-165: The computed to() currently calls nuxtApp.$localePath for
any non-external path which can double-localize already-prefixed strings and
will coerce route objects to strings (dropping state/params), breaking
downstream checks like isInternalLink; fix by only invoking nuxtApp.$localePath
when path is a plain non-prefixed string (e.g., typeof path === 'string' and
does not already include a locale prefix), leave route objects untouched (do not
call $localePath on objects) and keep external/absolute checks as-is so
already-localized or object routes are returned unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3d9449a8-6338-4e47-9736-3c9f4f80e2fe
📒 Files selected for processing (3)
docs/content/docs/1.getting-started/6.integrations/4.i18n/1.nuxt.mddocs/content/docs/2.components/link.mdsrc/runtime/components/Link.vue
There was a problem hiding this comment.
🧹 Nitpick comments (1)
docs/content/docs/2.components/link.md (1)
93-108: Document the external/absolute URL exception explicitly.The runtime logic skips localization for external/absolute URLs and also allows forcing this via
external; adding one sentence here would better match behavior and reduce ambiguity.✏️ Suggested doc tweak
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. + +External or absolute URLs (including protocol-based links) are not localized, and you can use the `external` prop to force bypass localization for edge cases.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/content/docs/2.components/link.md` around lines 93 - 108, Add a one-sentence clarification to the Link docs explaining that the Link/ULink component will not localize external or absolute URLs (it skips calling $localePath) and that this behavior can also be forced by setting the external prop; mention that developers can still manually call localePath() or localeRoute() for custom behavior. Reference the Link/ULink component and the helpers ($localePath, localePath(), localeRoute()) so readers know where the exception applies.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@docs/content/docs/2.components/link.md`:
- Around line 93-108: Add a one-sentence clarification to the Link docs
explaining that the Link/ULink component will not localize external or absolute
URLs (it skips calling $localePath) and that this behavior can also be forced by
setting the external prop; mention that developers can still manually call
localePath() or localeRoute() for custom behavior. Reference the Link/ULink
component and the helpers ($localePath, localePath(), localeRoute()) so readers
know where the exception applies.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b0d13a2c-296b-4976-aa9b-975592759076
📒 Files selected for processing (1)
docs/content/docs/2.components/link.md
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/runtime/components/Link.vue (1)
162-165: Prefer$localeRouteover$localePathto better preserve route semantics.
$localeRoutereturns a resolved route object preservingquery,params,state, and other route properties, whereas$localePathcollapses everything to a string. SinceRouterLinkProps['to']expectsRouteLocationRawand theisInternalLinkcomputed already handles non-string route objects (line 174), switching to$localeRouteis safe and improves route data preservation downstream.♻️ Proposed change
- // Use `$localePath` from `@nuxtjs/i18n` if available - const localePath = nuxtApp.$localePath as ((route: RouteLocationRaw, locale?: string) => string) | undefined - if (localePath) { - return localePath(path) + // Use `$localeRoute` from `@nuxtjs/i18n` if available (preserves route properties like `state`) + const localeRoute = nuxtApp.$localeRoute as ((route: RouteLocationRaw, locale?: string) => RouteLocationRaw | undefined) | undefined + if (localeRoute) { + return localeRoute(path) ?? path }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/runtime/components/Link.vue` around lines 162 - 165, Replace the use of nuxtApp.$localePath with nuxtApp.$localeRoute so you return a RouteLocationRaw instead of a string: create a const (e.g. localeRoute) typed as ((route: RouteLocationRaw, locale?: string) => RouteLocationRaw) | undefined, call localeRoute(path) when present and return that object (fall back to the original path when undefined); this preserves params/query/state for downstream consumers and matches RouterLinkProps['to'] and the existing isInternalLink handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/runtime/components/Link.vue`:
- Around line 149-168: Add a new prop locale?: boolean | string to the Link
component and modify the computed to to pass the explicit locale to
nuxtApp.$localePath when available: if locale === false skip calling $localePath
and return the raw path (to opt-out/detect already-localized paths), if locale
is a string call localePath(path, locale) to target a specific locale, and if
locale is undefined follow a global app.config default (e.g.,
app.config.globalProperties.linkLocaleBehavior or leave current behavior) so
existing use of $localePath remains unchanged; update references in the computed
to check props.locale before invoking nuxtApp.$localePath and keep the typeof
path !== 'string' guard and hasProtocol(props, ...) logic intact.
---
Nitpick comments:
In `@src/runtime/components/Link.vue`:
- Around line 162-165: Replace the use of nuxtApp.$localePath with
nuxtApp.$localeRoute so you return a RouteLocationRaw instead of a string:
create a const (e.g. localeRoute) typed as ((route: RouteLocationRaw, locale?:
string) => RouteLocationRaw) | undefined, call localeRoute(path) when present
and return that object (fall back to the original path when undefined); this
preserves params/query/state for downstream consumers and matches
RouterLinkProps['to'] and the existing isInternalLink handling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c753b75d-3965-41fb-a1d8-9bcf6219acb7
📒 Files selected for processing (1)
src/runtime/components/Link.vue
| const to = computed(() => { | ||
| const path = props.to ?? props.href | ||
| if (!path) return path | ||
|
|
||
| // Only localize string paths, leave route objects untouched to preserve state/params | ||
| if (typeof path !== 'string') return path | ||
|
|
||
| // Skip external links and absolute URLs | ||
| if (props.external || hasProtocol(path, { acceptRelative: true })) { | ||
| return path | ||
| } | ||
|
|
||
| // Use `$localePath` from `@nuxtjs/i18n` if available | ||
| const localePath = nuxtApp.$localePath as ((route: RouteLocationRaw, locale?: string) => string) | undefined | ||
| if (localePath) { | ||
| return localePath(path) | ||
| } | ||
|
|
||
| return path | ||
| }) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
@nuxtjs/i18n $localePath accept a locale argument signature and behavior
💡 Result:
Yes, $localePath in @nuxtjs/i18n accepts a locale argument as the second parameter. It is the global function provided by the module (also available via the useLocalePath composable), used to resolve a localized path for a given route. Signature (from TypeScript definition): (localePath function): (route: RawLocation | RouteLocation, locale?: Locale) => string Parameters: - route (first argument, required): Route name (string), path (string), or route object (e.g., { name: 'index' } or { name: 'category-slug', params: { slug: 'value' } }) - locale (second argument, optional): Locale code string (e.g., 'en', 'fr'). Defaults to the current locale if omitted. Behavior: - Returns the path localized according to the specified locale (or current locale). - Prefixes the path with the locale code based on the routing strategy (e.g., '/en/index' for prefix strategy). - Supports route params for dynamic routes. - Use in templates: <NuxtLink :to="$localePath('index', 'en')"> - In <script setup>: const localePath = useLocalePath; localePath('index', 'en') Examples: - $localePath('index') → '/en' (current locale 'en') - $localePath('index', 'fr') → '/fr' - $localePath({ name: 'products-id', params: { id: 123 } }, 'de') → '/de/produkte/123' (if customized)
Citations:
- 1: https://next.i18n.nuxtjs.org/docs/getting-started/usage
- 2: https://i18n.nuxtjs.org/docs/getting-started/usage
- 3: https://i18n.nuxtjs.org/docs/composables/use-locale-path
- 4: https://v8.i18n.nuxtjs.org/docs/v8/api/
- 5: https://v8.i18n.nuxtjs.org/docs/getting-started/usage
Add locale prop to allow opt-out and explicit locale targeting before shipping.
The typeof guard at line 154 correctly preserves route objects' state/params. However, the component still lacks control over locale selection. $localePath accepts a locale parameter (e.g., localePath(path, 'fr')), but the current code never uses it, always falling back to the current locale.
Two gaps remain:
-
No opt-out mechanism: Users intentionally linking to another locale (e.g.,
/fr/aboutfrom anenlocale) will have the link silently re-localized, breaking existing use cases. Alocale?: boolean | stringprop (wherelocale: falseopts out andlocale: 'fr'targets a specific locale) would preserve current behavior while enabling cross-locale links. -
Double-localization risk: Pre-localized paths like
/fr/aboutpassed to an already-localized$localePathacross different i18n strategies (prefix,prefix_except_default, etc.) can resolve unpredictably. Alocale: falseopt-out provides users an escape hatch.
Consider a global app.config toggle (opt-in vs. opt-out behavior by default) alongside the prop to mitigate backward-compat risk for apps already using useLocalePath() output in :to directly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/runtime/components/Link.vue` around lines 149 - 168, Add a new prop
locale?: boolean | string to the Link component and modify the computed to to
pass the explicit locale to nuxtApp.$localePath when available: if locale ===
false skip calling $localePath and return the raw path (to opt-out/detect
already-localized paths), if locale is a string call localePath(path, locale) to
target a specific locale, and if locale is undefined follow a global app.config
default (e.g., app.config.globalProperties.linkLocaleBehavior or leave current
behavior) so existing use of $localePath remains unchanged; update references in
the computed to check props.locale before invoking nuxtApp.$localePath and keep
the typeof path !== 'string' guard and hasProtocol(props, ...) logic intact.
🔗 Linked issue
Resolves #5536, resolves #5359, resolves #5637, resolves #4465
❓ Type of change
📚 Description
📝 Checklist