Intlayerの今後のリリースに関する通知を受け取る
    作成:2024-12-07最終更新:2025-06-29

    Intlayer と Next.js の Page Router を使った国際化(i18n)入門

    Intlayer とは?

    Intlayer は、最新のウェブアプリケーションにおける多言語対応を簡素化するために設計された革新的なオープンソースの国際化(i18n)ライブラリです。Intlayer は、従来の Page Router を含む最新の Next.js フレームワークとシームレスに統合されます。

    Intlayer を使うことで、以下が可能になります:

    • コンポーネントレベルで宣言的な辞書を使い、翻訳を簡単に管理できます。
    • メタデータ、ルート、コンテンツを動的にローカライズできます。
    • 自動生成された型により TypeScript サポートを保証し、オートコンプリートやエラー検出を向上させます。
    • 動的なロケール検出や切り替えなどの高度な機能を活用できます。

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


    Page Router を使用した Next.js アプリケーションでの Intlayer セットアップ手順

    ステップ 1: 依存関係のインストール

    お好みのパッケージマネージャーを使って必要なパッケージをインストールします:

    bash
    npm install intlayer next-intlayer
    • 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.ENGLISH,      Locales.FRENCH,      Locales.SPANISH,      // 他のロケールをここに追加してください    ],    defaultLocale: Locales.ENGLISH,  },};export default config;

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

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

    Next.jsの設定を修正してIntlayerを組み込みます:

    next.config.mjs
    import { withIntlayer } from "next-intlayer/server";/** @type {import('next').NextConfig} */const nextConfig = {  // 既存のNext.js設定};export default withIntlayer(nextConfig);

    withIntlayer() は Next.js と Intlayer を統合するためのプラグインです。コンテンツ宣言ファイルのビルドを保証し、開発モードでそれらを監視します。また、WebpackTurbopack 環境内で 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).*)",};

    matcher パラメータをアプリケーションのルートに合わせて調整してください。詳細については、Next.js の matcher 設定に関するドキュメントを参照してください。

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

    ユーザーのロケールに基づいてローカライズされたコンテンツを提供するために動的ルーティングを実装します。

    1. ロケール別ページの作成:

      メインページファイルの名前を [locale] の動的セグメントを含むように変更します。

      bash
      mv src/pages/index.tsx src/pages/[locale]/index.tsx
    2. ローカライズ対応のために _app.tsx を更新:

      `_app.tsx` を修正して Intlayer のプロバイダーを含めます。 ```tsx fileName="src/pages/_app.tsx" codeFormat="typescript" import type { FC } from "react"; import type { AppProps } from "next/app"; import { IntlayerClientProvider } from "next-intlayer"; const App = FC<AppProps>({ Component, pageProps }) => { const { locale } = pageProps; return ( <IntlayerClientProvider locale={locale}> <Component {...pageProps} /> </IntlayerClientProvider> ); } export default MyApp; ``` ```jsx fileName="src/pages/_app.mjx" codeFormat="esm" import { IntlayerClientProvider } from "next-intlayer"; const App = ({ Component, pageProps }) => ( <IntlayerClientProvider locale={locale}> <Component {...pageProps} /> </IntlayerClientProvider> ); export default App; ``` ```jsx fileName="src/pages/_app.csx" codeFormat="commonjs"

      const { IntlayerClientProvider } = require("next-intlayer");

    const App = ({ Component, pageProps }) => ( );

    module.exports = App;

    3. **`getStaticPaths` と `getStaticProps` の設定:** `[locale]/index.tsx` ファイル内で、異なるロケールを処理するためのパスとプロパティを定義します。 ```tsx fileName="src/pages/[locale]/index.tsx" codeFormat="typescript" import type { FC } from "react"; import type { GetStaticPaths, GetStaticProps } from "next"; import { type Locales, getConfiguration } from "intlayer"; const HomePage: FC = () => <div>{/* ここにコンテンツを記述 */}</div>; export const getStaticPaths: GetStaticPaths = () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; export const getStaticProps: GetStaticProps = ({ params }) => { const locale = params?.locale as string; return { props: { locale, }, }; }; export default HomePage; ``` ```jsx fileName="src/pages/[locale]/index.mjx" codeFormat="esm" import { getConfiguration } from "intlayer"; import { ComponentExample } from "@components/ComponentExample"; const HomePage = () => <div>{/* ここにコンテンツを記述 */}</div>; export const getStaticPaths = () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; export const getStaticProps = ({ params }) => { const locale = params?.locale; return { props: { locale, }, }; }; ``` ```jsx fileName="src/pages/[locale]/index.csx" codeFormat="commonjs" const { getConfiguration } = require("intlayer"); const { ComponentExample } = require("@components/ComponentExample"); const HomePage = () => <div>{/* ここにコンテンツを記述 */}</div>; const getStaticPaths = async () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; const getStaticProps = async ({ params }) => { const locale = params?.locale; return { props: { locale, }, }; }; module.exports = { getStaticProps, getStaticPaths, default: HomePage, }; ``` > `getStaticPaths` と `getStaticProps` は、Next.js のページルーターにおいて、すべてのロケールに対して必要なページを事前にビルドすることを保証します。このアプローチにより、実行時の計算が削減され、ユーザー体験が向上します。詳細については、Next.js のドキュメントの [`getStaticPaths`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-paths) および [`getStaticProps`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-props) を参照してください。 ### ステップ6: コンテンツを宣言する 翻訳を格納するためのコンテンツ宣言を作成および管理します。 ```tsx fileName="src/pages/[locale]/home.content.ts" contentDeclarationFormat="typescript" import { t, type Dictionary } from "intlayer"; const homeContent = { key: "home", content: { title: t({ ja: "私のウェブサイトへようこそ", en: "Welcome to My Website", fr: "Bienvenue sur mon site Web", es: "Bienvenido a mi sitio web", }), description: t({ ja: "このページを編集して始めましょう。", en: "Get started by editing this page.", fr: "Commencez par éditer cette page.", es: "Comience por editar esta página.", }), }, } satisfies Dictionary; export default homeContent;

    詳細なコンテンツ宣言については、コンテンツ宣言ガイドを参照してください。

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

    アプリケーション全体でコンテンツ辞書にアクセスし、翻訳されたコンテンツを表示します。

    src/pages/[locale]/index.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer";import { ComponentExample } from "@components/ComponentExample";const HomePage: FC = () => {  const content = useIntlayer("home");  return (    <div>      <h1>{content.title}</h1>      <p>{content.description}</p>      <ComponentExample />      {/* 追加のコンポーネント */}    </div>  );};// ... getStaticPaths と getStaticProps を含むコードの残りexport default HomePage;

    string属性(例:alttitlehrefaria-label)で翻訳を使用する場合は、関数の値を次のように呼び出してください:

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

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

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

    ページのタイトルなどのメタデータを国際化したい場合は、Next.js の Page Router が提供する getStaticProps 関数を使用できます。その中で、getIntlayer 関数からコンテンツを取得し、メタデータを翻訳できます。

    src/pages/[locale]/metadata.content.ts
    import { type Dictionary, t } from "intlayer";import { type Metadata } from "next";const metadataContent = {  key: "page-metadata",  content: {    title: t({      en: "Create Next App",      fr: "Créer une application Next.js",      es: "Crear una aplicación Next.js",    }),    description: t({      en: "Generated by create next app",      fr: "Généré par create next app",      es: "Generado por create next app",    }),  },} satisfies Dictionary<Metadata>;export default metadataContent;
    src/pages/[locale]/index.tsx
    import { GetStaticPaths, GetStaticProps } from "next";import { getIntlayer, getMultilingualUrls } from "intlayer";import { useIntlayer } from "next-intlayer";import Head from "next/head";import type { FC } from "react";interface HomePageProps {  locale: string;  metadata: {    title: string;    description: string;  };  multilingualUrls: Record<string, string>;}const HomePage: FC<HomePageProps> = ({  metadata,  multilingualUrls,  locale,}) => {  const content = useIntlayer("page");  return (    <div>      <Head>        <title>{metadata.title}</title>        <meta name="description" content={metadata.description} />        {/* SEOのためのhreflangタグを生成 */}        {Object.entries(multilingualUrls).map(([lang, url]) => (          <link key={lang} rel="alternate" hrefLang={lang} href={url} />        ))}        <link rel="canonical" href={multilingualUrls[locale]} />      </Head>      {/* ページコンテンツ */}      <main>{/* ここにページのコンテンツを記述 */}</main>    </div>  );};export const getStaticProps: GetStaticProps<HomePageProps> = async ({  params,}) => {  const locale = params?.locale as string;  const metadata = getIntlayer("page-metadata", locale);  /**   * 各ロケールのすべてのURLを含むオブジェクトを生成します。   *   * 例:   * ```ts   *  getMultilingualUrls('/about');   *   *  // 戻り値   *  // {   *  //   en: '/about',   *  //   fr: '/fr/about',   *  //   es: '/es/about',   *  // }   * ```   */  const multilingualUrls = getMultilingualUrls("/");  return {    props: {      locale,      metadata,      multilingualUrls,    },  };};export default HomePage;// ... getStaticPathsを含む残りのコード

    next-intlayer からインポートされる getIntlayer 関数は、コンテンツを IntlayerNode でラップして返すため、ビジュアルエディタとの統合が可能です。対照的に、intlayer からインポートされる getIntlayer 関数は、追加のプロパティなしでコンテンツを直接返します。

    代わりに、getTranslation 関数を使ってメタデータを宣言することもできます。ただし、メタデータの翻訳を自動化し、コンテンツを外部化するためには、コンテンツ宣言ファイルを使用することが推奨されます。

    src/pages/[locale]/index.tsx
    import { GetStaticPaths, GetStaticProps } from "next";import {  type IConfigLocales,  getTranslation,  getMultilingualUrls,} from "intlayer";import { useIntlayer } from "next-intlayer";import Head from "next/head";import type { FC } from "react";interface HomePageProps {  locale: string;  metadata: {    title: string;    description: string;  };  multilingualUrls: Record<string, string>;}const HomePage: FC<HomePageProps> = ({ metadata, multilingualUrls, locale }) => {  const content = useIntlayer("page");  return (    <div>      <Head>        <title>{metadata.title}</title>        <meta name="description" content={metadata.description} />        {/* SEOのためのhreflangタグを生成 */}        {Object.entries(multilingualUrls).map(([lang, url]) => (          <link            key={lang}            rel="alternate"            hrefLang={lang}            href={url}          />        ))}        <link rel="canonical" href={multilingualUrls[locale]} />      </Head>      {/* ページコンテンツ */}      <main>        {/* ここにページコンテンツを記述 */}      </main>    </div>  );};export const getStaticProps: GetStaticProps<HomePageProps> = async ({  params}) => {  const locale = params?.locale as string;  const t = <T>(content: IConfigLocales<T>) => getTranslation(content, locale);  const metadata = {    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",    }),  };  const multilingualUrls = getMultilingualUrls("/");  return {    props: {      locale,      metadata,      multilingualUrls,    },  };};export default HomePage;// ... getStaticPathsを含む残りのコード

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

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

    Next.js でコンテンツの言語を変更するには、推奨される方法として Link コンポーネントを使用して、ユーザーを適切なローカライズされたページにリダイレクトする方法があります。Link コンポーネントはページのプリフェッチを可能にし、完全なページリロードを回避するのに役立ちます。

    src/components/LanguageSwitcher.tsx
    import {  Locales,  getHTMLTextDir,  getLocaleName,  getLocalizedUrl,} from "intlayer";import { useLocalePageRouter } from "next-intlayer";import { type FC } from "react";import Link from "next/link";const LocaleSwitcher: FC = () => {  const { locale, pathWithoutLocale, availableLocales } = useLocalePageRouter();  const { setLocaleCookie } = useLocaleCookie();  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={() => setLocaleCookie(localeItem)}          >            <span>              {/* ロケール - 例: FR */}              {localeItem}            </span>            <span>              {/* そのロケールでの言語名 - 例: Français */}              {getLocaleName(localeItem, locale)}            </span>            <span dir={getHTMLTextDir(localeItem)} lang={localeItem}>              {/* 現在のロケールでの言語 - 例: Locales.SPANISHに設定された現在のロケールでのFrancés */}              {getLocaleName(localeItem)}            </span>            <span dir="ltr" lang={Locales.ENGLISH}>              {/* 英語での言語 - 例: French */}              {getLocaleName(localeItem, Locales.ENGLISH)}            </span>          </Link>        ))}      </div>    </div>  );};

    もう一つの方法として、useLocale フックが提供する setLocale 関数を使用する方法があります。この関数はページのプリフェッチを許可せず、ページをリロードします。

    この場合、router.push を使ったリダイレクトなしで、サーバーサイドのコードだけがコンテンツのロケールを変更します。

    src/components/LocaleSwitcher.tsx
    "use client";import { useRouter } from "next/navigation";import { useLocale } from "next-intlayer";import { getLocalizedUrl } from "intlayer";// ... 残りのコードconst router = useRouter();const { setLocale } = useLocale({  onLocaleChange: (locale) => {    router.push(getLocalizedUrl(pathWithoutLocale, locale));  },});return (  <button onClick={() => setLocale(Locales.FRENCH)}>フランス語に変更</button>);

    useLocalePageRouter APIはuseLocaleと同じです。useLocaleフックの詳細については、ドキュメントを参照してください。

    ドキュメント参照:

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

    アプリケーションのナビゲーションが現在のロケールを尊重するようにするために、カスタムの 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 { forwardRef, PropsWithChildren, type ForwardedRef } from "react";/** * 指定されたURLが外部リンクかどうかをチェックするユーティリティ関数。 * URLが http:// または https:// で始まる場合、外部リンクと見なされます。 */export const checkIsExternalLink = (href?: string): boolean =>  /^https?:\/\//.test(href ?? "");/** * 現在のロケールに基づいてhref属性を適応させるカスタムLinkコンポーネント。 * 内部リンクの場合、`getLocalizedUrl`を使用してURLにロケールをプレフィックスします(例: /fr/about)。 * これにより、ナビゲーションが同じロケールコンテキスト内に留まることを保証します。 */export const Link = forwardRef<  HTMLAnchorElement,  PropsWithChildren<NextLinkProps>>(({ href, children, ...props }, ref: ForwardedRef<HTMLAnchorElement>) => {  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} ref={ref} {...props}>      {children}    </NextLink>  );});Link.displayName = "Link";

    動作の仕組み

    • 外部リンクの検出:
      ヘルパー関数 checkIsExternalLink はURLが外部リンクかどうかを判定します。外部リンクはローカライズの必要がないため、そのままにされます。

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

    • URLのローカライズ:
      内部リンク(つまり外部リンクでない場合)には、getLocalizedUrl が使用され、URLの先頭に現在のロケールが自動的に付加されます。つまり、ユーザーがフランス語環境にいる場合、href/about を渡すと /fr/about に変換されます。

    • リンクの返却:
      コンポーネントはローカライズされたURLを持つ <a> 要素を返し、ナビゲーションがロケールに沿って一貫するようにします。

    この Link コンポーネントをアプリケーション全体に統合することで、一貫性のある言語対応のユーザー体験を維持しつつ、SEOや使いやすさの向上も期待できます。

    (オプション)ステップ11: バンドルサイズの最適化

    next-intlayerを使用すると、辞書はデフォルトですべてのページのバンドルに含まれます。バンドルサイズを最適化するために、Intlayerはマクロを使用してuseIntlayerの呼び出しをインテリジェントに置き換えるオプションのSWCプラグインを提供しています。これにより、辞書は実際に使用されているページのバンドルにのみ含まれるようになります。

    この最適化を有効にするには、@intlayer/swcパッケージをインストールしてください。インストール後、next-intlayerは自動的にプラグインを検出して使用します。

    bash
    npm install @intlayer/swc --save-dev

    注意: この最適化はNext.js 13以降でのみ利用可能です。

    注意: このパッケージは、Next.jsでのSWCプラグインがまだ実験的なため、デフォルトではインストールされていません。将来的に変更される可能性があります。

    TypeScriptの設定

    Intlayerはモジュール拡張を使用して、TypeScriptの利点を活かし、コードベースをより強固にします。

    alt text

    alt text

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

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

    Gitの設定

    リポジトリをクリーンに保ち、生成されたファイルのコミットを避けるために、Intlayerによって作成されたファイルを無視することを推奨します。

    以下の行を .gitignore ファイルに追加してください:

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

    VS Code 拡張機能

    Intlayerでの開発体験を向上させるために、公式の Intlayer VS Code 拡張機能 をインストールできます。

    VS Codeマーケットプレイスからインストール

    この拡張機能は以下を提供します:

    • 翻訳キーの オートコンプリート
    • 欠落している翻訳の リアルタイムエラー検出
    • 翻訳された内容の インラインプレビュー
    • 翻訳を簡単に作成・更新できる クイックアクション

    拡張機能の使用方法の詳細については、Intlayer VS Code 拡張機能のドキュメントを参照してください。

    追加リソース

    このガイドに従うことで、Next.js アプリケーションの Page Router を使用して Intlayer を効果的に統合し、ウェブプロジェクトに対して堅牢でスケーラブルな国際化サポートを実現できます。

    さらに進む

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

    ドキュメント履歴

    • 5.5.10 - 2025-06-29: 履歴の初期化
    Intlayerの今後のリリースに関する通知を受け取る