markdown
    # IntlayerとNext.js 15アプリルーターでの国際化 (i18n) の始め方
    
    ## Intlayerとは?
    
    **Intlayer**は、モダンウェブアプリケーションにおける多言語サポートを簡素化するために設計された革新的なオープンソース国際化(i18n)ライブラリです。Intlayerは最新の**Next.js 15**フレームワークとシームレスに統合され、強力な**アプリルーター**も含まれています。効率的なレンダリングのために**サーバーコンポーネント**での動作に最適化されており、[**Turbopack**](https://nextjs.org/docs/architecture/turbopack)にも完全に対応しています。
    
    Intlayerを使用すると、以下を実現できます:
    
    - **宣言型辞書を使用して翻訳を簡単に管理**することができます。
    - **メタデータ、ルート、およびコンテンツを動的にローカライズ**することができます。
    - **クライアントサイドおよびサーバーサイドコンポーネントで翻訳にアクセス**することができます。
    - **自動生成された型を使用してTypeScriptサポート**を確保し、オートコンプリートとエラーチェックを向上させます。
    - **動的ロケール検出や切り替え**などの高度な機能を利用できます。
    
    > IntlayerはNext.js 12、13、14、15に対応しています。Next.js Page Routerを使用している場合は、この[ガイド](/ja/doc/environment/nextjs/next-with-Page-Router)を参照してください。Next.js 12、13、14でApp Routerを使っている場合は、この[ガイド](/ja/doc/environment/nextjs/14)を参照してください。
    
    ---
    
    ## Next.jsアプリケーションにIntlayerをセットアップする手順ガイド
    
    ### ステップ1: 依存関係をインストール
    
    npmを使用して必要なパッケージをインストールします:
    
    ```bash packageManager="npm"
    npm install intlayer next-intlayer
    ```
    • intlayer

      設定管理、翻訳、コンテンツ宣言、トランスパイレーション、CLIコマンドのための国際化ツールを提供するコアパッケージです。

    • next-intlayer

      IntlayerとNext.jsを統合するパッケージです。Next.js国際化のためのコンテキストプロバイダーとフックを提供します。さらに、IntlayerをWebpackTurbopackと統合するNext.jsプラグイン、およびユーザーの好ましいロケールを検出し、クッキーを管理し、URLリダイレクトを処理するためのミドルウェアを含んでいます。

    ステップ2: プロジェクトを設定する

    アプリケーションの言語を設定するための構成ファイルを作成します:

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";
    
    const config: IntlayerConfig = {
      internationalization: {
        locales: [
          Locales.JAPANESE,
          Locales.FRENCH,
          Locales.SPANISH,
          // その他のロケール
        ],
        defaultLocale: Locales.JAPANESE,
      },
    };
    
    export default config;

    この構成ファイルを通じて、ローカライズされたURL、ミドルウェアのリダイレクト、クッキー名、コンテンツ宣言の場所と拡張子の設定、Intlayerのログをコンソールに表示しないようにするなどを設定できます。利用可能なパラメータの完全なリストについては、設定ドキュメントを参照してください。

    ステップ3: Next.js設定にIntlayerを統合

    Next.jsのセットアップをIntlayerを使用するように設定します:

    next.config.mjs
    import { withIntlayer } from "next-intlayer/server";
    
    /** @type {import('next').NextConfig} */
    const nextConfig = {};
    
    export default withIntlayer(nextConfig);

    withIntlayer() Next.jsプラグインは、IntlayerをNext.jsに統合するために使用されます。これは、コンテンツ宣言ファイルのビルドを保証し、開発モードでそれらを監視します。WebpackまたはTurbopack環境内でIntlayerの環境変数を定義します。さらに、パフォーマンスを最適化するためのエイリアスを提供し、サーバーコンポーネントとの互換性を確保します。

    ステップ4: ロケール検出のためのミドルウェアを設定

    ユーザーの好ましいロケールを検出するためのミドルウェアを設定します:

    src/middleware.ts
    export { intlayerMiddleware as middleware } from "next-intlayer/middleware";
    
    export const config = {
      matcher:
        "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\..*|_next).*)",
    };

    intlayerMiddlewareは、ユーザーの好ましいロケールを検出し、設定で指定された適切なURLにリダイレクトします。また、ユーザーの好ましいロケールをクッキーに保存することを可能にします。

    ステップ5: 動的ロケールルートを定義

    RootLayoutからすべてを削除し、次のコードに置き換えます:

    src/app/layout.tsx
    import type { PropsWithChildren, FC } from "react";
    import "./globals.css";
    
    const RootLayout: FC<PropsWithChildren> = ({ children }) => children;
    
    export default RootLayout;

    RootLayoutコンポーネントを空にすることで、<html>タグにlangdir属性を設定できます。

    動的ルーティングを実装するために、[locale]ディレクトリに新しいレイアウトを追加してロケールのパスを設定します:

    src/app/[locale]/layout.tsx
    import type { NextLayoutIntlayer } from "next-intlayer";
    import { Inter } from "next/font/google";
    import { getHTMLTextDir } from "intlayer";
    
    const inter = Inter({ subsets: ["latin"] });
    
    const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {
      const { locale } = await params;
      return (
        <html lang={locale} dir={getHTMLTextDir(locale)}>
          <body className={inter.className}>{children}</body>
        </html>
      );
    };
    
    export default LocaleLayout;

    [locale]パスセグメントはロケールを定義するために使用されます。例:/en-US/abouten-USに、/fr/aboutfrに関連します。

    次に、アプリケーションレイアウトにgenerateStaticParams関数を実装します。

    src/app/[locale]/layout.tsx
    export { generateStaticParams } from "next-intlayer"; // 挿入行
    
    const LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {
      /*... 後のコード*/
    };
    
    export default LocaleLayout;

    generateStaticParamsは、アプリケーションがすべてのロケールの必要なページを事前にビルドすることを保証し、ランタイム計算を減少させ、ユーザーエクスペリエンスを向上させます。詳細については、Next.jsのgenerateStaticParamsに関するドキュメントを参照してください。

    ステップ6: コンテンツを宣言する

    翻訳を保存するためのコンテンツ宣言を作成して管理します:

    src/app/[locale]/page.content.ts
    import { t, type DeclarationContent } from "intlayer";
    
    const pageContent = {
      key: "page",
      content: {
        getStarted: {
          main: t({
            ja: "編集を始めるには",
            fr: "Commencez par éditer",
            es: "Comience por editar",
          }),
          pageLink: "src/app/page.tsx",
        },
      },
    } satisfies DeclarationContent;
    
    export default pageContent;

    コンテンツ宣言は、アプリケーション内の任意の場所で定義できますが、contentDirディレクトリに含まれている必要があります(デフォルトは./src)。また、コンテンツ宣言ファイルの拡張子と一致する必要があります(デフォルトは.content.{ts,tsx,js,jsx,mjs,cjs})。 詳細については、コンテンツ宣言のドキュメントを参照してください。

    ステップ7: コード内のコンテンツを利用

    アプリケーション全体でコンテンツ辞書にアクセスします:

    src/app/[locale]/page.tsx
    import type { FC } from "react";
    import { ClientComponentExample } from "@components/ClientComponentExample";
    import { ServerComponentExample } from "@components/ServerComponentExample";
    import { type NextPageIntlayer, IntlayerClientProvider } from "next-intlayer";
    import { IntlayerServerProvider, useIntlayer } from "next-intlayer/server";
    
    const PageContent: FC = () => {
      const { title, content } = useIntlayer("page");
    
      return (
        <>
          <p>{content.getStarted.main}</p>
          <code>{content.getStarted.pageLink}</code>
        </>
      );
    };
    
    const Page: NextPageIntlayer = async ({ params }) => {
      const { locale } = await params;
    
      return (
        <>
          <IntlayerServerProvider locale={locale}>
            <PageContent />
            <ServerComponentExample />
    
            <IntlayerClientProvider locale={locale}>
              <ClientComponentExample />
            </IntlayerClientProvider>
          </IntlayerServerProvider>
        </>
      );
    };
    
    export default Page;
    • IntlayerClientProviderは、クライアントサイドコンポーネントにロケールを提供するために使用されます。親コンポーネントの中であれば、どこにでも配置できますが、レイアウトに配置することが推奨されます。これにより、Next.jsはページ間でレイアウトコードを共有し、効率が向上します。レイアウトでIntlayerClientProviderを使用すると、各ページで再初期化することを避け、パフォーマンスが向上し、アプリケーション全体で一貫したローカライズコンテキストを維持できます。
    • IntlayerServerProviderは、サーバーの子要素にロケールを提供するために使用されます。レイアウト内で設定することはできません。

      レイアウトとページで共通のサーバーコンテキストを共有することはできません。なぜなら、サーバーコンテキストシステムはリクエストごとのデータストア(Reactのキャッシュメカニズム経由)のために基づいているからです。これにより、アプリケーションの異なるセグメントに対して各「コンテキスト」が再作成されます。プロバイダを共有レイアウトに配置すると、この隔離が壊れ、サーバーコンテキストの値がサーバーコンポーネントに正しく伝播されなくなります。

    src/components/ClientComponentExample.tsx
    "use client";
    
    import type { FC } from "react";
    import { useIntlayer } from "next-intlayer";
    
    export const ClientComponentExample: FC = () => {
      const content = useIntlayer("client-component-example"); // 関連するコンテンツ宣言を作成
    
      return (
        <div>
          <h2>{content.title} </h2>
          <p>{content.content}</p>
        </div>
      );
    };
    src/components/ServerComponentExample.tsx
    import type { FC } from "react";
    import { useIntlayer } from "next-intlayer/server";
    
    export const ServerComponentExample: FC = () => {
      const content = useIntlayer("server-component-example"); // 関連するコンテンツ宣言を作成
    
      return (
        <div>
          <h2>{content.title} </h2>
          <p>{content.content}</p>
        </div>
      );
    };

    もし、alttitlehrefaria-labelなどのstring属性でコンテンツを使用したい場合は、関数の値を呼び出す必要があります。例えば:

    jsx
    <img src={content.image.src.value} alt={content.image.value} />

    useIntlayerフックの詳細については、ドキュメントを参照してください。

    (オプション) ステップ8: メタデータの国際化

    ページのタイトルなどのメタデータを国際化したい場合は、Next.jsが提供するgenerateMetadata関数を使用できます。この関数の中でgetTranslationContent関数を使用してメタデータを翻訳します。

    src/app/[locale]/layout.tsx or src/app/[locale]/page.tsx
    import {
      type IConfigLocales,
      getTranslationContent,
      getMultilingualUrls,
    } from "intlayer";
    import type { Metadata } from "next";
    import type { LocalParams } from "next-intlayer";
    
    export const generateMetadata = ({
      params: { locale },
    }: LocalParams): Metadata => {
      const t = <T>(content: IConfigLocales<T>) =>
        getTranslationContent(content, locale);
    
      /**
       * 各ロケールのすべてのURLを含むオブジェクトを生成します。
       *
       * 例:
       * ```ts
       *  getMultilingualUrls('/about');
       *
       *  // 返り値
       *  // {
       *  //   ja: '/about',
       *  //   fr: '/fr/about',
       *  //   es: '/es/about',
       *  // }
       * ```
       */
      const multilingualUrls = getMultilingualUrls("/");
    
      return {
        title: t<string>({
          ja: "私のタイトル",
          fr: "Mon titre",
          es: "Mi título",
        }),
        description: t({
          ja: "私の説明",
          fr: "Ma description",
          es: "Mi descripción",
        }),
        alternates: {
          canonical: "/",
          languages: { ...multilingualUrls, "x-default": "/" },
        },
        openGraph: {
          url: multilingualUrls[locale],
        },
      };
    };
    
    // ... 後のコード

    メタデータの最適化については、Next.jsの公式ドキュメントを参照してください。

    (オプション) ステップ9: sitemap.xmlとrobots.txtの国際化

    sitemap.xmlrobots.txtを国際化するために、Intlayerが提供するgetMultilingualUrls関数を使用できます。この関数を用いることで、サイトマップ用の多言語URLを生成できます。

    src/app/sitemap.ts
    import { getMultilingualUrls } from "intlayer";
    import type { MetadataRoute } from "next";
    
    const sitemap = (): MetadataRoute.Sitemap => [
      {
        url: "https://example.com",
        alternates: {
          languages: getMultilingualUrls("https://example.com"),
        },
      },
      {
        url: "https://example.com/login",
        alternates: {
          languages: getMultilingualUrls("https://example.com/login"),
        },
      },
      {
        url: "https://example.com/register",
        alternates: {
          languages: getMultilingualUrls("https://example.com/register"),
        },
      },
    ];
    
    export default sitemap;
    src/app/robots.ts
    import type { MetadataRoute } from "next";
    import { getMultilingualUrls } from "intlayer";
    
    const getAllMultilingualUrls = (urls: string[]) =>
      urls.flatMap((url) => Object.values(getMultilingualUrls(url)) as string[]);
    
    const robots = (): MetadataRoute.Robots => ({
      rules: {
        userAgent: "*",
        allow: ["/"],
        disallow: getAllMultilingualUrls(["/login", "/register"]),
      },
      host: "https://example.com",
      sitemap: `https://example.com/sitemap.xml`,
    });
    
    export default robots;

    サイトマップの最適化に関する詳細は、Next.jsの公式ドキュメントを参照してください。robots.txtの最適化に関する詳細は、Next.jsの公式ドキュメントを参照してください。

    (オプション) ステップ10: コンテンツの言語を変更

    コンテンツの言語を変更するには、useLocaleフックが提供するsetLocale関数を使用できます。この関数を使用すると、アプリケーションのロケールを設定し、それに応じてコンテンツを更新できます。

    src/components/LocaleSwitcher.tsx
    "use client";
    
    import type { FC } from "react";
    import {
      Locales,
      getHTMLTextDir,
      getLocaleName,
      getLocalizedUrl,
    } from "intlayer";
    import { useLocale } from "next-intlayer";
    import Link from "next/link";
    
    export const LocaleSwitcher: FC = () => {
      const { locale, pathWithoutLocale, availableLocales, setLocale } =
        useLocale();
    
      return (
        <ol>
          {availableLocales.map((localeItem) => (
            <li key={localeItem}>
              <Link
                href={getLocalizedUrl(pathWithoutLocale, localeItem)}
                hrefLang={localeItem}
                aria-current={locale === localeItem ? "page" : undefined}
                onClick={(e) => {
                  e.preventDefault();
                  setLocale(localeItem);
                }}
              >
                <span>
                  {/* 自国のロケールでの言語名 - 例: 日本語 */}
                  {getLocaleName(localeItem, locale)}
                </span>
                <span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
                  {/* 現在のロケールでの言語名 - 例: 日本語がLocales.SPANISHに設定されているときのEspañol */}
                  {getLocaleName(localeItem)}
                </span>
                <span dir="ltr" lang={Locales.ENGLISH}>
                  {/* 英語での言語名 - 例: Japanese */}
                  {getLocaleName(localeItem, Locales.ENGLISH)}
                </span>
                <span>
                  {/* 自国のロケールでの言語名 - 例: JA */}
                  {localeItem}
                </span>
              </Link>
            </li>
          ))}
        </ol>
      );
    };

    ドキュメント参照:

    TypeScriptの設定

    Intlayerは、TypeScriptの利点を活用し、コードベースを強化するためにモジュール拡張を使用します。

    alt text

    alt text

    TypeScript設定に自動生成された型が含まれていることを確認してください。

    tsconfig.json
    {
      // ... 既存のTypeScript構成
      "include": [
        // ... 既存のTypeScript構成
        "types", // 自動生成された型を含める
      ],
    }

    Gitの設定

    Intlayerによって生成されたファイルを無視することが推奨されます。これにより、それらをGitリポジトリにコミットすることを避けることができます。

    これを行うには、.gitignoreファイルに次の指示を追加します:

    .gitignore
    # Intlayerによって生成されたファイルを無視
    .intlayer

    このドキュメントを改善するアイデアがある場合は、GitHubでプルリクエストを送信することで自由に貢献してください。

    ドキュメントへのGitHubリンク