# gt-next: General Translation Next.js SDK: 静的サイト生成 URL: https://generaltranslation.com/ja/docs/next/guides/ssg.mdx --- title: 静的サイト生成 description: 最適なパフォーマンスを実現するため、ビルド時に多言語対応ページを事前レンダリングします --- ## 概要 静的サイト生成 (SSG) は、ビルド時にページを事前レンダリングし、サーバー側での処理なしに直接配信できる静的なHTMLファイルを生成します。 国際化と組み合わせると、SSG は各ロケール向けの事前レンダリング済みページを生成します。 設定方法は Router によって異なります。 * **Pages Router** - Next.js 組み込みの ロケールルーティング と `withGTStaticProps` を使用します * **App Router** - `createNextMiddleware`、`[locale]` ルート、`generateStaticParams` を使用します **SSG を設定する際は、次の問題に注意してください。** * [Next.js のバージョン互換性](#nextjs-version-compatibility) * [ページが静的に生成されない](#pages-not-generating-statically) * [本番環境での runtime エラー: "DYNAMIC_SERVER_USAGE"](#production-runtime-error-dynamic_server_usage) * ["Export locale doesn't exist in target module"](#export-locale-doesnt-exist-in-target-module) * [メタデータ画像ルート (OG 画像) が `getLocale()` で失敗する](#metadata-image-routes-og-images-fail-with-getlocale) *** ## Pages Router のセットアップ Pages Router では、ロケールごとに各ページの静的バージョンを個別に生成できます。`createNextMiddleware` ではなく、Next.js に組み込まれているロケールルーティングを使用してください。そうすることで、Next.js が生成されたロケールを `getStaticProps` に渡せます。 ### ステップ 1: ロケールルーティングを設定する デフォルトロケールを含む、対応するすべてのロケールを Next.js 設定の `i18n` に追加します。 ```ts title="next.config.ts" import type { NextConfig } from 'next'; import { withGTConfig } from 'gt-next/config'; const nextConfig: NextConfig = { i18n: { locales: ['en', 'es', 'fr', 'ja'], defaultLocale: 'en', }, }; export default withGTConfig(nextConfig); ``` Next.js は現在、設定されている各ロケールごとに `getStaticProps` を 1 回ずつ呼び出します。たとえば、`pages/about.tsx` のページでは、英語版では `/about`、スペイン語版では `/es/about`、フランス語版では `/fr/about` が生成されます。 **ロケールを揃えてください:** Next.js の `i18n` 設定内の locales と デフォルトロケール は、`gt.config.json` の設定と一致している必要があります。 ### ステップ2: getStaticProps をラップする ページの `getStaticProps` 関数を `withGTStaticProps` でラップします: ```tsx title="pages/about.tsx" import type { GetStaticProps } from 'next'; import { T, withGTStaticProps } from 'gt-next'; type AboutPageProps = { generatedAt: string; }; export const getStaticProps: GetStaticProps = withGTStaticProps(async () => ({ props: { generatedAt: new Date().toISOString(), }, })); export default function AboutPage({ generatedAt }: AboutPageProps) { return (

About us

