LoginSignup
24
21

More than 5 years have passed since last update.

Nuxtで言語別ルーティングを実装する

Last updated at Posted at 2018-09-05

前振り: 言語別ルーティング

言語別にディレクトリを切ってコンテンツ表示するサイトをnuxtで実装します。
(コンテンツ内容が違うだけで、使用コンポーネントは同一とします)

  • /en/index.html
  • /en/posts/index.html
  • /en/posts/1/index.html
  • /ja/index.html
  • /ja/posts/index.html
  • /ja/posts/2/index.html

nuxtでのディレクトリ構成

ルーティング - Nuxt.js

  • pages/
    • _lang/
      • index.vue
      • posts/
        • index.vue
        • _id.vue

nuxtではpages下に配置したコンポーネントの構成からroutesが作られます。
今回は、langとpostIdが動的となるため、langとidにアンダースコアでプレフィックスをつけることで実装できます。

生成されるroutes

nuxt/router.js
    routes: [
      {
        path: '/:lang',
        component: _4579e935,
        name: 'lang'
      },
      {
        path: '/:lang/posts',
        component: _14cb8c8e,
        name: 'lang-posts'
      },
      {
        path: '/:lang/posts/:id?',
        component: _08a5533e,
        name: 'lang-posts-id'
      }
    ]

ビルドして .nuxt/router.js を見るとディレクトリ構造から生成されたroutesを確認できます。
pathに : がついた部分が動的ルーティングとなり、params.lang、params.idとして値を受け取って表示を切り分けることができます。

本題: デフォルト言語の場合にはそのディレクトリを省略する構成

サイトのデフォルト言語の場合には、ディレクトリを切らずに直下に表示したい、とします。

  • /index.html
  • /posts/index.html
  • /posts/1/index.html
  • /ja/index.html
  • /ja/posts/index.html
  • /ja/posts/2/index.html

(enをデフォルトとした場合)

nuxtでのディレクトリ構成

  • pages/
    • _lang.vue
    • _lang/
      • posts/
        • index.vue
        • _id.vue

今回はlang部分が無いケースが出てくるため、lang部分を省略できるようにlang下にindex.vueを置かず、_lang.vueに変更してみます。

生成されるroutes

nuxt/router.js
    routes: [
      {
        path: '/:lang?',
        component: _655c5f9c,
        name: 'lang',
        children: [
          {
            path: 'posts',
            component: _14cb8c8e,
            name: 'lang-posts'
          },
          {
            path: 'posts/:id?',
            component: _08a5533e,
            name: 'lang-posts-id'
          }
        ]
      }
    ]

index.vueを持たない動的ルーティングのディレクトリは /:lang? のように ? がつくので省略された場合でもマッチするようになります。

ただしこれではうまく動きません。

/:lang? だと全てのrouteにマッチしてしまうからです。

本当に欲しいroutes

nuxt/router.js
    routes: [
      {
        path: '/',
        component: _0ff1b892,
        name: 'index'
      },
      {
        path: '/posts',
        component: _5e83ea8a,
        name: 'posts'
      },
      {
        path: '/posts/:id',
        component: _4f95003a,
        name: 'posts-id'
      },
      {
        path: '/:lang',
        component: _0ff1b892,
        name: 'lang-index'
      },
      {
        path: '/:lang/posts',
        component: _5e83ea8a,
        name: 'lang-posts'
      },
      {
        path: '/:lang/posts/:id',
        component: _4f95003a,
        name: 'lang-posts-id'
      }
    ]

最終的なroutesから考えるとこうならないといけません。

どうやるか

TL;DR

  • langディレクトリを作らない
  • extendで動的に生やす
  • nuxt-linkを拡張する

nuxtでのディレクトリ構成

  • pages/
    • index.vue
    • posts/
      • index.vue
      • _id.vue

とりあえずlang無しの構成を作ります。

nuxt/router.js
    routes: [
      {
        path: '/',
        component: _0ff1b892,
        name: 'index'
      },
      {
        path: '/posts',
        component: _5e83ea8a,
        name: 'posts'
      },
      {
        path: '/posts/:id',
        component: _4f95003a,
        name: 'posts-id'
      }
   ]

