|
| 1 | +--- |
| 2 | +title: Custom Route Paths |
| 3 | +description: Customize the names of the paths for specific locale. |
| 4 | +--- |
| 5 | + |
| 6 | +In some cases, you might want to translate URLs in addition to having them prefixed with the locale code. There are two methods of configuring custom paths, through [Module configuration](#module-configuration) or from within each [Page component](#page-component). |
| 7 | + |
| 8 | +Which method is used is configured by setting the [`customRoutes` options](/docs/options/routing#customroutes) this is set to `'page'` by default. Using both methods at the same time is not possible. |
| 9 | + |
| 10 | +::callout{icon="i-heroicons-exclamation-triangle" color="amber"} |
| 11 | +Custom paths are not supported when using the `no_prefix` [strategy](/docs/guide) unless combined with [`differentDomains`](/docs/guide/different-domains). |
| 12 | +:: |
| 13 | + |
| 14 | +### Module configuration |
| 15 | + |
| 16 | +Make sure you set the `customRoutes` option to `config` and add your custom paths in the `pages` option: |
| 17 | + |
| 18 | +```ts [nuxt.config.ts] |
| 19 | +export default defineNuxtConfig({ |
| 20 | + i18n: { |
| 21 | + customRoutes: 'config', // disable custom route with page components |
| 22 | + pages: { |
| 23 | + about: { |
| 24 | + en: '/about-us', // -> accessible at /about-us (no prefix since it's the default locale) |
| 25 | + fr: '/a-propos', // -> accessible at /fr/a-propos |
| 26 | + es: '/sobre' // -> accessible at /es/sobre |
| 27 | + } |
| 28 | + } |
| 29 | + } |
| 30 | +}) |
| 31 | +``` |
| 32 | + |
| 33 | +Note that each key within the `pages` object should **correspond to the relative file-based path (excluding `.vue` file extension) of the route within your `pages/` directory excluding the leading `/`**. |
| 34 | + |
| 35 | +Customized route paths **must start with a `/`** and **not include the locale prefix**. |
| 36 | + |
| 37 | +You can now use the `localePath` function or the `<NuxtLinkLocale>` component but be sure to use named routes. For example route `/services/advanced` should be `services-advanced`: |
| 38 | + |
| 39 | +```vue |
| 40 | +<script setup> |
| 41 | +const { t } = useI18n() |
| 42 | +</script> |
| 43 | +
|
| 44 | +<template> |
| 45 | + <NuxtLinkLocale to="about"> {{ t('about') }} </NuxtLinkLocale> |
| 46 | + <NuxtLinkLocale to="services-advanced"> {{ t('advanced') }} </NuxtLinkLocale> |
| 47 | +</template> |
| 48 | +``` |
| 49 | + |
| 50 | +Or: |
| 51 | + |
| 52 | +```vue |
| 53 | +<script setup> |
| 54 | +const { t } = useI18n() |
| 55 | +const localePath = useLocalePath() |
| 56 | +</script> |
| 57 | +
|
| 58 | +<template> |
| 59 | + <NuxtLink :to="localePath('about')"> {{ t('about') }} </NuxtLink> |
| 60 | + <NuxtLink :to="localePath('services-advanced')"> {{ t('advanced') }} </NuxtLink> |
| 61 | +</template> |
| 62 | +``` |
| 63 | + |
| 64 | +::callout{icon="i-heroicons-exclamation-triangle" color="amber"} |
| 65 | +Specifying a path to `localePath` is not supported, currently. |
| 66 | +:: |
| 67 | + |
| 68 | +#### Example 1: Basic URL localization |
| 69 | + |
| 70 | +You have some routes with the following `pages` directory: |
| 71 | + |
| 72 | +``` |
| 73 | +pages/ |
| 74 | +├── about.vue |
| 75 | +├── me.vue |
| 76 | +├── services/ |
| 77 | +├──── index.vue |
| 78 | +├──── advanced.vue |
| 79 | +``` |
| 80 | + |
| 81 | +You would need to set up your `pages` property as follows: |
| 82 | + |
| 83 | +```ts [nuxt.config.ts] |
| 84 | +export default defineNuxtConfig({ |
| 85 | + i18n: { |
| 86 | + customRoutes: 'config', |
| 87 | + pages: { |
| 88 | + about: { |
| 89 | + fr: '/a-propos', |
| 90 | + }, |
| 91 | + me: { |
| 92 | + fr: '/moi', |
| 93 | + }, |
| 94 | + 'services/index': { |
| 95 | + fr: '/offres', |
| 96 | + }, |
| 97 | + 'services/advanced': { |
| 98 | + fr: '/offres/avancee', |
| 99 | + } |
| 100 | + } |
| 101 | + } |
| 102 | +}) |
| 103 | +``` |
| 104 | + |
| 105 | +If you want customize the URL of a static vue file, you should use the file's name. |
| 106 | +If the view is in a sub-directory you should use folder name and vue files name with trailing slash. |
| 107 | + |
| 108 | +::callout{icon="i-heroicons-exclamation-triangle" color="amber"} |
| 109 | +All URLs must start with `/` |
| 110 | +:: |
| 111 | + |
| 112 | +#### Example 2: Localize the part of URL |
| 113 | + |
| 114 | +You have some routes with the following `pages` directory: |
| 115 | + |
| 116 | +``` |
| 117 | +pages/ |
| 118 | +├── about.vue |
| 119 | +├── services/ |
| 120 | +├──── coaching.vue |
| 121 | +├──── index.vue |
| 122 | +├──── development/ |
| 123 | +├────── app.vue |
| 124 | +├────── website.vue |
| 125 | +├────── index.vue |
| 126 | +``` |
| 127 | + |
| 128 | +You would need to set up your `pages` property as follows: |
| 129 | + |
| 130 | +```ts [nuxt.config.ts] |
| 131 | +export default defineNuxtConfig({ |
| 132 | + i18n: { |
| 133 | + customRoutes: 'config', |
| 134 | + pages: { |
| 135 | + about: { |
| 136 | + fr: '/a-propos' |
| 137 | + }, |
| 138 | + 'services/index': { |
| 139 | + fr: '/offres' |
| 140 | + }, |
| 141 | + 'services/development/index': { |
| 142 | + fr: '/offres/developement' |
| 143 | + }, |
| 144 | + 'services/development/app': { |
| 145 | + fr: '/offres/developement/app' |
| 146 | + }, |
| 147 | + 'services/development/website': { |
| 148 | + fr: '/offres/developement/site-web' |
| 149 | + }, |
| 150 | + 'services/coaching': { |
| 151 | + fr: '/offres/formation' |
| 152 | + } |
| 153 | + } |
| 154 | + } |
| 155 | +}) |
| 156 | +``` |
| 157 | + |
| 158 | +If a custom path is missing for one of the locales, the `defaultLocale` custom path is used, if set. |
| 159 | + |
| 160 | +#### Example 3: Dynamic Routes |
| 161 | + |
| 162 | +Say you have some dynamic routes like: |
| 163 | + |
| 164 | +``` |
| 165 | +pages/ |
| 166 | +├── blog/ |
| 167 | +├──── [date]/ |
| 168 | +├────── [slug].vue |
| 169 | +``` |
| 170 | + |
| 171 | +Here's how you would configure these particular pages in the configuration: |
| 172 | + |
| 173 | +```ts [nuxt.config.ts] |
| 174 | +export default defineNuxtConfig({ |
| 175 | + i18n: { |
| 176 | + customRoutes: 'config', |
| 177 | + pages: { |
| 178 | + 'blog/[date]/[slug]': { |
| 179 | + // params need to be put back here as you would with Nuxt Dynamic Routes |
| 180 | + // https://nuxt.com/docs/guide/directory-structure/pages#dynamic-routes |
| 181 | + ja: '/blog/tech/[date]/[slug]' |
| 182 | + // ... |
| 183 | + } |
| 184 | + } |
| 185 | + } |
| 186 | +}) |
| 187 | +``` |
| 188 | + |
| 189 | +### Page component |
| 190 | + |
| 191 | +::callout{icon="i-heroicons-exclamation-triangle" color="amber" title="notice"} |
| 192 | +Note for those updating to `v8.0.1` or higher |
| 193 | +:br :br |
| 194 | +Path parameters parsing has been changed to match that of [Nuxt 3](https://nuxt.com/docs/guide/directory-structure/pages#dynamic-routes), you will have to update your custom paths (e.g. `/example/:param` should now be `/example/[param]`) |
| 195 | +:: |
| 196 | + |
| 197 | +You can use the `defineI18nRoute` compiler macro to set custom paths for each page component. |
| 198 | + |
| 199 | +```html {}[pages/about.vue] |
| 200 | +<script setup> |
| 201 | + defineI18nRoute({ |
| 202 | + paths: { |
| 203 | + en: '/about-us', // -> accessible at /about-us (no prefix since it's the default locale) |
| 204 | + fr: '/a-propos', // -> accessible at /fr/a-propos |
| 205 | + es: '/sobre' // -> accessible at /es/sobre |
| 206 | + } |
| 207 | + }) |
| 208 | +</script> |
| 209 | +``` |
| 210 | + |
| 211 | +To configure a custom path for a dynamic route, you need to use it in double square brackets in the paths similarly to how you would do it in [Nuxt Dynamic Routes](https://nuxt.com/docs/guide/directory-structure/pages#dynamic-routes): |
| 212 | + |
| 213 | +```html {}[pages/articles/[name].vue] |
| 214 | +<script setup> |
| 215 | + defineI18nRoute({ |
| 216 | + paths: { |
| 217 | + en: '/articles/[name]', |
| 218 | + es: '/artículo/[name]' |
| 219 | + } |
| 220 | + }) |
| 221 | +</script> |
| 222 | +``` |
| 223 | + |
| 224 | +::callout{icon="i-heroicons-light-bulb"} |
| 225 | +`defineI18nRoute` compiler macro is tree-shaked out at build time and is not included in the dist files. |
| 226 | +:: |
| 227 | + |
| 228 | + |
| 229 | +### `definePageMeta({ name: '...' })` caveat |
| 230 | + |
| 231 | +By default Nuxt overwrites generated route values at build time which breaks custom named routes (setting `name` with `definePageMeta`) when resolving localized paths. |
| 232 | + |
| 233 | +Nuxt v3.10 introduced the experimental feature [`scanPageMeta`](https://nuxt.com/docs/guide/going-further/experimental-features#scanpagemeta), this needs to be enabled for custom named routes to work when using Nuxt I18n. |
| 234 | + |
| 235 | +This experimental feature can be enabled as shown here: |
| 236 | + |
| 237 | +```typescript {}[nuxt.config.ts] |
| 238 | +export default defineNuxtConfig({ |
| 239 | + experimental: { |
| 240 | + scanPageMeta: true, |
| 241 | + } |
| 242 | +}) |
| 243 | +``` |
0 commit comments