Generated at: {generatedAt}

); } ``` ページに独自のpropsが不要な場合は、引数なしの形式を使用します: ```tsx title="pages/index.tsx" import { withGTStaticProps } from 'gt-next'; export const getStaticProps = withGTStaticProps(); ``` `withGTStaticProps` は、生成された `locale` とその翻訳スナップショットをページの props に追加します。デフォルトロケールは元のコンテンツがすでに含まれているため、空のスナップショットを受け取ります。リダイレクト、`notFound`、およびインクリメンタル静的再生成のオプションは、そのまま変更されずに引き継がれます。 ### ステップ 3: GTProvider のナビゲーションを設定する 生成されたロケールと翻訳を `_app.tsx` の `GTProvider` に渡します。ロケールの変更を Pages Router と連携させ、ロケールを選択するとそのロケールの static props が読み込まれるようにします: ```tsx title="pages/_app.tsx" import type { AppProps } from 'next/app'; import { useRouter } from 'next/router'; import { GTProvider, type WithGTStaticProps } from 'gt-next'; export default function App({ Component, pageProps, }: AppProps) { const router = useRouter(); const { locale, translations, ...restPageProps } = pageProps; return ( { void router.push( { pathname: router.pathname, query: router.query }, router.asPath, { locale: nextLocale } ); }} > ); } ``` このコールバックは、ブラウザー全体を再読み込みするのではなく、Pages Router によるナビゲーションを実行します。Next.js は選択したロケールの事前生成済みページ props を読み込み、`GTProvider` は対応する翻訳スナップショットを受け取ります。 これで `LocaleSelector` を通常どおり使用できます: ```tsx title="pages/about.tsx" import { LocaleSelector } from 'gt-next'; export default function AboutPage() { return ; } ``` **動的ルート:** `pages/products/[id].tsx` のようなページでは、動的パラメータに対して引き続き `getStaticPaths` が必要です。Next.js は、設定されている各ロケールについて、返された各パスを 1 回ずつ生成します。 ## App Router のセットアップ ### セットアップ要件 GT で SSG を有効にするには、以下が必要です。 1. **ミドルウェアルーティングを使用する App Router** - [middleware guide](/docs/next/guides/middleware)を参照してください 2. **カスタム `getLocale` 関数** - 静的レンダリング時のロケール検出に使用します 3. **`getRegion` の無効化** - 静的レンダリングではリージョン検出はサポートされていません 4. **`generateStaticParams` 関数** - 各ロケールの静的パラメータを生成するために使用します 5. **`/[locale]` ディレクトリ内のレイアウトファイル** - すべてのレイアウトファイル (通常は `layout.tsx` と `page.tsx`) を `/[locale]` ディレクトリ配下に配置する必要があります ### ステップ 1: ミドルウェアを設定する 動的リクエスト向けにミドルウェアを設定します ([middleware guide](/docs/next/guides/middleware)を参照) : ```ts // proxy.ts (Next.js 16+) または middleware.ts (Next.js 15 以下) import { createNextMiddleware } from 'gt-next/middleware'; export default createNextMiddleware(); export const config = { matcher: ['/((?!api|static|.*\\..*|_next).*)'], }; ``` ### ステップ2: ロケールとリージョンの検出を定義する 静的レンダリング時にロケールとリージョンを検出するための `getLocale` 関数と `getRegion` 関数を作成します。 #### Next.js 15.5+ ```ts // getLocale.ts import { locale } from 'next/root-params'; export default async function getLocale() { return await locale(); } ``` #### Next.js 15.1-15.4 ```ts // getLocale.ts import { unstable_rootParams } from 'next/server'; export default async function getLocale() { return (await unstable_rootParams())?.locale; } ``` ### ステップ 3: getRegion を無効化する 静的レンダリングではリージョンの検出がサポートされていないため、`getRegion` 関数を固定のリージョンを返すように上書きする必要があります。 ```ts // getRegion.ts export default async function getRegion() { return undefined; } ``` ### ステップ 4: generateStaticParams を設定する 各ロケールに対して [`generateStaticParams`](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) が設定されていることを確認してください。 ```tsx title="page.tsx" import { getLocales } from 'gt-next/server'; export async function generateStaticParams() { return getLocales().map((locale) => ({ locale })); } export default async function Page() { ... } ``` ### ステップ 5: レイアウトファイルを `/[locale]` ディレクトリ配下に移動する すべてのファイルは、URL 内の `/[locale]` パスを通じてユーザーのロケールにアクセスできる必要があります。 そのため、これらのファイルは `/[locale]` ディレクトリ配下に配置する必要があります。 ルートレイアウトファイルは `/[locale]/layout.tsx` に移動してください。 *** ## 追加設定 ルートディレクトリに `getLocale.ts` と `getRegion.ts` を置きたくない場合は、`next.config.js` ファイルで別のディレクトリを指定できます。 ```js // next.config.js export default withGTConfig(nextConfig, { getLocalePath: './src/i18n/getLocale.ts', getRegionPath: './src/i18n/getRegion.ts', }); ``` ## App Router のよくある問題 [#common-issues] ### Next.js のバージョン互換性 Next.js 15.1 より前のバージョンでは、静的生成時に URL のパスパラメータへアクセスすることはできません。gt-next で SSG を使うには、Next.js 15.1 以降にアップグレードする必要があります。 ### ページが静的生成されない ページが静的に生成されない場合は、次の点を確認してください。 * `getLocale` と `getRegion` 関数が正しく設定されていること ### 本番環境での runtime エラー: "DYNAMIC_SERVER_USAGE" ```text ⨯ [Error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.] { digest: 'DYNAMIC_SERVER_USAGE' } ``` このエラーは、`getLocale` または `getRegion` のファイルが存在しないか、正しく設定されていない場合に発生します。 [ステップ 2](#step-2-define-locale-and-region-detection) と [ステップ 3](#step-3-disable-getregion) が完了していることをもう一度確認してください。 ### "対象モジュールにエクスポートされているロケールがありません" ```text ./getLocale.ts:2:1 Export locale doesn't exist in target module 1 | // getLocale.ts > 2 | import { locale } from 'next/root-params'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 3 | export default async function getLocale() { 4 | return await locale(); 5 | } ``` このエラーは通常、`/[locale]` ディレクトリ内にない `layout.tsx`、`page.tsx` などのファイルがある場合に発生します。 この問題を解決するには、すべてのルートセグメントファイル (通常は `layout.tsx` と `page.tsx`) が `/[locale]` ディレクトリ配下にあることを確認してください。 SSG では、ユーザーのロケールを特定する唯一の方法は URL パスです。 そのため、`/[locale]` ディレクトリの外にある `layout.tsx` ファイルを実行しようとすると、このルートパラメータにアクセスできないためエラーになります。 わかりにくいのは、ページがレンダリングされると、それをラップしているすべての `layout.tsx` ファイルも一緒にレンダリングされる点です。 つまり、`/[locale]` ディレクトリ内にある `page.tsx` ファイルに SSG を追加していても、実際には別の場所にある `layout.tsx` ファイルがエラーの原因になっている可能性があります。 この構成は強く非推奨です。 SSG を使用する場合、すべてのルートセグメントファイル (`not-found.tsx` を除く) は `/[locale]` ディレクトリ内に配置する必要があります。 それでも必要な場合は、`/[locale]` ディレクトリ内と外に、それぞれ別個のルートレイアウトファイルを 1 つずつ用意する必要があります。 こうすることで、SSG は `/[locale]` ディレクトリ内のレイアウトだけを実行します。 また、`/[locale]` ディレクトリの外にあるセグメントへルーティングするために、ローカライズ用ミドルウェアをスキップするようミドルウェアを変更する必要がある場合もあります。 ### メタデータ画像ルート (OG画像) は `getLocale()` で失敗する ```text Error: Route /[locale]/.../opengraph-image used import('next/root-params').locale() inside a Route Handler. ``` Next.js のメタデータ画像ルート (`opengraph-image.tsx`、`twitter-image.tsx`) は内部的に Route Handlers として扱われ、Route Handlers では `next/root-params` はサポートされていません。 そのため、`getLocale()`、`getMessages()`、`getGT()` はこれらのコンテキストでは動作しません。 **対処法:** handler の先頭で `registerLocale()` を使ってロケールを明示的に設定します。 ```tsx title="app/[locale]/opengraph-image.tsx" import { ImageResponse } from 'next/og'; import { registerLocale, getMessages, msg } from 'gt-next/server'; const HEADING = msg('Welcome to our site'); export default async function OGImage({ params, }: { params: Promise<{ locale: string }>; }) { const { locale } = await params; registerLocale(locale); const m = await getMessages(); return new ImageResponse( (
{m(HEADING)}
), { width: 1200, height: 630 } ); } ``` これは `twitter-image.tsx` やその他のメタデータ画像ルートにも当てはまります。 *** ## 関連資料 **実際の動作を見る:** 動作するデモは、[静的サイト生成 のサンプルアプリ](https://github.com/gt-examples/static-site-generation) をご覧ください — [ライブプレビュー](https://static-site-generation.generaltranslation.dev)。 * [Pages Router のロケールルーティング](/docs/next/guides/middleware#pages-router-locale-routing) を設定してください * App Router のロケールルーティングに必要な [middleware guide](/docs/next/guides/middleware) を確認してください * レガシーな SSG パターンから移行する場合は、[リリースノート](/devlog/gt-next_v6_10_0) を確認してください * monorepo 内のサンプルアプリの例は [こちら](https://github.com/generaltranslation/gt/tree/main/examples/next-ssg) を参照してください