# gt-next: General Translation Next.js SDK: 静态站点生成 URL: https://generaltranslation.com/zh/docs/next/guides/ssg.mdx --- title: 静态站点生成 description: 在构建阶段预渲染国际化页面,以实现最佳性能 --- ## 概述 静态站点生成 (SSG) 会在构建时预渲染页面,生成无需服务端处理即可直接提供的静态 HTML 文件。 与国际化结合使用时,SSG 会为每个区域设置生成预渲染版本。 具体设置因路由器而异: * **Pages Router** - 使用 Next.js 内置的区域设置路由和 `withGTStaticProps` * **App Router** - 使用 `createNextMiddleware`、`[locale]` 路由和 `generateStaticParams` **设置 SSG 时,请注意以下任一问题:** * [Next.js 版本兼容性](#nextjs-version-compatibility) * [页面未静态生成](#pages-not-generating-statically) * [生产环境 运行时 错误:“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 可为每个区域设置下的每个页面分别生成静态版本。请使用 Next.js 内置的区域设置路由,而不要使用 `createNextMiddleware`,这样 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`。例如,位于 `pages/about.tsx` 的页面会为英语生成 `/about`,为西班牙语生成 `/es/about`,为法语生成 `/fr/about`。 **保持区域设置一致:**Next.js `i18n` 配置中的 locales 和 default locale 必须与您的 `gt.config.json` 设置一致。 ### 第 2 步:封装 getStaticProps 使用 `withGTStaticProps` 封装页面的 `getStaticProps` 函数: ```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}

); } ``` 如果页面本身不需要传入属性,请使用无参数形式: ```tsx title="pages/index.tsx" import { withGTStaticProps } from 'gt-next'; export const getStaticProps = withGTStaticProps(); ``` `withGTStaticProps` 会将生成的 `locale` 及其翻译快照添加到页面属性中。默认区域设置会得到一个空快照,因为其源内容已包含在页面中。重定向、`notFound` 以及增量静态再生成选项都会保持不变并直接透传。 ### 第 3 步:配置 GTProvider 导航 将生成的区域设置和翻译传入 `_app.tsx` 中的 `GTProvider`。将区域设置切换与 Pages Router 关联起来,这样在选择某个区域设置时,就会加载该区域设置对应的静态属性: ```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 会加载所选区域设置下预先生成的页面属性,而 `GTProvider` 会接收对应的翻译快照。 现在,你可以像平常一样使用 `LocaleSelector`: ```tsx title="pages/about.tsx" import { LocaleSelector } from 'gt-next'; export default function AboutPage() { return ; } ``` **动态路由:** 像 `pages/products/[id].tsx` 这样的页面,仍需使用 `getStaticPaths` 为其动态参数生成路径。Next.js 会针对每个已配置的区域设置,对返回的每个路径各生成一次。 ## App Router 设置 ### 设置要求 要在 GT 中启用 SSG,您需要: 1. **使用中间件路由的 App Router** - 请参阅[中间件指南](/docs/next/guides/middleware) 2. **自定义 `getLocale` 函数** - 用于在静态渲染期间检测区域设置 3. **禁用 `getRegion`** - 静态渲染期间不支持区域检测 4. **`generateStaticParams` 函数** - 用于为每个区域设置生成静态参数 5. **位于 `/[locale]` 目录下的布局文件** - 所有布局文件 (通常是 `layout.tsx` 和 `page.tsx`) 都应位于 `/[locale]` 目录下 ### 第 1 步:配置中间件 为动态请求配置中间件 (请参阅[中间件指南](/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 确保你已为 locales 正确配置 [`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 路径参数。你需要升级到 Next.js 15.1 或更高版本,才能在 gt-next 中使用 SSG。 ### 页面未进行静态生成 如果你的页面未进行静态生成,请确保: * 你的 `getLocale` 和 `getRegion` 函数已正确配置 ### 生产环境运行时错误:"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)已完成。 ### "Export locale doesn't exist in target module" ```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 | } ``` 此错误通常发生在项目中存在 `layout.tsx`、`page.tsx` 等文件,但它们不在 `/[locale]` 目录内时。 要修复此问题,请确保所有路由段文件 (通常是 `layout.tsx` 和 `page.tsx`) 都位于 `/[locale]` 目录下。 在 SSG 期间,解析用户区域设置的唯一方式是通过 URL 路径。 因此,如果我们尝试执行位于 `/[locale]` 目录之外的 `layout.tsx` 文件,就会报错,因为它无法访问这个根参数。 这里不太直观的一点在于:页面渲染时,所有包裹该页面的 `layout.tsx` 文件也都会一起渲染。 所以,你可能为一个明确位于 `/[locale]` 目录内的 `page.tsx` 文件启用了 SSG,但实际导致报错的却是别处的某个 `layout.tsx` 文件。 我们强烈不建议这样做。 使用 SSG 时,所有路由段文件 (`not-found.tsx` 除外) 都应放在 `/[locale]` 目录内。 不过,如果你确实必须这样做,就需要两个独立的根布局文件:一个位于 `/[locale]` 目录内,另一个位于其外部。 这样,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 Handler,而 `next/root-params` 不支持在 Route Handler 中使用。 这意味着 `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 区域设置路由所需的[中间件指南](/docs/next/guides/middleware) * 查看有关从旧版 SSG 模式迁移的[发布说明](/devlog/gt-next_v6_10_0) * 在 monorepo 中[这里](https://github.com/generaltranslation/gt/tree/main/examples/next-ssg)查看示例应用