Nuxt.jsで多言語化デビューしよう！

## はじめに

こちらは[サポーターズCoLab](https://raw.githubusercontent.com/matsumana07384/resume/master/img/speak/spzcolab/supporterzcolab.com_event_776_20190325.png)の勉強会で使用した説明資料です。

`2019年04月末日`
再度[サポーターズCoLab](https://raw.githubusercontent.com/matsumana07384/resume/master/img/speak/spzcolab/supporterzcolab.com_event_807_20190423.png)の勉強会で使用するため、一部変更を加えました。

`2020年04月中旬`
情報が古くなっていたので、編集時現在時点のに更新しました。

---

### この記事に書いてあること
- Vue.jsを構築するためのフレームワークである[Nuxt.js](https://ja.nuxtjs.org/)のインストール
- [vue-i18n](https://kazupon.github.io/vue-i18n/)のインストールと設定
- 日本語と英語の切替ができる超簡易Webページの作成 

---

### この記事で省いていること
- 記事内で使用している技術およびコードの説明

---

## 環境
- 端末
  - Mac OS X 10.15.4
- インストール済みのライブラリ
  - [node.js](https://nodejs.org/ja/)
        - `v13.7.0`
  - [npm](https://www.npmjs.com/)
        - `6.14.2`

---

## Nuxt.jsのインストール
[公式ページ](https://ja.nuxtjs.org/guide/installation/)のインストール手順に沿って行っていきます。
今回は簡単に行うため、`npm`ライブラリの`npx`を使用します。

---

### npm（Node Package Manager）のバージョンの確認

``` shell:shell
npm -v 
```

`5.2.0`　以上が表示されていればOKです。

---

### Nuxtプロジェクトの作成

``` shell:shell
npx create-nuxt-app ColabApp
```
`ColabApp`の部分はホームページタイトルやフォルダ名に設定されます。
自身で管理しやすい名称を設定してください。

---

### サイト名の入力

``` 
? Project name (ColabApp)
```
（）の中身から変更しない場合はそのままEnterキーを押下しましょう。

---

### サイト詳細の入力

``` 
? Project description (My excellent Nuxt.js project)
```
（）の中身から変更しない場合はそのままEnterキーを押下しましょう。

---

### 著者名の選択

``` 
? Author name (your github name)
```
PCにGithubのアカウント設定がされていると自動的にアカウント名が表示されます。
今回はそのままEnterキーを押下します。

---

### 記述言語の選択

```
? Choose programming language (Use arrow keys)
❯ JavaScript
  TypeScript
```
今回は`JavaScript`を選択し、Enterキーを押下します。

型付け言語で記述したい場合は[TypeScript](https://www.typescriptlang.org/) を選択してください。

---

### パッケージマネージャの選択

``` 
? Choose the package manager
  Yarn
❯ Npm
```
処理速度やコマンドの簡単さで比較される両者ですが、今回は`npm`を選択します。

各パッケージマネージャーについては公式ページをご確認ください。

- npm
    - [Github](https://github.com/npm)
    - [公式ページ](https://www.npmjs.com/)
- yarn
    - [Github](https://github.com/yarnpkg/yarn/)
    - [公式ページ](https://yarnpkg.com/lang/ja/)

---

### UIフレームワークの選択

``` 
? Choose UI framework (Use arrow keys)
❯ None
  Ant Design Vue
  Bootstrap Vue
  Buefy
  Bulma
  Element
  Framevuerk
  iView
  Tachyons
  Tailwind CSS
  Vuesax
  Vuetify.js
```
今回は`None`を選択し、Enterキーを押下します。
このときに`None`を選んだ場合でも、あとから追加可能です。

---

各詳細については公式ページをご確認ください。

- Ant Design Vue
    - [Github](https://www.antdv.com/docs/vue/introduce/)
    - [公式ページ](https://github.com/vueComponent/ant-design-vue)

- Bootstrap Vue
    - [Github](https://github.com/bootstrap-vue/bootstrap-vue)
    - [公式ページ](https://bootstrap-vue.js.org/)

- Buefy
    - [Github](https://buefy.org)
    - [公式ページ](https://buefy.org/)

- Element
    - [Github](https://github.com/ElemeFE/element)
    - [公式ページ](https://element.eleme.io/#/en-US)

- Bulma
    - [Github](https://github.com/jgthms/bulma)
    - [公式ページ](https://bulma.io/)

- Framevuerk
    - [Github](https://github.com/framevuerk/framevuerk)
    - [公式ページ](https://framevuerk.com/)

- iView
    - [Github](https://github.com/view-design/ViewUI)
    - [公式ページ](https://www.iviewui.com/)

- Tachyons
    - [Github](https://github.com/tachyons-css/tachyons/)
    - [公式ページ](https://tachyons.io/) 

- Tailwind CSS
    - [Github](https://github.com/tailwindcss/tailwindcss)
    - [公式ページ](https://tailwindcss.com/) 

- Vuesax
    - [Github](https://github.com/lusaxweb/vuesax)
    - [公式ページ](https://lusaxweb.github.io/vuesax/) 

- Vuetify
    - [Github](https://github.com/vuetifyjs/vuetify)
    - [公式ページ](https://vuetifyjs.com/ja/)

---

### サーバーフレームワークの選択

``` 
? Choose custom server framework (Use arrow keys)
❯ None (Recommended)
  AdonisJs
  Express
  Fastify
  Feathers
  hapi
  Koa
  Micro
```
今回はNuxt.jsを使用するため`None (Recommended)`を選択し、Enterキーを押下します。
このときに`None`を選んだ場合でも、あとから追加可能です。

---

### Nuxt.jsの追加モジュールの選択

```
? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ Axios
 ◯ Progressive Web App (PWA) Support
 ◯ DotEnv
```
今回はいずれも使用しないため、そのままEnterキーを押下します。
このときに必要な機能を複数選択すること、あとから追加することも可能です。

各機能をざっくり説明しておきます。

- Axios
	- 非同期通信を簡HTTP通信ができるJavaScriptライブラリ
	- JSONの取得に利用されることが多い

- Progressive Web App (PWA) Support
	- ウェブとアプリの利点を備えたWebアプリの技術の一種であるPWAのサポート
	- PWAの詳細は[はじめてのプログレッシブ ウェブアプリ](https://developers.google.com/web/fundamentals/codelabs/your-first-pwapp/?hl=ja) を参照ください。

- DotEnv
  - 環境変数を定義するためのファイル
  - 開発環境や本番環境など環境ごとにリポジトリ内のファイルを変更せずに実行できる

---

### リンターツールの選択

``` 
? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to invert selection)　
❯◯ ESLint
 ◯ Prettier
 ◯ Lint staged files
 ◯ StyleLint
```

今回はESLintを使用するので、ESLintをspaceキーで選択し、Enterキーを押下します。
このときに必要な機能を複数選択すること、あとから追加することも可能です。

---

各機能をざっくり説明しておきます。

- ESLint
	- JavaScriptのソースコードの構文チェックを行うツール
	- 詳細は[公式ページ|ESLint - Pluggable JavaScript linter](https://eslint.org/)を参照ください。

- Prettier
	- ソースコードを整形するツール
	- 設定した規約に従ってソースコードを自動で整形するので、チーム開発で導入すると恩恵を受けられます。

- Lint staged files
  - gitのコミットの時にlintチェックができるアプリ
  - 詳細は[Github|okonet/lint-staged:— Run linters on git staged files](https://github.com/okonet/lint-staged)を参照ください。

- StyleLint
  - CSSのコードの構文チェックを行うツール
  - 詳細は [公式ページ|A mighty, modern style linter · stylelint](https://stylelint.io/)を参照ください。

---

### テストフレームワークの選択

``` 
? Choose test framework (Use arrow keys)
❯ None
  Jest
  AVA
```
今回はテストまでは行わないため、`None`を選択し、Enterキーを押下します。

---

それぞれの詳細については公式ページをご確認ください。

- JEST
    - [Github](https://github.com/facebook/jest)
    - [公式ページ](https://jestjs.io/ja/)
- AVA
    - [Github](https://github.com/avajs/ava)

---

### レンダリングモードの選択

``` 
? Choose rendering mode (Use arrow keys)
❯ Universal (SSR)
  Single Page App
```
今回は`Universal (SSR)`を選択し、Enterキーを押下します。

各モードについてざっくり説明します。

- Universal (SSR)
	- クライアントもサーバーサイドもNuxtで開発をする際に使用

- Single Page App
	- クライアントサイドのみの開発をする際に使用

---

### 開発ツールを選択

```
? Choose development tools (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ jsconfig.json (Recommended for VS Code)
 ◯ Semantic Pull Requests
```

今回は `jsconfig.json (Recommended for VS Code)` を spaceキーで選択し、Enterキーを押下します。


各ツールについてざっくり説明します。

- jsconfig.json (Recommended for VS Code)
   - 統合開発環境の1つである[Visual Studio Code](https://code.visualstudio.com/)でNuxt.jsの独自記法である `@` や `~` を認識できるようファイルを生成します。
   - Visual Studio Codeを使用している場合は、こちらをおすすめします。

- Semantic Pull Requests
   - コミット時のタイトルの付け方をチェックする機能
   - 詳細は [Github|zeke/semantic-pull-requests: Ensure your pull requests follow the Conventional Commits spec](https://github.com/zeke/semantic-pull-requests)を参照ください。

---

## インストール待機

ここまで設定を行うとインストールが開始されます。
インストールが完了するまでしばらく待ちましょう☕

---

## Nuxt.js の起動

``` shell:shell
🎉  Successfully created project ColabApp

  To get started:

	cd ColabApp
	npm run dev

  To build & start for production:

	cd ColabApp
	npm run build
	npm run start
```

インストールが完了すると、上記がターミナル上に表示されます。

---

### ディレクトリの移動

``` shell:shell
cd ColabApp
```

--- 

### `npm`でNuxt.jsを起動

``` shell:shell
npm run dev
```

---

### ブラウザでの表示確認

```
   ╭─────────────────────────────────────────────╮
   │                                             │
   │   Nuxt.js v2.12.2                           │
   │   Running in development mode (universal)   │
   │                                             │
   │   Listening on: http://localhost:3000/      │
   │                                             │
   ╰─────────────────────────────────────────────╯
```

```
ℹ Listening on: http://localhost:3000/
```

上記がターミナル上に表示されていれば、無事に起動成功です。
ブラウザから[http://localhost:3000](http://localhost:3000) へアクセスすると、下記のようなページが確認できます。

![localhost_3000_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186755/1bbaa95e-d94a-c035-89c1-c3a6b103ff8a.png)

---

## vue-i18nのインストール

[公式ドキュメント](https://ja.nuxtjs.org/examples/i18n/)に沿って進めていきます。
ライブラリのインストールを行うため、`Ctrl` + `C`のキーを押下し、開発環境を一度止めます。

---

### `npm`を使ってインストール

``` shell:shell
npm install vue-i18n --save
```

上記コマンドを実行します。
インストールが成功すると、下記が表示されます。

``` shell
+ vue-i18n@8.17.0
added 1 package from 1 contributor and audited 21144 packages in 33.795s
```

---

## 多言語化の設定

Webページで多言語の設定が反映されるように各ファイルの作成や修正を行います。

---

### 翻訳ファイルの管理フォルダを作成

`ColabApp` にフォルダを作成します。
フォルダ名は自由ですが、今回は `‌locales` ファイルを作成します。

---

### 翻訳ファイルを作成

`locales`フォルダに各言語のJSONファイルを作成します。
今回は英語用に`locales/en.json`と日本語用に`locales/ja.json`を作成します。

---

#### 英語翻訳ファイルの作成

``` json:en.json
{
  "links": {
    "home": "Home",
    "about": "About",
    "en": "English",
    "ja": "Japanese"
  },
  "home": {
    "title": "Hello,World!",
    "introduction": "This is an introduction in English."
  }
}
```

---

#### 日本語翻訳ファイルを作成

``` json:ja.json
{
  "links": {
    "home": "ほーむ",
    "about": "あばうと",
    "en": "えいご",
    "ja": "にほんご"
  },
  "home": {
    "title": "やぁ、世界！",
    "introduction": "このページは日本語で設定されています"
  }
}
```

---

※ 翻訳ファイル作成の注意点
各言語JSONの構造があっていないと、Webページ上で翻訳が表示されません。

---

## vue-i18nライブラリの設定
vue-i18nライブラリの設定を行っていきます。

---

### vue-i18n用のファイルを作成
`plugins`フォルダ直下に`i18n.js`ファイルを作成します。

---

### `plugins/i18n.js`に記述

``` javascript:plugins/i18n.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

export default ({ app, store }) => {
  app.i18n = new VueI18n({
    locale: store.state.locale,
    fallbackLocale: 'ja',
    messages: {
      ja: require('~/locales/ja.json'),
      en: require('~/locales/en.json')
    }
  })

  app.i18n.path = (link) => {
    if (app.i18n.locale === app.i18n.fallbackLocale) {
      return `/${link}`
    }

    return `/${app.i18n.locale}/${link}`
  }
}
```

### `plugins`について

`plugins`については[公式ドキュメント](https://ja.nuxtjs.org/guide/plugins/)をご確認ください。

---

## ページ表示ごとに言語を設定

ページの表示毎に現在の言語を設定できるようにしていきます。

---

### ファイルの作成

`middleware`フォルダに`i18n.js`ファイルを作成します。

---

### `middleware/i18n.js`に記述

``` javascript:middleware/i18n.js
export default function ({ isHMR, app, store, route, params, error, redirect }) {
  const defaultLocale = app.i18n.fallbackLocale

  if (isHMR) { return }

  const locale = params.lang || defaultLocale
  if (!store.state.locales.includes(locale)) {
    return error({ message: 'This page could not be found.', statusCode: 404 })
  }

  store.commit('SET_LANG', locale)
  app.i18n.locale = store.state.locale

  if (locale === defaultLocale && route.fullPath.indexOf('/' + defaultLocale) === 0) {
    const toReplace = '^/' + defaultLocale + (route.fullPath.indexOf('/' + defaultLocale + '/') === 0 ? '/' : '')
    const re = new RegExp(toReplace)
    return redirect(
      route.fullPath.replace(re, '/')
    )
  }
}
```

---

### `middleware`について
`middleware`については[公式ドキュメント](https://ja.nuxtjs.org/guide/routing#%E3%83%9F%E3%83%89%E3%83%AB%E3%82%A6%E3%82%A7%E3%82%A2)をご確認ください。

---

## Vuexストアを設定
Vuexストアに言語設定を行い、各ページで利用できるように設定します。

---

### ファイルの作成
`store`フォルダに`index.js`ファイルを作成します。

---

#### `store/index.js`の作成

``` javascript:store/index.js
export const state = () => ({
  locales: ['en', 'ja'],
  locale: 'ja'
})

export const mutations = {
  SET_LANG (state, locale) {
    if (state.locales.includes(locale)) {
      state.locale = locale
    }
  }
}
```

---

### `store`について
`store`については[公式ドキュメント](https://ja.nuxtjs.org/guide/vuex-store/)をご確認ください。

---

## Nuxt.js のカスタム設定
`nuxt.config.js` にプラグインとライブラリの設定をします。

---

### `nuxt.config.js`に追記
`nuxt.config.js`に追記します。

```javascript:nuxt.config.js
/*
他の要素を省略
*/
 //ここから
 router: {
  middleware: 'i18n'
  },
  plugins: [
    '~/plugins/i18n.js' //追加    '~/plugins/i18n.js' //追加
  ],
  generate: {
    routes: ['/', '/about', '/en', '/en/about']
  },
  //ここまでを追加
/*
他の要素を省略
*/
```

---
### `nuxt.config.js`について
`nuxt.config.js`の設定については[公式ドキュメント](https://ja.nuxtjs.org/guide/configuration/)をご確認ください。

---


以上で多言語化の設定は完了です🎉

---

## `index.vue`ファイルの編集
Webページに表示するため、`*.vue`ファイルを編集していきます。

---

### ディレクトリの作成
`pages`以下に`_lang`フォルダを作成します。

Nuxt.jsでは `pages`以下のディレクトリ構造がそのままURLのパス（ `http://domain/ファイル名` ）に対応します。

`_`をつけるとURLのパスから除外することができ、任意の文字列を埋め込むことが可能です。
今回は、`_lang`の部分に「en」を埋め込み、`http://domain/en`という形のURLにします。

---

### `index.vue`ファイルを`_lang`以下にコピー

`pages/index.vue`ファイルをコピーし、`pages/_lang/index.vue`を貼り付けます。

多言語化を行う上で、この工程は必須ではありません。
今回は作業の効率化するために行っています。

---

### `pages/index.vue`の編集

``` javascript:pages/index.vue
<script>
import Index from '~/pages/_lang/index'
export default Index
</script>

``` 

http://localhost:3000/ にアクセスした際に`pages/_lang/index.vue`を参照するように変更しました。

---

### `npm`でNuxt.jsを起動（再掲）

``` shell:shell
npm run dev
```

ここからは開発環境を起動しながら進めていきます。

---

### `pages/_lang/index.vue`ファイルの編集

``` javascript:pages/_lang/index.vue
<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        {{ $t('home.title') }} <!-- 編集前：ColabApp -->
      </h1>
      <h2 class="subtitle">
        {{ $t('home.introduction') }} <!-- 編集前：My excellent Nuxt.js project -->
      </h2>
      <div class="links">
        <a
          href="https://nuxtjs.org/"
          target="_blank"
          class="button--green"
        >
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>
      </div>
    </div>
  </div>
</template>
/*他の要素は省略*/
```

翻訳テキストで設定したJSONを指定します。
現時点でページには`locales/ja.json`で設定した日本語が表示されています。

![localhost_3000_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186755/3e5f252a-531c-9f44-cf05-cec5655a3013.png)


---

## `AppHeader.vue`ファイルの作成
言語切替を行うためのコンポーネント`components/AppHeader.vue`を作成します。

---
### `components/AppHeader.vue`への記述

``` html:components/AppHeader.vue
<template>
  <header class="Header">
    <div class="links">
      <NuxtLink :to="$i18n.path('')">
        {{ $t('links.home') }}
      </NuxtLink>
      <NuxtLink v-if="$i18n.locale === 'ja'" :to="`/en` + $route.fullPath">
        {{ $t('links.en') }}
      </NuxtLink>
      <NuxtLink v-else :to="$route.fullPath.replace(/^\/[^\/]+/, '')">
        {{ $t('links.ja') }}
      </NuxtLink>
    </div>
  </header>
</template>
```

今回は日本語表示のときは`英語`の切替、英語表示のときは`日本語`切替を表示する処理を入れています。

---

### レイアウトファイルの編集
`‌components/AppHeader.vue`がすべてのページで表示されるように設定します。

---

### `layouts/default.vue`ファイルを編集

``` javascript:layouts/default.vue
<template>
  <div>
    <AppHeader /><!-- 追加 -->
    <nuxt />
  </div>
</template>


//ここから↓
<script>
import AppHeader from '@/components/AppHeader.vue'

export default {
  components: {
    AppHeader
  }
}
</script>
//ここまで↑を追加
<style>
/* 内容を省略 */
</style>
```
`layouts/default.vue`では、全ページのレイアウトを設定できます。
今回は、作成したコンポーネント`AppHeader.vue`をインポートし、ヘッダーとして表示できるように設定しています。

---

## ページの確認

[http://localhost:3000](http://localhost:3000)を確認し、`locales/ja.json`に設定した日本語とヘッダーにリンクが表示されていれば設定完了です！

![localhost_3000_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186755/c295abd0-0852-402c-315c-c91e5d28b155.png)

---

`えいご`のリンクテキスト[http://localhost:3000/en](http://localhost:3000/en)をクリックすると、`locales/en.json`で設定したテキストが表示されます。

![localhost_3000_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186755/a29a9161-a199-cce6-a2be-19fd91d6f3e9.png)

---

以上で設定完了です。
ここまでお付き合い頂き、ありがとうございました😀

---

## 追加演習
`pages/_lang/about.vue`ファイルを追加し、ページを表示してみましょう

### やること
 - `en.json`と`ja.json`に`about`ページで使用するための文言を設定
 - `components/AppHeader.vue`にAboutページへのリンクを追加
 - `pages/about.vue`ページの作成と記述
 - `pages/_lang/about.vue`の作成と記述

### 回答例
[matsumana07384/nuxtjs-and-i18n-sample: for supporterzcolab event ( sample setting nuxt.js and i18n)](https://github.com/matsumana07384/nuxtjs-and-i18n-sample)

---

## 参考ページ
- [Nuxt.js公式ページ](https://ja.nuxtjs.org/)
- [Vue-i18n公式ページ](https://kazupon.github.io/vue-i18n/)
- [Nakamubolg|Nuxt.jsの国際化(i18n)対応](https://blog.nakamu.life/posts/nuxt-js-i18n/)
- [Nuxt.js(v2)でgenerate納品する前にやっておきたい設定 - Qiita](https://qiita.com/amishiro/items/11bd642728f6b5838189)

---