こんにちは。dendeです。
今回はNext.js製のアプリを国際化対応したので、その備忘録です。
SEO対策やアドセンス審査でも英語のサイトが有利ということで、私のアプリも対応させることにしました!
Re:teratureという小説作成アプリを公開しています。
今回は色々調べましたが、汎用性の高そうな下記の記事の方法を参考させて頂きました。
参考記事
Next.jsで多言語対応のサイトを作るのが簡単すぎた件
たった10分で Next.js アプリを日本語対応させた話
Next.jsでi18n対応する方法
i18nのルーティング設定
国際化対応のためのi18nはNext.jsにビルトインされていますのでそれを使います。
特に特別なライブラリをインストールするわけではなく、next.congig.js
に設定を追加するだけで利用できます。
// next.config.js
module.exports = {
i18n: {
locales: ['en', 'ja'],
defaultLocale: 'en',
},
}
上記のように設定するとルーティングの際にロケールが判定されます。
例えばRe:teratureのトップにアクセスする場合
https://novel-editor-ver2.vercel.app/ja/
https://novel-editor-ver2.vercel.app/en/
というパスでアクセスされるようになります。
useRouter
でロケールを判定するカスタムフックを使う
色々調べると、Next.js用のライブラリを使った方法が良く出てきます。
こちらも試しましたが、今回紹介するカスタムフックを使った方法が簡単に実装できて特に余計なライブラリをインストールする必要がないのでそちらを実施します。
カスタムフックの詳細は下記の記事を参考にしています。
カスタムフックのコード
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
として返すカスタムフックです。
翻訳ファイルはそれぞれ下記のように作成
///locales/ja/ja.ts
export default {
errorPage: {
error: "送信エラーが発生しました。",
afterShortTime: "暫く経ってからお問い合わせください。"
},
}
///locales/en/en.ts
export default {
errorPage: {
error: "A transmission error has occurred.",
afterShortTime: "Please contact us after a while."
},
}
ページでカスタムフックを使ってt.errorPage.errorとオブジェクトへアクセス
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>
</>
);
}
ちなみにこんな感じで日本語と英語のコンポーネントをあらかじめ二つ作って出し分けも使えます。
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
などでダイナミックルーティングを利用する場合
こちらは私のサイトだと該当しないため実際に行っていませんが、参考した記事の中で解説されています。
以下引用
getStaticPropsを使ってる場合は以下のようにpathsを余計に返すことに注意する必要がありました。
// 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を作っておいてそれを元に別の言語を複製する方法が楽でおすすめです。
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』