Next.jsのアプリを国際化対応する

こんにちは。**dende**です。

今回は**Next.js製のアプリを国際化対応**したので、その備忘録です。

**SEO対策やアドセンス審査でも英語のサイトが有利**ということで、私のアプリも対応させることにしました！

[**Re:terature**](https://novel-editor-ver2.vercel.app/)という**小説作成アプリ**を公開しています。

今回は色々調べましたが、**汎用性の高そうな下記の記事**の方法を参考させて頂きました。

## 参考記事

[Next.jsで多言語対応のサイトを作るのが簡単すぎた件](https://zenn.dev/steelydylan/articles/nextjs-with-i18n)
[たった10分で Next.js アプリを日本語対応させた話](https://zenn.dev/yuyao17/articles/5cfa65d7e7eb11)
[Next.jsでi18n対応する方法](https://zenn.dev/mybest_dev/articles/324aed92f8086f)

## i18nのルーティング設定

**国際化対応**のための**i18n**は**Next.jsにビルトイン**されていますのでそれを使います。

[公式](https://nextjs.org/docs/pages/building-your-application/routing/internationalization)

特に特別なライブラリをインストールするわけではなく、`next.congig.js`に設定を追加するだけで利用できます。

```
// next.config.js
module.exports = {
  i18n: {
    locales: ['en', 'ja'],
    defaultLocale: 'en',
  },
}
```

上記のように設定すると**ルーティングの際にロケールが判定**されます。

例えば[**Re:terature**](https://novel-editor-ver2.vercel.app/)のトップにアクセスする場合

```tsx
https://novel-editor-ver2.vercel.app/ja/

https://novel-editor-ver2.vercel.app/en/
```

という**パス**で**アクセス**されるようになります。

## `useRouter`でロケールを判定するカスタムフックを使う

色々調べると、**Next.js用のライブラリを使った方法**が良く出てきます。

[Next.jsでi18n対応する方法](https://zenn.dev/mybest_dev/articles/324aed92f8086f)

こちらも試しましたが、今回紹介する**カスタムフックを使った方法**が簡単に実装できて特に余計なライブラリをインストールする必要がないのでそちらを実施します。

**カスタムフックの詳細は下記の記事を参考にしています。**

[たった10分でNext.jsアプリを日本語対応させた話](https://zenn.dev/yuyao17/articles/5cfa65d7e7eb11)

### カスタムフックのコード

```tsx
import { useRouter } from "next/router";
import en from "../../locales/en/en";
import ja from "../../locales/ja/ja";

export const useLocale = () => {
	const { locale } = useRouter();
	const t = locale === "en" ? en : ja;
	return { locale, t };
};
```

上記のカスタムフックは`useRouter`を使用してロケールを取得し、その**取得したロケール**、もしくは**インポートした翻訳を定義したオブジェクト**を`t`として返す**カスタムフック**です。

### 翻訳ファイルはそれぞれ下記のように作成

```tsx
///locales/ja/ja.ts
export default {
	errorPage: {
		error: "送信エラーが発生しました。",
		afterShortTime: "暫く経ってからお問い合わせください。"
	},
｝
```

```tsx
///locales/en/en.ts
export default {
	errorPage: {
		error: "A transmission error has occurred.",
		afterShortTime: "Please contact us after a while."
	},
｝
```

### ページでカスタムフックを使ってt.errorPage.errorとオブジェクトへアクセス

```tsx
import { Heading, Text } from "@chakra-ui/react";
import { useLocale } from "../hooks/useLocale";

export default function Error() {
	const { t } = useLocale();
	return (
		<>
				　<Heading as="h1" fontSize="3xl" mb="4">
						Error!
					</Heading>
					<Text>{t.errorPage.error}</Text>
					<Text>{t.errorPage.afterShortTime}</Text>
		</>
	);
}
```

ちなみにこんな感じで**日本語と英語のコンポーネントをあらかじめ二つ作って**出し分けも使えます。

```tsx
import { EnPolicy } from "../components/policy/EnPolicy";
import { JaPolicy } from "../components/policy/JaPolicy";
import { useLocale } from "../hooks/useLocale";

export default function PrivacyPolicy() {
	const { locale } = useLocale();

	return (
		<>
			{locale === "ja" && <JaPolicy />}
			{locale === "en" && <EnPolicy />}
		</>
	);
}
```

上記はPolicyのページで、**文章量が多く一つ一つの置き換えが大変＋普段あまり更新しない箇所**ですので全部まとめて翻訳したコンポーネントにしてしまいました。

## `ISR`や`SSG`などでダイナミックルーティングを利用する場合

こちらは**私のサイトだと該当しない**ため実際に行っていませんが、参考した記事の中で解説されています。

[Next.jsで多言語対応のサイトを作るのが簡単すぎた件](https://zenn.dev/steelydylan/articles/nextjs-with-i18n)

### **以下引用**

> getStaticPropsを使ってる場合は以下のようにpathsを余計に返すことに注意する必要がありました。
> 

```tsx
// pages/blog/[slug].js
export const getStaticPaths = ({ locales }) => {
  return {
    paths: [
      // if no `locale` is provided only the defaultLocale will be generated
      { params: { slug: 'post-1' }, locale: 'en' },
      { params: { slug: 'post-1' }, locale: 'ja' },
    ],
    fallback: true,
  }
}
```

> 以下のように初めに一つの言語でpathを作っておいてそれを元に別の言語を複製する方法が楽でおすすめです。
> 

```tsx
export const getStaticPaths: GetStaticPaths = async () => {
  const paths = categories.map(c => ({
    params: {
      slug: c.slug as string,
    },
    locale: 'ja',
  }))
  // 英語に対してもpathを作成
  paths.push(...paths.map(p => ({ ...p, locale: 'en' })))

  return {
    paths,
    fallback: false
  }
}
```

## 最後にアプリ紹介

ここまで読んで頂いた方は**ありがとうございました**。

最後に**公開しているアプリ**を載せておきますのでもしよろしければ拝見いただき、改善の**アドバイス**など頂けると幸いです。

**小説作成アプリ『[Re:terature](https://novel-editor-ver2.vercel.app/)』**