Next.js 国際化 (i18n) with next-intl and Intlayer

    next-intl と Intlayer は、Next.js アプリケーション向けに設計されたオープンソースの国際化 (i18n) フレームワークです。これらは、ソフトウェアプロジェクトにおける翻訳、ローカリゼーション、および言語切替の管理に広く使用されています。

    それぞれは、3 つの主要な概念を共有しています。

    1. コンテンツ宣言: アプリケーションの翻訳可能なコンテンツを定義するためのメソッド。

      • Intlayer では content declaration file と呼ばれ、構造化データをエクスポートする JSON、JS、または TS ファイルです。詳細は Intlayer documentation を参照してください。
      • next-intl では messages または locale messages と呼ばれ、通常は JSON ファイルにあります。詳細は next-intl documentation を参照してください。
    2. ユーティリティ: アプリケーション内でコンテンツ宣言を構築および解釈するためのツールで、Intlayer の useIntlayer() または useLocale()、および next-intl の useTranslations() などがあります。

    3. プラグインとミドルウェア: URL リダイレクション、バンドル最適化などの機能—例として、Intlayer の intlayerMiddleware や next-intl の createMiddleware があります。

    Intlayer と next-intl の主な違い

    Intlayer が Next.js 用の他の i18n ライブラリ(next-intl など)と比較される方法の詳細な分析については、next-i18next vs. next-intl vs. Intlayer blog post をチェックしてください。

    Intlayer で next-intl メッセージを生成する方法

    なぜ Intlayer を next-intl と一緒に使用するのか?

    Intlayer のコンテンツ宣言ファイルは、一般的により良い開発者体験を提供します。主に 2 つの利点により、柔軟性と保守性が向上しています。

    1. 柔軟な配置: Intlayer のコンテンツ宣言ファイルをアプリケーションのファイルツリーのどこにでも配置できます。これにより、使用されていないメッセージファイルやダングリングファイルを残すことなく、コンポーネントの名前を変更したり削除したりすることが容易になります。

      例となるファイル構造:

      bash
      .└── src    └── components        └── MyComponent            ├── index.content.ts # コンテンツ宣言ファイル            └── index.tsx
    2. 中央集約型の翻訳: Intlayer はすべての翻訳を単一のコンテンツ宣言に保存し、翻訳が失われることはありません。TypeScript プロジェクトでは、失われた翻訳が自動的に型エラーとしてフラグされ、開発者に即座にフィードバックを提供します。

    インストール

    Intlayer と next-intl を一緒に使用するには、両方のライブラリをインストールします:

    bash
    npm install intlayer next-intl

    Intlayer を設定して next-intl メッセージをエクスポートする

    注意: Intlayer から next-intl へメッセージをエクスポートすると、構造にわずかな違いが生じることがあります。統合を簡素化するために、可能であれば Intlayer のみまたは next-intl のみのフローを維持してください。Intlayer から next-intl メッセージを生成する必要がある場合は、以下の手順に従ってください。

    プロジェクトのルートに intlayer.config.ts ファイル(.mjs / .cjs)を作成または更新します:

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  internationalization: {    locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],    defaultLocale: Locales.ENGLISH,  },  content: {    dictionaryOutput: ["next-intl"], // next-intl 出力を使用する    nextIntlMessagesDir: "./intl/messages", // next-intl メッセージを保存する場所  },};export default config;

    コンテンツ宣言

    以下は、複数のフォーマットのコンテンツ宣言ファイルの例です。Intlayer はこれらを next-intl が消費できるメッセージファイルにコンパイルします。

    **/*.content.ts
    import { t, type DeclarationContent } from "intlayer";const content = {  key: "my-component",  content: {    helloWorld: t({      en: "Hello World",      fr: "Bonjour le monde",      es: "Hola Mundo",    }),  },} satisfies DeclarationContent;export default content;

    next-intl メッセージのビルド

    next-intl 用のメッセージファイルをビルドするには、次のコマンドを実行します:

    bash
    npx intlayer build

    これにより、./intl/messages ディレクトリにリソースが生成されます(intlayer.config.* で構成された通り)。期待される出力:

    bash
    .└── intl    └── messages       └── /ja           └── my-content.json       └── /fr           └── my-content.json       └── /es           └── my-content.json

    各ファイルには、すべての Intlayer コンテンツ宣言からのコンパイル済みのメッセージが含まれています。トップレベルのキーは通常、content.key フィールドに一致します。

    次.js アプリで next-intl を使用する

    詳細については、公式の next-intl usage docs を参照してください。

    1. ミドルウェアを作成する (オプション):
      自動的なロケール検出またはリダイレクションを管理したい場合、next-intl の createMiddleware を使用してください。

      middleware.ts
      import createMiddleware from "next-intl/middleware";import { NextResponse } from "next/server";export default createMiddleware({  locales: ["ja", "fr", "es"],  defaultLocale: "ja",});export const config = {  matcher: ["/((?!api|_next|.*\\..*).*)"],};
    2. メッセージを読み込むための layout.tsx または _app.tsx を作成する:
      App Router(Next.js 13+)を使用している場合は、レイアウトを作成します。

      app/[locale]/layout.tsx
      import { NextIntlClientProvider } from 'next-intl';import { notFound } from 'next/navigation';import React, { ReactNode } from 'react';export const dynamic = 'force-dynamic';export default async function RootLayout({  children,  params}: {  children: ReactNode;  params: { locale: string };}) {  let messages;  try {    messages = (await import(`../../intl/messages/${params.locale}.json`)).default;  } catch (error) {    notFound();  }  return (    <html lang={params.locale}>      <body>        <NextIntlClientProvider locale={params.locale} messages={messages}>          {children}        </NextIntlClientProvider>      </body>    </html>  );}

      Pages Router(Next.js 12 以下)を使用している場合は、_app.tsx でメッセージを読み込みます。

      pages/_app.tsx
      import type { AppProps } from 'next/app';import { NextIntlProvider } from 'next-intl';function MyApp({ Component, pageProps }: AppProps) {  return (    <NextIntlProvider locale={pageProps.locale} messages={pageProps.messages}>      <Component {...pageProps} />    </NextIntlProvider>  );}export default MyApp;
    3. サーバーサイドでメッセージを取得する (Pages Router の例):

      pages/index.tsx
      import { GetServerSideProps } from "next";import HomePage from "../components/HomePage";export default HomePage;export const getServerSideProps: GetServerSideProps = async ({ locale }) => {  const messages = (await import(`../intl/messages/${locale}.json`)).default;  return {    props: {      locale,      messages,    },  };};

    Next.js コンポーネントでのコンテンツの使用

    メッセージが next-intl に読み込まれると、useTranslations() フックを介してコンポーネント内で使用できます:

    src/components/MyComponent/index.tsx
    import type { FC } from "react";import { useTranslations } from 'next-intl';const MyComponent: FC = () => {  const t = useTranslations('my-component');  // 'my-component' は Intlayer 内のコンテンツキーに対応します  return (    <div>      <h1>{t('helloWorld')}</h1>    </div>  );};export default MyComponent;

    これで完了です! Intlayer コンテンツ宣言ファイルを更新または追加するたびに、intlayer build コマンドを再実行して next-intl JSON メッセージを再生成してください。next-intl は更新されたコンテンツを自動的に取得します。

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

    ブログへのGitHubリンク