早押しボタンweb

Next.jsのSSG(Static Exports)にて、自作コードで簡易的に多言語対応する方法

成果物

ソースコード

GitHub

デモ

デモページ
フッターから言語を選択すれば、URLやtitleが変更されることがわかると思います。

やりたいこと

なかなか上手いこといくライブラリが無かったので自分で実装してみました。今回は日本語と英語のみの実装となります。

環境

実装

ディレクトリイメージ

[lang]page.tsx
    └ demo - page.tsx

分かりにくいかと思いますが、全てのページは [lang] 以下に配置され、lang のパラメータによってテキストが変更されるというイメージです。言語ファイルは Language.tsx にまとめて書きます。

トップページ

トップページからは各言語のトップページへの遷移のみを行います。つまり言語が ja ならば、トップページは /ja/ になります。

1'use client';
2import { useRouter } from 'next/navigation';
3import { useEffect } from 'react';
4
5export default function Home() {
6  const router = useRouter();
7  useEffect(() => {
8    const language: string =
9      window.navigator.language || window.navigator.language[0];
10    if (language.includes('ja')) {
11      router.push('/ja/');
12    } else {
13      router.push('/en/');
14    }
15  }, [router]);
16  return <></>;
17}
18

Language.tsx

言語ごとに文章をまとめたファイルです。これはページごとに管理してもいいかもしれません。

1const langType = ['ja', 'en'];
2
3type LangType = (typeof langType)[number];
4
5type Meta = {
6  title: string;
7  description: string;
8};
9type I18nText = {
10  home: Meta & {
11    head: string;
12    body: string;
13    demo: string;
14  };
15  demo: Meta & {
16    head: string;
17    body: string;
18    home: string;
19  };
20  footer: {
21    footer: string;
22  };
23};
24
25const i18nText: Record<LangType, I18nText> = {
26  ja: {
27    home: {
28      title: 'サイトタイトル',
29      description: 'サイト説明文。',
30      head: 'トップページ',
31      body: 'ここはトップページです。',
32      demo: 'デモページ',
33    },
34    demo: {
35      title: 'ページ',
36      description: 'ページ説明文。',
37      head: 'デモページ',
38      body: 'ここはデモページです。',
39      home: 'ホームページ',
40    },
41    footer: {
42      footer: 'フッター',
43    },
44  },
45  en: {
46    home: {
47      title: 'Site Title',
48      description: 'Site description.',
49      head: 'Home Page',
50      body: 'Here is Home Page',
51      demo: 'Demo Page',
52    },
53    demo: {
54      title: 'Page',
55      description: 'Page description.',
56      head: 'Demo Page',
57      body: 'Here is Demo Page',
58      home: 'Home Page',
59    },
60    footer: {
61      footer: 'footer',
62    },
63  },
64};
65
66export { i18nText, langType };
67export type { LangType };
68

/[lang]/layout.tsx

メタタグは layout.tsx で書きます。

1import { ReactNode } from 'react';
2import type { Metadata } from 'next';
3import { i18nText, LangType } from '@/components/Language';
4
5type Params = {
6  lang: LangType;
7};
8
9export async function generateMetadata({
10  params,
11}: {
12  params: Promise<Params>;
13}): Promise<Metadata> {
14  const { lang } = await params;
15  const t = i18nText[lang];
16
17  const title = t.home.title;
18  const description = t.home.description;
19
20  return {
21    title,
22    description,
23  };
24}
25
26export const dynamicParams = false;
27
28export function generateStaticParams() {
29  return [{ lang: 'ja' }, { lang: 'en' }];
30}
31
32export default function Layout({ children }: { children: ReactNode }) {
33  return <>{children}</>;
34}
35

/[lang]/page.tsx

languseParams() により取得します。

1'use client';
2import { useParams } from 'next/navigation';
3import { i18nText, LangType } from '@/components/Language';
4import Container from '@/components/Container';
5import Link from 'next/link';
6
7export default function Page() {
8  const params = useParams<{ lang: LangType }>();
9  const { lang } = params;
10  const t = i18nText[lang];
11
12  return (
13    <Container lang={lang}>
14      <div className='bg-white p-2'>
15        <h1 className='text-center text-2xl'>{t.home.head}</h1>
16        <p>{t.home.body}</p>
17        <Link href={`/${lang}/demo/`} className='text-sky-600'>
18          {t.home.demo}
19        </Link>
20      </div>
21    </Container>
22  );
23}
24

まとめ

小規模のサイトや web アプリでは使えるのではないでしょうか。

記事一覧へ戻る