この段階ではこれだけです。

extendRoutesで言語別ルーティングを追加

nuxt.config.js
const makeLangRoutes = (routes, paramName) =>
  routes.map(route => ({
    ...route,
    path: `/:${paramName}${route.path}`.replace(/\/$/, ''),
    name: `${paramName}-${route.name}`
  }))

const conf = {
  router: {
    extendRoutes(routes) {
      routes.push(...makeLangRoutes(routes, 'lang'))
    },
    middleware: ['lang']
  },
  plugins: ['@plugins/langLink.js'],
}

module.exports = conf

routerのextendRoutesで現在のroutesをベースにして、pathとnameに paramNameとして 'lang' を加えたrouteを作って追加します。

routes生成についてはこれで対応できます。

nuxt-linkを拡張して表示中の言語で遷移

routesは作れましたが、言語別でrouteのnameが変わるため、ページ遷移する際にはnuxt-linkに対してnameとparamを適切に指定する必要が出てきます。

(デフォルト言語の場合)

<template lang="pug">
  nuxt-link(
    :to="{ name:'posts-id', params: {id: post.id, lang} }"
  ) {{post.title}}

(デフォルト言語以外の場合)

<template lang="pug">
  nuxt-link(
    :to="{ name:'lang-posts-id', params: {id: post.id, lang} }"
  ) {{post.title}}

いちいち場合分けして入れるのは大変なので、nuxt-linkを拡張して自動的に設定されるようにします。

middleware

middleware/lang.js
export default function({ params: { lang }, store }) {
  store.commit('lang', { lang })
}

まずはページ遷移時にstoreにparamを保存します。

store

store/index.js
const state = () => ({
  lang: null,
  defaultLang: 'en'
})
const mutations = {
  lang(state, { lang }) {
    state.lang = lang || state.defaultLang
  }
}
export default {
  state,
  mutations
}

store側ではstateに現在のlangを保持するように作っておきます。

link component

components/LangLink.js
const newData = (data, { lang, defaultLang }) => {
  const to = data.attrs.to
  const currentLang = (to && to.params && to.params.lang) || lang
  if (currentLang && currentLang !== defaultLang) {
    to.name = `lang-${to.name}`
  }
  return data
}

export default {
  name: 'lang-link',
  functional: true,
  render(h, {data, children, parent}) {
    return h('nuxt-link', newData(data, parent.$store.state), children)
  }
}

nuxt-linkを拡張したコンポーネントです。
storeから現在の言語を取得して、デフォルト言語でなければroute先の名前を変更する処理を行います。
(functional componentからstoreへのアクセスはparent経由で行います)

plugin

plugins/langLink.js
import Vue from 'vue'
import LangLink from '@/components/LangLink.js'

Vue.component(LangLink.name, LangLink)

上で作ったコンポーネントをグローバルで使えるようにpluginで登録します。

コンポーネントで使用

<template lang="pug">
  lang-link(
    :to="{ name:'posts-id', params: {id: post.id} }"
  ) {{post.title}}

nuxt-linkだった部分をlang-linkに書き換えます。
これで各コンポーネント側からはlang情報を指定せずに遷移できるようになります。

参照

(generate時)

module.js
    const langs = await api.get('langs')
    const posts = await api.get('posts')
    const langRE = new RegExp(`^${defaultLang}$`)
    const omitLangIfDefault = lang => lang.replace(langRE, '')
    this.options.generate.routes = [
      ...langs.reduce(
        (acc, lang) => [
          ...acc,
          ...['', 'posts'].map(route => ({
            route: `${omitLangIfDefault(lang)}/${route}`
          })),
        ],
        []
      ),
      ...posts.map(post => ({
        route: `${omitLangIfDefault(post.lang)}/posts/${post.id}`
      }))
    ]

generateする場合は、APIからコンテンツ取得後にroutesを構築しますが、その際にデフォルト言語であればpathを削るという処理をします。

24
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
21