Skip to content

Commit

Permalink
feat: add localeRoute API for getting the localized route (#729)
Browse files Browse the repository at this point in the history
Resolves #728
  • Loading branch information
rchl authored May 25, 2020
1 parent a671c49 commit 0c4bd52
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 21 deletions.
23 changes: 18 additions & 5 deletions docs/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ sidebar: auto

::: tip NOTE

All [Vue I18n properties and methods](http://kazupon.github.io/vue-i18n/api/#vue-injected-methods) (like `$t`, `$i18n`, `v-t` directive and others) are also available even though they are not listed here. Below are only ones added by `nuxt-i18n`.
All [Vue I18n properties and methods](http://kazupon.github.io/vue-i18n/api/#vue-injected-methods) (like `$t`, `$i18n`, `v-t` directive and others) are also available even though they are not listed here. Below are only the ones that are added by `nuxt-i18n`.
:::

### Methods
Expand Down Expand Up @@ -47,11 +47,26 @@ In `no_prefix` strategy, passing `locale` other than the current one is not supp
#### getRouteBaseName

- **Arguments**:
- route (type: `Route`(https://github.com/vuejs/vue-router/blob/f40139c27a9736efcbda69ec136cb00d8e00fa97/types/router.d.ts#L135), default: current route)
- route (type: [`Route`](https://github.com/vuejs/vue-router/blob/f40139c27a9736efcbda69ec136cb00d8e00fa97/types/router.d.ts#L135), default: current route)
- **Returns**: `string`

Returns base name of current (if argument not provided) or passed in `route`. Base name is name of the route without locale suffix and other metadata added by `nuxt-i18n`.

#### localeRoute <Badge text="v6.12.0+" />

- **Arguments**:
- route (type: `string` | [`Location`](https://github.com/vuejs/vue-router/blob/f40139c27a9736efcbda69ec136cb00d8e00fa97/types/router.d.ts#L125))
- locale (type: `string`, default: current locale)
- **Returns**: [`Route`](https://github.com/vuejs/vue-router/blob/f40139c27a9736efcbda69ec136cb00d8e00fa97/types/router.d.ts#L135) | `undefined`

Returns localized route for passed in `route`. If `locale` is not specified, uses current locale.

See also [Basic usage - nuxt-link](../basic-usage.html#nuxt-link).

::: warning
In `no_prefix` strategy, passing `locale` other than the current one is not supported.
:::

#### $nuxtI18nSeo

- **Arguments**:
Expand Down Expand Up @@ -84,9 +99,7 @@ Instance of [VueI18n class](http://kazupon.github.io/vue-i18n/api/#vuei18n-class

Updates stored locale cookie with specified locale code. Consider using `setLocale` instead if you want to switch locale.

#### setLocale

> :new: 6.1.0+
#### setLocale <Badge text="v6.1.0+" />

- **Arguments**:
- locale (type: `string`)
Expand Down
11 changes: 11 additions & 0 deletions docs/basic-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,14 @@ export default ({ app }) => {
const switchLocalePath = app.switchLocalePath('fr')
}
```

* `localeRoute` <Badge text="v6.12+" /> – Returns the route object for a given page. It works like `localePath` but returns full route resolved by Vue Router rather than just its full path. This can be useful since full path returned from `localePath` might not carry all information from provided input (for example route params that the page doesn't specify).

```vue
<a
href="#"
@click="$router.push(localeRoute({
name: 'index',
params: { foo: '1' }
})">Navigate</a>
```
38 changes: 25 additions & 13 deletions src/templates/plugin.routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ import {
} from './options'

function localePath (route, locale) {
const localizedRoute = localeRoute.call(this, route, locale)

if (!localizedRoute) {
return
}

return localizedRoute.fullPath
}

function localeRoute (route, locale) {
// Abort if no route or no locale
if (!route) {
return
Expand Down Expand Up @@ -63,26 +73,15 @@ function localePath (route, locale) {
localizedRoute.name = this.getRouteBaseName()
}

// otherwise resolve route via the route name
// Build localized route options
let name = localizedRoute.name + (strategy === STRATEGIES.NO_PREFIX ? '' : routesNameSeparator + locale)

// Match route without prefix for default locale
if (locale === defaultLocale && strategy === STRATEGIES.PREFIX_AND_DEFAULT) {
name += routesNameSeparator + defaultLocaleRouteNameSuffix
}

localizedRoute.name = name
localizedRoute.name = getLocaleRouteName(localizedRoute.name, locale)

const { params } = localizedRoute
if (params && params['0'] === undefined && params.pathMatch) {
params['0'] = params.pathMatch
}
}

// Resolve localized route
const { route: { fullPath } } = this.router.resolve(localizedRoute)
return fullPath
return this.router.resolve(localizedRoute).route
}

function switchLocalePath (locale) {
Expand Down Expand Up @@ -142,6 +141,17 @@ function getRouteBaseName (givenRoute) {
return route.name.split(routesNameSeparator)[0]
}

function getLocaleRouteName (routeName, locale) {
const name = routeName + (strategy === STRATEGIES.NO_PREFIX ? '' : routesNameSeparator + locale)

// Match route without prefix for default locale
if (locale === defaultLocale && strategy === STRATEGIES.PREFIX_AND_DEFAULT) {
return name + routesNameSeparator + defaultLocaleRouteNameSuffix
}

return name
}

const VueInstanceProxy = function (targetFunction) {
return function () {
const proxy = {
Expand Down Expand Up @@ -181,6 +191,7 @@ const plugin = {
Vue.mixin({
methods: {
localePath: VueInstanceProxy(localePath),
localeRoute: VueInstanceProxy(localeRoute),
switchLocalePath: VueInstanceProxy(switchLocalePath),
getRouteBaseName: VueInstanceProxy(getRouteBaseName)
}
Expand All @@ -192,6 +203,7 @@ export default (context) => {
Vue.use(plugin)
const { app } = context
app.localePath = NuxtContextProxy(context, localePath)
app.localeRoute = NuxtContextProxy(context, localeRoute)
app.switchLocalePath = NuxtContextProxy(context, switchLocalePath)
app.getRouteBaseName = NuxtContextProxy(context, getRouteBaseName)
}
4 changes: 4 additions & 0 deletions test/browser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ describe(browserString, () => {

expect(await (await page.$('#paths')).textContent()).toBe('/middleware,/fr/middleware-fr')
expect(await (await page.$('#name')).textContent()).toBe('middleware')
expect(JSON.parse(await (await page.$('#localizedRoute')).textContent())).toMatchObject({
name: 'middleware___fr',
fullPath: '/fr/middleware-fr'
})
})
})

Expand Down
1 change: 1 addition & 0 deletions test/fixture/basic/middleware/i18n-middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export default function ({ app }) {
// Tests localePath, switchLocalePath and getRouteBaseName from app context.
app.allLocalePaths = localeCodes.map(locale => app.switchLocalePath(locale))
app.routeBaseName = app.getRouteBaseName()
app.localizedRoute = app.localeRoute(app.routeBaseName, 'fr')
}
10 changes: 8 additions & 2 deletions test/fixture/basic/pages/middleware.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<div>
<div id="paths">{{ allLocalePaths }}</div>
<div id="name">{{ routeBaseName }}</div>
<div id="localizedRoute">{{ JSON.stringify(localizedRoute) }}</div>
</div>
</template>

Expand All @@ -17,13 +18,18 @@ export default {
asyncData ({ app }) {
return {
allLocalePaths: app.allLocalePaths.join(','),
routeBaseName: app.routeBaseName
routeBaseName: app.routeBaseName,
localizedRoute: {
name: app.localizedRoute.name,
fullPath: app.localizedRoute.fullPath
}
}
},
data () {
return {
allLocalePaths: [],
routeBaseName: ''
routeBaseName: '',
localizedRoute: {}
}
}
}
Expand Down
31 changes: 30 additions & 1 deletion test/module.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,11 +302,34 @@ describe('basic', () => {
expect(window.$nuxt.getRouteBaseName(aboutRoute)).toBe('about')
})

test('localePath, switchLocalePath, getRouteBaseName works from a middleware', async () => {
test('localeRoute returns localized route', async () => {
const window = await nuxt.renderAndGetWindow(url('/'))
expect(window.$nuxt.localeRoute('about', 'en')).toMatchObject({
name: 'about___en',
fullPath: '/about-us'
})
})

test('localeRoute with custom location object retains params', async () => {
const window = await nuxt.renderAndGetWindow(url('/'))
expect(window.$nuxt.localeRoute({ name: 'about', params: { foo: '1' } }, 'en')).toMatchObject({
name: 'about___en',
fullPath: '/about-us',
params: {
foo: '1'
}
})
})

test('localePath, switchLocalePath, getRouteBaseName, localeRoute works from a middleware', async () => {
const html = await get('/middleware')
const dom = getDom(html)
expect(dom.querySelector('#paths').textContent).toBe('/middleware,/fr/middleware-fr')
expect(dom.querySelector('#name').textContent).toBe('middleware')
expect(JSON.parse(dom.querySelector('#localizedRoute').textContent)).toMatchObject({
name: 'middleware___fr',
fullPath: '/fr/middleware-fr'
})
})

test('redirects to existing route', async () => {
Expand Down Expand Up @@ -594,6 +617,12 @@ describe('prefix_and_default strategy', () => {
await expect(get('/fr')).resolves.toContain('page: Accueil')
})

test('localeRoute returns localized route name for default locale', async () => {
const window = await nuxt.renderAndGetWindow(url('/'))
expect(window.$nuxt.localeRoute('index', 'en')).toMatchObject({ name: 'index___en___default', fullPath: '/' })
expect(window.$nuxt.localeRoute('index', 'fr')).toMatchObject({ name: 'index___fr', fullPath: '/fr' })
})

test('canonical SEO link is added to prefixed default locale', async () => {
const html = await get('/en')
const dom = getDom(html)
Expand Down
5 changes: 5 additions & 0 deletions types/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/* eslint-disable no-unused-vars */
import Vue from 'vue'
import Vuex from 'vuex'
import { Route } from 'vue-router'
import '../index'

const vm = new Vue()
Expand All @@ -24,6 +25,10 @@ path = vm.switchLocalePath(locale)

const routeBaseName: string = vm.getRouteBaseName(vm.$route)

// localeRoute

const localizedRoute: Route | undefined = vm.localeRoute('about', 'fr')

// $i18n

const cookieLocale: string | undefined = vm.$i18n.getLocaleCookie()
Expand Down
1 change: 1 addition & 0 deletions types/vue.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ declare module 'vue-i18n' {
declare module 'vue/types/vue' {
interface Vue {
localePath(route: RawLocation, locale?: string): string
localeRoute(route: RawLocation, locale?: string): Route | undefined
switchLocalePath(locale: string): string
getRouteBaseName(route?: Route): string
$nuxtI18nSeo(): NuxtI18nSeo
Expand Down

0 comments on commit 0c4bd52

Please sign in to comment.