このページのコンテンツはAIを使用して翻訳されました。

    英語の元のコンテンツの最新バージョンを見る

    Getting Started internationalizing (i18n) with Intlayer and Next.js 15 App Router

    アプリケーションテンプレートをGitHubで見る。

    Intlayerを始める

    Intlayerは、最新のウェブアプリケーションで多言語対応を簡素化するために設計された、革新的なオープンソースの国際化(i18n)ライブラリです。Intlayerは、強力なApp Routerを含む最新のNext.js 15フレームワークとシームレスに統合されます。Server Componentsでの効率的なレンダリングに最適化されており、Turbopackとも完全に互換性があります。

    Intlayerを使用すると、以下のことが可能です:

    • コンポーネントレベルで宣言的な辞書を使用して簡単に翻訳を管理
    • メタデータ、ルート、およびコンテンツを動的にローカライズ
    • クライアントサイドおよびサーバーサイドのコンポーネントで翻訳にアクセス
    • 自動生成された型でTypeScriptサポートを確保し、オートコンプリートとエラー検出を改善。
    • 動的なロケール検出や切り替えなどの高度な機能を活用。

    IntlayerはNext.js 12、13、14、15と互換性があります。Next.js Page Routerを使用している場合は、このガイドを参照してください。Next.js 12、13、14でApp Routerを使用している場合は、このガイドを参照してください。


    Next.jsアプリケーションでIntlayerをセットアップするステップバイステップガイド

    ステップ1: 依存関係をインストールする

    npmを使用して必要なパッケージをインストールします:

    bash
    npm install intlayer next-intlayer
    • intlayer

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

    • next-intlayer

      IntlayerをNext.jsと統合するパッケージ。Next.jsの国際化のためのコンテキストプロバイダーとフックを提供します。また、WebpackまたはTurbopackとIntlayerを統合するNext.jsプラグインや、ユーザーの優先ロケールを検出し、クッキーを管理し、URLリダイレクションを処理するミドルウェアも含まれています。

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

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

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

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

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

    Next.jsの設定をIntlayerを使用するように構成します:

    typescript
    import type { NextConfig } from "next";
    import { withIntlayer } from "next-intlayer/server";
    
    const nextConfig: NextConfig = {
      /* 設定オプションをここに記述 */
    };
    
    export default withIntlayer(nextConfig);

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

    ステップ4: 動的ロケールルートを定義する

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

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

    RootLayoutコンポーネントを空に保つことで、langおよびdir属性を<html>タグに設定できます。

    動的ルーティングを実装するには、[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に関するドキュメントを参照してください。

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

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

    src/app/[locale]/page.content.ts
    import { t, type Dictionary } from "intlayer";
    
    const pageContent = {
      key: "page",
      content: {
        getStarted: {
          main: t({
            en: "Get started by editing",
            fr: "Commencez par éditer",
            es: "Comience por editar",
          }),
          pageLink: "src/app/page.tsx",
        },
      },
    } satisfies Dictionary;
    
    export default pageContent;

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

    ステップ6: コード内でコンテンツを利用する

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

    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 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フックの詳細については、ドキュメントを参照してください。

    (オプション) ステップ7: ロケール検出用ミドルウェアを設定する

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

    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にリダイレクトします。また、ユーザーの優先ロケールをクッキーに保存することも可能です。

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

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

    src/app/[locale]/layout.tsx or src/app/[locale]/page.tsx
    import {
      type IConfigLocales,
      getTranslation,
      getMultilingualUrls,
    } from "intlayer";
    import type { Metadata } from "next";
    import type { LocalPromiseParams } from "next-intlayer";
    
    export const generateMetadata = async ({
      params,
    }: LocalPromiseParams): Promise<Metadata> => {
      const { locale } = await params;
      const t = <T>(content: IConfigLocales<T>) => getTranslation(content, locale);
    
      /**
       * 各ロケールのすべてのURLを含むオブジェクトを生成します。
       *
       * 例:
       * ```ts
       *  getMultilingualUrls('/about');
       *
       *  // 結果
       *  // {
       *  //   en: '/about',
       *  //   fr: '/fr/about',
       *  //   es: '/es/about',
       *  // }
       * ```
       */
      const multilingualUrls = getMultilingualUrls("/");
    
      return {
        title: t<string>({
          en: "My title",
          fr: "Mon titre",
          es: "Mi título",
        }),
        description: t({
          en: "My description",
          fr: "Ma description",
          es: "Mi descripción",
        }),
        alternates: {
          canonical: multilingualUrls[locale as keyof typeof multilingualUrls],
          languages: { ...multilingualUrls, "x-default": "/" },
        },
        openGraph: {
          url: multilingualUrls[locale],
        },
      };
    };
    
    // ... 残りのコード

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

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

    sitemap.xmlおよびrobots.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 (
        <div>
          <button popoverTarget="localePopover">{getLocaleName(locale)}</button>
          <div id="localePopover" popover="auto">
            {availableLocales.map((localeItem) => (
              <Link
                href={getLocalizedUrl(pathWithoutLocale, localeItem)}
                hrefLang={localeItem}
                key={localeItem}
                aria-current={locale === localeItem ? "page" : undefined}
                onClick={(e) => {
                  e.preventDefault();
                  setLocale(localeItem);
                }}
              >
                <span>
                  {/* ロケール - 例: FR */}
                  {localeItem}
                </span>
                <span>
                  {/* 自身のロケールでの言語 - 例: Français */}
                  {getLocaleName(localeItem, locale)}
                </span>
                <span dir={getHTMLTextDir(localeItem)} lang={localeItem}>
                  {/* 現在のロケールでの言語 - 例: Francés (現在のロケールがLocales.SPANISHに設定されている場合) */}
                  {getLocaleName(localeItem)}
                </span>
                <span dir="ltr" lang={Locales.ENGLISH}>
                  {/* 英語での言語 - 例: French */}
                  {getLocaleName(localeItem, Locales.ENGLISH)}
                </span>
              </Link>
            ))}
          </div>
        </div>
      );
    };

    ドキュメント参照:

    (オプション) ステップ11: ローカライズされたリンクコンポーネントを作成する

    アプリケーションのナビゲーションが現在のロケールを尊重するようにするために、カスタムLinkコンポーネントを作成できます。このコンポーネントは、内部URLを自動的に現在の言語でプレフィックスします。例えば、フランス語を話すユーザーが「About」ページへのリンクをクリックすると、/aboutではなく/fr/aboutにリダイレクトされます。

    この動作は以下の理由で便利です:

    • SEOとユーザーエクスペリエンス:ローカライズされたURLは、検索エンジンが言語固有のページを正しくインデックスし、ユーザーに好みの言語でコンテンツを提供するのに役立ちます。
    • 一貫性:アプリケーション全体でローカライズされたリンクを使用することで、ナビゲーションが現在のロケール内に留まり、予期しない言語の切り替えを防ぎます。
    • 保守性:ローカリゼーションロジックを単一のコンポーネントに集中させることで、URLの管理が簡素化され、アプリケーションが成長するにつれてコードベースの保守性が向上します。

    以下は、TypeScriptでのローカライズされたLinkコンポーネントの実装例です:

    src/components/Link.tsx
    "use client";
    
    import { getLocalizedUrl } from "intlayer";
    import NextLink, { type LinkProps as NextLinkProps } from "next/link";
    import { useLocale } from "next-intlayer";
    import type { PropsWithChildren, FC } from "react";
    
    /**
     * 指定されたURLが外部リンクかどうかを確認するユーティリティ関数。
     * URLがhttp://またはhttps://で始まる場合、外部リンクと見なされます。
     */
    export const checkIsExternalLink = (href?: string): boolean =>
      /^https?:///.test(href ?? "");
    
    /**
     * 現在のロケールに基づいてhref属性を適応させるカスタムLinkコンポーネント。
     * 内部リンクの場合、`getLocalizedUrl`を使用してURLにロケールをプレフィックスします(例:/fr/about)。
     * これにより、ナビゲーションが同じロケールコンテキスト内に留まることが保証されます。
     */
    export const Link: FC<PropsWithChildren<NextLinkProps>> = ({
      href,
      children,
      ...props
    }) => {
      const { locale } = useLocale();
      const isExternalLink = checkIsExternalLink(href.toString());
    
      // リンクが内部リンクで有効なhrefが提供されている場合、ローカライズされたURLを取得します。
      const hrefI18n: NextLinkProps["href"] =
        href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;
    
      return (
        <NextLink href={hrefI18n} {...props}>
          {children}
        </NextLink>
      );
    };

    動作の仕組み

    • 外部リンクの検出
      ヘルパー関数checkIsExternalLinkは、URLが外部リンクかどうかを判定します。外部リンクは変更されません。なぜなら、それらはローカリゼーションを必要としないからです。

    • 現在のロケールの取得
      useLocaleフックは、現在のロケール(例:frはフランス語)を提供します。

    • URLのローカライズ
      内部リンク(つまり、外部リンクではないもの)については、getLocalizedUrlを使用してURLに現在のロケールを自動的にプレフィックスします。これにより、ユーザーがフランス語を使用している場合、/abouthrefとして渡すと、/fr/aboutに変換されます。

    • リンクの返却
      コンポーネントは、ローカライズされたURLを持つ<a>要素を返し、一貫性のあるロケールに基づいたナビゲーションを保証します。

    このLinkコンポーネントをアプリケーション全体で統合することで、一貫性があり、言語に配慮したユーザーエクスペリエンスを維持しながら、SEOと使いやすさを向上させることができます。

    TypeScriptを設定する

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

    alt text

    alt text

    自動生成された型を含めるようにTypeScript設定を確認してください。

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

    Git設定

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

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

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

    さらに進む

    さらに進むために、ビジュアルエディターを実装するか、CMSを使用してコンテンツを外部化することができます。

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

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